@oasys/oecs 0.3.1 → 0.4.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.
- package/README.md +196 -267
- package/dist/array-CxbPyiHO.cjs +1 -0
- package/dist/array-uFR7Q8fU.js +132 -0
- package/dist/core/ecs/access_check.d.ts +77 -0
- package/dist/core/ecs/access_check.d.ts.map +1 -0
- package/dist/core/ecs/archetype.d.ts +474 -0
- package/dist/core/ecs/archetype.d.ts.map +1 -0
- package/dist/core/ecs/builtin_relations.d.ts +43 -0
- package/dist/core/ecs/builtin_relations.d.ts.map +1 -0
- package/dist/core/ecs/command_log.d.ts +113 -0
- package/dist/core/ecs/command_log.d.ts.map +1 -0
- package/dist/core/ecs/component.d.ts +106 -0
- package/dist/core/ecs/component.d.ts.map +1 -0
- package/dist/core/ecs/compute_backend.d.ts +37 -0
- package/dist/core/ecs/compute_backend.d.ts.map +1 -0
- package/dist/core/ecs/dispatch_trace.d.ts +93 -0
- package/dist/core/ecs/dispatch_trace.d.ts.map +1 -0
- package/dist/core/ecs/ecs.d.ts +533 -0
- package/dist/core/ecs/ecs.d.ts.map +1 -0
- package/dist/core/ecs/ecs_memory.d.ts +179 -0
- package/dist/core/ecs/ecs_memory.d.ts.map +1 -0
- package/dist/core/ecs/entity.d.ts +28 -0
- package/dist/core/ecs/entity.d.ts.map +1 -0
- package/dist/core/ecs/event.d.ts +54 -0
- package/dist/core/ecs/event.d.ts.map +1 -0
- package/dist/core/ecs/frame_trace.d.ts +133 -0
- package/dist/core/ecs/frame_trace.d.ts.map +1 -0
- package/dist/core/ecs/host_commands.d.ts +252 -0
- package/dist/core/ecs/host_commands.d.ts.map +1 -0
- package/dist/core/ecs/index.d.ts +41 -0
- package/dist/core/ecs/index.d.ts.map +1 -0
- package/dist/core/ecs/observer.d.ts +142 -0
- package/dist/core/ecs/observer.d.ts.map +1 -0
- package/dist/core/ecs/query.d.ts +557 -0
- package/dist/core/ecs/query.d.ts.map +1 -0
- package/dist/core/ecs/ref.d.ts +31 -0
- package/dist/core/ecs/ref.d.ts.map +1 -0
- package/dist/core/ecs/relation.d.ts +231 -0
- package/dist/core/ecs/relation.d.ts.map +1 -0
- package/dist/core/ecs/resource.d.ts +33 -0
- package/dist/core/ecs/resource.d.ts.map +1 -0
- package/dist/core/ecs/resume.d.ts +85 -0
- package/dist/core/ecs/resume.d.ts.map +1 -0
- package/dist/core/ecs/run_condition.d.ts +75 -0
- package/dist/core/ecs/run_condition.d.ts.map +1 -0
- package/dist/core/ecs/schedule.d.ts +133 -0
- package/dist/core/ecs/schedule.d.ts.map +1 -0
- package/dist/core/ecs/sparse_store.d.ts +107 -0
- package/dist/core/ecs/sparse_store.d.ts.map +1 -0
- package/dist/core/ecs/store.d.ts +1149 -0
- package/dist/core/ecs/store.d.ts.map +1 -0
- package/dist/core/ecs/store_layout_listener.d.ts +23 -0
- package/dist/core/ecs/store_layout_listener.d.ts.map +1 -0
- package/dist/core/ecs/system.d.ts +134 -0
- package/dist/core/ecs/system.d.ts.map +1 -0
- package/dist/core/ecs/utils/arrays.d.ts +7 -0
- package/dist/core/ecs/utils/arrays.d.ts.map +1 -0
- package/dist/core/ecs/utils/constants.d.ts +12 -0
- package/dist/core/ecs/utils/constants.d.ts.map +1 -0
- package/dist/core/ecs/utils/error.d.ts +51 -0
- package/dist/core/ecs/utils/error.d.ts.map +1 -0
- package/dist/core/reactive/array.d.ts +24 -0
- package/dist/core/reactive/array.d.ts.map +1 -0
- package/dist/core/reactive/index.cjs +1 -0
- package/dist/core/reactive/index.d.ts +10 -0
- package/dist/core/reactive/index.d.ts.map +1 -0
- package/dist/core/reactive/index.js +17 -0
- package/dist/core/reactive/interop.d.ts +19 -0
- package/dist/core/reactive/interop.d.ts.map +1 -0
- package/dist/core/reactive/kernel.d.ts +71 -0
- package/dist/core/reactive/kernel.d.ts.map +1 -0
- package/dist/core/reactive/map.d.ts +16 -0
- package/dist/core/reactive/map.d.ts.map +1 -0
- package/dist/core/reactive/struct.d.ts +10 -0
- package/dist/core/reactive/struct.d.ts.map +1 -0
- package/dist/core/store/__generated__/abi.d.ts +43 -0
- package/dist/core/store/__generated__/abi.d.ts.map +1 -0
- package/dist/core/store/action_ring.d.ts +136 -0
- package/dist/core/store/action_ring.d.ts.map +1 -0
- package/dist/core/store/allocator.d.ts +238 -0
- package/dist/core/store/allocator.d.ts.map +1 -0
- package/dist/core/store/buffer_backed_column.d.ts +69 -0
- package/dist/core/store/buffer_backed_column.d.ts.map +1 -0
- package/dist/core/store/column_store.d.ts +265 -0
- package/dist/core/store/column_store.d.ts.map +1 -0
- package/dist/core/store/command_dispatch.d.ts +52 -0
- package/dist/core/store/command_dispatch.d.ts.map +1 -0
- package/dist/core/store/command_ring.d.ts +107 -0
- package/dist/core/store/command_ring.d.ts.map +1 -0
- package/dist/core/store/descriptor.d.ts +80 -0
- package/dist/core/store/descriptor.d.ts.map +1 -0
- package/dist/core/store/entity_index.d.ts +108 -0
- package/dist/core/store/entity_index.d.ts.map +1 -0
- package/dist/core/store/event_ring.d.ts +95 -0
- package/dist/core/store/event_ring.d.ts.map +1 -0
- package/dist/core/store/extend.d.ts +109 -0
- package/dist/core/store/extend.d.ts.map +1 -0
- package/dist/core/store/grow.d.ts +39 -0
- package/dist/core/store/grow.d.ts.map +1 -0
- package/dist/core/store/header.d.ts +64 -0
- package/dist/core/store/header.d.ts.map +1 -0
- package/dist/core/store/index.d.ts +16 -0
- package/dist/core/store/index.d.ts.map +1 -0
- package/dist/core/store/region_table.d.ts +74 -0
- package/dist/core/store/region_table.d.ts.map +1 -0
- package/dist/core/store/snapshot.d.ts +43 -0
- package/dist/core/store/snapshot.d.ts.map +1 -0
- package/dist/core/store/state_hash.d.ts +38 -0
- package/dist/core/store/state_hash.d.ts.map +1 -0
- package/dist/core/store/store_regions.d.ts +38 -0
- package/dist/core/store/store_regions.d.ts.map +1 -0
- package/dist/extensions/editor/editor.d.ts +149 -0
- package/dist/extensions/editor/editor.d.ts.map +1 -0
- package/dist/extensions/editor/field_handle.d.ts +35 -0
- package/dist/extensions/editor/field_handle.d.ts.map +1 -0
- package/dist/extensions/editor/index.cjs +1 -0
- package/dist/extensions/editor/index.d.ts +21 -0
- package/dist/extensions/editor/index.d.ts.map +1 -0
- package/dist/extensions/editor/index.js +209 -0
- package/dist/extensions/reactive/ecs_sync.d.ts +210 -0
- package/dist/extensions/reactive/ecs_sync.d.ts.map +1 -0
- package/dist/extensions/reactive/index.cjs +1 -0
- package/dist/extensions/reactive/index.d.ts +23 -0
- package/dist/extensions/reactive/index.d.ts.map +1 -0
- package/dist/extensions/reactive/index.js +225 -0
- package/dist/extensions/solid/index.cjs +1 -0
- package/dist/extensions/solid/index.d.ts +6 -0
- package/dist/extensions/solid/index.d.ts.map +1 -0
- package/dist/extensions/solid/index.js +32 -0
- package/dist/extensions/solid/kernel_solid.d.ts +42 -0
- package/dist/extensions/solid/kernel_solid.d.ts.map +1 -0
- package/dist/index.cjs +2 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8565 -1310
- package/dist/interop-CT-REx0W.cjs +1 -0
- package/dist/interop-CcY6ASQc.js +18 -0
- package/dist/kernel-DgyrLFjW.js +227 -0
- package/dist/kernel-yWV3XnAb.cjs +1 -0
- package/dist/log/console_sink.d.ts +4 -0
- package/dist/log/console_sink.d.ts.map +1 -0
- package/dist/log/index.d.ts +3 -0
- package/dist/log/index.d.ts.map +1 -0
- package/dist/log/logger.d.ts +27 -0
- package/dist/log/logger.d.ts.map +1 -0
- package/dist/primitives.cjs +1 -0
- package/dist/primitives.d.ts +18 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +44 -0
- package/dist/shared-BXSZnxx4.cjs +1 -0
- package/dist/shared-C678TAPY.js +99 -0
- package/dist/shared.cjs +1 -0
- package/dist/shared.d.ts +22 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +7 -0
- package/dist/topological_sort-DlRpSrxu.js +391 -0
- package/dist/topological_sort-WAT-VHb-.cjs +1 -0
- package/dist/type_primitives/assertions.d.ts +12 -8
- package/dist/type_primitives/assertions.d.ts.map +1 -1
- package/dist/type_primitives/binary_heap/binary_heap.d.ts +6 -2
- package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -1
- package/dist/type_primitives/bitset/bitset.d.ts +16 -4
- package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
- package/dist/type_primitives/brand.d.ts +6 -1
- package/dist/type_primitives/brand.d.ts.map +1 -1
- package/dist/type_primitives/error.d.ts +4 -0
- package/dist/type_primitives/error.d.ts.map +1 -1
- package/dist/type_primitives/index.d.ts +3 -0
- package/dist/type_primitives/index.d.ts.map +1 -1
- package/dist/type_primitives/sparse_map/sparse_map.d.ts +7 -3
- package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
- package/dist/type_primitives/sparse_set/sparse_set.d.ts +4 -0
- package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
- package/dist/type_primitives/topological_sort/topological_sort.d.ts +7 -3
- package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -1
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +53 -16
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
- package/dist/utils/arrays.d.ts +1 -1
- package/dist/utils/arrays.d.ts.map +1 -1
- package/dist/utils/error.d.ts +2 -20
- package/dist/utils/error.d.ts.map +1 -1
- package/package.json +36 -9
- package/dist/archetype.d.ts +0 -108
- package/dist/archetype.d.ts.map +0 -1
- package/dist/component.d.ts +0 -45
- package/dist/component.d.ts.map +0 -1
- package/dist/ecs.d.ts +0 -104
- package/dist/ecs.d.ts.map +0 -1
- package/dist/entity.d.ts +0 -11
- package/dist/entity.d.ts.map +0 -1
- package/dist/event.d.ts +0 -30
- package/dist/event.d.ts.map +0 -1
- package/dist/query.d.ts +0 -94
- package/dist/query.d.ts.map +0 -1
- package/dist/ref.d.ts +0 -23
- package/dist/ref.d.ts.map +0 -1
- package/dist/resource.d.ts +0 -23
- package/dist/resource.d.ts.map +0 -1
- package/dist/schedule.d.ts +0 -45
- package/dist/schedule.d.ts.map +0 -1
- package/dist/store.d.ts +0 -118
- package/dist/store.d.ts.map +0 -1
- package/dist/system.d.ts +0 -16
- package/dist/system.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,289 +1,188 @@
|
|
|
1
1
|
# oecs
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
|
|
3
|
+
**A full-featured, archetype-based Entity Component System for TypeScript.**
|
|
4
|
+
|
|
5
|
+
`@oasys/oecs` is a complete ECS — not just storage-and-queries, but the whole toolkit you expect from a
|
|
6
|
+
mature engine: observers, relations with wildcards, sparse storage, system sets and run conditions,
|
|
7
|
+
entity enable/disable, templates, deterministic hashing with snapshot/restore, a typed host→ECS write
|
|
8
|
+
seam, and an optional reactive UI bridge. It is **pure TypeScript and zero-dependency by default** — it
|
|
9
|
+
runs over a plain resizable `ArrayBuffer`, so it needs no `SharedArrayBuffer` and no cross-origin
|
|
10
|
+
isolation (COOP/COEP). An opt-in shared-memory profile swaps in a `SharedArrayBuffer` for worker offload
|
|
11
|
+
or a WASM compute backend; both profiles share one core and agree, byte-for-byte, on `stateHash`.
|
|
12
|
+
|
|
13
|
+
- **Fast** — struct-of-arrays column storage grouped by archetype; iteration is a tight loop over typed
|
|
14
|
+
arrays with no per-entity object allocation.
|
|
15
|
+
- **Type-safe** — components are branded integers at runtime and fully-typed schemas at compile time;
|
|
16
|
+
misspelled fields are compile errors.
|
|
17
|
+
- **Deterministic** — an opt-in mode gives a backing-agnostic `stateHash` plus snapshot/restore and
|
|
18
|
+
command-log replay.
|
|
19
|
+
- **Complete** — the feature surface below is the whole engine, not a starting point.
|
|
17
20
|
|
|
18
21
|
## Installation
|
|
19
22
|
|
|
20
23
|
```bash
|
|
21
|
-
pnpm add @oasys/oecs
|
|
24
|
+
pnpm add @oasys/oecs # npm / pnpm / yarn
|
|
25
|
+
# or
|
|
26
|
+
deno add jsr:@oasys/oecs # JSR (Deno)
|
|
27
|
+
# or
|
|
28
|
+
npx jsr add @oasys/oecs # JSR (npm-compatible)
|
|
22
29
|
```
|
|
23
30
|
|
|
24
31
|
## Quick start
|
|
25
32
|
|
|
26
33
|
```ts
|
|
27
|
-
import { ECS, SCHEDULE
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
// System — query resolved once at registration
|
|
51
|
-
const moveSys = world.register_system(
|
|
52
|
-
(q, ctx, dt) => {
|
|
53
|
-
q.for_each((arch) => {
|
|
54
|
-
const px = arch.get_column_mut(Pos, "x", ctx.world_tick);
|
|
55
|
-
const py = arch.get_column_mut(Pos, "y", ctx.world_tick);
|
|
56
|
-
const vx = arch.get_column(Vel, "vx");
|
|
57
|
-
const vy = arch.get_column(Vel, "vy");
|
|
58
|
-
for (let i = 0; i < arch.entity_count; i++) {
|
|
59
|
-
px[i] += vx[i] * dt;
|
|
60
|
-
py[i] += vy[i] * dt;
|
|
34
|
+
import { ECS, SCHEDULE } from "@oasys/oecs";
|
|
35
|
+
|
|
36
|
+
const world = new ECS(); // pure-TS heap profile — no SharedArrayBuffer needed
|
|
37
|
+
|
|
38
|
+
// Components — record syntax (per-field type) or array shorthand (defaults to "f64")
|
|
39
|
+
const Pos = world.registerComponent({ x: "f64", y: "f64" });
|
|
40
|
+
const Vel = world.registerComponent(["vx", "vy"] as const);
|
|
41
|
+
|
|
42
|
+
// A query is a live, cached view over matching archetypes — build it once, reuse it.
|
|
43
|
+
const movers = world.query(Pos, Vel);
|
|
44
|
+
|
|
45
|
+
// Systems declare the components they read/write (checked in dev builds).
|
|
46
|
+
const move = world.registerSystem({
|
|
47
|
+
reads: [Vel],
|
|
48
|
+
writes: [Pos], // a declared write implies read of the same component
|
|
49
|
+
fn: (ctx, dt) => {
|
|
50
|
+
movers.eachChunk((cols, count) => {
|
|
51
|
+
const { x, y } = cols.mut(Pos); // whole group; stamps Pos's change tick once
|
|
52
|
+
const { vx, vy } = cols.read(Vel); // read-only group
|
|
53
|
+
for (let i = 0; i < count; i++) {
|
|
54
|
+
x[i] += vx[i] * dt;
|
|
55
|
+
y[i] += vy[i] * dt;
|
|
61
56
|
}
|
|
62
57
|
});
|
|
63
58
|
},
|
|
64
|
-
(qb) => qb.every(Pos, Vel),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
world.add_systems(SCHEDULE.UPDATE, moveSys);
|
|
68
|
-
world.startup();
|
|
69
|
-
|
|
70
|
-
let last = performance.now();
|
|
71
|
-
function frame() {
|
|
72
|
-
const now = performance.now();
|
|
73
|
-
const dt = (now - last) / 1000;
|
|
74
|
-
last = now;
|
|
75
|
-
const t = world.resource(Time);
|
|
76
|
-
world.set_resource(Time, { delta: dt, elapsed: t.elapsed + dt });
|
|
77
|
-
world.update(dt);
|
|
78
|
-
requestAnimationFrame(frame);
|
|
79
|
-
}
|
|
80
|
-
requestAnimationFrame(frame);
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## World options
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
const world = new ECS({
|
|
87
|
-
initial_capacity: 4096,
|
|
88
|
-
fixed_timestep: 1 / 50,
|
|
89
|
-
max_fixed_steps: 4,
|
|
90
59
|
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
| Option | Type | Default | Description |
|
|
94
|
-
| ------------------ | -------- | ------- | ----------- |
|
|
95
|
-
| `initial_capacity` | `number` | `1024` | Starting size of each archetype's entity-ID and column buffers. Buffers double on overflow; pick close to your expected per-archetype entity count to avoid early reallocations. |
|
|
96
|
-
| `fixed_timestep` | `number` | `1/60` | Interval (seconds) at which `SCHEDULE.FIXED_UPDATE` systems run. |
|
|
97
|
-
| `max_fixed_steps` | `number` | `5` | Hard cap on fixed-update iterations per frame. Protects against spiral of death. |
|
|
98
|
-
|
|
99
|
-
## Components
|
|
100
|
-
|
|
101
|
-
Records give per-field type control; array shorthand defaults to `f64`. Tags have no fields.
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
const Pos = world.register_component({ x: "f64", y: "f64" });
|
|
105
|
-
const Health = world.register_component({ current: "i32", max: "i32" });
|
|
106
|
-
const Vel = world.register_component(["vx", "vy"] as const);
|
|
107
|
-
const IsEnemy = world.register_tag();
|
|
108
|
-
|
|
109
|
-
world.add_components(e, [
|
|
110
|
-
{ def: Pos, values: { x: 0, y: 0 } },
|
|
111
|
-
{ def: Vel, values: { vx: 1, vy: 0 } },
|
|
112
|
-
{ def: IsEnemy },
|
|
113
|
-
]);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Supported tags: `f32`, `f64`, `i8`, `i16`, `i32`, `u8`, `u16`, `u32`.
|
|
117
|
-
|
|
118
|
-
See [docs/api/components.md](docs/api/components.md).
|
|
119
|
-
|
|
120
|
-
## Queries
|
|
121
|
-
|
|
122
|
-
Live, cached views over matching archetypes. Iterate with `for_each`.
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
const q = world.query(Pos, Vel);
|
|
126
|
-
|
|
127
|
-
q.for_each((arch) => {
|
|
128
|
-
const px = arch.get_column(Pos, "x");
|
|
129
|
-
const py = arch.get_column(Pos, "y");
|
|
130
|
-
for (let i = 0; i < arch.entity_count; i++) { /* ... */ }
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Chaining returns new cached queries
|
|
134
|
-
const targets = world.query(Pos).and(Health).not(Shield).any_of(IsEnemy, IsBoss);
|
|
135
|
-
|
|
136
|
-
// Change detection — only archetypes whose Pos column changed since last run
|
|
137
|
-
q.changed(Pos).for_each((arch) => { /* ... */ });
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
See [docs/api/queries.md](docs/api/queries.md) and [docs/api/change-detection.md](docs/api/change-detection.md).
|
|
141
|
-
|
|
142
|
-
## Systems
|
|
143
|
-
|
|
144
|
-
Systems are plain functions. Three registration shapes all return a `SystemDescriptor`.
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
// Bare function
|
|
148
|
-
const logSys = world.register_system((ctx, dt) => { /* ... */ });
|
|
149
|
-
|
|
150
|
-
// Function + query builder (query resolved once at registration)
|
|
151
|
-
const moveSys = world.register_system(
|
|
152
|
-
(q, ctx, dt) => { q.for_each((arch) => { /* ... */ }); },
|
|
153
|
-
(qb) => qb.every(Pos, Vel),
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
// Full config — lifecycle hooks, name
|
|
157
|
-
const spawnSys = world.register_system({
|
|
158
|
-
name: "spawn",
|
|
159
|
-
fn(ctx, dt) { /* every frame */ },
|
|
160
|
-
on_added(ctx) { /* once during world.startup() */ },
|
|
161
|
-
dispose() { /* during world.dispose() */ },
|
|
162
|
-
});
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
`SystemContext` exposes deferred structural ops, per-entity access, events, resources, and tick bookkeeping (`ctx.world_tick`, `ctx.last_run_tick`).
|
|
166
|
-
|
|
167
|
-
See [docs/api/systems.md](docs/api/systems.md).
|
|
168
|
-
|
|
169
|
-
## Resources
|
|
170
|
-
|
|
171
|
-
Global singletons keyed by `ResourceKey<T>`. Values can be any type — objects, typed arrays, class instances.
|
|
172
|
-
|
|
173
|
-
```ts
|
|
174
|
-
import { resource_key } from "@oasys/oecs";
|
|
175
|
-
|
|
176
|
-
const Time = resource_key<{ delta: number; elapsed: number }>("Time");
|
|
177
|
-
const Assets = resource_key<Map<string, ImageBitmap>>("Assets");
|
|
178
|
-
|
|
179
|
-
world.register_resource(Time, { delta: 0, elapsed: 0 });
|
|
180
|
-
world.register_resource(Assets, new Map());
|
|
181
|
-
|
|
182
|
-
const t = world.resource(Time); // typed as { delta, elapsed }
|
|
183
|
-
world.set_resource(Time, { delta: 0.016, elapsed: 0 }); // swap in a new value
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
See [docs/api/resources.md](docs/api/resources.md).
|
|
187
|
-
|
|
188
|
-
## Events
|
|
189
60
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
import { event_key, signal_key } from "@oasys/oecs";
|
|
194
|
-
|
|
195
|
-
const DamageEvent = event_key<readonly ["target", "amount"]>("Damage");
|
|
196
|
-
const GameOver = signal_key("GameOver");
|
|
197
|
-
|
|
198
|
-
world.register_event(DamageEvent, ["target", "amount"] as const);
|
|
199
|
-
world.register_signal(GameOver);
|
|
200
|
-
|
|
201
|
-
ctx.emit(DamageEvent, { target: victimId, amount: 25 });
|
|
202
|
-
ctx.emit(GameOver);
|
|
203
|
-
|
|
204
|
-
const dmg = ctx.read(DamageEvent);
|
|
205
|
-
for (let i = 0; i < dmg.length; i++) {
|
|
206
|
-
dmg.target[i]; dmg.amount[i]; // number columns
|
|
207
|
-
}
|
|
208
|
-
if (ctx.read(GameOver).length > 0) { /* fired */ }
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
See [docs/api/events.md](docs/api/events.md).
|
|
212
|
-
|
|
213
|
-
## Refs
|
|
214
|
-
|
|
215
|
-
Cached single-entity handles — resolve archetype + row + column once, then read/write fields by name.
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
const pos = ctx.ref_mut(Pos, entity); // writable; bumps Pos change tick
|
|
219
|
-
const vel = ctx.ref(Vel, entity); // readonly
|
|
220
|
-
pos.x += vel.vx * dt;
|
|
221
|
-
pos.y += vel.vy * dt;
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Prefer `ctx.ref` by default; reach for `ctx.ref_mut` at the point of mutation. Do not hold refs across archetype transitions or phase flushes.
|
|
225
|
-
|
|
226
|
-
See [docs/api/refs.md](docs/api/refs.md).
|
|
227
|
-
|
|
228
|
-
## Schedule
|
|
229
|
-
|
|
230
|
-
Seven phases run in a fixed order:
|
|
231
|
-
|
|
232
|
-
| Phase | When | Typical use |
|
|
233
|
-
| -------------- | -------------------------------- | ----------------------- |
|
|
234
|
-
| `PRE_STARTUP` | Once, before `STARTUP` | Resource loading |
|
|
235
|
-
| `STARTUP` | Once | Initial entity spawning |
|
|
236
|
-
| `POST_STARTUP` | Once, after `STARTUP` | Validation |
|
|
237
|
-
| `FIXED_UPDATE` | Zero+ times per frame (fixed dt) | Physics, simulation |
|
|
238
|
-
| `PRE_UPDATE` | Every frame, first | Input, time |
|
|
239
|
-
| `UPDATE` | Every frame | Game logic, AI |
|
|
240
|
-
| `POST_UPDATE` | Every frame, last | Rendering, cleanup |
|
|
241
|
-
|
|
242
|
-
```ts
|
|
243
|
-
world.add_systems(SCHEDULE.UPDATE, moveSys, damageSys, {
|
|
244
|
-
system: deathSys,
|
|
245
|
-
ordering: { after: [damageSys] },
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
Within a phase, systems are topologically sorted by `before` / `after` constraints. `ctx.flush()` runs automatically between phases.
|
|
250
|
-
|
|
251
|
-
See [docs/api/schedule.md](docs/api/schedule.md).
|
|
61
|
+
world.addSystems(SCHEDULE.UPDATE, move);
|
|
62
|
+
world.startup();
|
|
252
63
|
|
|
253
|
-
|
|
64
|
+
const e = world.createEntity();
|
|
65
|
+
world.addComponent(e, Pos, { x: 0, y: 0 });
|
|
66
|
+
world.addComponent(e, Vel, { vx: 100, vy: 50 });
|
|
254
67
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
world.is_alive(e); // true
|
|
258
|
-
world.destroy_entity_deferred(e);
|
|
259
|
-
world.flush();
|
|
260
|
-
world.is_alive(e); // false
|
|
68
|
+
world.update(1 / 60);
|
|
69
|
+
world.getField(e, Pos, "x"); // ≈ 1.667
|
|
261
70
|
```
|
|
262
71
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
See [docs/api/entities.md](docs/api/entities.md).
|
|
266
|
-
|
|
267
|
-
## Dev vs Prod modes
|
|
268
|
-
|
|
269
|
-
A compile-time `__DEV__` flag gates runtime sanity checks: bounds checks, liveness checks, duplicate-system detection, and registration validation. These are tree-shaken out of production bundles by the Vite build. Scheduler cycle detection is always active and throws `ECS_ERROR.CIRCULAR_SYSTEM_DEPENDENCY` on the first offending run.
|
|
270
|
-
|
|
271
|
-
## Development
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
pnpm install
|
|
275
|
-
pnpm test # vitest
|
|
276
|
-
pnpm bench # vitest bench
|
|
277
|
-
pnpm build # vite library build
|
|
278
|
-
pnpm tsc --noEmit # type check
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Guides
|
|
72
|
+
## Features
|
|
282
73
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
-
|
|
286
|
-
-
|
|
74
|
+
**Storage & data model**
|
|
75
|
+
|
|
76
|
+
- **Archetype SoA storage** over a backing-neutral `ColumnStore` — entities with the same component set
|
|
77
|
+
share contiguous typed-array columns; cache-friendly loops, no per-entity object allocation.
|
|
78
|
+
- **Phantom-typed components** — `registerComponent({ x: "f64", y: "f64" })` is a branded integer at
|
|
79
|
+
runtime and a fully-typed schema at compile time. Record syntax for per-field types, array shorthand
|
|
80
|
+
for uniform `f64`, and `registerTag()` for data-free markers. Field types: `f32 f64 i8 i16 i32 u8 u16 u32`.
|
|
81
|
+
- **Two storage profiles, one core** — pure-TS heap (`ArrayBuffer`) by default; opt-in
|
|
82
|
+
`SharedArrayBuffer` for workers / WASM. Same code path, same `stateHash`, sized through a single
|
|
83
|
+
`memory` surface (entity budget, byte cap, or pinned capacity).
|
|
84
|
+
|
|
85
|
+
**Queries**
|
|
86
|
+
|
|
87
|
+
- **Live, cached queries** — `world.query(Pos, Vel)` refined with `.and()` / `.without()` / `.anyOf()`;
|
|
88
|
+
new matching archetypes are pushed in automatically.
|
|
89
|
+
- **Two iteration verbs** — `forEach(arch => …)` for read-only archetype iteration, `eachChunk((cols, count) => …)`
|
|
90
|
+
for the mutable hot path (`cols.mut` / `cols.read` resolve a whole component's columns at once).
|
|
91
|
+
- **Change detection** — per-`(archetype, component)` change ticks; `query.changed(Pos)` visits only
|
|
92
|
+
archetypes written since the system's threshold tick.
|
|
93
|
+
- **Relation & hierarchy queries** — `(R, *)` / `(*, T)` wildcards, `forEachRelatedTo`, and
|
|
94
|
+
`query.hierarchy(rel, depth)`. **Sparse queries** via `query.withSparse(...)`; disabled entities are
|
|
95
|
+
skipped unless you opt in with `query.includeDisabled()`.
|
|
96
|
+
|
|
97
|
+
**Systems & scheduling**
|
|
98
|
+
|
|
99
|
+
- **Declarative systems** — plain functions in a `SystemConfig` declaring `reads` / `writes`, enforced by
|
|
100
|
+
a dev-mode access checker (tree-shaken in production). Bare `(ctx, dt)` and `(q, ctx, dt)` +
|
|
101
|
+
query-builder overloads exist for access-free glue; lifecycle hooks `onAdded` / `onRemoved` / `dispose`;
|
|
102
|
+
`exclusive: true` for full-world setup/teardown.
|
|
103
|
+
- **Topological scheduler** — seven phases (`PRE_STARTUP` → `STARTUP` → `POST_STARTUP`, `FIXED_UPDATE`,
|
|
104
|
+
`PRE_UPDATE` → `UPDATE` → `POST_UPDATE`); per-phase Kahn sort by `before` / `after`, with insertion
|
|
105
|
+
order as a deterministic tiebreaker. Always-on cycle detection.
|
|
106
|
+
- **Fixed timestep** — accumulator loop with configurable `fixedTimestep` and spiral-of-death protection.
|
|
107
|
+
- **System sets & run conditions** — `systemSet(...)` + `configureSet(...)`; `runIfResourceEq`,
|
|
108
|
+
`runEveryNTicks`, `runIfAnyMatch`, and custom `RunCondition`s.
|
|
109
|
+
|
|
110
|
+
**Structural changes**
|
|
111
|
+
|
|
112
|
+
- **Deferred by default** — `ctx.commands` (a Bevy-`Commands`-style facade) buffers
|
|
113
|
+
spawn / add / remove / despawn / enable / disable until the phase flush, so iterators stay valid.
|
|
114
|
+
`world.addComponent` etc. are the immediate counterparts.
|
|
115
|
+
- **Entity enable/disable** — `disable` / `enable` / `isDisabled`; disabled rows sit in a partitioned
|
|
116
|
+
tail and are skipped by default queries.
|
|
117
|
+
- **Templates & bundles** — `world.template([...])` blueprints consumed by `createEntity` /
|
|
118
|
+
`createEntities` for zero-transition spawns; `bundle(...)` + `spawnBundle(...)`.
|
|
119
|
+
|
|
120
|
+
**Reactivity & relationships**
|
|
121
|
+
|
|
122
|
+
- **Observers** — `world.observe(...)` for `onAdd` / `onRemove` / `onSet` / `onEnable` / `onDisable`,
|
|
123
|
+
structural or per-entity.
|
|
124
|
+
- **Relations** — `(relation, target)` pairs with `ChildOf` / `IsA` presets, exclusive / multi arities,
|
|
125
|
+
bidirectional queries (`targetOf` / `sourcesOf` / `ancestorsOf` / `rootOf` / `cascadeOf`), and
|
|
126
|
+
configurable on-delete cleanup (`delete` / `clear` / `orphan`). Stored sparsely — no archetype
|
|
127
|
+
transition, no identity bit.
|
|
128
|
+
- **Sparse storage** — `registerSparseComponent` / `registerSparseTag`, `addSparse` / `removeSparse` for
|
|
129
|
+
churny or rare data that shouldn't cause archetype transitions.
|
|
130
|
+
- **Resources** — typed global singletons via `resourceKey<T>`. **Events** — fire-and-forget SoA channels
|
|
131
|
+
via `eventKey<F>` / `signalKey`, cleared at the end of each `update`.
|
|
132
|
+
- **Cached refs** — `ctx.ref(def, e)` (mutable, bumps the change tick) / `ctx.refRead(def, e)`
|
|
133
|
+
(read-only): resolve archetype + row + column once, then `pos.x += vel.vx * dt`.
|
|
134
|
+
|
|
135
|
+
**Determinism, persistence & integration**
|
|
136
|
+
|
|
137
|
+
- **Determinism** (opt-in) — `new ECS({ deterministic: true })`, then `world.stateHash()` (FNV-1a over
|
|
138
|
+
live column bytes), `snapshot()` / `restoreInto(...)`, plus sparse variants. Backing-agnostic: a heap
|
|
139
|
+
world and a shared world with identical history produce identical hashes.
|
|
140
|
+
- **Host → ECS write seam** — `installHostCommandSeam(world)` applies typed `HostCommand`s off-schedule
|
|
141
|
+
via a blessed `exclusive` system, with record/replay (`HostCommandRecorder`, `replayCommandLog`) and a
|
|
142
|
+
cross-thread ring transport.
|
|
143
|
+
- **Reactive UI seam** (optional) — a zero-dep signals kernel (`@oasys/oecs/reactive`), an ECS→reactive
|
|
144
|
+
bridge that publishes only dirty entities/columns (`@oasys/oecs/reactive-sync`), and a SolidJS adapter
|
|
145
|
+
(`@oasys/oecs/solid`).
|
|
146
|
+
- **Editor layer** — undo/redo + field handles over the write seam (`@oasys/oecs/editor`).
|
|
147
|
+
- **Frame tracing** — `world.setTrace(sink)` + `FrameTraceRecorder` for a structured per-frame event
|
|
148
|
+
stream (dev-gated).
|
|
149
|
+
- **Compute backend seam** — `world.attachBackend(...)` to run a system body on a compiled backend (WASM,
|
|
150
|
+
…) instead of its TS closure.
|
|
151
|
+
|
|
152
|
+
**Reference**
|
|
153
|
+
|
|
154
|
+
- **Typed errors** — an `ECSError` taxonomy with a `category` enum and an `isEcsError` guard, all exported.
|
|
155
|
+
- **Reusable primitives** (`@oasys/oecs/primitives`) — `BitSet`, `SparseSet`, `SparseMap`,
|
|
156
|
+
`GrowableTypedArray`, `BinaryHeap`, and `topologicalSort`, usable standalone.
|
|
157
|
+
|
|
158
|
+
## Entry points
|
|
159
|
+
|
|
160
|
+
The core is `@oasys/oecs`; everything else is opt-in and costs nothing until imported.
|
|
161
|
+
|
|
162
|
+
| Import | What it is |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| `@oasys/oecs` | the ECS — pure-TS heap profile by default |
|
|
165
|
+
| `@oasys/oecs/shared` | opt-in `SharedArrayBuffer` allocators for worker offload / a WASM backend (needs COOP/COEP) |
|
|
166
|
+
| `@oasys/oecs/reactive` | zero-dependency reactive kernel (`signal`/`computed`/`effect`, reactive collections) |
|
|
167
|
+
| `@oasys/oecs/reactive-sync` | ECS→reactive bridge — publishes only dirty entities/columns |
|
|
168
|
+
| `@oasys/oecs/editor` | undo/redo + field-handle layer over the host-write seam |
|
|
169
|
+
| `@oasys/oecs/solid` | SolidJS adapter (`solid-js` is an **optional** peer dependency) |
|
|
170
|
+
| `@oasys/oecs/primitives` | the standalone data structures oecs is built on |
|
|
171
|
+
|
|
172
|
+
## Dev vs prod
|
|
173
|
+
|
|
174
|
+
A compile-time `__DEV__` flag gates every runtime check — bounds and liveness checks, duplicate-system
|
|
175
|
+
detection, registration validation, and the system access checker (`reads`/`writes`). These are
|
|
176
|
+
**tree-shaken out of production builds**, so treat "throws in dev" as a development tripwire, not a
|
|
177
|
+
production guarantee. The scheduler's cycle detection is the one check that is always active.
|
|
178
|
+
|
|
179
|
+
## Documentation
|
|
180
|
+
|
|
181
|
+
- **New to oecs?** Start with the [Getting Started](docs/GETTING_STARTED.md) tutorial, then
|
|
182
|
+
[Best Practices](docs/BEST_PRACTICES.md) and the [Architecture](docs/ARCHITECTURE.md) overview.
|
|
183
|
+
- **Upgrading from 0.3?** See the [Migration guide (0.3 → 0.4)](docs/MIGRATION-0.3-to-0.4.md) and the
|
|
184
|
+
[CHANGELOG](CHANGELOG.md).
|
|
185
|
+
- **Full API reference** — start at the [reference index](docs/api/index.md):
|
|
287
186
|
[components](docs/api/components.md) ·
|
|
288
187
|
[entities](docs/api/entities.md) ·
|
|
289
188
|
[queries](docs/api/queries.md) ·
|
|
@@ -293,8 +192,38 @@ pnpm tsc --noEmit # type check
|
|
|
293
192
|
[events](docs/api/events.md) ·
|
|
294
193
|
[refs](docs/api/refs.md) ·
|
|
295
194
|
[change detection](docs/api/change-detection.md) ·
|
|
296
|
-
[
|
|
195
|
+
[observers](docs/api/observers.md) ·
|
|
196
|
+
[relations](docs/api/relations.md) ·
|
|
197
|
+
[sparse storage](docs/api/sparse-storage.md) ·
|
|
198
|
+
[determinism](docs/api/determinism.md) ·
|
|
199
|
+
[memory](docs/api/memory.md) ·
|
|
200
|
+
[host-write seam](docs/api/host-write-seam.md) ·
|
|
201
|
+
[reactive](docs/api/reactive.md) ·
|
|
202
|
+
[editor](docs/api/editor.md) ·
|
|
203
|
+
[tracing](docs/api/tracing.md) ·
|
|
204
|
+
[primitives](docs/api/primitives.md) ·
|
|
205
|
+
[errors](docs/api/errors.md)
|
|
206
|
+
|
|
207
|
+
## Development
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
pnpm install
|
|
211
|
+
pnpm test # vitest
|
|
212
|
+
pnpm bench # vitest bench
|
|
213
|
+
pnpm build # vite library build (multi-entry → dist/)
|
|
214
|
+
pnpm exec tsc --noEmit # type check
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Acknowledgements
|
|
218
|
+
|
|
219
|
+
oecs stands on the shoulders of the ECS community. Special thanks to:
|
|
220
|
+
|
|
221
|
+
- **[Bevy](https://bevyengine.org)**, **[Flecs](https://github.com/SanderMertens/flecs)**, and
|
|
222
|
+
**[bitECS](https://github.com/NateTheGreatt/bitECS)** — a constant source of inspiration; their
|
|
223
|
+
designs shaped how oecs approaches archetypes, relations, scheduling, and change detection.
|
|
224
|
+
- **[@clinuxrulz](https://github.com/clinuxrulz)** — for his amazing showcase and invaluable input on
|
|
225
|
+
the ECS.
|
|
297
226
|
|
|
298
227
|
## License
|
|
299
228
|
|
|
300
|
-
MIT
|
|
229
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const g=require("./kernel-yWV3XnAb.cjs");function w(v=Object.is){const l=new Map,[t,f]=g.signal(0);let u=0;const h=(c,n)=>c!==void 0&&n!==void 0?v(c,n):c===n;return{get(c){const n=l.get(c);if(n!==void 0)return n[0]();t()},set(c,n){const s=l.get(c);s!==void 0?s[1](n):(l.set(c,g.signal(n,h)),f(++u))},delete(c){const n=l.get(c);return n===void 0?!1:(l.delete(c),g.batch(()=>{n[1](void 0),f(++u)}),!0)},has(c){return t(),l.has(c)},size(){return t(),l.size},keys(){return t(),[...l.keys()]}}}function M(v,l={}){const t={},f={},u=Object.keys(v),h=new Set(u);for(const n of u){const[s,y]=g.signal(v[n],l[n]);t[n]=s,f[n]=y}return[new Proxy({},{get:(n,s)=>h.has(s)?t[s]():Reflect.get(n,s),has:(n,s)=>h.has(s),ownKeys:()=>u,getOwnPropertyDescriptor:(n,s)=>h.has(s)?{get:()=>t[s](),enumerable:!0,configurable:!0}:void 0}),f]}const m=Symbol("reactiveArray.absent");function A(v=[],l=Object.is){const t=[],[f,u]=g.signal(0);let h=0;const c=(e,r)=>e!==m&&r!==m?l(e,r):e===r,n=e=>g.signal(e,c),s=e=>g.untrack(t[e][0]);for(const e of v)t.push(n(e));function y(e){g.batch(()=>{const r=t.length,a=e.length,p=a<r?a:r;for(let o=0;o<p;o++)t[o][1](e[o]);if(a>r){for(let o=r;o<a;o++)t.push(n(e[o]));u(++h)}else if(a<r){const o=t.splice(a);for(let d=0;d<o.length;d++)o[d][1](m);u(++h)}})}return{get(e){if(e>=0&&e<t.length){const r=t[e][0]();if(r!==m)return r}f()},length(){return f(),t.length},snapshot(){f();const e=new Array(t.length);for(let r=0;r<t.length;r++)e[r]=t[r][0]();return e},set(e,r){e>=0&&e<t.length&&t[e][1](r)},push(e){t.push(n(e)),u(++h)},pop(){const e=t.length;if(e===0)return;const r=s(e-1),[a]=t.splice(e-1);return g.batch(()=>{a[1](m),u(++h)}),r},splice(e,r,...a){const p=t.length,o=e<0?Math.max(p+e,0):Math.min(e,p),d=r===void 0?p-o:Math.max(0,Math.min(r,p-o)),S=[];for(let i=0;i<d;i++)S.push(s(o+i));const b=[];for(let i=0;i<o;i++)b.push(s(i));for(let i=0;i<a.length;i++)b.push(a[i]);for(let i=o+d;i<p;i++)b.push(s(i));return y(b),S},reconcile(e){y(e)}}}exports.reactiveArray=A;exports.reactiveMap=w;exports.reactiveStruct=M;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { s as m, b as w, u as M } from "./kernel-DgyrLFjW.js";
|
|
2
|
+
function A(g = Object.is) {
|
|
3
|
+
const i = /* @__PURE__ */ new Map(), [t, f] = m(0);
|
|
4
|
+
let u = 0;
|
|
5
|
+
const a = (c, n) => c !== void 0 && n !== void 0 ? g(c, n) : c === n;
|
|
6
|
+
return {
|
|
7
|
+
get(c) {
|
|
8
|
+
const n = i.get(c);
|
|
9
|
+
if (n !== void 0) return n[0]();
|
|
10
|
+
t();
|
|
11
|
+
},
|
|
12
|
+
set(c, n) {
|
|
13
|
+
const s = i.get(c);
|
|
14
|
+
s !== void 0 ? s[1](n) : (i.set(c, m(n, a)), f(++u));
|
|
15
|
+
},
|
|
16
|
+
delete(c) {
|
|
17
|
+
const n = i.get(c);
|
|
18
|
+
return n === void 0 ? !1 : (i.delete(c), w(() => {
|
|
19
|
+
n[1](void 0), f(++u);
|
|
20
|
+
}), !0);
|
|
21
|
+
},
|
|
22
|
+
has(c) {
|
|
23
|
+
return t(), i.has(c);
|
|
24
|
+
},
|
|
25
|
+
size() {
|
|
26
|
+
return t(), i.size;
|
|
27
|
+
},
|
|
28
|
+
keys() {
|
|
29
|
+
return t(), [...i.keys()];
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function O(g, i = {}) {
|
|
34
|
+
const t = {}, f = {}, u = Object.keys(g), a = new Set(u);
|
|
35
|
+
for (const n of u) {
|
|
36
|
+
const [s, y] = m(
|
|
37
|
+
g[n],
|
|
38
|
+
i[n]
|
|
39
|
+
);
|
|
40
|
+
t[n] = s, f[n] = y;
|
|
41
|
+
}
|
|
42
|
+
return [new Proxy({}, {
|
|
43
|
+
// A field read subscribes; a NON-field key must not throw. `JSON.stringify`
|
|
44
|
+
// (`toJSON`), `await proxy` (`then`), `String(proxy)` (`Symbol.toPrimitive`)
|
|
45
|
+
// and `for..of` (`Symbol.iterator`) all probe keys that aren't fields — fall
|
|
46
|
+
// through to the (empty) target so they see the ordinary undefined/inherited
|
|
47
|
+
// value instead of calling `undefined()`. `fieldSet` (not `k in reads`) so
|
|
48
|
+
// inherited `toString`/`constructor` aren't mistaken for fields.
|
|
49
|
+
get: (n, s) => a.has(s) ? t[s]() : Reflect.get(n, s),
|
|
50
|
+
has: (n, s) => a.has(s),
|
|
51
|
+
// Enumerable without subscribing: enumeration calls `ownKeys` +
|
|
52
|
+
// `getOwnPropertyDescriptor`, never `get`, so `Object.keys(proxy)` returns
|
|
53
|
+
// the field set and tracks nothing. The descriptor is an ACCESSOR whose `get`
|
|
54
|
+
// reads the live signal, so `{...proxy}` / `Object.values(proxy)` /
|
|
55
|
+
// `Object.getOwnPropertyDescriptor(proxy, f).value` see the current value
|
|
56
|
+
// (a value-less descriptor would normalize to `value: undefined`). Non-field
|
|
57
|
+
// keys report no own descriptor. The target is the empty (extensible) object,
|
|
58
|
+
// so these configurable own keys satisfy the Proxy invariants.
|
|
59
|
+
ownKeys: () => u,
|
|
60
|
+
getOwnPropertyDescriptor: (n, s) => a.has(s) ? { get: () => t[s](), enumerable: !0, configurable: !0 } : void 0
|
|
61
|
+
}), f];
|
|
62
|
+
}
|
|
63
|
+
const v = Symbol("reactiveArray.absent");
|
|
64
|
+
function j(g = [], i = Object.is) {
|
|
65
|
+
const t = [], [f, u] = m(0);
|
|
66
|
+
let a = 0;
|
|
67
|
+
const c = (e, r) => e !== v && r !== v ? i(e, r) : e === r, n = (e) => m(e, c), s = (e) => M(t[e][0]);
|
|
68
|
+
for (const e of g) t.push(n(e));
|
|
69
|
+
function y(e) {
|
|
70
|
+
w(() => {
|
|
71
|
+
const r = t.length, h = e.length, p = h < r ? h : r;
|
|
72
|
+
for (let o = 0; o < p; o++) t[o][1](e[o]);
|
|
73
|
+
if (h > r) {
|
|
74
|
+
for (let o = r; o < h; o++) t.push(n(e[o]));
|
|
75
|
+
u(++a);
|
|
76
|
+
} else if (h < r) {
|
|
77
|
+
const o = t.splice(h);
|
|
78
|
+
for (let d = 0; d < o.length; d++) o[d][1](v);
|
|
79
|
+
u(++a);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
get(e) {
|
|
85
|
+
if (e >= 0 && e < t.length) {
|
|
86
|
+
const r = t[e][0]();
|
|
87
|
+
if (r !== v) return r;
|
|
88
|
+
}
|
|
89
|
+
f();
|
|
90
|
+
},
|
|
91
|
+
length() {
|
|
92
|
+
return f(), t.length;
|
|
93
|
+
},
|
|
94
|
+
snapshot() {
|
|
95
|
+
f();
|
|
96
|
+
const e = new Array(t.length);
|
|
97
|
+
for (let r = 0; r < t.length; r++) e[r] = t[r][0]();
|
|
98
|
+
return e;
|
|
99
|
+
},
|
|
100
|
+
set(e, r) {
|
|
101
|
+
e >= 0 && e < t.length && t[e][1](r);
|
|
102
|
+
},
|
|
103
|
+
push(e) {
|
|
104
|
+
t.push(n(e)), u(++a);
|
|
105
|
+
},
|
|
106
|
+
pop() {
|
|
107
|
+
const e = t.length;
|
|
108
|
+
if (e === 0) return;
|
|
109
|
+
const r = s(e - 1), [h] = t.splice(e - 1);
|
|
110
|
+
return w(() => {
|
|
111
|
+
h[1](v), u(++a);
|
|
112
|
+
}), r;
|
|
113
|
+
},
|
|
114
|
+
splice(e, r, ...h) {
|
|
115
|
+
const p = t.length, o = e < 0 ? Math.max(p + e, 0) : Math.min(e, p), d = r === void 0 ? p - o : Math.max(0, Math.min(r, p - o)), S = [];
|
|
116
|
+
for (let l = 0; l < d; l++) S.push(s(o + l));
|
|
117
|
+
const b = [];
|
|
118
|
+
for (let l = 0; l < o; l++) b.push(s(l));
|
|
119
|
+
for (let l = 0; l < h.length; l++) b.push(h[l]);
|
|
120
|
+
for (let l = o + d; l < p; l++) b.push(s(l));
|
|
121
|
+
return y(b), S;
|
|
122
|
+
},
|
|
123
|
+
reconcile(e) {
|
|
124
|
+
y(e);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
O as a,
|
|
130
|
+
j as b,
|
|
131
|
+
A as r
|
|
132
|
+
};
|