@warp-ts/reactivity 0.0.0-stage → 0.1.3

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.
@@ -0,0 +1,973 @@
1
+ //#region src/types.ts
2
+ function isComputation(n) {
3
+ return n._pure !== void 0;
4
+ }
5
+
6
+ //#endregion
7
+ //#region src/error.ts
8
+ var WarpError = class extends Error {
9
+ constructor(code, message, hint) {
10
+ super(`[${code}] ${message}`);
11
+ this.name = "WarpError";
12
+ this.code = code;
13
+ this.hint = hint;
14
+ }
15
+ };
16
+ const MSG = {
17
+ "WARP-R101": (n) => `effect "${n}" 创建于无 owner 上下文,将永不释放;请放入组件 / createRoot 内`,
18
+ "WARP-R102": (n) => `检测到依赖环:${n}`,
19
+ "WARP-R103": (n) => `不能写入 computed "${n}"`,
20
+ "WARP-R104": (n) => `computed "${n}" 内写入了 signal,computed 必须是纯函数`,
21
+ "WARP-R105": (n) => `computed "${n}" 内创建了 effect / createRoot,computed 必须是纯函数(重算不会释放它们)`
22
+ };
23
+ function err(code, name) {
24
+ {
25
+ const f = MSG[code];
26
+ if (f) return new WarpError(code, f(name || "(anonymous)"));
27
+ }
28
+ return new WarpError(code, code);
29
+ }
30
+ function warn(code, name) {
31
+ console.warn(err(code, name).message);
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/core.ts
36
+ let Listener = null;
37
+ let Owner_ = null;
38
+ let Updates = null;
39
+ let BatchDepth = 0;
40
+ let Pending = false;
41
+ let Flushing = false;
42
+ let Clock = 0;
43
+ let NextId = 0;
44
+ let Seq = 0;
45
+ /** FD-43:失去最后一个订阅者的 computed,flush 尾部退订上游(对齐 Vue 3.5:computed 不归 owner)*/
46
+ let Orphans = null;
47
+ /** FD-48:flush 中首个未处理错误,整轮跑完后再抛 */
48
+ let Failed = false;
49
+ let FailErr;
50
+ /** dev:顶层 root 登记,供依赖图快照 */
51
+ const allRoots = /* @__PURE__ */ new Set();
52
+ /** dev:flush 尾部回调(DevTools 探针的唯一插桩点,REQ-LFE-003 RB-301) */
53
+ const flushCbs = /* @__PURE__ */ new Set();
54
+ /**
55
+ * REQ-LFE-002 ADR-205 / RB-202 · 服务端模式:无 DOM 则无需订阅,也不能让订阅跨请求存活。
56
+ * 开启后 `effect / renderEffect` **同步求值一次即返回、不建订阅**;
57
+ * `signal / computed / owner` 语义不变(computed 仍惰性可缓存)。由 `@warp-ts/server` 在进程启动时开启。
58
+ *
59
+ * `batch / flushSync` **不加分支**(03 §1 要求「空转」,但那是结果不是手段):serverMode 下
60
+ * 没有任何非纯 computation 会进 `Updates`,两者本来就什么都不做,多写两个 `if` 只是白给
61
+ * 客户端产物字节(NFR-002)。
62
+ */
63
+ let ServerMode = false;
64
+ const NOOP = () => {};
65
+ function eqOf(o) {
66
+ if (!o || o.equals === void 0) return Object.is;
67
+ if (o.equals === false) return () => false;
68
+ return o.equals;
69
+ }
70
+ function signal(v, opts) {
71
+ const n = {
72
+ _v: v,
73
+ _eq: eqOf(opts),
74
+ _subs: null,
75
+ _subSlots: null
76
+ };
77
+ n._name = opts?.name;
78
+ n._id = ++NextId;
79
+ const read = (() => readSignal(n));
80
+ read.set = (x) => writeSignal(n, typeof x === "function" ? x(n._v) : x);
81
+ read.peek = () => n._v;
82
+ return read;
83
+ }
84
+ function readSignal(n) {
85
+ const l = Listener;
86
+ if (l) {
87
+ let lSlot;
88
+ const ls = l._sources;
89
+ if (ls === null) {
90
+ l._sources = n;
91
+ lSlot = 0;
92
+ } else if (!Array.isArray(ls)) {
93
+ l._sources = [ls, n];
94
+ l._sourceSlots = [l._sourceSlots, 0];
95
+ lSlot = 1;
96
+ } else {
97
+ ls.push(n);
98
+ l._sourceSlots.push(0);
99
+ lSlot = ls.length - 1;
100
+ }
101
+ let sSlot;
102
+ const ns = n._subs;
103
+ if (ns === null || ns === void 0) {
104
+ n._subs = l;
105
+ n._subSlots = lSlot;
106
+ sSlot = 0;
107
+ } else if (!Array.isArray(ns)) {
108
+ n._subs = [ns, l];
109
+ n._subSlots = [n._subSlots, lSlot];
110
+ sSlot = 1;
111
+ } else {
112
+ ns.push(l);
113
+ n._subSlots.push(lSlot);
114
+ sSlot = ns.length - 1;
115
+ }
116
+ if (Array.isArray(l._sourceSlots)) l._sourceSlots[lSlot] = sSlot;
117
+ else l._sourceSlots = sSlot;
118
+ }
119
+ return n._v;
120
+ }
121
+ function writeSignal(n, v) {
122
+ if (isComputation(n)) throw err("WARP-R103", n._name);
123
+ if (Listener && Listener._pure && Listener._running) warn("WARP-R104", Listener._name);
124
+ if (n._eq(n._v, v)) return;
125
+ n._v = v;
126
+ ++Clock;
127
+ if (n._subs) runUpdates(() => eachSub(n._subs, (c) => markDirty(c, 2)));
128
+ }
129
+ /** 遍历订阅者(单值或数组);遍历期间不修改集合 */
130
+ function eachSub(subs, fn) {
131
+ if (!subs) return;
132
+ if (Array.isArray(subs)) for (let i = 0; i < subs.length; i++) fn(subs[i]);
133
+ else fn(subs);
134
+ }
135
+ /** 非 computed 的 signal 节点被外部(reactive)触发 */
136
+ function _notify(n) {
137
+ ++Clock;
138
+ if (n._subs) runUpdates(() => eachSub(n._subs, (c) => markDirty(c, 2)));
139
+ }
140
+ const _track = readSignal;
141
+ function _mkSignalNode() {
142
+ return {
143
+ _v: void 0,
144
+ _eq: () => false,
145
+ _subs: null,
146
+ _subSlots: null
147
+ };
148
+ }
149
+ function _hasListener() {
150
+ return Listener !== null;
151
+ }
152
+ /** 内部:轻量信号(runtime-dom 行 item / index 用):只有节点 + 一个读闭包,写入走 _write,省去 set / peek 闭包(内存专项 M-1) */
153
+ function _lightSignal(v) {
154
+ const node = {
155
+ _v: v,
156
+ _eq: Object.is,
157
+ _subs: null,
158
+ _subSlots: null
159
+ };
160
+ node._id = ++NextId;
161
+ return {
162
+ read: () => readSignal(node),
163
+ node
164
+ };
165
+ }
166
+ const _write = writeSignal;
167
+ function markDirty(c, state) {
168
+ if (c._state >= state) return;
169
+ const was = c._state;
170
+ c._state = state;
171
+ if (was === 0 && !c._pure) Updates.push(c);
172
+ eachSub(c._subs, (s) => markDirty(s, 1));
173
+ }
174
+ function readComputed(c) {
175
+ if (c._running) throw err("WARP-R102", `computed "${c._name || "(anonymous)"}" 在自身求值期间被读取`);
176
+ if (c._state !== 0 && !(c._seen === Clock && !Listener)) updateIfNecessary(c);
177
+ if (!Listener && !c._subs && c._sources) orphan(c);
178
+ return readSignal(c);
179
+ }
180
+ function updateIfNecessary(c) {
181
+ if (c._disposed) return;
182
+ if (c._state === 1) {
183
+ const src = c._sources;
184
+ const check = (s) => {
185
+ if (isComputation(s)) {
186
+ updateIfNecessary(s);
187
+ if (s._updatedAt > c._updatedAt) {
188
+ c._state = 2;
189
+ return true;
190
+ }
191
+ }
192
+ return false;
193
+ };
194
+ if (src) {
195
+ if (Array.isArray(src)) {
196
+ for (let i = 0; i < src.length; i++) if (check(src[i])) break;
197
+ } else check(src);
198
+ }
199
+ if (c._state === 1) c._state = 0;
200
+ }
201
+ if (c._state === 2) runComputation(c);
202
+ }
203
+ function runComputation(c) {
204
+ cleanNode(c);
205
+ c._state = 0;
206
+ const pL = Listener, pO = Owner_, pR = RunningC;
207
+ Listener = c;
208
+ Owner_ = c._pure ? c._parent : c;
209
+ RunningC = c;
210
+ c._running = true;
211
+ try {
212
+ const nv = c._fn(ON_CLEANUP);
213
+ if (c._pure && (!c._updatedAt || !c._eq(c._v, nv))) {
214
+ c._v = nv;
215
+ c._updatedAt = ++Clock;
216
+ eachSub(c._subs, (s) => markDirty(s, 2));
217
+ } else if (!c._pure) c._updatedAt = Clock;
218
+ } catch (e) {
219
+ handleError(c, e);
220
+ } finally {
221
+ c._running = false;
222
+ Listener = pL;
223
+ Owner_ = pO;
224
+ RunningC = pR;
225
+ }
226
+ }
227
+ let RunningC = null;
228
+ /**
229
+ * dev 诊断(prod 整体 DCE):`WARP-R101` 无 owner;
230
+ * `WARP-R105`(RDC-33 / 006 L-23)computed 内创建 effect / createRoot —— computed 必须是纯函数,
231
+ * 重算不会释放它们(行为不改,只出告警;设计 01-R §7 已登记为设计约束)
232
+ */
233
+ function devPure() {
234
+ if (RunningC && RunningC._pure && RunningC._running) warn("WARP-R105", RunningC._name);
235
+ }
236
+ function devNewOwner(n) {
237
+ if (!Owner_) warn("WARP-R101", n);
238
+ devPure();
239
+ }
240
+ const ON_CLEANUP = (f) => {
241
+ const c = RunningC;
242
+ if (c) (c._cleanups || (c._cleanups = [])).push(f);
243
+ else warn("WARP-R101", "onCleanup(fn 参数) 仅在计算同步运行期内有效(MEM-02)");
244
+ };
245
+ function handleError(from, e) {
246
+ let o = from;
247
+ while (o) {
248
+ if (o._err && o._err(e) !== false) return;
249
+ o = o._parent;
250
+ }
251
+ throw e;
252
+ }
253
+ function runUpdates(fn) {
254
+ if (Updates) return fn();
255
+ Updates = [];
256
+ try {
257
+ return fn();
258
+ } finally {
259
+ if (BatchDepth === 0) scheduleFlush();
260
+ }
261
+ }
262
+ function scheduleFlush() {
263
+ if (Pending) return;
264
+ Pending = true;
265
+ queueMicrotask(flush);
266
+ }
267
+ const bySeq = (a, b) => a._seq - b._seq;
268
+ /** 跑完当前队列(含本轮新入队):FD-38 每轮按创建序执行(守卫先于其内容);FD-48 单项抛错不中断 */
269
+ function drain() {
270
+ let q = Updates;
271
+ Updates = [];
272
+ let round = 0;
273
+ while (q && q.length) {
274
+ if (++round > 100) throw err("WARP-R102", "flush 超过 100 轮,疑似 effect 互写");
275
+ q.sort(bySeq);
276
+ for (let i = 0; i < q.length; i++) {
277
+ const c = q[i];
278
+ if (c._state !== 0) try {
279
+ updateIfNecessary(c);
280
+ } catch (e) {
281
+ if (!Failed) {
282
+ Failed = true;
283
+ FailErr = e;
284
+ }
285
+ }
286
+ }
287
+ q = Updates;
288
+ Updates = [];
289
+ }
290
+ }
291
+ /** 仍无订阅者的孤儿 computed:退订全部上游(可被 GC),保留缓存值;_seen 记下当时时钟,期间无写入则读缓存 */
292
+ function orphan(c) {
293
+ (Orphans || (Orphans = [])).push(c);
294
+ if (!Flushing) scheduleFlush();
295
+ }
296
+ function detachOrphans() {
297
+ while (Orphans) {
298
+ const list = Orphans;
299
+ Orphans = null;
300
+ for (let i = 0; i < list.length; i++) {
301
+ const c = list[i];
302
+ if (c._subs || c._running || !c._sources) continue;
303
+ c._seen = c._state === 0 ? Clock : -1;
304
+ cleanNode(c);
305
+ c._state = 2;
306
+ }
307
+ }
308
+ }
309
+ function flush() {
310
+ if (Flushing) {
311
+ drain();
312
+ return;
313
+ }
314
+ Pending = false;
315
+ Flushing = true;
316
+ try {
317
+ drain();
318
+ } finally {
319
+ Updates = null;
320
+ Flushing = false;
321
+ ++Clock;
322
+ detachOrphans();
323
+ if (flushCbs.size) runFlushCbs();
324
+ }
325
+ if (Failed) {
326
+ const e = FailErr;
327
+ Failed = false;
328
+ FailErr = void 0;
329
+ throw e;
330
+ }
331
+ }
332
+ /** dev:逐个调用 flush 回调;探针抛错不得影响应用(拷贝一份,允许回调内注销自己) */
333
+ function runFlushCbs() {
334
+ const list = Array.from(flushCbs);
335
+ for (let i = 0; i < list.length; i++) try {
336
+ list[i]();
337
+ } catch (e) {
338
+ console.warn("[warp] __DEV_onFlush 回调抛错(已忽略):" + String(e));
339
+ }
340
+ }
341
+ function batch(fn) {
342
+ BatchDepth++;
343
+ try {
344
+ return runUpdates(fn);
345
+ } finally {
346
+ if (--BatchDepth === 0 && Updates) scheduleFlush();
347
+ }
348
+ }
349
+ /** 同步刷新待处理 effect(测试与 HMR 用) */
350
+ function flushSync() {
351
+ if (Updates || Pending) flush();
352
+ }
353
+ function untrack(fn) {
354
+ const pL = Listener;
355
+ Listener = null;
356
+ try {
357
+ return fn();
358
+ } finally {
359
+ Listener = pL;
360
+ }
361
+ }
362
+ function cleanNode(c) {
363
+ const src = c._sources;
364
+ if (src) {
365
+ if (Array.isArray(src)) {
366
+ const ss = c._sourceSlots;
367
+ for (let i = src.length - 1; i >= 0; i--) unsubscribe(src[i], ss[i]);
368
+ } else unsubscribe(src, c._sourceSlots);
369
+ c._sources = null;
370
+ c._sourceSlots = null;
371
+ }
372
+ disposeChildren(c);
373
+ const cl = c._cleanups;
374
+ if (cl) {
375
+ c._cleanups = null;
376
+ for (let i = cl.length - 1; i >= 0; i--) cl[i]();
377
+ }
378
+ }
379
+ /** owner 子节点:单值直接存,第二个起升级为数组(R-M9) */
380
+ function addChild(o, child) {
381
+ const ch = o._children;
382
+ if (!ch) o._children = child;
383
+ else if (Array.isArray(ch)) ch.push(child);
384
+ else o._children = [ch, child];
385
+ }
386
+ function disposeChildren(o) {
387
+ const ch = o._children;
388
+ if (!ch) return;
389
+ o._children = null;
390
+ o._dead = 0;
391
+ if (Array.isArray(ch)) for (let i = ch.length - 1; i >= 0; i--) disposeNode(ch[i]);
392
+ else disposeNode(ch);
393
+ }
394
+ /** 把 s._subs 中位于 slot 的订阅者移除(swap-remove),并维护被搬动者的反向索引;长度回到 1 时塌回单值 */
395
+ function unsubscribe(s, slot) {
396
+ const subs = s._subs;
397
+ if (!subs) return;
398
+ if (!Array.isArray(subs)) {
399
+ s._subs = null;
400
+ s._subSlots = null;
401
+ if (s._pure) orphan(s);
402
+ return;
403
+ }
404
+ const slots = s._subSlots;
405
+ const last = subs.pop(), lastSlot = slots.pop();
406
+ if (slot < subs.length) {
407
+ subs[slot] = last;
408
+ slots[slot] = lastSlot;
409
+ setSourceSlot(last, lastSlot, slot);
410
+ }
411
+ if (subs.length === 1) {
412
+ const only = subs[0], onlySlot = slots[0];
413
+ s._subs = only;
414
+ s._subSlots = onlySlot;
415
+ setSourceSlot(only, onlySlot, 0);
416
+ }
417
+ }
418
+ function setSourceSlot(c, idxInSources, value) {
419
+ if (Array.isArray(c._sourceSlots)) c._sourceSlots[idxInSources] = value;
420
+ else c._sourceSlots = value;
421
+ }
422
+ function mkComputation(fn, pure, opts) {
423
+ const c = pure ? {
424
+ _v: void 0,
425
+ _eq: eqOf(opts),
426
+ _subs: null,
427
+ _subSlots: null,
428
+ _fn: fn,
429
+ _sources: null,
430
+ _sourceSlots: null,
431
+ _state: 2,
432
+ _pure: true,
433
+ _running: false,
434
+ _updatedAt: 0,
435
+ _seq: ++Seq,
436
+ _seen: -1,
437
+ _parent: Owner_,
438
+ _children: null,
439
+ _cleanups: null,
440
+ _context: null,
441
+ _disposed: false
442
+ } : {
443
+ _fn: fn,
444
+ _sources: null,
445
+ _sourceSlots: null,
446
+ _state: 2,
447
+ _pure: false,
448
+ _running: false,
449
+ _updatedAt: 0,
450
+ _seq: ++Seq,
451
+ _parent: Owner_,
452
+ _children: null,
453
+ _cleanups: null,
454
+ _context: null,
455
+ _disposed: false
456
+ };
457
+ c._name = opts?.name;
458
+ c._id = ++NextId;
459
+ if (Owner_ && !pure) addChild(Owner_, c);
460
+ if (pure && Owner_) (Owner_._devC || (Owner_._devC = [])).push(c);
461
+ return c;
462
+ }
463
+ function effect(fn, opts) {
464
+ if (ServerMode) {
465
+ fn(onCleanup);
466
+ return NOOP;
467
+ }
468
+ const c = mkComputation(fn, false, opts);
469
+ devNewOwner(opts?.name);
470
+ if (Flushing || BatchDepth > 0) Updates.push(c);
471
+ else runComputation(c);
472
+ return () => disposeNode(c);
473
+ }
474
+ /** 渲染 effect:创建即同步执行,即使处于 flush / batch 中(runtime-dom `bind` 用;Solid createRenderEffect 语义,DC-23) */
475
+ function renderEffect(fn, opts) {
476
+ if (ServerMode) {
477
+ fn(onCleanup);
478
+ return NOOP;
479
+ }
480
+ const c = mkComputation(fn, false, opts);
481
+ devNewOwner(opts?.name);
482
+ runComputation(c);
483
+ return () => disposeNode(c);
484
+ }
485
+ function computed(fn, opts) {
486
+ const c = mkComputation(fn, true, opts);
487
+ return () => readComputed(c);
488
+ }
489
+ function createRoot(fn, owner = Owner_) {
490
+ devPure();
491
+ const out = { root: null };
492
+ return _rootRun(() => fn(() => disposeNode(out.root)), owner, out);
493
+ }
494
+ /** 内部:在新 root 下运行 fn,返回 root 对象本身(不分配 dispose 闭包;runtime-dom 行用,R-M8) */
495
+ function _rootRun(fn, owner, out) {
496
+ const parent = owner ?? null;
497
+ const root = {
498
+ _parent: parent,
499
+ _children: null,
500
+ _cleanups: null,
501
+ _context: null,
502
+ _disposed: false
503
+ };
504
+ root._id = ++NextId;
505
+ if (!parent) allRoots.add(root);
506
+ if (parent) addChild(parent, root);
507
+ out.root = root;
508
+ const pL = Listener, pO = Owner_;
509
+ Listener = null;
510
+ Owner_ = root;
511
+ try {
512
+ return fn();
513
+ } finally {
514
+ Listener = pL;
515
+ Owner_ = pO;
516
+ }
517
+ }
518
+ function _disposeOwner(o) {
519
+ disposeNode(o);
520
+ }
521
+ /** 内部(devtools / 测试):owner 子节点数组视图(单值形态时分配临时数组) */
522
+ const _childrenOf = (o) => {
523
+ const ch = o._children;
524
+ return !ch ? [] : Array.isArray(ch) ? ch : [ch];
525
+ };
526
+ function disposeNode(n) {
527
+ if (n._disposed) return;
528
+ n._disposed = true;
529
+ const p = n._parent;
530
+ if (p && !p._disposed && p._children) {
531
+ if (!Array.isArray(p._children)) {
532
+ if (p._children === n) p._children = null;
533
+ } else {
534
+ const dead = p._dead = (p._dead || 0) + 1;
535
+ if (dead > 8 && dead * 2 > p._children.length) {
536
+ p._children = p._children.filter((c) => !c._disposed);
537
+ p._dead = 0;
538
+ }
539
+ }
540
+ }
541
+ n._context = null;
542
+ if (isComputation(n)) {
543
+ cleanNode(n);
544
+ n._fn = null;
545
+ if (n._pure) n._v = void 0;
546
+ n._state = 0;
547
+ } else {
548
+ disposeChildren(n);
549
+ const cl = n._cleanups;
550
+ if (cl) {
551
+ n._cleanups = null;
552
+ for (let i = cl.length - 1; i >= 0; i--) cl[i]();
553
+ }
554
+ if (!n._parent) allRoots.delete(n);
555
+ }
556
+ }
557
+ /**
558
+ * @internal 服务端模式开关(ADR-205)。`@warp-ts/server` 进程启动时 `enableServerMode(true)`;
559
+ * 测试用 `enableServerMode(false)` 还原。
560
+ */
561
+ function enableServerMode(on) {
562
+ ServerMode = on;
563
+ }
564
+ function onCleanup(f) {
565
+ if (Owner_) (Owner_._cleanups || (Owner_._cleanups = [])).push(f);
566
+ else warn("WARP-R101", "onCleanup");
567
+ }
568
+ function getOwner() {
569
+ return Owner_;
570
+ }
571
+ function runWithOwner(owner, fn) {
572
+ const pL = Listener, pO = Owner_;
573
+ Listener = null;
574
+ Owner_ = owner;
575
+ try {
576
+ return fn();
577
+ } finally {
578
+ Listener = pL;
579
+ Owner_ = pO;
580
+ }
581
+ }
582
+ /** 内部:owner 上挂错误处理器(core 用) */
583
+ function _setOwnerError(owner, h) {
584
+ owner._err = h;
585
+ }
586
+ /**
587
+ * 内部:dev 下把组件元信息写到 owner(比照 `_setOwnerError`,REQ-LFE-003 RB-302)。
588
+ * 与 `__DEV_graph` 同形(prod 为 `undefined`)而不是空函数:prod 里连函数壳都不留(NFR-002 逐字节)。
589
+ */
590
+ const _setOwnerDev = (owner, info) => {
591
+ const o = owner;
592
+ if (info.name !== void 0) o._name = info.name;
593
+ if (info.hmrId !== void 0) o._hmrId = info.hmrId;
594
+ if (info.node !== void 0) o._node = info.node;
595
+ };
596
+ /** dev:flush 尾部回调登记,返回注销函数;prod 为 `undefined`(与 `__DEV_graph` 同口径) */
597
+ const __DEV_onFlush = (cb) => {
598
+ flushCbs.add(cb);
599
+ return () => {
600
+ flushCbs.delete(cb);
601
+ };
602
+ };
603
+ function _ownerContext(owner, create) {
604
+ const o = owner;
605
+ if (!o._context && create) o._context = /* @__PURE__ */ new Map();
606
+ return o._context;
607
+ }
608
+ function ownerView(o) {
609
+ if (!o) return null;
610
+ const n = o;
611
+ return {
612
+ parent: n._parent,
613
+ context: n._context,
614
+ disposed: n._disposed
615
+ };
616
+ }
617
+ const __DEV_graph = () => {
618
+ const nodes = [], edges = [], seen = /* @__PURE__ */ new Set();
619
+ const visitSig = (s) => {
620
+ if (s._id === void 0 || seen.has(s._id)) return;
621
+ seen.add(s._id);
622
+ if (isComputation(s)) nodes.push({
623
+ id: s._id,
624
+ kind: s._pure ? "computed" : "effect",
625
+ name: s._name,
626
+ state: s._state,
627
+ value: s._v
628
+ });
629
+ else nodes.push({
630
+ id: s._id,
631
+ kind: "signal",
632
+ name: s._name,
633
+ value: s._v
634
+ });
635
+ eachSub(s._subs, (c) => {
636
+ visitSig(c);
637
+ edges.push({
638
+ from: s._id,
639
+ to: c._id,
640
+ kind: "dep"
641
+ });
642
+ });
643
+ };
644
+ const visitOwner = (o) => {
645
+ if (o._disposed) return;
646
+ if (isComputation(o)) {
647
+ visitSig(o);
648
+ const srcs = o._sources;
649
+ if (srcs) for (const s of Array.isArray(srcs) ? srcs : [srcs]) {
650
+ if (s._id === void 0) continue;
651
+ visitSig(s);
652
+ edges.push({
653
+ from: s._id,
654
+ to: o._id,
655
+ kind: "dep"
656
+ });
657
+ }
658
+ } else if (o._id !== void 0 && !seen.has(o._id)) {
659
+ seen.add(o._id);
660
+ nodes.push({
661
+ id: o._id,
662
+ kind: "owner",
663
+ name: o._name,
664
+ hmrId: o._hmrId
665
+ });
666
+ }
667
+ const kids = o._children;
668
+ if (kids) for (const c of Array.isArray(kids) ? kids : [kids]) {
669
+ if (c._disposed) continue;
670
+ visitOwner(c);
671
+ edges.push({
672
+ from: o._id,
673
+ to: c._id,
674
+ kind: "own"
675
+ });
676
+ }
677
+ const dc = o._devC;
678
+ if (dc) for (const c of dc) {
679
+ if (c._disposed) continue;
680
+ visitOwner(c);
681
+ edges.push({
682
+ from: o._id,
683
+ to: c._id,
684
+ kind: "own"
685
+ });
686
+ }
687
+ };
688
+ for (const r of allRoots) visitOwner(r);
689
+ return {
690
+ nodes,
691
+ edges
692
+ };
693
+ };
694
+ /** 返回 `(key) => boolean`:source 变化时只通知旧 key 与新 key 的订阅者,而非全部行(Solid 同名 API) */
695
+ function createSelector(source, equals = ((a, b) => a === b)) {
696
+ const subs = /* @__PURE__ */ new Map();
697
+ let pruneAt = 64;
698
+ let value = untrack(source);
699
+ effect(() => {
700
+ const v = source();
701
+ if (Object.is(v, value)) return;
702
+ const prev = value;
703
+ value = v;
704
+ let n;
705
+ subs.forEach((node, key) => {
706
+ if (!node._subs) {
707
+ subs.delete(key);
708
+ return;
709
+ }
710
+ if (equals(key, prev) || equals(key, v)) {
711
+ n = node;
712
+ if (n) _notify(n);
713
+ }
714
+ });
715
+ });
716
+ return (key) => {
717
+ if (Listener) {
718
+ let n = subs.get(key);
719
+ if (!n) {
720
+ if (subs.size >= pruneAt) {
721
+ subs.forEach((node, k) => {
722
+ if (!node._subs) subs.delete(k);
723
+ });
724
+ pruneAt = Math.max(64, subs.size * 2);
725
+ }
726
+ n = _mkSignalNode();
727
+ subs.set(key, n);
728
+ }
729
+ readSignal(n);
730
+ }
731
+ return equals(key, value);
732
+ };
733
+ }
734
+
735
+ //#endregion
736
+ //#region src/reactive.ts
737
+ const RAW = Symbol("warp.raw");
738
+ const ITERATE = Symbol("warp.iterate");
739
+ const proxyMap = /* @__PURE__ */ new WeakMap();
740
+ const shallowMap = /* @__PURE__ */ new WeakMap();
741
+ const depMap = /* @__PURE__ */ new WeakMap();
742
+ function track(raw, key) {
743
+ if (!_hasListener()) return;
744
+ let m = depMap.get(raw);
745
+ if (!m) depMap.set(raw, m = /* @__PURE__ */ new Map());
746
+ let n = m.get(key);
747
+ if (!n) m.set(key, n = _mkSignalNode());
748
+ _track(n);
749
+ }
750
+ function trigger(raw, key, alsoIterate = false, keys) {
751
+ const m = depMap.get(raw);
752
+ if (!m) return;
753
+ batch(() => {
754
+ const fire = (n) => {
755
+ if (n) _notify(n);
756
+ };
757
+ fire(m.get(key));
758
+ if (keys) for (const k of keys) fire(m.get(k));
759
+ if (alsoIterate) {
760
+ fire(m.get(ITERATE));
761
+ if (Array.isArray(raw)) fire(m.get("length"));
762
+ }
763
+ });
764
+ }
765
+ function toRaw(o) {
766
+ return o && o[RAW] || o;
767
+ }
768
+ function isReactive(o) {
769
+ return !!(o && o[RAW]);
770
+ }
771
+ function canProxy(o) {
772
+ if (!o || typeof o !== "object" || Object.isFrozen(o) || !Object.isExtensible(o)) return false;
773
+ const t = Object.prototype.toString.call(o);
774
+ return /^\[object (Object|Array|Map|Set|WeakMap|WeakSet)\]$/.test(t);
775
+ }
776
+ function wrap(v, shallow) {
777
+ return shallow || !canProxy(v) ? v : createProxy(v, false);
778
+ }
779
+ const arrayInst = {};
780
+ for (const m of [
781
+ "push",
782
+ "pop",
783
+ "shift",
784
+ "unshift",
785
+ "splice",
786
+ "sort",
787
+ "reverse"
788
+ ]) arrayInst[m] = function(...args) {
789
+ const raw = toRaw(this);
790
+ const before = raw.length;
791
+ args = args.map(toRaw);
792
+ const r = batch(() => untrack(() => Array.prototype[m].apply(raw, args)));
793
+ if (m === "sort" || m === "reverse") trigger(raw, ITERATE, true, Array.from(raw.keys(), String));
794
+ else {
795
+ const after = raw.length;
796
+ const lo = m === "push" ? before : m === "pop" ? after : 0;
797
+ const hi = Math.max(before, after);
798
+ const ks = [];
799
+ for (let i = lo; i < hi; i++) ks.push(String(i));
800
+ trigger(raw, "length", true, ks);
801
+ }
802
+ return r;
803
+ };
804
+ for (const m of [
805
+ "includes",
806
+ "indexOf",
807
+ "lastIndexOf"
808
+ ]) arrayInst[m] = function(...args) {
809
+ const raw = toRaw(this);
810
+ track(raw, "length");
811
+ for (let i = 0; i < raw.length; i++) track(raw, String(i));
812
+ const fn = Array.prototype[m];
813
+ const r = fn.apply(raw, args);
814
+ if ((r === -1 || r === false) && args.length) return fn.apply(raw, [toRaw(args[0]), ...args.slice(1)]);
815
+ return r;
816
+ };
817
+ function baseHandlers(shallow) {
818
+ return {
819
+ get(raw, key, receiver) {
820
+ if (key === RAW) return raw;
821
+ if (Array.isArray(raw) && typeof key === "string" && Object.prototype.hasOwnProperty.call(arrayInst, key)) return arrayInst[key];
822
+ track(raw, key);
823
+ return wrap(Reflect.get(raw, key, receiver), shallow);
824
+ },
825
+ set(raw, key, v, receiver) {
826
+ v = toRaw(v);
827
+ const had = Object.prototype.hasOwnProperty.call(raw, key);
828
+ const old = raw[key];
829
+ const isArr = Array.isArray(raw);
830
+ const oldLen = isArr ? raw.length : 0;
831
+ const ok = Reflect.set(raw, key, v, receiver);
832
+ if (!had) trigger(raw, key, true);
833
+ else if (!Object.is(old, v)) {
834
+ if (isArr && key === "length") {
835
+ const ks = [];
836
+ for (let i = v; i < oldLen; i++) ks.push(String(i));
837
+ trigger(raw, "length", true, ks);
838
+ } else trigger(raw, key);
839
+ }
840
+ if (isArr && key !== "length" && raw.length !== oldLen) trigger(raw, "length", true);
841
+ return ok;
842
+ },
843
+ deleteProperty(raw, key) {
844
+ const had = Object.prototype.hasOwnProperty.call(raw, key);
845
+ const ok = Reflect.deleteProperty(raw, key);
846
+ if (had && ok) trigger(raw, key, true);
847
+ return ok;
848
+ },
849
+ has(raw, key) {
850
+ if (key !== RAW) track(raw, key);
851
+ return Reflect.has(raw, key);
852
+ },
853
+ ownKeys(raw) {
854
+ track(raw, ITERATE);
855
+ if (Array.isArray(raw)) track(raw, "length");
856
+ return Reflect.ownKeys(raw);
857
+ }
858
+ };
859
+ }
860
+ function collHandlers(shallow, weak) {
861
+ const inst = {
862
+ get(k) {
863
+ const raw = toRaw(this);
864
+ k = toRaw(k);
865
+ track(raw, k);
866
+ return wrap(raw.get(k), shallow);
867
+ },
868
+ has(k) {
869
+ const raw = toRaw(this);
870
+ k = toRaw(k);
871
+ track(raw, k);
872
+ return raw.has(k);
873
+ },
874
+ set(k, v) {
875
+ const raw = toRaw(this);
876
+ k = toRaw(k);
877
+ v = toRaw(v);
878
+ const had = raw.has(k);
879
+ const old = raw.get(k);
880
+ raw.set(k, v);
881
+ if (!had) trigger(raw, k, true);
882
+ else if (!Object.is(old, v)) trigger(raw, k);
883
+ return this;
884
+ },
885
+ add(v) {
886
+ const raw = toRaw(this);
887
+ v = toRaw(v);
888
+ if (!raw.has(v)) {
889
+ raw.add(v);
890
+ trigger(raw, v, true);
891
+ }
892
+ return this;
893
+ },
894
+ delete(k) {
895
+ const raw = toRaw(this);
896
+ k = toRaw(k);
897
+ const had = raw.has(k);
898
+ const r = raw.delete(k);
899
+ if (had) trigger(raw, k, true);
900
+ return r;
901
+ },
902
+ clear() {
903
+ const raw = toRaw(this);
904
+ const ks = [...raw.keys()];
905
+ const had = ks.length > 0;
906
+ raw.clear();
907
+ if (had) trigger(raw, ITERATE, true, ks);
908
+ },
909
+ forEach(cb, thisArg) {
910
+ const raw = toRaw(this);
911
+ track(raw, ITERATE);
912
+ raw.forEach((v, k) => cb.call(thisArg, wrap(v, shallow), wrap(k, shallow), this));
913
+ }
914
+ };
915
+ const iter = (name) => function() {
916
+ const raw = toRaw(this);
917
+ track(raw, ITERATE);
918
+ const isEntries = name === "entries" || name === Symbol.iterator && raw instanceof Map;
919
+ const it = raw[name].call(raw);
920
+ return {
921
+ next() {
922
+ const r = it.next();
923
+ if (r.done) return r;
924
+ const v = r.value;
925
+ return {
926
+ done: false,
927
+ value: isEntries ? [wrap(v[0], shallow), wrap(v[1], shallow)] : wrap(v, shallow)
928
+ };
929
+ },
930
+ [Symbol.iterator]() {
931
+ return this;
932
+ }
933
+ };
934
+ };
935
+ if (weak) {
936
+ delete inst.clear;
937
+ delete inst.forEach;
938
+ } else {
939
+ inst.keys = iter("keys");
940
+ inst.values = iter("values");
941
+ inst.entries = iter("entries");
942
+ inst[Symbol.iterator] = iter(Symbol.iterator);
943
+ }
944
+ return { get(raw, key, receiver) {
945
+ if (key === RAW) return raw;
946
+ if (key === "size" && !weak) {
947
+ track(raw, ITERATE);
948
+ return Reflect.get(raw, "size", raw);
949
+ }
950
+ if (Object.prototype.hasOwnProperty.call(inst, key)) return inst[key];
951
+ return Reflect.get(raw, key, receiver);
952
+ } };
953
+ }
954
+ function createProxy(o, shallow) {
955
+ const raw = toRaw(o);
956
+ const cache = shallow ? shallowMap : proxyMap;
957
+ const hit = cache.get(raw);
958
+ if (hit) return hit;
959
+ const weak = raw instanceof WeakMap || raw instanceof WeakSet;
960
+ const isColl = weak || raw instanceof Map || raw instanceof Set;
961
+ const p = new Proxy(raw, isColl ? collHandlers(shallow, weak) : baseHandlers(shallow));
962
+ cache.set(raw, p);
963
+ return p;
964
+ }
965
+ function reactive(o) {
966
+ return canProxy(o) ? createProxy(o, false) : o;
967
+ }
968
+ function shallowReactive(o) {
969
+ return canProxy(o) ? createProxy(o, true) : o;
970
+ }
971
+
972
+ //#endregion
973
+ export { WarpError, __DEV_graph, __DEV_onFlush, _childrenOf, _disposeOwner, _hasListener, _lightSignal, _mkSignalNode, _notify, _ownerContext, _rootRun, _setOwnerDev, _setOwnerError, _track, _write, batch, computed, createRoot, createSelector, effect, enableServerMode, flushSync, getOwner, isReactive, onCleanup, ownerView, reactive, renderEffect, runWithOwner, shallowReactive, signal, toRaw, untrack };