@forgeax/engine-ecs 0.1.26 → 0.1.28

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 (50) hide show
  1. package/README.md +94 -35
  2. package/dist/__tests__/world-read.unit.test.d.ts +2 -0
  3. package/dist/__tests__/world-read.unit.test.d.ts.map +1 -0
  4. package/dist/commands.d.ts +2 -0
  5. package/dist/commands.d.ts.map +1 -1
  6. package/dist/index.mjs +3471 -4263
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal.d.ts +2 -3
  9. package/dist/internal.d.ts.map +1 -1
  10. package/dist/internal.mjs +3 -333
  11. package/dist/internal.mjs.map +1 -1
  12. package/dist/projection/index.mjs.map +1 -1
  13. package/dist/shared.mjs.map +1 -1
  14. package/dist/world-entity-lifecycle.d.ts +3 -14
  15. package/dist/world-entity-lifecycle.d.ts.map +1 -1
  16. package/dist/world-internal.d.ts +65 -5
  17. package/dist/world-internal.d.ts.map +1 -1
  18. package/dist/world-read.d.ts +16 -0
  19. package/dist/world-read.d.ts.map +1 -0
  20. package/dist/world-read.mjs +8 -0
  21. package/dist/world-read.mjs.map +1 -0
  22. package/dist/world-scheduling.d.ts +0 -4
  23. package/dist/world-scheduling.d.ts.map +1 -1
  24. package/dist/world-storage-primitives.d.ts +26 -0
  25. package/dist/world-storage-primitives.d.ts.map +1 -0
  26. package/dist/world.d.ts +352 -157
  27. package/dist/world.d.ts.map +1 -1
  28. package/package.json +8 -4
  29. package/src/__tests__/command-buffer.test.ts +29 -3
  30. package/src/__tests__/hierarchy.unit.test.ts +3 -3
  31. package/src/__tests__/world-health.contract.test.ts +195 -2
  32. package/src/__tests__/world-read.unit.test.ts +30 -0
  33. package/src/commands.ts +22 -9
  34. package/src/internal.ts +5 -3
  35. package/src/world-entity-lifecycle.ts +23 -252
  36. package/src/world-internal-augmentation.d.ts +11 -0
  37. package/src/world-internal.ts +114 -63
  38. package/src/world-read.ts +38 -0
  39. package/src/world-scheduling.ts +0 -26
  40. package/src/world-storage-primitives.ts +179 -0
  41. package/src/world.ts +2590 -509
  42. package/dist/world-component-access.d.ts +0 -311
  43. package/dist/world-component-access.d.ts.map +0 -1
  44. package/dist/world-component-storage.d.ts +0 -298
  45. package/dist/world-component-storage.d.ts.map +0 -1
  46. package/dist/world-core.d.ts +0 -39
  47. package/dist/world-core.d.ts.map +0 -1
  48. package/src/world-component-access.ts +0 -1769
  49. package/src/world-component-storage.ts +0 -1264
  50. package/src/world-core.ts +0 -74
@@ -0,0 +1,179 @@
1
+ import { fieldTypeToMetaKey, type ManagedArrayElementType, TYPE_METADATA } from './component';
2
+
3
+ /** Freeze the detached POD produced by World.inspect(). */
4
+ export function detachWorldInspection<T extends object>(snapshot: T): Readonly<T> {
5
+ return freezeInspection(snapshot);
6
+ }
7
+
8
+ function freezeInspection<T>(value: T): Readonly<T> {
9
+ if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {
10
+ return value as Readonly<T>;
11
+ }
12
+ for (const key of Reflect.ownKeys(value as object)) {
13
+ const child = (value as Record<PropertyKey, unknown>)[key];
14
+ if (child !== null && (typeof child === 'object' || typeof child === 'function')) {
15
+ freezeInspection(child);
16
+ }
17
+ }
18
+ return Object.freeze(value) as Readonly<T>;
19
+ }
20
+
21
+ /**
22
+ * Element-byte-width for a managed-array element type, read off the global
23
+ * TYPE_METADATA table (single SSOT). `entity` stores as `u32` (4 bytes); every
24
+ * scalar maps to its own key. `fieldTypeToMetaKey` always resolves a key for a
25
+ * ManagedArrayElementType, and every such row carries a concrete `byteSize`.
26
+ */
27
+ export function elementByteSize(elementType: ManagedArrayElementType): number {
28
+ const key = fieldTypeToMetaKey(elementType);
29
+ // biome-ignore lint/style/noNonNullAssertion: every ManagedArrayElementType resolves to a row with a concrete byteSize
30
+ return TYPE_METADATA[key!]!.byteSize!;
31
+ }
32
+
33
+ export function reinterpretSlotBytes(
34
+ bytes: Uint8Array,
35
+ elementType: ManagedArrayElementType,
36
+ elementCount: number,
37
+ ): ReturnType<typeof reinterpretBufferRegion> {
38
+ return reinterpretBufferRegion(bytes.buffer, bytes.byteOffset, elementType, elementCount);
39
+ }
40
+
41
+ export function reinterpretBufferRegion(
42
+ buffer: ArrayBufferLike,
43
+ byteOffset: number,
44
+ elementType: ManagedArrayElementType,
45
+ elementCount: number,
46
+ ):
47
+ | Float32Array
48
+ | Float64Array
49
+ | Int32Array
50
+ | Uint32Array
51
+ | Int16Array
52
+ | Uint16Array
53
+ | Int8Array
54
+ | Uint8Array {
55
+ // shared<X> template literals: column-stored as u32 handles, reinterpret
56
+ // as Uint32Array. The brand is applied at the FieldValueType level;
57
+ // runtime storage is plain u32 (feat-20260614 M5; replaces the retired
58
+ // 'handle<X>' arm).
59
+ if (elementType.startsWith('shared<')) {
60
+ return new Uint32Array(buffer, byteOffset, elementCount);
61
+ }
62
+ switch (elementType) {
63
+ case 'f32':
64
+ return new Float32Array(buffer, byteOffset, elementCount);
65
+ case 'f64':
66
+ return new Float64Array(buffer, byteOffset, elementCount);
67
+ case 'i32':
68
+ return new Int32Array(buffer, byteOffset, elementCount);
69
+ case 'u32':
70
+ case 'enum':
71
+ case 'ref':
72
+ case 'entity':
73
+ return new Uint32Array(buffer, byteOffset, elementCount);
74
+ case 'i16':
75
+ return new Int16Array(buffer, byteOffset, elementCount);
76
+ case 'u16':
77
+ return new Uint16Array(buffer, byteOffset, elementCount);
78
+ case 'i8':
79
+ return new Int8Array(buffer, byteOffset, elementCount);
80
+ case 'u8':
81
+ case 'bool':
82
+ return new Uint8Array(buffer, byteOffset, elementCount);
83
+ }
84
+ // Exhaustiveness fallthrough: TypeScript template-literal type
85
+ // (`shared<${string}>`) is structurally not narrowed away by the
86
+ // `startsWith` guard above, so this branch is unreachable yet TS still
87
+ // requires a return path.
88
+ return new Uint32Array(buffer, byteOffset, elementCount);
89
+ }
90
+
91
+ /**
92
+ * Reinterpret `bytes` as the typed view for `elementType` and write `value`
93
+ * at element index `idx`. Mirrors the `array<T>` storage law (4/8/2/1-byte
94
+ * element widths per TYPE_METADATA.byteSize) and is used by `world.push`
95
+ * to land a new tail element into BufferPool slot bytes after a `grow`.
96
+ */
97
+ export function writeArrayElementAt(
98
+ bytes: Uint8Array,
99
+ idx: number,
100
+ elementType: ManagedArrayElementType,
101
+ value: number,
102
+ ): void {
103
+ const buf = bytes.buffer;
104
+ const offset = bytes.byteOffset;
105
+ const byteLen = bytes.byteLength;
106
+ switch (elementType) {
107
+ case 'f32':
108
+ new Float32Array(buf, offset, byteLen >>> 2)[idx] = value;
109
+ return;
110
+ case 'f64':
111
+ new Float64Array(buf, offset, byteLen >>> 3)[idx] = value;
112
+ return;
113
+ case 'i32':
114
+ new Int32Array(buf, offset, byteLen >>> 2)[idx] = value;
115
+ return;
116
+ case 'u32':
117
+ case 'enum':
118
+ case 'ref':
119
+ case 'entity':
120
+ new Uint32Array(buf, offset, byteLen >>> 2)[idx] = value;
121
+ return;
122
+ case 'i16':
123
+ new Int16Array(buf, offset, byteLen >>> 1)[idx] = value;
124
+ return;
125
+ case 'u16':
126
+ new Uint16Array(buf, offset, byteLen >>> 1)[idx] = value;
127
+ return;
128
+ case 'i8':
129
+ new Int8Array(buf, offset, byteLen)[idx] = value;
130
+ return;
131
+ case 'u8':
132
+ case 'bool':
133
+ new Uint8Array(buf, offset, byteLen)[idx] = value;
134
+ return;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Reinterpret `bytes` as the typed view for `elementType` and read element
140
+ * `idx`. Mirrors `writeArrayElementAt` -- consumed by `world.pop` to materialise
141
+ * the tail value before the count is decremented.
142
+ */
143
+ export function readArrayElementAt(
144
+ bytes: Uint8Array,
145
+ idx: number,
146
+ elementType: ManagedArrayElementType,
147
+ ): number {
148
+ const buf = bytes.buffer;
149
+ const offset = bytes.byteOffset;
150
+ const byteLen = bytes.byteLength;
151
+ // shared<X> template literals: column-stored as u32 handles, read as
152
+ // Uint32Array. (feat-20260614 M5; replaces retired 'handle<X>' arm.)
153
+ if (elementType.startsWith('shared<')) {
154
+ return new Uint32Array(buf, offset, byteLen >>> 2)[idx] ?? 0;
155
+ }
156
+ switch (elementType) {
157
+ case 'f32':
158
+ return new Float32Array(buf, offset, byteLen >>> 2)[idx] ?? 0;
159
+ case 'f64':
160
+ return new Float64Array(buf, offset, byteLen >>> 3)[idx] ?? 0;
161
+ case 'i32':
162
+ return new Int32Array(buf, offset, byteLen >>> 2)[idx] ?? 0;
163
+ case 'u32':
164
+ case 'enum':
165
+ case 'ref':
166
+ case 'entity':
167
+ return new Uint32Array(buf, offset, byteLen >>> 2)[idx] ?? 0;
168
+ case 'i16':
169
+ return new Int16Array(buf, offset, byteLen >>> 1)[idx] ?? 0;
170
+ case 'u16':
171
+ return new Uint16Array(buf, offset, byteLen >>> 1)[idx] ?? 0;
172
+ case 'i8':
173
+ return new Int8Array(buf, offset, byteLen)[idx] ?? 0;
174
+ case 'u8':
175
+ case 'bool':
176
+ return new Uint8Array(buf, offset, byteLen)[idx] ?? 0;
177
+ }
178
+ return 0;
179
+ }