@ecsia/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -0
  3. package/dist/bitmask/bitmask.d.ts +21 -0
  4. package/dist/bitmask/bitmask.d.ts.map +1 -0
  5. package/dist/bitmask/bitmask.js +103 -0
  6. package/dist/bitmask/bitmask.js.map +1 -0
  7. package/dist/bitmask/index.d.ts +3 -0
  8. package/dist/bitmask/index.d.ts.map +1 -0
  9. package/dist/bitmask/index.js +2 -0
  10. package/dist/bitmask/index.js.map +1 -0
  11. package/dist/component/accessor.d.ts +40 -0
  12. package/dist/component/accessor.d.ts.map +1 -0
  13. package/dist/component/accessor.js +220 -0
  14. package/dist/component/accessor.js.map +1 -0
  15. package/dist/component/column-set.d.ts +20 -0
  16. package/dist/component/column-set.d.ts.map +1 -0
  17. package/dist/component/column-set.js +60 -0
  18. package/dist/component/column-set.js.map +1 -0
  19. package/dist/component/define.d.ts +23 -0
  20. package/dist/component/define.d.ts.map +1 -0
  21. package/dist/component/define.js +155 -0
  22. package/dist/component/define.js.map +1 -0
  23. package/dist/component/descriptors.d.ts +3 -0
  24. package/dist/component/descriptors.d.ts.map +1 -0
  25. package/dist/component/descriptors.js +147 -0
  26. package/dist/component/descriptors.js.map +1 -0
  27. package/dist/component/index.d.ts +10 -0
  28. package/dist/component/index.d.ts.map +1 -0
  29. package/dist/component/index.js +6 -0
  30. package/dist/component/index.js.map +1 -0
  31. package/dist/component/sidecar.d.ts +58 -0
  32. package/dist/component/sidecar.d.ts.map +1 -0
  33. package/dist/component/sidecar.js +136 -0
  34. package/dist/component/sidecar.js.map +1 -0
  35. package/dist/config.d.ts +55 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +70 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/entity/codec.d.ts +45 -0
  40. package/dist/entity/codec.d.ts.map +1 -0
  41. package/dist/entity/codec.js +53 -0
  42. package/dist/entity/codec.js.map +1 -0
  43. package/dist/entity/index-allocator.d.ts +46 -0
  44. package/dist/entity/index-allocator.d.ts.map +1 -0
  45. package/dist/entity/index-allocator.js +121 -0
  46. package/dist/entity/index-allocator.js.map +1 -0
  47. package/dist/entity/index.d.ts +13 -0
  48. package/dist/entity/index.d.ts.map +1 -0
  49. package/dist/entity/index.js +7 -0
  50. package/dist/entity/index.js.map +1 -0
  51. package/dist/entity/record.d.ts +28 -0
  52. package/dist/entity/record.d.ts.map +1 -0
  53. package/dist/entity/record.js +42 -0
  54. package/dist/entity/record.js.map +1 -0
  55. package/dist/entity/ref.d.ts +70 -0
  56. package/dist/entity/ref.d.ts.map +1 -0
  57. package/dist/entity/ref.js +104 -0
  58. package/dist/entity/ref.js.map +1 -0
  59. package/dist/entity/reservation.d.ts +12 -0
  60. package/dist/entity/reservation.d.ts.map +1 -0
  61. package/dist/entity/reservation.js +28 -0
  62. package/dist/entity/reservation.js.map +1 -0
  63. package/dist/entity/store.d.ts +60 -0
  64. package/dist/entity/store.d.ts.map +1 -0
  65. package/dist/entity/store.js +193 -0
  66. package/dist/entity/store.js.map +1 -0
  67. package/dist/env.d.ts +2 -0
  68. package/dist/env.d.ts.map +1 -0
  69. package/dist/env.js +12 -0
  70. package/dist/env.js.map +1 -0
  71. package/dist/ids.d.ts +9 -0
  72. package/dist/ids.d.ts.map +1 -0
  73. package/dist/ids.js +8 -0
  74. package/dist/ids.js.map +1 -0
  75. package/dist/index.d.ts +29 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +33 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/inspect-surface.d.ts +27 -0
  80. package/dist/inspect-surface.d.ts.map +1 -0
  81. package/dist/inspect-surface.js +14 -0
  82. package/dist/inspect-surface.js.map +1 -0
  83. package/dist/internal.d.ts +19 -0
  84. package/dist/internal.d.ts.map +1 -0
  85. package/dist/internal.js +19 -0
  86. package/dist/internal.js.map +1 -0
  87. package/dist/memory/allocU32.d.ts +25 -0
  88. package/dist/memory/allocU32.d.ts.map +1 -0
  89. package/dist/memory/allocU32.js +95 -0
  90. package/dist/memory/allocU32.js.map +1 -0
  91. package/dist/memory/buffers.d.ts +94 -0
  92. package/dist/memory/buffers.d.ts.map +1 -0
  93. package/dist/memory/buffers.js +308 -0
  94. package/dist/memory/buffers.js.map +1 -0
  95. package/dist/memory/index.d.ts +7 -0
  96. package/dist/memory/index.d.ts.map +1 -0
  97. package/dist/memory/index.js +4 -0
  98. package/dist/memory/index.js.map +1 -0
  99. package/dist/memory/layout.d.ts +37 -0
  100. package/dist/memory/layout.d.ts.map +1 -0
  101. package/dist/memory/layout.js +116 -0
  102. package/dist/memory/layout.js.map +1 -0
  103. package/dist/query/compile.d.ts +73 -0
  104. package/dist/query/compile.d.ts.map +1 -0
  105. package/dist/query/compile.js +158 -0
  106. package/dist/query/compile.js.map +1 -0
  107. package/dist/query/engine.d.ts +48 -0
  108. package/dist/query/engine.d.ts.map +1 -0
  109. package/dist/query/engine.js +230 -0
  110. package/dist/query/engine.js.map +1 -0
  111. package/dist/query/index.d.ts +8 -0
  112. package/dist/query/index.d.ts.map +1 -0
  113. package/dist/query/index.js +10 -0
  114. package/dist/query/index.js.map +1 -0
  115. package/dist/query/live-query.d.ts +122 -0
  116. package/dist/query/live-query.d.ts.map +1 -0
  117. package/dist/query/live-query.js +543 -0
  118. package/dist/query/live-query.js.map +1 -0
  119. package/dist/query/sparse-set.d.ts +18 -0
  120. package/dist/query/sparse-set.d.ts.map +1 -0
  121. package/dist/query/sparse-set.js +126 -0
  122. package/dist/query/sparse-set.js.map +1 -0
  123. package/dist/reactivity/change-version.d.ts +19 -0
  124. package/dist/reactivity/change-version.d.ts.map +1 -0
  125. package/dist/reactivity/change-version.js +76 -0
  126. package/dist/reactivity/change-version.js.map +1 -0
  127. package/dist/reactivity/index.d.ts +12 -0
  128. package/dist/reactivity/index.d.ts.map +1 -0
  129. package/dist/reactivity/index.js +12 -0
  130. package/dist/reactivity/index.js.map +1 -0
  131. package/dist/reactivity/log.d.ts +83 -0
  132. package/dist/reactivity/log.d.ts.map +1 -0
  133. package/dist/reactivity/log.js +260 -0
  134. package/dist/reactivity/log.js.map +1 -0
  135. package/dist/reactivity/observer-commands.d.ts +40 -0
  136. package/dist/reactivity/observer-commands.d.ts.map +1 -0
  137. package/dist/reactivity/observer-commands.js +111 -0
  138. package/dist/reactivity/observer-commands.js.map +1 -0
  139. package/dist/reactivity/observers.d.ts +50 -0
  140. package/dist/reactivity/observers.d.ts.map +1 -0
  141. package/dist/reactivity/observers.js +127 -0
  142. package/dist/reactivity/observers.js.map +1 -0
  143. package/dist/reactivity/reactivity.d.ts +141 -0
  144. package/dist/reactivity/reactivity.d.ts.map +1 -0
  145. package/dist/reactivity/reactivity.js +479 -0
  146. package/dist/reactivity/reactivity.js.map +1 -0
  147. package/dist/reactivity/structural-journal.d.ts +30 -0
  148. package/dist/reactivity/structural-journal.d.ts.map +1 -0
  149. package/dist/reactivity/structural-journal.js +77 -0
  150. package/dist/reactivity/structural-journal.js.map +1 -0
  151. package/dist/registry.d.ts +26 -0
  152. package/dist/registry.d.ts.map +1 -0
  153. package/dist/registry.js +58 -0
  154. package/dist/registry.js.map +1 -0
  155. package/dist/serialize-surface.d.ts +170 -0
  156. package/dist/serialize-surface.d.ts.map +1 -0
  157. package/dist/serialize-surface.js +6 -0
  158. package/dist/serialize-surface.js.map +1 -0
  159. package/dist/storage/archetype.d.ts +38 -0
  160. package/dist/storage/archetype.d.ts.map +1 -0
  161. package/dist/storage/archetype.js +47 -0
  162. package/dist/storage/archetype.js.map +1 -0
  163. package/dist/storage/cold-store.d.ts +41 -0
  164. package/dist/storage/cold-store.d.ts.map +1 -0
  165. package/dist/storage/cold-store.js +100 -0
  166. package/dist/storage/cold-store.js.map +1 -0
  167. package/dist/storage/index.d.ts +10 -0
  168. package/dist/storage/index.d.ts.map +1 -0
  169. package/dist/storage/index.js +5 -0
  170. package/dist/storage/index.js.map +1 -0
  171. package/dist/storage/signature.d.ts +27 -0
  172. package/dist/storage/signature.d.ts.map +1 -0
  173. package/dist/storage/signature.js +115 -0
  174. package/dist/storage/signature.js.map +1 -0
  175. package/dist/storage/storage.d.ts +72 -0
  176. package/dist/storage/storage.d.ts.map +1 -0
  177. package/dist/storage/storage.js +192 -0
  178. package/dist/storage/storage.js.map +1 -0
  179. package/dist/storage/store.d.ts +88 -0
  180. package/dist/storage/store.d.ts.map +1 -0
  181. package/dist/storage/store.js +473 -0
  182. package/dist/storage/store.js.map +1 -0
  183. package/dist/util/stable-index.d.ts +29 -0
  184. package/dist/util/stable-index.d.ts.map +1 -0
  185. package/dist/util/stable-index.js +51 -0
  186. package/dist/util/stable-index.js.map +1 -0
  187. package/dist/world.d.ts +262 -0
  188. package/dist/world.d.ts.map +1 -0
  189. package/dist/world.js +831 -0
  190. package/dist/world.js.map +1 -0
  191. package/package.json +52 -0
@@ -0,0 +1,479 @@
1
+ // The reactivity facade: owns the write log + shape log, the per-row changeVersion
2
+ // columns, the deferred observers, and the query-flavor hooks. It fills the trackWrite stub, the
3
+ // enqueueRemoveLog stub, and the LiveQuery.changed()/eachChanged() stubs — wiring the dual
4
+ // mechanism the spec mandates:
5
+ // - the WRITE LOG drives the `.changed` query FILTER (drainChanged);
6
+ // - the per-row changeVersion drives the PUBLIC changedSince predicate + delta serializer.
7
+ // They never read each other's mechanism.
8
+ import { LogRing, OVERFLOW_SENTINEL, ShapeKind, WriteCorral } from './log.js';
9
+ import { ChangeVersionStore } from './change-version.js';
10
+ import { StructuralJournal } from './structural-journal.js';
11
+ import { ObserverRegistry } from './observers.js';
12
+ export class Reactivity {
13
+ #deps;
14
+ #writeLog;
15
+ #shapeLog;
16
+ #changeVersion;
17
+ #structuralJournal;
18
+ #observers;
19
+ #corral;
20
+ /** One per LiveQuery that declares the `changed` flavor (lazily allocated). */
21
+ #changedFlavors = new WeakMap();
22
+ /** Strong list of every changed-flavor pointer, so frame-recycle can rewind lagging cursors. */
23
+ #changedPointers = [];
24
+ #indexMask;
25
+ #componentIdBits;
26
+ #wide;
27
+ // Saved consumer pointers for the serial drains.
28
+ #maintainShapePtr;
29
+ #observerShapePtr;
30
+ #observerWritePtr;
31
+ // Filled by the world wiring so MAINTAIN_STRUCTURAL + observer "all present" can re-test entities.
32
+ #maintainHook = null;
33
+ #currentMembers = null;
34
+ #spilledThisFrame = false;
35
+ /** True iff the write log has a consumer (a changed-flavor pointer or a change observer); else the
36
+ * per-write push is dead (recomputed on (de)registration, not per write). */
37
+ #writeLogActive = false;
38
+ constructor(deps) {
39
+ this.#deps = deps;
40
+ this.#wide = deps.logEntryWords === 2;
41
+ this.#indexMask = deps.indexBits >= 32 ? 0xffffffff : ((1 << deps.indexBits) - 1) >>> 0;
42
+ this.#componentIdBits = 32 - deps.indexBits;
43
+ // One-word write entry / two-word shape entry by default; wide worlds add a word each.
44
+ const writeWords = this.#wide ? 2 : 1;
45
+ const shapeWords = this.#wide ? 3 : 2;
46
+ this.#writeLog = new LogRing({
47
+ buffers: deps.buffers,
48
+ kind: 'write',
49
+ entryWords: writeWords,
50
+ capacityEntries: deps.maxWritesPerFrame,
51
+ keyPrefix: 'reactivity.log.write',
52
+ shrinkRings: deps.shrinkRings,
53
+ });
54
+ this.#shapeLog = new LogRing({
55
+ buffers: deps.buffers,
56
+ kind: 'shape',
57
+ entryWords: shapeWords,
58
+ capacityEntries: deps.maxShapeChangesPerFrame,
59
+ keyPrefix: 'reactivity.log.shape',
60
+ shrinkRings: deps.shrinkRings,
61
+ });
62
+ this.#changeVersion = new ChangeVersionStore(deps.buffers, Math.min(64, deps.maxEntities));
63
+ this.#structuralJournal = new StructuralJournal(Math.max(1024, deps.maxShapeChangesPerFrame));
64
+ this.#corral = new WriteCorral();
65
+ const obsDeps = {
66
+ idOf: deps.idOf,
67
+ holdsAll: deps.holdsAll,
68
+ refOf: deps.refOf,
69
+ tick: deps.tick,
70
+ };
71
+ this.#observers = new ObserverRegistry(obsDeps);
72
+ this.#maintainShapePtr = this.#shapeLog.makePointer();
73
+ this.#observerShapePtr = this.#shapeLog.makePointer();
74
+ this.#observerWritePtr = this.#writeLog.makePointer();
75
+ }
76
+ /** Late-bind the single-entity maintenance hook (the query engine's maintainEntity). */
77
+ setMaintainHook(fn) {
78
+ this.#maintainHook = fn;
79
+ }
80
+ /** Late-bind a "current matching members across all queries" source for the conservative path. */
81
+ setCurrentMembersSource(fn) {
82
+ this.#currentMembers = fn;
83
+ }
84
+ // --- entry packing -------------------------------------------
85
+ #packWriteEntry(index, componentId) {
86
+ if (this.#wide)
87
+ return [index >>> 0, componentId >>> 0];
88
+ return [(((componentId << this.#deps.indexBits) | (index & this.#indexMask)) >>> 0)];
89
+ }
90
+ #unpackWrite(source, base) {
91
+ if (this.#wide) {
92
+ return { index: source[base] >>> 0, componentId: source[base + 1] >>> 0 };
93
+ }
94
+ const w = source[base];
95
+ return { index: w & this.#indexMask, componentId: (w >>> this.#deps.indexBits) >>> 0 };
96
+ }
97
+ #packShapeEntry(index, componentId, kind, targetIndex = 0) {
98
+ if (this.#wide) {
99
+ const a = index >>> 0;
100
+ const c = (((targetIndex << 3) | (kind & 0x7)) >>> 0);
101
+ return [a, componentId >>> 0, c];
102
+ }
103
+ const a = (((componentId << this.#deps.indexBits) | (index & this.#indexMask)) >>> 0);
104
+ const b = (((targetIndex << 3) | (kind & 0x7)) >>> 0);
105
+ return [a, b];
106
+ }
107
+ #unpackShape(source, base) {
108
+ if (this.#wide) {
109
+ const a = source[base];
110
+ const componentId = source[base + 1] >>> 0;
111
+ const c = source[base + 2];
112
+ return { index: a >>> 0, componentId, kind: (c & 0x7), target: c >>> 3 };
113
+ }
114
+ const a = source[base];
115
+ const b = source[base + 1];
116
+ return {
117
+ index: a & this.#indexMask,
118
+ componentId: (a >>> this.#deps.indexBits) >>> 0,
119
+ kind: (b & 0x7),
120
+ target: b >>> 3,
121
+ };
122
+ }
123
+ // --- hot-path hooks -------------------------------------
124
+ /** +: push the write entry (when a consumer exists) and stamp changeVersion (when enabled).
125
+ * Single-thread. The write log is read ONLY by changed-flavor query pointers and change observers; with
126
+ * neither present every appended word is dead (rewound at frame-recycle), so the push is a pure cost on
127
+ * the iteration hot path. Gate it on `#writeLogActive` (recomputed on flavor/observer (de)registration) —
128
+ * a semantics-preserving fast-out, since a later-attached changed flavor's pointer starts at the live head
129
+ * and sees only writes after it attaches. Pack the word inline (no per-write array). */
130
+ trackWrite(index, componentId, fieldIndex) {
131
+ if (this.#writeLogActive) {
132
+ // Main thread: append directly to the ring. (Worker corrals merge at mergeCorrals — .)
133
+ if (this.#wide) {
134
+ this.#writeLog.pushWord(index >>> 0);
135
+ this.#writeLog.pushWord(componentId >>> 0);
136
+ }
137
+ else {
138
+ this.#writeLog.pushWord((((componentId << this.#deps.indexBits) | (index & this.#indexMask)) >>> 0));
139
+ }
140
+ }
141
+ if (this.#changeVersion.enabled) {
142
+ // fieldIndex affects only field-granular stamping (deferred); component-granular default
143
+ // stamps the whole-entity slot regardless of which component/field changed. Keyed by entity INDEX
144
+ // so the stamp follows the entity across any later relocation.
145
+ void fieldIndex;
146
+ this.#changeVersion.stamp(index, this.#deps.tick());
147
+ }
148
+ }
149
+ /**: append one shape entry. Main thread only, O(1). */
150
+ trackShape(index, componentId, kind) {
151
+ this.#shapeLog.push(this.#packShapeEntry(index, componentId, kind));
152
+ // Persistent since-T mirror for the delta serializer. Resolve the
153
+ // FULL handle NOW — for Destroy this hook runs BEFORE identity invalidation,
154
+ // so the dying handle is still recoverable. No-op until a delta serializer enables journaling.
155
+ if (this.#structuralJournal.enabled) {
156
+ this.#structuralJournal.record(this.#deps.tick(), kind, this.#deps.resolveHandle(index), componentId, 0);
157
+ }
158
+ }
159
+ /**: carries the target index in word B/C. */
160
+ trackShapePair(index, pairId, targetIndex, kind) {
161
+ this.#shapeLog.push(this.#packShapeEntry(index, pairId, kind, targetIndex));
162
+ if (this.#structuralJournal.enabled) {
163
+ this.#structuralJournal.record(this.#deps.tick(), kind, this.#deps.resolveHandle(index), pairId, this.#deps.resolveHandle(targetIndex));
164
+ }
165
+ }
166
+ /**
167
+ * _PAYLOAD: a non-exclusive overflow pair's payload changed on an already-live pair. This is
168
+ * NOT a membership change (no shape-log entry, no add/remove observer), but it IS a structural delta
169
+ * the since-T stream must carry, so we record it in the persistent journal only
170
+ * (overflow payload changes are explicit OP_PAIR_PAYLOAD records).
171
+ */
172
+ trackShapeSetPayload(index, pairId, targetIndex) {
173
+ if (this.#structuralJournal.enabled) {
174
+ this.#structuralJournal.record(this.#deps.tick(), ShapeKind.SetPayload, this.#deps.resolveHandle(index), pairId, this.#deps.resolveHandle(targetIndex));
175
+ }
176
+ }
177
+ /**
178
+ * The enqueueRemoveLog stub body: storage calls this for each component in fromArch \ toArch at
179
+ * a migration, and for each held component at despawn (BEFORE removeRow + identity invalidation,
180
+ * ). It emits a shape-log Remove entry — the single source for onRemove dispatch + Removed
181
+ * delta maintenance.
182
+ */
183
+ enqueueRemoveLog(index, componentId) {
184
+ this.trackShape(index, componentId, ShapeKind.Remove);
185
+ }
186
+ // --- public predicate + delta ---------------------------------------
187
+ /** Enable per-row changeVersion stamping (a `.changed` predicate consumer / serializer exists). */
188
+ enableChangeVersion() {
189
+ this.#changeVersion.enabled = true;
190
+ this.#deps.tracking.active = true;
191
+ }
192
+ /**
193
+ * Enable the persistent structural journal (the since-T STRUCTURAL source). A delta serializer
194
+ * that includes the structural section calls this once at construction — it is the structural twin of
195
+ * `enableChangeVersion`. Until then, zero record cost.
196
+ */
197
+ enableStructuralJournal() {
198
+ this.#structuralJournal.enabled = true;
199
+ }
200
+ /**
201
+ * /: the structural ops committed with tick > since, in commit order, as portable full-handle
202
+ * records. `gap` is true when `since` predates the bounded journal's live window (the caller must
203
+ * resync from a fresh snapshot — the no-partial-apply delta-gap rule).
204
+ */
205
+ drainStructuralSince(since) {
206
+ return this.#structuralJournal.drainSince(since);
207
+ }
208
+ /**: "did any component on `handle` change since tick `since`?" (strict >). */
209
+ changedSince(handle, since) {
210
+ const index = handle & this.#indexMask;
211
+ return this.#changeVersion.changedSince(index, since);
212
+ }
213
+ /**
214
+ * /: rows of `archetypeId` whose ENTITY's stamp is > since (the delta-serializer scan).
215
+ * `indexOfRow` maps a live row of the archetype to its entity index — the stamp is keyed by entity
216
+ * index (it follows the entity across relocations), so we resolve each row's current occupant.
217
+ */
218
+ *changedRows(_archetypeId, since, count, indexOfRow) {
219
+ for (let row = 0; row < count; row++) {
220
+ if (this.#changeVersion.changedSince(indexOfRow(row), since))
221
+ yield row;
222
+ }
223
+ }
224
+ currentTick() {
225
+ return this.#deps.tick();
226
+ }
227
+ // --- observers --------------------------------------------------------
228
+ /** Recompute the write-log fast-out flag after any consumer (de)registers. Also refreshes the
229
+ * accessor's shared `tracking.active` cell: true iff trackWrite would do real work (a write-log
230
+ * consumer exists OR changeVersion stamping is enabled). When false the setter skips the whole chain. */
231
+ #refreshWriteLogActive() {
232
+ this.#writeLogActive = this.#changedPointers.length > 0 || this.#observers.hasChangeObservers;
233
+ this.#deps.tracking.active = this.#writeLogActive || this.#changeVersion.enabled;
234
+ }
235
+ observe(term, handler) {
236
+ const handle = this.#observers.observe(term, handler);
237
+ this.#refreshWriteLogActive();
238
+ const dispose = handle.dispose;
239
+ return {
240
+ id: handle.id,
241
+ dispose: () => {
242
+ dispose();
243
+ this.#refreshWriteLogActive();
244
+ },
245
+ };
246
+ }
247
+ /**: is there a remove-observer on `componentId` (gates deferred row reclaim)? */
248
+ hasRemoveObserver(componentId) {
249
+ return this.#observers.hasKindFor('remove', componentId);
250
+ }
251
+ // --- query-flavor hooks ---------------------
252
+ /**
253
+ *: allocate the `changed` flavor's LogPointer + dedup bitset for `q`. `added`/`removed` lists
254
+ * are owned by the LiveQuery itself and filled by maintenance; this hook wires only `changed`.
255
+ */
256
+ attachChangedFlavor(q, componentIds) {
257
+ this.enableChangeVersion();
258
+ if (this.#changedFlavors.has(q))
259
+ return;
260
+ const ptr = this.#writeLog.makePointer();
261
+ this.#changedPointers.push(ptr);
262
+ this.#changedFlavors.set(q, {
263
+ ptr,
264
+ dedup: new Uint8Array(Math.min(64, this.#deps.maxEntities)),
265
+ out: new Uint32Array(64),
266
+ componentIds: new Set(componentIds),
267
+ });
268
+ this.#refreshWriteLogActive();
269
+ }
270
+ /**
271
+ * _CHANGED: drain `q`'s write-log pointer, returning this frame's changed indices (deduped,
272
+ * intersected with `q.current` and the query's filtered components). Write-log driven — never
273
+ * consults changeVersion.
274
+ */
275
+ drainChanged(q) {
276
+ const flavor = this.#changedFlavors.get(q);
277
+ if (flavor === undefined)
278
+ return EMPTY_U32;
279
+ let count = 0;
280
+ const current = q.current;
281
+ // Size the dedup bitset to the current matching high-water (entity indices).
282
+ const need = this.#maxIndexOf(current) + 1;
283
+ if (need > flavor.dedup.length)
284
+ flavor.dedup = new Uint8Array(need);
285
+ flavor.dedup.fill(0);
286
+ const emit = (index) => {
287
+ if (count >= flavor.out.length) {
288
+ const grown = new Uint32Array(flavor.out.length * 2);
289
+ grown.set(flavor.out);
290
+ flavor.out = grown;
291
+ }
292
+ flavor.out[count++] = index;
293
+ };
294
+ this.#writeLog.consume(flavor.ptr, (source, base) => {
295
+ if (source.length === 1 && source[0] === OVERFLOW_SENTINEL) {
296
+ // Treat every current match as changed.
297
+ for (const index of current) {
298
+ if (index < flavor.dedup.length && flavor.dedup[index] === 1)
299
+ continue;
300
+ if (index < flavor.dedup.length)
301
+ flavor.dedup[index] = 1;
302
+ emit(index);
303
+ }
304
+ return;
305
+ }
306
+ const { index, componentId } = this.#unpackWrite(source, base);
307
+ if (flavor.componentIds.size > 0 && !flavor.componentIds.has(componentId))
308
+ return;
309
+ if (!current.has(index))
310
+ return;
311
+ if (index < flavor.dedup.length && flavor.dedup[index] === 1)
312
+ return;
313
+ if (index < flavor.dedup.length)
314
+ flavor.dedup[index] = 1;
315
+ emit(index);
316
+ });
317
+ return flavor.out.subarray(0, count);
318
+ }
319
+ #maxIndexOf(set) {
320
+ let max = 0;
321
+ for (const i of set)
322
+ if (i > max)
323
+ max = i;
324
+ return max;
325
+ }
326
+ // --- lifecycle -------------------------------
327
+ /**: start of frame — advance the world tick, snapshot peak, recycle the rings. */
328
+ frameReset() {
329
+ this.#deps.advanceTick();
330
+ if (this.#deps.tick() === 0xffffffff) {
331
+ this.#changeVersion.resetAll(); //
332
+ this.#structuralJournal.resetAll();
333
+ }
334
+ this.#spilledThisFrame = false;
335
+ this.#writeLog.frameReset(this.#minWriteCursor());
336
+ this.#shapeLog.frameReset(this.#minShapeCursor());
337
+ // When the ring recycled to slot 0 (all consumers caught up), rewind every caught-up
338
+ // consumer's cursor to 0 too, so it scans the new frame's entries from the head. A still-lagging
339
+ // consumer (cursor below the now-zero head is impossible) keeps its cursor.
340
+ this.#rewindCaughtUp(this.#writeLog.header[0], [this.#observerWritePtr, ...this.#changedPointers]);
341
+ this.#rewindCaughtUp(this.#shapeLog.header[0], [this.#observerShapePtr, this.#maintainShapePtr]);
342
+ }
343
+ #rewindCaughtUp(head, pointers) {
344
+ if (head !== 0)
345
+ return; // ring was not recycled (a lagging consumer pinned it)
346
+ for (const ptr of pointers) {
347
+ ptr.cursor = 0;
348
+ ptr.spillCursor = 0;
349
+ }
350
+ }
351
+ /**: merge per-worker write corrals into the shared ring (deterministic). No-op single-thread. */
352
+ mergeCorrals() {
353
+ const c = this.#corral;
354
+ for (let i = 0; i < c.count; i++) {
355
+ this.#writeLog.pushWord(c.data[i]);
356
+ }
357
+ c.reset();
358
+ }
359
+ /**
360
+ * /+: merge ONE worker's staged value writes into the shared write log. `pairs` is a
361
+ * flat `[index, componentId, index, componentId, …]` buffer (the worker's raw corral payload); the
362
+ * caller drives this in ASCENDING worker-index order so the merged stream is deterministic. We
363
+ * (re)pack each pair through the module's own packWrite so single/wide layout stays the single
364
+ * source of truth — the worker never duplicates the packing scheme. Writes flow into the SAME ring
365
+ * the main thread appends to, so `.changed` filters and onChange observers fire for worker writes
366
+ * exactly as for single-thread writes.: when changeVersion is enabled we also
367
+ * stamp each row here (the worker hot path stays atomic-free; the stamp lands at the serial merge).
368
+ */
369
+ mergeWorkerWrites(pairs, count) {
370
+ for (let i = 0; i < count; i++) {
371
+ const index = pairs[i * 2] >>> 0;
372
+ const componentId = pairs[i * 2 + 1] >>> 0;
373
+ for (const w of this.#packWriteEntry(index, componentId))
374
+ this.#writeLog.pushWord(w);
375
+ if (this.#changeVersion.enabled) {
376
+ this.#changeVersion.stamp(index, this.#deps.tick());
377
+ }
378
+ }
379
+ }
380
+ /**
381
+ * _STRUCTURAL: drain the shape log, re-testing each affected entity against the queries
382
+ * referencing the changed component. In single-thread mode already maintains `current`
383
+ * synchronously at the commit point, so this re-runs the same idempotent re-test off the log (the
384
+ * drain is the spec's serial mechanism; the synchronous path is the optimization that agrees with
385
+ * it). Add/remove coalesce within the frame because the drain happens once.
386
+ */
387
+ maintainStructural() {
388
+ const hook = this.#maintainHook;
389
+ if (hook === null) {
390
+ this.#maintainShapePtr.cursor = this.#shapeLogHead();
391
+ return;
392
+ }
393
+ this.#shapeLog.consume(this.#maintainShapePtr, (source, base) => {
394
+ if (source.length === 1 && source[0] === OVERFLOW_SENTINEL)
395
+ return;
396
+ const { index, componentId, kind } = this.#unpackShape(source, base);
397
+ if (kind === ShapeKind.Add || kind === ShapeKind.Remove)
398
+ hook(index, componentId);
399
+ });
400
+ }
401
+ /** _DRAIN: fire deferred observers from the saved shape/write pointers. */
402
+ observerDrain() {
403
+ if (!this.#observers.hasObservers) {
404
+ this.#observerShapePtr.cursor = this.#shapeLogHead();
405
+ this.#observerWritePtr.cursor = this.#writeLogHead();
406
+ return;
407
+ }
408
+ this.#observers.resetChangeDedup();
409
+ // Capture BOTH log heads at drain entry. A structural (add/remove) handler may
410
+ // call entity.write(C), which appends to the write log; bounding the change consume to this snapshot
411
+ // defers that observer-issued write to the NEXT drain — no intra-drain write-cascade (review #2).
412
+ const writeHeadSnapshot = this.#writeLogHead();
413
+ // Structural observers (add/remove) off the shape log.
414
+ this.#shapeLog.consume(this.#observerShapePtr, (source, base) => {
415
+ if (source.length === 1 && source[0] === OVERFLOW_SENTINEL)
416
+ return;
417
+ const { index, componentId, kind } = this.#unpackShape(source, base);
418
+ const okind = kind === ShapeKind.Add || kind === ShapeKind.AddPair
419
+ ? 'add'
420
+ : kind === ShapeKind.Remove || kind === ShapeKind.RemovePair || kind === ShapeKind.Destroy
421
+ ? 'remove'
422
+ : null;
423
+ if (okind === null)
424
+ return; // CREATE has no per-component observer
425
+ this.#observers.dispatchStructural(okind, index, componentId);
426
+ });
427
+ // Change observers off the write log.
428
+ this.#writeLog.consume(this.#observerWritePtr, (source, base) => {
429
+ if (source.length === 1 && source[0] === OVERFLOW_SENTINEL) {
430
+ if (this.#currentMembers !== null)
431
+ this.#observers.fireAllChangeConservatively(this.#currentMembers());
432
+ return;
433
+ }
434
+ const { index, componentId } = this.#unpackWrite(source, base);
435
+ this.#observers.dispatchChange(index, componentId);
436
+ }, writeHeadSnapshot);
437
+ }
438
+ /** _LOGS: drain/merge spill (consumers already drained it), schedule next-frame resize. */
439
+ flushLogs() {
440
+ if (this.#writeLog.spill.length > 0 || this.#shapeLog.spill.length > 0)
441
+ this.#spilledThisFrame = true;
442
+ this.#writeLog.observePeak();
443
+ this.#shapeLog.observePeak();
444
+ if (this.#deps.dev && this.#spilledThisFrame && typeof console !== 'undefined') {
445
+ console.warn(`[ecsia] reactivity log overflowed its ring this frame; entries spilled to the main-thread ` +
446
+ `array and the ring will grow next frame. Set createWorld({ reactivity: { maxWritesPerFrame / ` +
447
+ `maxShapeChangesPerFrame } }) to pre-size.`);
448
+ }
449
+ }
450
+ // --- internals -------------------------------------------------------------
451
+ #shapeLogHead() {
452
+ return this.#shapeLog.header[0];
453
+ }
454
+ #writeLogHead() {
455
+ return this.#writeLog.header[0];
456
+ }
457
+ #minWriteCursor() {
458
+ let min = this.#writeLog.header[0];
459
+ if (this.#observerWritePtr.cursor < min)
460
+ min = this.#observerWritePtr.cursor;
461
+ // changed-flavor pointers are drained lazily; they pin the ring until read so the recycle never
462
+ // overruns an unread consumer ("ring is not recycled past a lagging pointer"). In the common
463
+ // case every system reads its filter within the frame, so they equal head and the ring recycles.
464
+ for (const ptr of this.#changedPointers)
465
+ if (ptr.cursor < min)
466
+ min = ptr.cursor;
467
+ return min;
468
+ }
469
+ #minShapeCursor() {
470
+ let min = this.#shapeLog.header[0];
471
+ if (this.#observerShapePtr.cursor < min)
472
+ min = this.#observerShapePtr.cursor;
473
+ if (this.#maintainShapePtr.cursor < min)
474
+ min = this.#maintainShapePtr.cursor;
475
+ return min;
476
+ }
477
+ }
478
+ const EMPTY_U32 = new Uint32Array(0);
479
+ //# sourceMappingURL=reactivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactivity.js","sourceRoot":"","sources":["../../src/reactivity/reactivity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { ShapeKind } from './log.js';
2
+ export interface StructuralRecord {
3
+ readonly tick: number;
4
+ readonly kind: ShapeKind;
5
+ /** The FULL (generational) subject/entity handle, resolved at commit time (portable across boundary). */
6
+ readonly handle: number;
7
+ /** User/synthetic component id (Add/Remove) or synthetic pair id (AddPair/RemovePair). 0 for Create/Destroy. */
8
+ readonly componentId: number;
9
+ /** The FULL pair-target handle (AddPair/RemovePair/SetPayload), resolved at commit time; else 0. */
10
+ readonly target: number;
11
+ }
12
+ export declare class StructuralJournal {
13
+ #private;
14
+ /** False ⇒ zero record cost (no delta serializer attached). Mirrors ChangeVersionStore.enabled. */
15
+ enabled: boolean;
16
+ constructor(initialCapacityRecords?: number);
17
+ /** Append one structural op at `tick`. O(1); drops the oldest record once the ring is full. */
18
+ record(tick: number, kind: ShapeKind, handle: number, componentId: number, target: number): void;
19
+ /**
20
+ * Records with tick > since, in commit (append) order. Returns `gap: true` if `since` predates the
21
+ * oldest resident record (the live window evicted it — the caller must resync from a snapshot).
22
+ */
23
+ drainSince(since: number): {
24
+ records: StructuralRecord[];
25
+ gap: boolean;
26
+ };
27
+ /**: clear the journal at a serial flush (alongside changeVersion.resetAll). */
28
+ resetAll(): void;
29
+ }
30
+ //# sourceMappingURL=structural-journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural-journal.d.ts","sourceRoot":"","sources":["../../src/reactivity/structural-journal.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,yGAAyG;IACzG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,gHAAgH;IAChH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,oGAAoG;IACpG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAID,qBAAa,iBAAiB;;IAC5B,mGAAmG;IACnG,OAAO,UAAQ;gBASH,sBAAsB,SAAO;IAKzC,+FAA+F;IAC/F,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgBhG;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE;IAoBxE,+EAA+E;IAC/E,QAAQ,IAAI,IAAI;CAIjB"}
@@ -0,0 +1,77 @@
1
+ // The persistent, bounded, tick-keyed STRUCTURAL JOURNAL — the since-T structural source the
2
+ // delta serializer consumes. The per-frame shape log (log.ts) is RECYCLED at every frame
3
+ // boundary, so a "since tick T" serializer cannot pin that ring across frames. Exactly as
4
+ // `changeVersion` is the persistent
5
+ // since-T source for VALUE changes, this journal is the persistent since-T source for STRUCTURAL
6
+ // changes (Create/Destroy/ComponentAdd/ComponentRemove/AddPair/RemovePair/SetPayload).
7
+ //
8
+ // It is appended SYNCHRONOUSLY at the structural commit point (from trackShape/trackShapePair), so the
9
+ // dying entity's FULL handle is still resolvable (Destroy is emitted BEFORE identity
10
+ // invalidation). Each record is keyed by the current frame tick; drainSince(T) returns the ops
11
+ // with tick > T in commit order. The journal is a bounded ring (drop-oldest); when a requested T has
12
+ // been evicted, the caller must resync from a fresh snapshot (the delta-gap rule).
13
+ // LAZILY enabled: zero memory/zero record cost until a delta serializer attaches.
14
+ const FIELDS_PER_RECORD = 5;
15
+ export class StructuralJournal {
16
+ /** False ⇒ zero record cost (no delta serializer attached). Mirrors ChangeVersionStore.enabled. */
17
+ enabled = false;
18
+ /** A flat ring: [tick, kind, handle, componentId, target] × capacity. Drop-oldest on overflow. */
19
+ #ring;
20
+ #capacity;
21
+ /** Total records ever appended (monotonic). The live window is [count-capacity, count). */
22
+ #count = 0;
23
+ /** The oldest tick still resident; a drainSince(T) with T < this floor signals an evicted gap. */
24
+ #oldestResidentTick = 0;
25
+ constructor(initialCapacityRecords = 1024) {
26
+ this.#capacity = Math.max(16, initialCapacityRecords);
27
+ this.#ring = new Uint32Array(this.#capacity * FIELDS_PER_RECORD);
28
+ }
29
+ /** Append one structural op at `tick`. O(1); drops the oldest record once the ring is full. */
30
+ record(tick, kind, handle, componentId, target) {
31
+ if (!this.enabled)
32
+ return;
33
+ const slot = (this.#count % this.#capacity) * FIELDS_PER_RECORD;
34
+ if (this.#count >= this.#capacity) {
35
+ // About to overwrite the oldest live record — advance the resident-floor to its successor's tick.
36
+ const nextOldest = ((this.#count + 1) % this.#capacity) * FIELDS_PER_RECORD;
37
+ this.#oldestResidentTick = this.#ring[nextOldest];
38
+ }
39
+ this.#ring[slot] = tick >>> 0;
40
+ this.#ring[slot + 1] = kind >>> 0;
41
+ this.#ring[slot + 2] = handle >>> 0;
42
+ this.#ring[slot + 3] = componentId >>> 0;
43
+ this.#ring[slot + 4] = target >>> 0;
44
+ this.#count += 1;
45
+ }
46
+ /**
47
+ * Records with tick > since, in commit (append) order. Returns `gap: true` if `since` predates the
48
+ * oldest resident record (the live window evicted it — the caller must resync from a snapshot).
49
+ */
50
+ drainSince(since) {
51
+ const out = [];
52
+ if (!this.enabled)
53
+ return { records: out, gap: false };
54
+ const gap = this.#count > this.#capacity && since < this.#oldestResidentTick;
55
+ const start = this.#count > this.#capacity ? this.#count - this.#capacity : 0;
56
+ for (let i = start; i < this.#count; i++) {
57
+ const slot = (i % this.#capacity) * FIELDS_PER_RECORD;
58
+ const tick = this.#ring[slot];
59
+ if (tick <= since)
60
+ continue;
61
+ out.push({
62
+ tick,
63
+ kind: this.#ring[slot + 1],
64
+ handle: this.#ring[slot + 2],
65
+ componentId: this.#ring[slot + 3],
66
+ target: this.#ring[slot + 4],
67
+ });
68
+ }
69
+ return { records: out, gap };
70
+ }
71
+ /**: clear the journal at a serial flush (alongside changeVersion.resetAll). */
72
+ resetAll() {
73
+ this.#count = 0;
74
+ this.#oldestResidentTick = 0;
75
+ }
76
+ }
77
+ //# sourceMappingURL=structural-journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural-journal.js","sourceRoot":"","sources":["../../src/reactivity/structural-journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ComponentDef, ComponentId, Schema } from '@ecsia/schema';
2
+ import type { ComponentRuntime } from './component/index.js';
3
+ export declare class ComponentRegistry {
4
+ #private;
5
+ constructor(..._legacy: readonly unknown[]);
6
+ register(components: readonly ComponentDef<Schema>[]): void;
7
+ /**
8
+ * Mint the next dense ComponentId WITHOUT binding a def (allocSyntheticComponentId).
9
+ * Pair/presence/overflow ids draw from the SAME dense space as ordinary
10
+ * components so storage, queries, and the bitmask treat them identically. Serial / main-thread.
11
+ */
12
+ allocSyntheticId(): ComponentId;
13
+ /**
14
+ * Intern a synthetic ComponentDef (a relation presence/overflow def, or a per-pair def) at an id
15
+ * already minted by `allocSyntheticId`, so `defOf`/`idOf` resolve it and storage can build its
16
+ * ColumnSet. Serial / main-thread; relations is the only caller (the acyclic boundary holds —
17
+ * the world exposes this through a seam, core never imports relations).
18
+ */
19
+ registerSynthetic(def: ComponentDef<Schema>, id: ComponentId): void;
20
+ idOf(def: ComponentDef<Schema>): ComponentId | undefined;
21
+ defOf(id: ComponentId): ComponentDef<Schema> | undefined;
22
+ /**: seeds the bitmask/sigWords fixed stride = ceil(nextComponentId/32) ( C4). */
23
+ get nextComponentId(): number;
24
+ }
25
+ export type { ComponentRuntime };
26
+ //# sourceMappingURL=registry.d.ts.map