@oasys/oecs 0.5.1 → 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.
- package/CHANGELOG.md +24 -0
- package/README.md +12 -4
- package/dist/dev_flag.d.ts.map +1 -1
- package/dist/extensions/editor/index.development.cjs +1 -0
- package/dist/extensions/editor/index.development.js +239 -0
- package/dist/extensions/reactive/index.development.cjs +1 -0
- package/dist/extensions/reactive/index.development.js +215 -0
- package/dist/extensions/solid/index.development.cjs +1 -0
- package/dist/extensions/solid/index.development.js +40 -0
- package/dist/index.cjs +1 -1
- package/dist/index.development.cjs +1 -0
- package/dist/index.development.js +8116 -0
- package/dist/index.js +1 -1
- package/dist/internal-BMDgWZbt.js +2485 -0
- package/dist/internal-LiTSB_tG.cjs +2 -0
- package/dist/internal.development.cjs +1 -0
- package/dist/internal.development.js +24 -0
- package/dist/interop-DqxleBo7.js +18 -0
- package/dist/interop-T1HZ-Dpa.cjs +1 -0
- package/dist/kernel-Cz2Kxlwl.js +240 -0
- package/dist/kernel-sw7ScKWu.cjs +1 -0
- package/dist/primitives.development.cjs +1 -0
- package/dist/primitives.development.js +45 -0
- package/dist/reactive/index.development.cjs +1 -0
- package/dist/reactive/index.development.js +18 -0
- package/dist/shallow-9S1pY_Iw.js +159 -0
- package/dist/shallow-C5YmTbtb.cjs +1 -0
- package/dist/shared.development.cjs +1 -0
- package/dist/shared.development.js +7 -0
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +31 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ 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
|
+
|
|
8
32
|
## [0.5.1] — 2026-07-06
|
|
9
33
|
|
|
10
34
|
### Changed (breaking) — one attach grammar
|
package/README.md
CHANGED
|
@@ -168,7 +168,8 @@ The core is `@oasys/oecs`; everything else is opt-in and costs nothing until imp
|
|
|
168
168
|
|
|
169
169
|
| Import | What it is |
|
|
170
170
|
| --- | --- |
|
|
171
|
-
| `@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) |
|
|
172
173
|
| `@oasys/oecs/shared` | opt-in `SharedArrayBuffer` allocators for worker offload / a WASM backend (needs COOP/COEP) |
|
|
173
174
|
| `@oasys/oecs/reactive` | zero-dependency reactive kernel (`signal`/`computed`/`effect`, reactive collections) |
|
|
174
175
|
| `@oasys/oecs/reactive-sync` | ECS→reactive bridge — publishes only dirty entities/columns |
|
|
@@ -182,9 +183,16 @@ The core is `@oasys/oecs`; everything else is opt-in and costs nothing until imp
|
|
|
182
183
|
A compile-time `__DEV__` flag gates every runtime check — bounds and liveness checks, duplicate-system
|
|
183
184
|
detection, registration validation, and the system access checker (`reads`/`writes`). These are
|
|
184
185
|
**tree-shaken out of production builds**, so treat "throws in dev" as a development tripwire, not a
|
|
185
|
-
production guarantee.
|
|
186
|
-
|
|
187
|
-
|
|
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.
|
|
188
196
|
|
|
189
197
|
## Documentation
|
|
190
198
|
|
package/dist/dev_flag.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev_flag.d.ts","sourceRoot":"","sources":["../src/dev_flag.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev_flag.d.ts","sourceRoot":"","sources":["../src/dev_flag.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,GAAG,EAAE,OAA0D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function o(r,t,e){return`${r}:${t.id}:${e}`}const a=new WeakMap;class h{constructor(t,e){this.readField=t,this.shadow=e,a.set(this,{forward:[],inverse:[]})}staged=new Map;get _txn(){return a.get(this)}spawn(t,e){const s={kind:"despawn",eid:0};return this._txn.inverse.push(s),this._txn.forward.push({kind:"spawn",components:t,onSpawned:n=>{s.eid=n,e?.(n)}}),this}despawn(t,e){const s={kind:"despawn",eid:t};return this._txn.forward.push(s),this._txn.inverse.push({kind:"spawn",components:e,onSpawned:n=>{s.eid=n}}),this}setField(t,e,s,n){const i=o(t,e,s),d=this.staged.get(i)??this.shadow.get(i)??this.readField(t,e,s)??0;return this.staged.set(i,n),this._txn.forward.push({kind:"set_field",eid:t,def:e,field:s,value:n}),this._txn.inverse.push({kind:"set_field",eid:t,def:e,field:s,value:d}),this}add(t,e,s){return this._txn.forward.push({kind:"add_component",eid:t,def:e,values:s}),this._txn.inverse.push({kind:"remove_component",eid:t,def:e}),this}remove(t,e,s){return this._txn.forward.push({kind:"remove_component",eid:t,def:e}),this._txn.inverse.push({kind:"add_component",eid:t,def:e,values:s}),this}disable(t){return this._txn.forward.push({kind:"disable",eid:t}),this._txn.inverse.push({kind:"enable",eid:t}),this}enable(t){return this._txn.forward.push({kind:"enable",eid:t}),this._txn.inverse.push({kind:"disable",eid:t}),this}}class u{constructor(t,e){this.queue=t,this.readField=e}undoStack=[];redoStack=[];listeners=[];shadow=new Map;transaction(t){const e=new h(this.readField,this.shadow);return t(e),this.commit(a.get(e))}spawn(t,e){return this.transaction(s=>s.spawn(t,e))}despawn(t,e){return this.transaction(s=>s.despawn(t,e))}setField(t,e,s,n){return this.transaction(i=>i.setField(t,e,s,n))}add(t,e,s){return this.transaction(n=>n.add(t,e,s))}remove(t,e,s){return this.transaction(n=>n.remove(t,e,s))}disable(t){return this.transaction(e=>e.disable(t))}enable(t){return this.transaction(e=>e.enable(t))}undo(){const t=this.undoStack.pop();if(t===void 0)return!1;const e=t.inverse.slice().reverse();for(const s of e)this.queue.push(s);return this.applyShadow(e),this.redoStack.push(t),this.notify(),!0}redo(){const t=this.redoStack.pop();if(t===void 0)return!1;for(const e of t.forward)this.queue.push(e);return this.applyShadow(t.forward),this.undoStack.push(t),this.notify(),!0}clear(){this.undoStack.length=0,this.redoStack.length=0,this.shadow.clear(),this.notify()}depths(){return{undo:this.undoStack.length,redo:this.redoStack.length}}get canUndo(){return this.undoStack.length>0}get canRedo(){return this.redoStack.length>0}onChange(t){return this.listeners.push(t),()=>{const e=this.listeners.indexOf(t);e!==-1&&this.listeners.splice(e,1)}}notify(){for(const t of this.listeners.slice())t()}committedField(t,e,s){return this.readField(t,e,s)}pendingField(t,e,s){const n=o(t,e,s),i=this.shadow.get(n);if(i===void 0)return;const d=this.readField(t,e,s);if(d===i||d===void 0){this.shadow.delete(n);return}return i}commit(t){if(t.forward.length===0)return t;for(const e of t.forward)this.queue.push(e);return this.applyShadow(t.forward),this.undoStack.push(t),this.redoStack.length=0,this.notify(),t}applyShadow(t){for(const e of t)e.kind==="set_field"&&this.shadow.set(o(e.eid,e.def,e.field),e.value)}}function c(r,t,e,s,n){return{get value(){return n!==void 0?n():r.committedField(t,e,s)},set(i){r.setField(t,e,s,i)},get pending(){return r.pendingField(t,e,s)}}}exports.Editor=u;exports.TransactionBuilder=h;exports.fieldHandle=c;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
function o(r, t, e) {
|
|
2
|
+
return `${r}:${t.id}:${e}`;
|
|
3
|
+
}
|
|
4
|
+
const a = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
class h {
|
|
6
|
+
/** @internal */
|
|
7
|
+
constructor(t, e) {
|
|
8
|
+
this.readField = t, this.shadow = e, a.set(this, { forward: [], inverse: [] });
|
|
9
|
+
}
|
|
10
|
+
/** Values staged by THIS build, layered over the editor's shared shadow. Kept
|
|
11
|
+
* transaction-local so an aborted build (the callback throws before commit)
|
|
12
|
+
* leaves the shared shadow untouched — a phantom staged value would poison
|
|
13
|
+
* `pendingField` and seed the NEXT edit's inverse with a value the world never
|
|
14
|
+
* held. The one merge point into the shared shadow is commit's `applyShadow`. */
|
|
15
|
+
staged = /* @__PURE__ */ new Map();
|
|
16
|
+
get _txn() {
|
|
17
|
+
return a.get(this);
|
|
18
|
+
}
|
|
19
|
+
spawn(t, e) {
|
|
20
|
+
const s = {
|
|
21
|
+
kind: "despawn",
|
|
22
|
+
eid: 0
|
|
23
|
+
};
|
|
24
|
+
return this._txn.inverse.push(s), this._txn.forward.push({
|
|
25
|
+
kind: "spawn",
|
|
26
|
+
components: t,
|
|
27
|
+
onSpawned: (n) => {
|
|
28
|
+
s.eid = n, e?.(n);
|
|
29
|
+
}
|
|
30
|
+
}), this;
|
|
31
|
+
}
|
|
32
|
+
despawn(t, e) {
|
|
33
|
+
const s = { kind: "despawn", eid: t };
|
|
34
|
+
return this._txn.forward.push(s), this._txn.inverse.push({
|
|
35
|
+
kind: "spawn",
|
|
36
|
+
components: e,
|
|
37
|
+
onSpawned: (n) => {
|
|
38
|
+
s.eid = n;
|
|
39
|
+
}
|
|
40
|
+
}), this;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set `field` of `def` on `entityId` to `value`. Inverse: set it back to the value
|
|
44
|
+
* this edit replaced — read from the staged overlay / shadow (so stacked edits,
|
|
45
|
+
* within one build or before a commit, invert correctly) or, failing that, the
|
|
46
|
+
* read channel (`0` if unknown).
|
|
47
|
+
*/
|
|
48
|
+
setField(t, e, s, n) {
|
|
49
|
+
const i = o(t, e, s), d = this.staged.get(i) ?? this.shadow.get(i) ?? this.readField(t, e, s) ?? 0;
|
|
50
|
+
return this.staged.set(i, n), this._txn.forward.push({ kind: "set_field", eid: t, def: e, field: s, value: n }), this._txn.inverse.push({ kind: "set_field", eid: t, def: e, field: s, value: d }), this;
|
|
51
|
+
}
|
|
52
|
+
/** Attach `def` (with complete `values`) to `entityId`. Inverse: remove it.
|
|
53
|
+
* Bare `add` — the namespaced-handle grammar (matches `ctx.commands.add` and
|
|
54
|
+
* the queue's `add`), not `addComponent`. */
|
|
55
|
+
add(t, e, s) {
|
|
56
|
+
return this._txn.forward.push({ kind: "add_component", eid: t, def: e, values: s }), this._txn.inverse.push({ kind: "remove_component", eid: t, def: e }), this;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Detach `def` from `entityId`. Inverse: re-add it from `restore` (the field values
|
|
60
|
+
* to recreate — read them from the channel before removing). Bare `remove` — see `add`.
|
|
61
|
+
*/
|
|
62
|
+
remove(t, e, s) {
|
|
63
|
+
return this._txn.forward.push({ kind: "remove_component", eid: t, def: e }), this._txn.inverse.push({
|
|
64
|
+
kind: "add_component",
|
|
65
|
+
eid: t,
|
|
66
|
+
def: e,
|
|
67
|
+
values: s
|
|
68
|
+
}), this;
|
|
69
|
+
}
|
|
70
|
+
/** Disable `entityId`. Inverse: enable it. */
|
|
71
|
+
disable(t) {
|
|
72
|
+
return this._txn.forward.push({ kind: "disable", eid: t }), this._txn.inverse.push({ kind: "enable", eid: t }), this;
|
|
73
|
+
}
|
|
74
|
+
/** Enable `entityId`. Inverse: disable it. */
|
|
75
|
+
enable(t) {
|
|
76
|
+
return this._txn.forward.push({ kind: "enable", eid: t }), this._txn.inverse.push({ kind: "disable", eid: t }), this;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class u {
|
|
80
|
+
constructor(t, e) {
|
|
81
|
+
this.queue = t, this.readField = e;
|
|
82
|
+
}
|
|
83
|
+
undoStack = [];
|
|
84
|
+
redoStack = [];
|
|
85
|
+
/** onChange subscribers — see {@link onChange}. */
|
|
86
|
+
listeners = [];
|
|
87
|
+
/** Per-`(entity, component, field)` shadow of edited values, for inverse correctness. */
|
|
88
|
+
shadow = /* @__PURE__ */ new Map();
|
|
89
|
+
/**
|
|
90
|
+
* Group several actions into ONE undo entry. Build them on the passed
|
|
91
|
+
* {@link TransactionBuilder}; the whole group commits (enqueues its forward
|
|
92
|
+
* commands) and lands on the undo stack atomically, clearing the redo stack.
|
|
93
|
+
*/
|
|
94
|
+
transaction(t) {
|
|
95
|
+
const e = new h(this.readField, this.shadow);
|
|
96
|
+
return t(e), this.commit(a.get(e));
|
|
97
|
+
}
|
|
98
|
+
spawn(t, e) {
|
|
99
|
+
return this.transaction((s) => s.spawn(t, e));
|
|
100
|
+
}
|
|
101
|
+
despawn(t, e) {
|
|
102
|
+
return this.transaction((s) => s.despawn(t, e));
|
|
103
|
+
}
|
|
104
|
+
/** Set one field as its own undo entry. */
|
|
105
|
+
setField(t, e, s, n) {
|
|
106
|
+
return this.transaction((i) => i.setField(t, e, s, n));
|
|
107
|
+
}
|
|
108
|
+
/** Attach a component as its own undo entry. Bare `add` (see `TransactionBuilder.add`). */
|
|
109
|
+
add(t, e, s) {
|
|
110
|
+
return this.transaction((n) => n.add(t, e, s));
|
|
111
|
+
}
|
|
112
|
+
/** Detach a component as its own undo entry; `restore` re-adds it on undo. Bare `remove`. */
|
|
113
|
+
remove(t, e, s) {
|
|
114
|
+
return this.transaction((n) => n.remove(t, e, s));
|
|
115
|
+
}
|
|
116
|
+
/** Disable `entityId` as its own undo entry. */
|
|
117
|
+
disable(t) {
|
|
118
|
+
return this.transaction((e) => e.disable(t));
|
|
119
|
+
}
|
|
120
|
+
/** Enable `entityId` as its own undo entry. */
|
|
121
|
+
enable(t) {
|
|
122
|
+
return this.transaction((e) => e.enable(t));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Undo the most recent transaction: enqueue its inverse commands (in reverse
|
|
126
|
+
* order — a group unwinds last-action-first) on the bus and move it to the redo
|
|
127
|
+
* stack. Returns `false` if the undo stack is empty.
|
|
128
|
+
*/
|
|
129
|
+
undo() {
|
|
130
|
+
const t = this.undoStack.pop();
|
|
131
|
+
if (t === void 0) return !1;
|
|
132
|
+
const e = t.inverse.slice().reverse();
|
|
133
|
+
for (const s of e) this.queue.push(s);
|
|
134
|
+
return this.applyShadow(e), this.redoStack.push(t), this.notify(), !0;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Redo the most recently undone transaction: re-enqueue its forward commands on
|
|
138
|
+
* the bus and move it back to the undo stack. Returns `false` if the redo stack
|
|
139
|
+
* is empty.
|
|
140
|
+
*/
|
|
141
|
+
redo() {
|
|
142
|
+
const t = this.redoStack.pop();
|
|
143
|
+
if (t === void 0) return !1;
|
|
144
|
+
for (const e of t.forward) this.queue.push(e);
|
|
145
|
+
return this.applyShadow(t.forward), this.undoStack.push(t), this.notify(), !0;
|
|
146
|
+
}
|
|
147
|
+
/** Drop both stacks (e.g. on load). Does not touch the world. */
|
|
148
|
+
clear() {
|
|
149
|
+
this.undoStack.length = 0, this.redoStack.length = 0, this.shadow.clear(), this.notify();
|
|
150
|
+
}
|
|
151
|
+
/** Current stack depths — for an "Undo (3)" / "Redo" affordance. */
|
|
152
|
+
depths() {
|
|
153
|
+
return { undo: this.undoStack.length, redo: this.redoStack.length };
|
|
154
|
+
}
|
|
155
|
+
/** `true` when `undo()` would do something — allocation-free (M10). */
|
|
156
|
+
get canUndo() {
|
|
157
|
+
return this.undoStack.length > 0;
|
|
158
|
+
}
|
|
159
|
+
/** `true` when `redo()` would do something — allocation-free (M10). */
|
|
160
|
+
get canRedo() {
|
|
161
|
+
return this.redoStack.length > 0;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Subscribe to undo/redo-stack changes: fires after every commit, undo,
|
|
165
|
+
* redo, and clear — the push signal an "Undo (3)" affordance needs instead
|
|
166
|
+
* of polling `depths()` per frame (M10). Returns an unsubscribe function.
|
|
167
|
+
* Callbacks run synchronously in subscription order; read `canUndo` /
|
|
168
|
+
* `canRedo` / `depths()` inside.
|
|
169
|
+
*/
|
|
170
|
+
onChange(t) {
|
|
171
|
+
return this.listeners.push(t), () => {
|
|
172
|
+
const e = this.listeners.indexOf(t);
|
|
173
|
+
e !== -1 && this.listeners.splice(e, 1);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
notify() {
|
|
177
|
+
for (const t of this.listeners.slice()) t();
|
|
178
|
+
}
|
|
179
|
+
/** Read one committed `(entity, component, field)` slot through the reader
|
|
180
|
+
* this editor was constructed with — the default read for `fieldHandle`
|
|
181
|
+
* when no channel thunk is supplied (M11). */
|
|
182
|
+
committedField(t, e, s) {
|
|
183
|
+
return this.readField(t, e, s);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The pending value the editor believes for a field that the committed read
|
|
187
|
+
* channel has NOT caught up to yet (the shadow), or `undefined` if none. Lets an
|
|
188
|
+
* inspector echo an edit between the `set` and the tick that commits it.
|
|
189
|
+
*
|
|
190
|
+
* Self-resolving: once the read channel reports the shadowed value (the edit
|
|
191
|
+
* landed) — or reports `undefined` (the slot is gone: entity despawned or
|
|
192
|
+
* component removed) — the entry is dropped and this returns `undefined`, so
|
|
193
|
+
* `pending` does not outlive its set→commit window and shadow a later external
|
|
194
|
+
* write, nor a dead slot's lifetime. The one
|
|
195
|
+
* residual: if an external write changes the field to a *different* value within
|
|
196
|
+
* the same window before this is next consulted, `pending` can read stale until
|
|
197
|
+
* the next edit to the slot. `value` (the read channel) is always the source of
|
|
198
|
+
* truth; `pending` is only the optimistic bridge.
|
|
199
|
+
*/
|
|
200
|
+
pendingField(t, e, s) {
|
|
201
|
+
const n = o(t, e, s), i = this.shadow.get(n);
|
|
202
|
+
if (i === void 0) return;
|
|
203
|
+
const d = this.readField(t, e, s);
|
|
204
|
+
if (d === i || d === void 0) {
|
|
205
|
+
this.shadow.delete(n);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
return i;
|
|
209
|
+
}
|
|
210
|
+
/** Enqueue forward commands, record the transaction, clear redo, sync shadow. */
|
|
211
|
+
commit(t) {
|
|
212
|
+
if (t.forward.length === 0) return t;
|
|
213
|
+
for (const e of t.forward) this.queue.push(e);
|
|
214
|
+
return this.applyShadow(t.forward), this.undoStack.push(t), this.redoStack.length = 0, this.notify(), t;
|
|
215
|
+
}
|
|
216
|
+
/** Keep the setField shadow in step with the commands just enqueued. */
|
|
217
|
+
applyShadow(t) {
|
|
218
|
+
for (const e of t)
|
|
219
|
+
e.kind === "set_field" && this.shadow.set(o(e.eid, e.def, e.field), e.value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function c(r, t, e, s, n) {
|
|
223
|
+
return {
|
|
224
|
+
get value() {
|
|
225
|
+
return n !== void 0 ? n() : r.committedField(t, e, s);
|
|
226
|
+
},
|
|
227
|
+
set(i) {
|
|
228
|
+
r.setField(t, e, s, i);
|
|
229
|
+
},
|
|
230
|
+
get pending() {
|
|
231
|
+
return r.pendingField(t, e, s);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export {
|
|
236
|
+
u as Editor,
|
|
237
|
+
h as TransactionBuilder,
|
|
238
|
+
c as fieldHandle
|
|
239
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("../../kernel-sw7ScKWu.cjs"),_=require("../../shallow-C5YmTbtb.cjs");class w{constructor(n){this.def=n}_ctx;_eid=0;get eid(){return this._eid}field(n){return this._ctx.getField(this._eid,this.def,n)}}class m{constructor(n){this.def=n}_eid=0;_row=0;cols=new Map;arch;bind(n){this.arch=n,this.cols.clear()}get eid(){return this._eid}field(n){let s=this.cols.get(n);return s===void 0&&(s=this.arch.getColumnRead(this.def,n),this.cols.set(n,s)),s[this._row]}}class v{_ctx;_eid=0;get eid(){return this._eid}field(n,s){return this._ctx.getField(this._eid,n,s)}}function p(l,n,s,r={}){const c=_.reactiveMap(r.eq),a={...r.access,reads:[n,...r.access?.reads??[]]},h=r.grain??"entity",u=r.seedExisting??!0,b=new w(n),y=(i,o)=>{o.isDisabled(i)||(b._ctx=o,b._eid=i,c.set(i,s(b)))};let g;return f.batch(()=>{if(h==="column"){const i=new m(n);g=l.observe(n,{granularity:"archetype",onSet:o=>{i.bind(o);const t=o.entityIds;for(let e=0;e<o.entityCount;e++)i._row=e,i._eid=t[e],c.set(i._eid,s(i))},onAdd:(o,t)=>y(o,t),onRemove:o=>c.delete(o),onDisable:o=>c.delete(o),onEnable:(o,t)=>y(o,t),access:a,yieldExisting:u})}else g=l.observe(n,{granularity:"entity",onSet:(i,o)=>y(i,o),onAdd:(i,o)=>y(i,o),onRemove:i=>c.delete(i),onDisable:i=>c.delete(i),onEnable:(i,o)=>y(i,o),access:a,yieldExisting:u})}),{map:c,dispose:()=>g.dispose()}}function E(l,n,s,r={}){return p(l,n,a=>{const h={};for(let u=0;u<s.length;u++)h[s[u]]=a.field(s[u]);return h},{...r,eq:_.shallow})}function S(l,n,s,r={}){const c=n,a=_.reactiveMap(r.eq),h={...r.access,reads:[...c,...r.access?.reads??[]]},u=r.seedExisting??!0,b=new v,y=(t,e)=>{for(let d=0;d<c.length;d++)if(!t.hasComponent(e,c[d]))return!1;return!0},g=(t,e)=>{e.isDisabled(t)||y(e,t)&&(b._ctx=e,b._eid=t,a.set(t,s(b)))},i=t=>{a.delete(t)};let o;return f.batch(()=>{o=c.map(t=>l.observe(t,{granularity:"entity",onSet:g,onAdd:g,onRemove:i,onDisable:i,onEnable:g,access:h,yieldExisting:u}))}),{map:a,dispose:()=>{for(let t=0;t<o.length;t++)o[t].dispose()}}}function R(l,n,s,r,c={}){const a={};for(let e=0;e<r.length;e++)a[r[e]]=0;const[h,u]=c.into??_.reactiveStruct(a,c.eq),b=f.untrack(()=>r.map(e=>h[e])),y={...c.access,reads:[n,...c.access?.reads??[]]},g=c.seedExisting??!0,i=e=>{for(let d=0;d<r.length;d++)u[r[d]](e.getField(s,n,r[d]))},o=()=>{for(let e=0;e<r.length;e++)u[r[e]](b[e])};let t;return f.batch(()=>{t=l.observe(n,{granularity:"entity",onSet:(e,d)=>{e===s&&i(d)},onAdd:(e,d)=>{e===s&&!d.isDisabled(s)&&i(d)},onRemove:e=>{e===s&&o()},onDisable:e=>{e===s&&o()},onEnable:(e,d)=>{e===s&&i(d)},access:y,yieldExisting:g})}),{struct:h,dispose:()=>t.dispose()}}function T(l,n,s,r,c={}){const a=c.into??_.reactiveArray(new Array(r.length).fill(0),c.eq),h=f.untrack(()=>a.snapshot());if(c.into!==void 0&&h.length!==r.length)throw new Error(`syncSingletonToArray: into.length (${h.length}) must equal fields.length (${r.length})`);const u={...c.access,reads:[n,...c.access?.reads??[]]},b=c.seedExisting??!0,y=new Array(r.length),g=t=>{for(let e=0;e<r.length;e++)y[e]=t.getField(s,n,r[e]);a.reconcile(y)},i=()=>a.reconcile(h);let o;return f.batch(()=>{o=l.observe(n,{granularity:"entity",onSet:(t,e)=>{t===s&&g(e)},onAdd:(t,e)=>{t===s&&!e.isDisabled(s)&&g(e)},onRemove:t=>{t===s&&i()},onDisable:t=>{t===s&&i()},onEnable:(t,e)=>{t===s&&g(e)},access:u,yieldExisting:b})}),{array:a,dispose:()=>o.dispose()}}function x(l,n){f.batch(()=>l.update(n))}exports.shallow=_.shallow;exports.batchedUpdate=x;exports.syncComponentToMap=p;exports.syncFieldsToMap=E;exports.syncJoinToMap=S;exports.syncSingletonToArray=T;exports.syncSingletonToStruct=R;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { b as f, u as _ } from "../../kernel-Cz2Kxlwl.js";
|
|
2
|
+
import { r as m, s as E, a as p, b as w } from "../../shallow-9S1pY_Iw.js";
|
|
3
|
+
class v {
|
|
4
|
+
constructor(n) {
|
|
5
|
+
this.def = n;
|
|
6
|
+
}
|
|
7
|
+
_ctx;
|
|
8
|
+
_eid = 0;
|
|
9
|
+
get eid() {
|
|
10
|
+
return this._eid;
|
|
11
|
+
}
|
|
12
|
+
field(n) {
|
|
13
|
+
return this._ctx.getField(this._eid, this.def, n);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class R {
|
|
17
|
+
constructor(n) {
|
|
18
|
+
this.def = n;
|
|
19
|
+
}
|
|
20
|
+
_eid = 0;
|
|
21
|
+
_row = 0;
|
|
22
|
+
cols = /* @__PURE__ */ new Map();
|
|
23
|
+
arch;
|
|
24
|
+
/** Bind to an archetype for one dispatch (drops the previous column cache). */
|
|
25
|
+
bind(n) {
|
|
26
|
+
this.arch = n, this.cols.clear();
|
|
27
|
+
}
|
|
28
|
+
get eid() {
|
|
29
|
+
return this._eid;
|
|
30
|
+
}
|
|
31
|
+
field(n) {
|
|
32
|
+
let s = this.cols.get(n);
|
|
33
|
+
return s === void 0 && (s = this.arch.getColumnRead(this.def, n), this.cols.set(n, s)), s[this._row];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class x {
|
|
37
|
+
_ctx;
|
|
38
|
+
_eid = 0;
|
|
39
|
+
get eid() {
|
|
40
|
+
return this._eid;
|
|
41
|
+
}
|
|
42
|
+
field(n, s) {
|
|
43
|
+
return this._ctx.getField(this._eid, n, s);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function A(l, n, s, r = {}) {
|
|
47
|
+
const c = m(r.eq), a = {
|
|
48
|
+
...r.access,
|
|
49
|
+
reads: [n, ...r.access?.reads ?? []]
|
|
50
|
+
}, h = r.grain ?? "entity", u = r.seedExisting ?? !0, b = new v(n), y = (o, i) => {
|
|
51
|
+
i.isDisabled(o) || (b._ctx = i, b._eid = o, c.set(o, s(b)));
|
|
52
|
+
};
|
|
53
|
+
let g;
|
|
54
|
+
return f(() => {
|
|
55
|
+
if (h === "column") {
|
|
56
|
+
const o = new R(n);
|
|
57
|
+
g = l.observe(n, {
|
|
58
|
+
granularity: "archetype",
|
|
59
|
+
onSet: (i) => {
|
|
60
|
+
o.bind(i);
|
|
61
|
+
const t = i.entityIds;
|
|
62
|
+
for (let e = 0; e < i.entityCount; e++)
|
|
63
|
+
o._row = e, o._eid = t[e], c.set(o._eid, s(o));
|
|
64
|
+
},
|
|
65
|
+
onAdd: (i, t) => y(i, t),
|
|
66
|
+
onRemove: (i) => c.delete(i),
|
|
67
|
+
// Disable = soft remove from the channel; enable = re-add (#677). The
|
|
68
|
+
// column sweep is bounded by enabled rows, so a disabled row would never
|
|
69
|
+
// refresh — drop it on disable, republish on enable.
|
|
70
|
+
onDisable: (i) => c.delete(i),
|
|
71
|
+
onEnable: (i, t) => y(i, t),
|
|
72
|
+
access: a,
|
|
73
|
+
yieldExisting: u
|
|
74
|
+
});
|
|
75
|
+
} else
|
|
76
|
+
g = l.observe(n, {
|
|
77
|
+
granularity: "entity",
|
|
78
|
+
onSet: (o, i) => y(o, i),
|
|
79
|
+
onAdd: (o, i) => y(o, i),
|
|
80
|
+
onRemove: (o) => c.delete(o),
|
|
81
|
+
onDisable: (o) => c.delete(o),
|
|
82
|
+
onEnable: (o, i) => y(o, i),
|
|
83
|
+
access: a,
|
|
84
|
+
yieldExisting: u
|
|
85
|
+
});
|
|
86
|
+
}), { map: c, dispose: () => g.dispose() };
|
|
87
|
+
}
|
|
88
|
+
function q(l, n, s, r = {}) {
|
|
89
|
+
return A(l, n, (a) => {
|
|
90
|
+
const h = {};
|
|
91
|
+
for (let u = 0; u < s.length; u++) h[s[u]] = a.field(s[u]);
|
|
92
|
+
return h;
|
|
93
|
+
}, { ...r, eq: E });
|
|
94
|
+
}
|
|
95
|
+
function M(l, n, s, r = {}) {
|
|
96
|
+
const c = n, a = m(r.eq), h = {
|
|
97
|
+
...r.access,
|
|
98
|
+
reads: [...c, ...r.access?.reads ?? []]
|
|
99
|
+
}, u = r.seedExisting ?? !0, b = new x(), y = (t, e) => {
|
|
100
|
+
for (let d = 0; d < c.length; d++) if (!t.hasComponent(e, c[d])) return !1;
|
|
101
|
+
return !0;
|
|
102
|
+
}, g = (t, e) => {
|
|
103
|
+
e.isDisabled(t) || y(e, t) && (b._ctx = e, b._eid = t, a.set(t, s(b)));
|
|
104
|
+
}, o = (t) => {
|
|
105
|
+
a.delete(t);
|
|
106
|
+
};
|
|
107
|
+
let i;
|
|
108
|
+
return f(() => {
|
|
109
|
+
i = c.map(
|
|
110
|
+
(t) => l.observe(t, {
|
|
111
|
+
granularity: "entity",
|
|
112
|
+
onSet: g,
|
|
113
|
+
onAdd: g,
|
|
114
|
+
onRemove: o,
|
|
115
|
+
// Disabling any joined component soft-removes the entity from the
|
|
116
|
+
// channel; enabling re-evaluates membership and republishes (#677). A
|
|
117
|
+
// disable fires once per carried joined component — `dropRow` is
|
|
118
|
+
// idempotent, and `publishIfMember` re-checks the full join.
|
|
119
|
+
onDisable: o,
|
|
120
|
+
onEnable: g,
|
|
121
|
+
access: h,
|
|
122
|
+
yieldExisting: u
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
}), {
|
|
126
|
+
map: a,
|
|
127
|
+
dispose: () => {
|
|
128
|
+
for (let t = 0; t < i.length; t++) i[t].dispose();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function T(l, n, s, r, c = {}) {
|
|
133
|
+
const a = {};
|
|
134
|
+
for (let e = 0; e < r.length; e++) a[r[e]] = 0;
|
|
135
|
+
const [h, u] = c.into ?? p(a, c.eq), b = _(() => r.map((e) => h[e])), y = {
|
|
136
|
+
...c.access,
|
|
137
|
+
reads: [n, ...c.access?.reads ?? []]
|
|
138
|
+
}, g = c.seedExisting ?? !0, o = (e) => {
|
|
139
|
+
for (let d = 0; d < r.length; d++) u[r[d]](e.getField(s, n, r[d]));
|
|
140
|
+
}, i = () => {
|
|
141
|
+
for (let e = 0; e < r.length; e++) u[r[e]](b[e]);
|
|
142
|
+
};
|
|
143
|
+
let t;
|
|
144
|
+
return f(() => {
|
|
145
|
+
t = l.observe(n, {
|
|
146
|
+
granularity: "entity",
|
|
147
|
+
onSet: (e, d) => {
|
|
148
|
+
e === s && o(d);
|
|
149
|
+
},
|
|
150
|
+
onAdd: (e, d) => {
|
|
151
|
+
e === s && !d.isDisabled(s) && o(d);
|
|
152
|
+
},
|
|
153
|
+
onRemove: (e) => {
|
|
154
|
+
e === s && i();
|
|
155
|
+
},
|
|
156
|
+
onDisable: (e) => {
|
|
157
|
+
e === s && i();
|
|
158
|
+
},
|
|
159
|
+
onEnable: (e, d) => {
|
|
160
|
+
e === s && o(d);
|
|
161
|
+
},
|
|
162
|
+
access: y,
|
|
163
|
+
yieldExisting: g
|
|
164
|
+
});
|
|
165
|
+
}), { struct: h, dispose: () => t.dispose() };
|
|
166
|
+
}
|
|
167
|
+
function C(l, n, s, r, c = {}) {
|
|
168
|
+
const a = c.into ?? w(new Array(r.length).fill(0), c.eq), h = _(() => a.snapshot());
|
|
169
|
+
if (c.into !== void 0 && h.length !== r.length)
|
|
170
|
+
throw new Error(
|
|
171
|
+
`syncSingletonToArray: into.length (${h.length}) must equal fields.length (${r.length})`
|
|
172
|
+
);
|
|
173
|
+
const u = {
|
|
174
|
+
...c.access,
|
|
175
|
+
reads: [n, ...c.access?.reads ?? []]
|
|
176
|
+
}, b = c.seedExisting ?? !0, y = new Array(r.length), g = (t) => {
|
|
177
|
+
for (let e = 0; e < r.length; e++) y[e] = t.getField(s, n, r[e]);
|
|
178
|
+
a.reconcile(y);
|
|
179
|
+
}, o = () => a.reconcile(h);
|
|
180
|
+
let i;
|
|
181
|
+
return f(() => {
|
|
182
|
+
i = l.observe(n, {
|
|
183
|
+
granularity: "entity",
|
|
184
|
+
onSet: (t, e) => {
|
|
185
|
+
t === s && g(e);
|
|
186
|
+
},
|
|
187
|
+
onAdd: (t, e) => {
|
|
188
|
+
t === s && !e.isDisabled(s) && g(e);
|
|
189
|
+
},
|
|
190
|
+
onRemove: (t) => {
|
|
191
|
+
t === s && o();
|
|
192
|
+
},
|
|
193
|
+
onDisable: (t) => {
|
|
194
|
+
t === s && o();
|
|
195
|
+
},
|
|
196
|
+
onEnable: (t, e) => {
|
|
197
|
+
t === s && g(e);
|
|
198
|
+
},
|
|
199
|
+
access: u,
|
|
200
|
+
yieldExisting: b
|
|
201
|
+
});
|
|
202
|
+
}), { array: a, dispose: () => i.dispose() };
|
|
203
|
+
}
|
|
204
|
+
function F(l, n) {
|
|
205
|
+
f(() => l.update(n));
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
F as batchedUpdate,
|
|
209
|
+
E as shallow,
|
|
210
|
+
A as syncComponentToMap,
|
|
211
|
+
q as syncFieldsToMap,
|
|
212
|
+
M as syncJoinToMap,
|
|
213
|
+
C as syncSingletonToArray,
|
|
214
|
+
T as syncSingletonToStruct
|
|
215
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("solid-js"),i=require("../../interop-T1HZ-Dpa.cjs");function l(e){const[t,o]=u.createSignal(e());return u.onCleanup(i.subscribe(e,s=>o(()=>s))),t}function c(e){return{keys:l(()=>e.keys()),cell:t=>l(()=>e.get(t))}}function f(e){const t={},o=Object.keys(e),s=new Set(o);for(const n of o)t[n]=l(()=>e[n]);return new Proxy({},{get:(n,r)=>s.has(r)?t[r]():Reflect.get(n,r),has:(n,r)=>s.has(r),ownKeys:()=>o,getOwnPropertyDescriptor:(n,r)=>s.has(r)?{get:()=>t[r](),enumerable:!0,configurable:!0}:void 0,set:(n,r)=>{throw new TypeError(`fromKernelStruct view is read-only: write through the kernel struct's setters (set.${String(r)}(value)), not the bridged view`)}})}function a(e){return l(()=>e.snapshot())}exports.fromKernel=l;exports.fromKernelArray=a;exports.fromKernelMap=c;exports.fromKernelStruct=f;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createSignal as u, onCleanup as c } from "solid-js";
|
|
2
|
+
import { s as f } from "../../interop-DqxleBo7.js";
|
|
3
|
+
function i(e) {
|
|
4
|
+
const [t, o] = u(e());
|
|
5
|
+
return c(f(e, (s) => o(() => s))), t;
|
|
6
|
+
}
|
|
7
|
+
function g(e) {
|
|
8
|
+
return {
|
|
9
|
+
keys: i(() => e.keys()),
|
|
10
|
+
cell: (t) => i(() => e.get(t))
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function h(e) {
|
|
14
|
+
const t = {}, o = Object.keys(e), s = new Set(o);
|
|
15
|
+
for (const n of o)
|
|
16
|
+
t[n] = i(() => e[n]);
|
|
17
|
+
return new Proxy({}, {
|
|
18
|
+
get: (n, r) => s.has(r) ? t[r]() : Reflect.get(n, r),
|
|
19
|
+
has: (n, r) => s.has(r),
|
|
20
|
+
ownKeys: () => o,
|
|
21
|
+
getOwnPropertyDescriptor: (n, r) => s.has(r) ? { get: () => t[r](), enumerable: !0, configurable: !0 } : void 0,
|
|
22
|
+
// Read surface only (`Readonly<T>`, POLISH_AUDIT #8) — mirrors the kernel
|
|
23
|
+
// struct proxy's trap so a JS-side assignment fails loudly instead of
|
|
24
|
+
// sticking a non-reactive value on the hidden target.
|
|
25
|
+
set: (n, r) => {
|
|
26
|
+
throw new TypeError(
|
|
27
|
+
`fromKernelStruct view is read-only: write through the kernel struct's setters (set.${String(r)}(value)), not the bridged view`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function y(e) {
|
|
33
|
+
return i(() => e.snapshot());
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
i as fromKernel,
|
|
37
|
+
y as fromKernelArray,
|
|
38
|
+
g as fromKernelMap,
|
|
39
|
+
h as fromKernelStruct
|
|
40
|
+
};
|