@warp-ts/reactivity 0.0.0-stage → 0.1.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/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.dev.js +973 -0
- package/dist/index.js +1 -0
- package/dist/types/core.d.ts +94 -0
- package/dist/types/core.js +714 -0
- package/dist/types/error.d.ts +7 -0
- package/dist/types/error.js +28 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +8 -0
- package/dist/types/reactive.d.ts +4 -0
- package/dist/types/reactive.js +208 -0
- package/dist/types/types.d.ts +40 -0
- package/dist/types/types.js +5 -0
- package/package.json +19 -3
- package/README.md +0 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class WarpError extends Error {
|
|
2
|
+
code: string;
|
|
3
|
+
hint?: string | undefined;
|
|
4
|
+
constructor(code: string, message: string, hint?: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function err(code: string, name?: string): WarpError;
|
|
7
|
+
export declare function warn(code: string, name?: string): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// WARP-R1xx 诊断(设计 01-R §9)
|
|
2
|
+
export class WarpError extends Error {
|
|
3
|
+
constructor(code, message, hint) {
|
|
4
|
+
super(`[${code}] ${message}`);
|
|
5
|
+
this.name = 'WarpError';
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.hint = hint;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const MSG = __DEV__ ? {
|
|
11
|
+
'WARP-R101': n => `effect "${n}" 创建于无 owner 上下文,将永不释放;请放入组件 / createRoot 内`,
|
|
12
|
+
'WARP-R102': n => `检测到依赖环:${n}`,
|
|
13
|
+
'WARP-R103': n => `不能写入 computed "${n}"`,
|
|
14
|
+
'WARP-R104': n => `computed "${n}" 内写入了 signal,computed 必须是纯函数`,
|
|
15
|
+
'WARP-R105': n => `computed "${n}" 内创建了 effect / createRoot,computed 必须是纯函数(重算不会释放它们)`,
|
|
16
|
+
} : {};
|
|
17
|
+
export function err(code, name) {
|
|
18
|
+
if (__DEV__) {
|
|
19
|
+
const f = MSG[code];
|
|
20
|
+
if (f)
|
|
21
|
+
return new WarpError(code, f(name || '(anonymous)'));
|
|
22
|
+
}
|
|
23
|
+
return new WarpError(code, code);
|
|
24
|
+
}
|
|
25
|
+
export function warn(code, name) {
|
|
26
|
+
if (__DEV__)
|
|
27
|
+
console.warn(err(code, name).message);
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { signal, computed, effect, renderEffect, createSelector, batch, untrack, flushSync, onCleanup, createRoot, getOwner, runWithOwner, ownerView, __DEV_graph, __DEV_onFlush } from './core';
|
|
2
|
+
export type { Signal, Owner, SignalOptions, GraphSnapshot, OwnerDevInfo } from './core';
|
|
3
|
+
export { reactive, shallowReactive, isReactive, toRaw } from './reactive';
|
|
4
|
+
export { WarpError } from './error';
|
|
5
|
+
/** @internal 供 core / runtime-dom 使用 */
|
|
6
|
+
export { _setOwnerError, _setOwnerDev, _ownerContext, _notify, _track, _mkSignalNode, _hasListener, _lightSignal, _write, _rootRun, _disposeOwner, _childrenOf } from './core';
|
|
7
|
+
/** @internal REQ-LFE-002 ADR-205:服务端模式(@warp-ts/server 启用) */
|
|
8
|
+
export { enableServerMode } from './core';
|
|
9
|
+
export type { SignalNode } from './types';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @warp-ts/reactivity · REQ-LFE-001-R · 公开 API(设计 00 §4.1)
|
|
2
|
+
export { signal, computed, effect, renderEffect, createSelector, batch, untrack, flushSync, onCleanup, createRoot, getOwner, runWithOwner, ownerView, __DEV_graph, __DEV_onFlush } from './core';
|
|
3
|
+
export { reactive, shallowReactive, isReactive, toRaw } from './reactive';
|
|
4
|
+
export { WarpError } from './error';
|
|
5
|
+
/** @internal 供 core / runtime-dom 使用 */
|
|
6
|
+
export { _setOwnerError, _setOwnerDev, _ownerContext, _notify, _track, _mkSignalNode, _hasListener, _lightSignal, _write, _rootRun, _disposeOwner, _childrenOf } from './core';
|
|
7
|
+
/** @internal REQ-LFE-002 ADR-205:服务端模式(@warp-ts/server 启用) */
|
|
8
|
+
export { enableServerMode } from './core';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// @warp-ts/reactivity · REQ-LFE-001-R-05 · Proxy 深响应(设计 01-R §6)
|
|
2
|
+
import { _hasListener, _mkSignalNode, _notify, _track, batch, untrack } from './core';
|
|
3
|
+
const RAW = Symbol('warp.raw');
|
|
4
|
+
const ITERATE = Symbol('warp.iterate');
|
|
5
|
+
const proxyMap = new WeakMap();
|
|
6
|
+
const shallowMap = new WeakMap();
|
|
7
|
+
const depMap = new WeakMap();
|
|
8
|
+
function track(raw, key) {
|
|
9
|
+
if (!_hasListener())
|
|
10
|
+
return;
|
|
11
|
+
let m = depMap.get(raw);
|
|
12
|
+
if (!m)
|
|
13
|
+
depMap.set(raw, m = new Map());
|
|
14
|
+
let n = m.get(key);
|
|
15
|
+
if (!n)
|
|
16
|
+
m.set(key, n = _mkSignalNode());
|
|
17
|
+
_track(n);
|
|
18
|
+
}
|
|
19
|
+
function trigger(raw, key, alsoIterate = false, keys) {
|
|
20
|
+
const m = depMap.get(raw);
|
|
21
|
+
if (!m)
|
|
22
|
+
return;
|
|
23
|
+
batch(() => {
|
|
24
|
+
const fire = (n) => { if (n)
|
|
25
|
+
_notify(n); };
|
|
26
|
+
fire(m.get(key));
|
|
27
|
+
if (keys)
|
|
28
|
+
for (const k of keys)
|
|
29
|
+
fire(m.get(k));
|
|
30
|
+
if (alsoIterate) {
|
|
31
|
+
fire(m.get(ITERATE));
|
|
32
|
+
if (Array.isArray(raw))
|
|
33
|
+
fire(m.get('length'));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function toRaw(o) { return (o && o[RAW]) || o; }
|
|
38
|
+
export function isReactive(o) { return !!(o && o[RAW]); }
|
|
39
|
+
function canProxy(o) {
|
|
40
|
+
if (!o || typeof o !== 'object' || Object.isFrozen(o) || !Object.isExtensible(o))
|
|
41
|
+
return false;
|
|
42
|
+
const t = Object.prototype.toString.call(o);
|
|
43
|
+
return /^\[object (Object|Array|Map|Set|WeakMap|WeakSet)\]$/.test(t);
|
|
44
|
+
}
|
|
45
|
+
function wrap(v, shallow) { return shallow || !canProxy(v) ? v : createProxy(v, false); }
|
|
46
|
+
// ---- 数组 instrumentation ----
|
|
47
|
+
const arrayInst = {};
|
|
48
|
+
for (const m of ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']) {
|
|
49
|
+
arrayInst[m] = function (...args) {
|
|
50
|
+
const raw = toRaw(this);
|
|
51
|
+
const before = raw.length;
|
|
52
|
+
args = args.map(toRaw); // RDC-15(006 L-12):写入参数与 set 陷阱同口径落 raw(`toRaw` 对非对象原样返回)
|
|
53
|
+
const r = batch(() => untrack(() => Array.prototype[m].apply(raw, args)));
|
|
54
|
+
if (m === 'sort' || m === 'reverse')
|
|
55
|
+
trigger(raw, ITERATE, true, Array.from(raw.keys(), String)); // 键统一为字符串(RDC-12)
|
|
56
|
+
else {
|
|
57
|
+
const after = raw.length;
|
|
58
|
+
const lo = m === 'push' ? before : m === 'pop' ? after : 0;
|
|
59
|
+
const hi = Math.max(before, after);
|
|
60
|
+
const ks = [];
|
|
61
|
+
for (let i = lo; i < hi; i++)
|
|
62
|
+
ks.push(String(i));
|
|
63
|
+
trigger(raw, 'length', true, ks);
|
|
64
|
+
}
|
|
65
|
+
return r;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
for (const m of ['includes', 'indexOf', 'lastIndexOf']) {
|
|
69
|
+
arrayInst[m] = function (...args) {
|
|
70
|
+
const raw = toRaw(this);
|
|
71
|
+
track(raw, 'length');
|
|
72
|
+
for (let i = 0; i < raw.length; i++)
|
|
73
|
+
track(raw, String(i));
|
|
74
|
+
const fn = Array.prototype[m];
|
|
75
|
+
const r = fn.apply(raw, args);
|
|
76
|
+
if ((r === -1 || r === false) && args.length)
|
|
77
|
+
return fn.apply(raw, [toRaw(args[0]), ...args.slice(1)]);
|
|
78
|
+
return r;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function baseHandlers(shallow) {
|
|
82
|
+
return {
|
|
83
|
+
get(raw, key, receiver) {
|
|
84
|
+
if (key === RAW)
|
|
85
|
+
return raw;
|
|
86
|
+
if (Array.isArray(raw) && typeof key === 'string' && Object.prototype.hasOwnProperty.call(arrayInst, key))
|
|
87
|
+
return arrayInst[key];
|
|
88
|
+
track(raw, key);
|
|
89
|
+
return wrap(Reflect.get(raw, key, receiver), shallow);
|
|
90
|
+
},
|
|
91
|
+
set(raw, key, v, receiver) {
|
|
92
|
+
v = toRaw(v);
|
|
93
|
+
const had = Object.prototype.hasOwnProperty.call(raw, key);
|
|
94
|
+
const old = raw[key];
|
|
95
|
+
const isArr = Array.isArray(raw);
|
|
96
|
+
const oldLen = isArr ? raw.length : 0;
|
|
97
|
+
const ok = Reflect.set(raw, key, v, receiver);
|
|
98
|
+
if (!had)
|
|
99
|
+
trigger(raw, key, true);
|
|
100
|
+
else if (!Object.is(old, v)) {
|
|
101
|
+
if (isArr && key === 'length') {
|
|
102
|
+
const ks = [];
|
|
103
|
+
for (let i = v; i < oldLen; i++)
|
|
104
|
+
ks.push(String(i));
|
|
105
|
+
trigger(raw, 'length', true, ks);
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
trigger(raw, key);
|
|
109
|
+
}
|
|
110
|
+
if (isArr && key !== 'length' && raw.length !== oldLen)
|
|
111
|
+
trigger(raw, 'length', true);
|
|
112
|
+
return ok;
|
|
113
|
+
},
|
|
114
|
+
deleteProperty(raw, key) {
|
|
115
|
+
const had = Object.prototype.hasOwnProperty.call(raw, key);
|
|
116
|
+
const ok = Reflect.deleteProperty(raw, key);
|
|
117
|
+
if (had && ok)
|
|
118
|
+
trigger(raw, key, true);
|
|
119
|
+
return ok;
|
|
120
|
+
},
|
|
121
|
+
has(raw, key) { if (key !== RAW)
|
|
122
|
+
track(raw, key); return Reflect.has(raw, key); },
|
|
123
|
+
ownKeys(raw) { track(raw, ITERATE); if (Array.isArray(raw))
|
|
124
|
+
track(raw, 'length'); return Reflect.ownKeys(raw); },
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function collHandlers(shallow, weak) {
|
|
128
|
+
const inst = {
|
|
129
|
+
get(k) { const raw = toRaw(this); k = toRaw(k); track(raw, k); return wrap(raw.get(k), shallow); },
|
|
130
|
+
has(k) { const raw = toRaw(this); k = toRaw(k); track(raw, k); return raw.has(k); },
|
|
131
|
+
set(k, v) {
|
|
132
|
+
const raw = toRaw(this);
|
|
133
|
+
k = toRaw(k);
|
|
134
|
+
v = toRaw(v);
|
|
135
|
+
const had = raw.has(k);
|
|
136
|
+
const old = raw.get(k);
|
|
137
|
+
raw.set(k, v);
|
|
138
|
+
if (!had)
|
|
139
|
+
trigger(raw, k, true);
|
|
140
|
+
else if (!Object.is(old, v))
|
|
141
|
+
trigger(raw, k);
|
|
142
|
+
return this;
|
|
143
|
+
},
|
|
144
|
+
add(v) { const raw = toRaw(this); v = toRaw(v); if (!raw.has(v)) {
|
|
145
|
+
raw.add(v);
|
|
146
|
+
trigger(raw, v, true);
|
|
147
|
+
} return this; },
|
|
148
|
+
delete(k) { const raw = toRaw(this); k = toRaw(k); const had = raw.has(k); const r = raw.delete(k); if (had)
|
|
149
|
+
trigger(raw, k, true); return r; },
|
|
150
|
+
clear() { const raw = toRaw(this); const ks = [...raw.keys()]; const had = ks.length > 0; raw.clear(); if (had)
|
|
151
|
+
trigger(raw, ITERATE, true, ks); },
|
|
152
|
+
forEach(cb, thisArg) {
|
|
153
|
+
const raw = toRaw(this);
|
|
154
|
+
track(raw, ITERATE);
|
|
155
|
+
raw.forEach((v, k) => cb.call(thisArg, wrap(v, shallow), wrap(k, shallow), this));
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const iter = (name) => function () {
|
|
159
|
+
const raw = toRaw(this);
|
|
160
|
+
track(raw, ITERATE);
|
|
161
|
+
const isEntries = name === 'entries' || (name === Symbol.iterator && raw instanceof Map);
|
|
162
|
+
const it = raw[name].call(raw);
|
|
163
|
+
return {
|
|
164
|
+
next() { const r = it.next(); if (r.done)
|
|
165
|
+
return r; const v = r.value; return { done: false, value: isEntries ? [wrap(v[0], shallow), wrap(v[1], shallow)] : wrap(v, shallow) }; },
|
|
166
|
+
[Symbol.iterator]() { return this; },
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
// RDC-14 / U-4(006 L-15):Weak* 只挂 get / has / set / delete / add ——
|
|
170
|
+
// 其余成员不装配,取值时落到 `Reflect.get(raw, …)`,与原生 WeakMap / WeakSet 一样是 undefined
|
|
171
|
+
if (weak) {
|
|
172
|
+
delete inst.clear;
|
|
173
|
+
delete inst.forEach;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
inst.keys = iter('keys');
|
|
177
|
+
inst.values = iter('values');
|
|
178
|
+
inst.entries = iter('entries');
|
|
179
|
+
inst[Symbol.iterator] = iter(Symbol.iterator);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
get(raw, key, receiver) {
|
|
183
|
+
if (key === RAW)
|
|
184
|
+
return raw;
|
|
185
|
+
if (key === 'size' && !weak) {
|
|
186
|
+
track(raw, ITERATE);
|
|
187
|
+
return Reflect.get(raw, 'size', raw);
|
|
188
|
+
}
|
|
189
|
+
if (Object.prototype.hasOwnProperty.call(inst, key))
|
|
190
|
+
return inst[key];
|
|
191
|
+
return Reflect.get(raw, key, receiver);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function createProxy(o, shallow) {
|
|
196
|
+
const raw = toRaw(o);
|
|
197
|
+
const cache = shallow ? shallowMap : proxyMap;
|
|
198
|
+
const hit = cache.get(raw);
|
|
199
|
+
if (hit)
|
|
200
|
+
return hit;
|
|
201
|
+
const weak = raw instanceof WeakMap || raw instanceof WeakSet;
|
|
202
|
+
const isColl = weak || raw instanceof Map || raw instanceof Set;
|
|
203
|
+
const p = new Proxy(raw, isColl ? collHandlers(shallow, weak) : baseHandlers(shallow));
|
|
204
|
+
cache.set(raw, p);
|
|
205
|
+
return p;
|
|
206
|
+
}
|
|
207
|
+
export function reactive(o) { return (canProxy(o) ? createProxy(o, false) : o); }
|
|
208
|
+
export function shallowReactive(o) { return (canProxy(o) ? createProxy(o, true) : o); }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const CLEAN = 0, CHECK = 1, DIRTY = 2;
|
|
2
|
+
export type State = 0 | 1 | 2;
|
|
3
|
+
export interface SignalNode<T = unknown> {
|
|
4
|
+
_v: T;
|
|
5
|
+
_eq: (a: T, b: T) => boolean;
|
|
6
|
+
_subs: Computation<any> | Computation<any>[] | null;
|
|
7
|
+
_subSlots: number | number[] | null;
|
|
8
|
+
_name?: string | undefined;
|
|
9
|
+
_id?: number;
|
|
10
|
+
}
|
|
11
|
+
/** computed 与 effect 共用;effect 同时是 Owner(承载其内部创建的子节点) */
|
|
12
|
+
export interface Computation<T = unknown> extends SignalNode<T>, OwnerNode {
|
|
13
|
+
_fn: ((onCleanup: (f: () => void) => void) => T) | null;
|
|
14
|
+
_sources: SignalNode<any> | SignalNode<any>[] | null;
|
|
15
|
+
_sourceSlots: number | number[] | null;
|
|
16
|
+
_state: State;
|
|
17
|
+
_pure: boolean;
|
|
18
|
+
_running: boolean;
|
|
19
|
+
_updatedAt: number;
|
|
20
|
+
_seq: number;
|
|
21
|
+
_seen?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface OwnerNode {
|
|
24
|
+
_parent: OwnerNode | null;
|
|
25
|
+
_children: OwnerNode | Computation<any> | (OwnerNode | Computation<any>)[] | null;
|
|
26
|
+
_cleanups: (() => void)[] | null;
|
|
27
|
+
_context: Map<symbol, unknown> | null;
|
|
28
|
+
_disposed: boolean;
|
|
29
|
+
_dead?: number;
|
|
30
|
+
_err?: ((e: unknown) => boolean | void) | null;
|
|
31
|
+
_name?: string | undefined;
|
|
32
|
+
_id?: number;
|
|
33
|
+
_hmrId?: string | undefined;
|
|
34
|
+
_devC?: Computation<any>[];
|
|
35
|
+
_node?: unknown;
|
|
36
|
+
}
|
|
37
|
+
export declare function isComputation(n: SignalNode<any> | OwnerNode): n is Computation<any>;
|
|
38
|
+
declare global {
|
|
39
|
+
var __DEV__: boolean;
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warp-ts/reactivity",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Signal / Computed / Effect / batch / owner 树 / reactive",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"development": "./dist/index.dev.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {}
|
|
6
22
|
}
|
package/README.md
DELETED