@echojs-ecosystem/reactivity 0.1.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 +78 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +250 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @echojs-ecosystem/reactivity
|
|
4
|
+
|
|
5
|
+
**Fine-grained reactive primitives — signals, computed, effects, and batching.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@echojs-ecosystem/reactivity)
|
|
8
|
+
[](https://echojs.dev/docs/packages/reactivity)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
The reactive foundation of EchoJS. Built on a fast signal engine, wrapped in a **strict, object-oriented API** that encourages predictable updates and immutable patterns.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **`signal`** — writable cells with `.value()`, `.set()`, `.update()`, `.peek()`, `.subscribe()`
|
|
19
|
+
- **`computed`** — lazy derived values with automatic dependency tracking
|
|
20
|
+
- **`effect` / `scope`** — side effects with automatic cleanup
|
|
21
|
+
- **`batch`** — coalesce multiple writes into one notification wave
|
|
22
|
+
- **`DeepReadonly`** — object reads cannot be mutated through `.value()`; use `.set()` / `.update()`
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @echojs-ecosystem/reactivity
|
|
28
|
+
# bun add @echojs-ecosystem/reactivity
|
|
29
|
+
# pnpm add @echojs-ecosystem/reactivity
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { signal, computed, effect, batch } from "@echojs-ecosystem/reactivity";
|
|
36
|
+
|
|
37
|
+
const $count = signal(0);
|
|
38
|
+
const $double = computed(() => $count.value() * 2);
|
|
39
|
+
|
|
40
|
+
effect(() => {
|
|
41
|
+
console.log("count:", $count.value(), "double:", $double.value());
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
batch(() => {
|
|
45
|
+
$count.set(1);
|
|
46
|
+
$count.update((n) => n + 1);
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API
|
|
51
|
+
|
|
52
|
+
| Export | Description |
|
|
53
|
+
|--------|-------------|
|
|
54
|
+
| `signal(initial)` | Writable reactive cell |
|
|
55
|
+
| `computed(getter)` | Readonly derived signal |
|
|
56
|
+
| `effect(fn)` | Runs immediately, re-runs on dependency changes; returns disposer |
|
|
57
|
+
| `scope(fn)` | Effect scope — nested effects cleaned up together |
|
|
58
|
+
| `batch(fn)` | Defer reactions until the batch completes |
|
|
59
|
+
| `readonly($sig)` | Readonly facade without `.set()` / `.update()` |
|
|
60
|
+
| `isSignal` / `isReadonlySignal` | Runtime type guards |
|
|
61
|
+
|
|
62
|
+
### Design notes
|
|
63
|
+
|
|
64
|
+
- **Write only via `.set()` / `.update()`** — no public `trigger()` API
|
|
65
|
+
- **`subscribe()`** fires on changes only, not on initial subscribe
|
|
66
|
+
- **Dev mode** deep-freezes object/array values written to signals
|
|
67
|
+
|
|
68
|
+
## Related packages
|
|
69
|
+
|
|
70
|
+
| Package | Role |
|
|
71
|
+
|---------|------|
|
|
72
|
+
| [`@echojs-ecosystem/hyperdom`](https://www.npmjs.com/package/@echojs-ecosystem/hyperdom) | DOM views wired to signals |
|
|
73
|
+
| [`@echojs-ecosystem/store`](https://www.npmjs.com/package/@echojs-ecosystem/store) | Structured app state on top of signals |
|
|
74
|
+
| [`@echojs-ecosystem/framework`](https://www.npmjs.com/package/@echojs-ecosystem/framework) | Meta-package — entire ecosystem via subpath imports |
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
[echojs.dev/docs/packages/reactivity](https://echojs.dev/docs/packages/reactivity)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
declare const batch: <T>(fn: () => T) => T;
|
|
2
|
+
|
|
3
|
+
type CleanupFn = () => void;
|
|
4
|
+
declare const cleanup: (fn: CleanupFn) => void;
|
|
5
|
+
|
|
6
|
+
type DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends readonly (infer U)[] ? readonly DeepReadonly<U>[] : T extends object ? {
|
|
7
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
8
|
+
} : T;
|
|
9
|
+
type ReadValue<T> = T extends object ? DeepReadonly<T> : T;
|
|
10
|
+
interface ReadonlySignal<T> {
|
|
11
|
+
value(): ReadValue<T>;
|
|
12
|
+
peek(): ReadValue<T>;
|
|
13
|
+
subscribe(fn: () => void): () => void;
|
|
14
|
+
}
|
|
15
|
+
interface Signal<T> extends ReadonlySignal<T> {
|
|
16
|
+
set(next: T): void;
|
|
17
|
+
update(fn: (prev: T) => T): void;
|
|
18
|
+
readonly(): ReadonlySignal<T>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare const computed: <T>(getter: () => T) => ReadonlySignal<T>;
|
|
22
|
+
|
|
23
|
+
declare const effect: (fn: () => void) => (() => void);
|
|
24
|
+
|
|
25
|
+
declare const readonly: <T>(sig: Signal<T> | ReadonlySignal<T>) => ReadonlySignal<T>;
|
|
26
|
+
|
|
27
|
+
declare const scope: (fn: () => void) => (() => void);
|
|
28
|
+
|
|
29
|
+
declare const signal: <T>(initial: T) => Signal<T>;
|
|
30
|
+
|
|
31
|
+
declare const isSignal: (value: unknown) => value is Signal<unknown> | ReadonlySignal<unknown>;
|
|
32
|
+
declare const isReadonlySignal: (value: unknown) => value is ReadonlySignal<unknown>;
|
|
33
|
+
|
|
34
|
+
export { type DeepReadonly, type ReadValue, type ReadonlySignal, type Signal, batch, cleanup, computed, effect, isReadonlySignal, isSignal, readonly, scope, signal };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { startBatch, endBatch, computed as computed$1, effect as effect$1, effectScope, signal as signal$1, setActiveSub } from 'alien-signals';
|
|
2
|
+
|
|
3
|
+
// src/utils.ts
|
|
4
|
+
var isObjectLike = (value) => {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
};
|
|
7
|
+
var isFunction = (value) => {
|
|
8
|
+
return typeof value === "function";
|
|
9
|
+
};
|
|
10
|
+
var createAlienSignal = (initial) => {
|
|
11
|
+
return signal$1(initial);
|
|
12
|
+
};
|
|
13
|
+
var createAlienComputed = (getter) => {
|
|
14
|
+
return computed$1(getter);
|
|
15
|
+
};
|
|
16
|
+
var createAlienEffect = (fn) => {
|
|
17
|
+
return effect$1(fn);
|
|
18
|
+
};
|
|
19
|
+
var createAlienScope = (fn) => {
|
|
20
|
+
return effectScope(fn);
|
|
21
|
+
};
|
|
22
|
+
var batch = (fn) => {
|
|
23
|
+
startBatch();
|
|
24
|
+
try {
|
|
25
|
+
return fn();
|
|
26
|
+
} finally {
|
|
27
|
+
endBatch();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var untrack = (fn) => {
|
|
31
|
+
const prev = setActiveSub(void 0);
|
|
32
|
+
try {
|
|
33
|
+
return fn();
|
|
34
|
+
} finally {
|
|
35
|
+
setActiveSub(prev);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/batch.ts
|
|
40
|
+
var batch2 = (fn) => {
|
|
41
|
+
if (!isFunction(fn)) {
|
|
42
|
+
throw new TypeError("batch(fn) expects a function");
|
|
43
|
+
}
|
|
44
|
+
return batch(fn);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/cleanup.ts
|
|
48
|
+
var currentBucket;
|
|
49
|
+
var __withCleanupBucket = (bucket, fn) => {
|
|
50
|
+
const prev = currentBucket;
|
|
51
|
+
currentBucket = bucket;
|
|
52
|
+
try {
|
|
53
|
+
return fn();
|
|
54
|
+
} finally {
|
|
55
|
+
currentBucket = prev;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var __runCleanupBucket = (bucket) => {
|
|
59
|
+
for (let i = bucket.length - 1; i >= 0; i--) {
|
|
60
|
+
try {
|
|
61
|
+
bucket[i]?.();
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
bucket.length = 0;
|
|
66
|
+
};
|
|
67
|
+
var cleanup = (fn) => {
|
|
68
|
+
if (!isFunction(fn)) {
|
|
69
|
+
throw new TypeError("cleanup(fn) expects a function");
|
|
70
|
+
}
|
|
71
|
+
if (!currentBucket) {
|
|
72
|
+
throw new Error("cleanup(fn) must be called inside scope()");
|
|
73
|
+
}
|
|
74
|
+
currentBucket.push(fn);
|
|
75
|
+
};
|
|
76
|
+
var __wrapDisposerWithCleanup = (dispose, bucket) => {
|
|
77
|
+
let disposed = false;
|
|
78
|
+
return () => {
|
|
79
|
+
if (disposed) return;
|
|
80
|
+
disposed = true;
|
|
81
|
+
try {
|
|
82
|
+
dispose();
|
|
83
|
+
} finally {
|
|
84
|
+
__runCleanupBucket(bucket);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/subscribe.ts
|
|
90
|
+
var createSubscribe = (readTracked) => {
|
|
91
|
+
return (fn) => {
|
|
92
|
+
if (!isFunction(fn)) {
|
|
93
|
+
throw new TypeError("subscribe(fn) expects a function");
|
|
94
|
+
}
|
|
95
|
+
let inited = false;
|
|
96
|
+
let prev;
|
|
97
|
+
const stop = createAlienEffect(() => {
|
|
98
|
+
const next = readTracked();
|
|
99
|
+
if (!inited) {
|
|
100
|
+
inited = true;
|
|
101
|
+
prev = next;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (Object.is(prev, next)) return;
|
|
105
|
+
prev = next;
|
|
106
|
+
fn();
|
|
107
|
+
});
|
|
108
|
+
return stop;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/internals/guards.ts
|
|
113
|
+
var kSignalBrand = /* @__PURE__ */ Symbol.for("echojs-ecosystem.reactivity.signal");
|
|
114
|
+
var kReadonlyBrand = /* @__PURE__ */ Symbol.for("echojs-ecosystem.reactivity.readonlySignal");
|
|
115
|
+
var brandWritable = (obj) => {
|
|
116
|
+
Object.defineProperty(obj, kSignalBrand, { value: true });
|
|
117
|
+
return obj;
|
|
118
|
+
};
|
|
119
|
+
var brandReadonly = (obj) => {
|
|
120
|
+
Object.defineProperty(obj, kSignalBrand, { value: true });
|
|
121
|
+
Object.defineProperty(obj, kReadonlyBrand, { value: true });
|
|
122
|
+
return obj;
|
|
123
|
+
};
|
|
124
|
+
var isBrandedSignal = (value) => {
|
|
125
|
+
return typeof value === "object" && value !== null && value[kSignalBrand] === true;
|
|
126
|
+
};
|
|
127
|
+
var isBrandedReadonlySignal = (value) => {
|
|
128
|
+
return typeof value === "object" && value !== null && value[kSignalBrand] === true && value[kReadonlyBrand] === true;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/computed.ts
|
|
132
|
+
var computed = (getter) => {
|
|
133
|
+
if (!isFunction(getter)) {
|
|
134
|
+
throw new TypeError("computed(getter) expects a function");
|
|
135
|
+
}
|
|
136
|
+
const engine = createAlienComputed(() => getter());
|
|
137
|
+
const readTracked = () => engine();
|
|
138
|
+
const readUntracked = () => untrack(() => engine());
|
|
139
|
+
const subscribe = createSubscribe(readTracked);
|
|
140
|
+
return brandReadonly({
|
|
141
|
+
value: () => readTracked(),
|
|
142
|
+
peek: () => readUntracked(),
|
|
143
|
+
subscribe
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/effect.ts
|
|
148
|
+
var effect = (fn) => {
|
|
149
|
+
if (!isFunction(fn)) {
|
|
150
|
+
throw new TypeError("effect(fn) expects a function");
|
|
151
|
+
}
|
|
152
|
+
return createAlienEffect(fn);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/readonly.ts
|
|
156
|
+
var readonly = (sig) => {
|
|
157
|
+
if (!isFunction(sig.set)) return sig;
|
|
158
|
+
if (!isFunction(sig.readonly)) return sig.readonly();
|
|
159
|
+
return brandReadonly({
|
|
160
|
+
value: () => sig.value(),
|
|
161
|
+
peek: () => sig.peek(),
|
|
162
|
+
subscribe: (fn) => sig.subscribe(fn)
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/scope.ts
|
|
167
|
+
var scope = (fn) => {
|
|
168
|
+
if (!isFunction(fn)) {
|
|
169
|
+
throw new TypeError("scope(fn) expects a function");
|
|
170
|
+
}
|
|
171
|
+
const bucket = [];
|
|
172
|
+
const dispose = createAlienScope(() => __withCleanupBucket(bucket, fn));
|
|
173
|
+
return __wrapDisposerWithCleanup(dispose, bucket);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/freeze.ts
|
|
177
|
+
var DEV = (() => {
|
|
178
|
+
const env = globalThis?.process?.env;
|
|
179
|
+
const nodeEnv = env?.NODE_ENV;
|
|
180
|
+
return nodeEnv !== "production";
|
|
181
|
+
})();
|
|
182
|
+
var deepFreezeImpl = (value, seen) => {
|
|
183
|
+
if (!isObjectLike(value)) return;
|
|
184
|
+
if (Object.isFrozen(value)) return;
|
|
185
|
+
if (seen.has(value)) return;
|
|
186
|
+
seen.add(value);
|
|
187
|
+
const keys = Object.keys(value);
|
|
188
|
+
for (const k of keys) {
|
|
189
|
+
deepFreezeImpl(value[k], seen);
|
|
190
|
+
}
|
|
191
|
+
Object.freeze(value);
|
|
192
|
+
};
|
|
193
|
+
var freezeIfDev = (value) => {
|
|
194
|
+
if (!DEV) return value;
|
|
195
|
+
if (!isObjectLike(value)) return value;
|
|
196
|
+
deepFreezeImpl(value, /* @__PURE__ */ new WeakSet());
|
|
197
|
+
return value;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// src/signal.ts
|
|
201
|
+
var createReadonlySignalFacade = (impl) => {
|
|
202
|
+
return brandReadonly({
|
|
203
|
+
value: impl.value,
|
|
204
|
+
peek: impl.peek,
|
|
205
|
+
subscribe: impl.subscribe
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
var signalImpl = (...args) => {
|
|
209
|
+
if (args.length === 0) {
|
|
210
|
+
throw new TypeError("signal(initial) expects 1 argument");
|
|
211
|
+
}
|
|
212
|
+
const initial = args[0];
|
|
213
|
+
const engine = createAlienSignal(freezeIfDev(initial));
|
|
214
|
+
const readTracked = () => engine();
|
|
215
|
+
const readUntracked = () => untrack(() => engine());
|
|
216
|
+
const subscribe = createSubscribe(readTracked);
|
|
217
|
+
const readonlyFacade = createReadonlySignalFacade({
|
|
218
|
+
value: () => readTracked(),
|
|
219
|
+
peek: () => readUntracked(),
|
|
220
|
+
subscribe
|
|
221
|
+
});
|
|
222
|
+
const writable = brandWritable({
|
|
223
|
+
value: () => readTracked(),
|
|
224
|
+
peek: () => readUntracked(),
|
|
225
|
+
subscribe,
|
|
226
|
+
set: (next) => {
|
|
227
|
+
engine(freezeIfDev(next));
|
|
228
|
+
},
|
|
229
|
+
update: (fn) => {
|
|
230
|
+
const prev = readUntracked();
|
|
231
|
+
const next = fn(prev);
|
|
232
|
+
engine(freezeIfDev(next));
|
|
233
|
+
},
|
|
234
|
+
readonly: () => readonlyFacade
|
|
235
|
+
});
|
|
236
|
+
return writable;
|
|
237
|
+
};
|
|
238
|
+
var signal = signalImpl;
|
|
239
|
+
|
|
240
|
+
// src/public-guards.ts
|
|
241
|
+
var isSignal = (value) => {
|
|
242
|
+
return isBrandedSignal(value);
|
|
243
|
+
};
|
|
244
|
+
var isReadonlySignal = (value) => {
|
|
245
|
+
return isBrandedReadonlySignal(value);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export { batch2 as batch, cleanup, computed, effect, isReadonlySignal, isSignal, readonly, scope, signal };
|
|
249
|
+
//# sourceMappingURL=index.js.map
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/internals/alien.ts","../src/batch.ts","../src/cleanup.ts","../src/subscribe.ts","../src/internals/guards.ts","../src/computed.ts","../src/effect.ts","../src/readonly.ts","../src/scope.ts","../src/freeze.ts","../src/signal.ts","../src/public-guards.ts"],"names":["alienSignal","alienComputed","alienEffect","alienEffectScope","alienStartBatch","alienEndBatch","alienSetActiveSub","batch"],"mappings":"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","file":"index.js","sourcesContent":["export type AnyFn = (...args: any[]) => any;\n\nexport const isObjectLike = (value: unknown): value is Record<string, unknown> => {\n return typeof value === \"object\" && value !== null;\n};\n\nexport const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (!isObjectLike(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n};\n\nexport const isFunction = (value: unknown): value is Function => {\n return typeof value === \"function\";\n};\n","import {\n computed as alienComputed,\n effect as alienEffect,\n effectScope as alienEffectScope,\n endBatch as alienEndBatch,\n setActiveSub as alienSetActiveSub,\n signal as alienSignal,\n startBatch as alienStartBatch,\n} from \"alien-signals\";\n\nexport type Disposer = () => void;\n\nexport const createAlienSignal = <T>(initial: T) => {\n return alienSignal(initial);\n};\n\nexport const createAlienComputed = <T>(getter: (prev?: T) => T) => {\n return alienComputed(getter);\n};\n\nexport const createAlienEffect = (fn: () => void): Disposer => {\n return alienEffect(fn);\n};\n\nexport const createAlienScope = (fn: () => void): Disposer => {\n return alienEffectScope(fn);\n};\n\nexport const batch = <T>(fn: () => T): T => {\n alienStartBatch();\n try {\n return fn();\n } finally {\n alienEndBatch();\n }\n};\n\nexport const untrack = <T>(fn: () => T): T => {\n const prev = alienSetActiveSub(undefined);\n try {\n return fn();\n } finally {\n alienSetActiveSub(prev);\n }\n};\n","import { isFunction } from \"./utils\";\nimport { batch as alienBatch } from \"./internals/alien.js\";\n\nexport const batch = <T>(fn: () => T): T => {\n if (!isFunction(fn)) {\n throw new TypeError(\"batch(fn) expects a function\");\n }\n return alienBatch(fn);\n};\n","import { isFunction } from \"./utils\";\nimport type { Disposer } from \"./internals/alien.js\";\n\ntype CleanupFn = () => void;\n\nlet currentBucket: CleanupFn[] | undefined;\n\nexport const __withCleanupBucket = <T>(bucket: CleanupFn[], fn: () => T): T => {\n const prev = currentBucket;\n currentBucket = bucket;\n try {\n return fn();\n } finally {\n currentBucket = prev;\n }\n};\n\nexport const __runCleanupBucket = (bucket: CleanupFn[]): void => {\n for (let i = bucket.length - 1; i >= 0; i--) {\n try {\n bucket[i]?.();\n } catch {\n // best-effort cleanup: userland errors shouldn't break disposal\n }\n }\n bucket.length = 0;\n};\n\nexport const cleanup = (fn: CleanupFn): void => {\n if (!isFunction(fn)) {\n throw new TypeError(\"cleanup(fn) expects a function\");\n }\n if (!currentBucket) {\n throw new Error(\"cleanup(fn) must be called inside scope()\");\n }\n currentBucket.push(fn);\n};\n\nexport const __wrapDisposerWithCleanup = (dispose: Disposer, bucket: CleanupFn[]): Disposer => {\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n try {\n dispose();\n } finally {\n __runCleanupBucket(bucket);\n }\n };\n};\n","import { createAlienEffect, type Disposer } from \"./internals/alien.js\";\nimport { isFunction } from \"./utils\";\n\nexport const createSubscribe = (readTracked: () => unknown): ((fn: () => void) => Disposer) => {\n return (fn) => {\n if (!isFunction(fn)) {\n throw new TypeError(\"subscribe(fn) expects a function\");\n }\n\n let inited = false;\n let prev: unknown;\n\n const stop = createAlienEffect(() => {\n const next = readTracked();\n\n if (!inited) {\n inited = true;\n prev = next;\n return;\n }\n\n if (Object.is(prev, next)) return;\n prev = next;\n fn();\n });\n\n return stop;\n };\n};\n","const kSignalBrand: unique symbol = Symbol.for(\"echojs-ecosystem.reactivity.signal\");\nconst kReadonlyBrand: unique symbol = Symbol.for(\"echojs-ecosystem.reactivity.readonlySignal\");\n\nexport type BrandedSignal = {\n [kSignalBrand]: true;\n [kReadonlyBrand]?: true;\n};\n\nexport const brandWritable = <T extends object>(obj: T): T & BrandedSignal => {\n Object.defineProperty(obj, kSignalBrand, { value: true });\n return obj as T & BrandedSignal;\n};\n\nexport const brandReadonly = <T extends object>(obj: T): T & BrandedSignal => {\n Object.defineProperty(obj, kSignalBrand, { value: true });\n Object.defineProperty(obj, kReadonlyBrand, { value: true });\n return obj as T & BrandedSignal;\n};\n\nexport const isBrandedSignal = (value: unknown): value is BrandedSignal => {\n return typeof value === \"object\" && value !== null && (value as any)[kSignalBrand] === true;\n};\n\nexport const isBrandedReadonlySignal = (value: unknown): value is BrandedSignal => {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as any)[kSignalBrand] === true &&\n (value as any)[kReadonlyBrand] === true\n );\n};\n","import { createSubscribe } from \"./subscribe\";\nimport { createAlienComputed, untrack } from \"./internals/alien.js\";\nimport { brandReadonly } from \"./internals/guards.js\";\nimport type { ReadonlySignal, ReadValue } from \"./types\";\nimport { isFunction } from \"./utils\";\n\nexport const computed = <T>(getter: () => T): ReadonlySignal<T> => {\n if (!isFunction(getter)) {\n throw new TypeError(\"computed(getter) expects a function\");\n }\n\n const engine = createAlienComputed<T>(() => getter());\n\n const readTracked = (): T => engine();\n const readUntracked = (): T => untrack(() => engine());\n\n const subscribe = createSubscribe(readTracked);\n\n return brandReadonly({\n value: () => readTracked() as ReadValue<T>,\n peek: () => readUntracked() as ReadValue<T>,\n subscribe,\n }) as ReadonlySignal<T>;\n};\n","import { isFunction } from \"./utils\";\nimport { createAlienEffect } from \"./internals/alien.js\";\n\nexport const effect = (fn: () => void): (() => void) => {\n if (!isFunction(fn)) {\n throw new TypeError(\"effect(fn) expects a function\");\n }\n return createAlienEffect(fn);\n};\n","import { brandReadonly } from \"./internals/guards.js\";\nimport type { ReadonlySignal, Signal } from \"./types\";\nimport { isFunction } from \"./utils\";\n\nexport const readonly = <T>(sig: Signal<T> | ReadonlySignal<T>): ReadonlySignal<T> => {\n if (!isFunction((sig as any).set)) return sig as ReadonlySignal<T>;\n if (!isFunction((sig as any).readonly)) return (sig as Signal<T>).readonly();\n\n return brandReadonly({\n value: () => sig.value(),\n peek: () => sig.peek(),\n subscribe: (fn: () => void) => sig.subscribe(fn),\n }) as ReadonlySignal<T>;\n};\n","import { createAlienScope } from \"./internals/alien.js\";\nimport { __withCleanupBucket, __wrapDisposerWithCleanup } from \"./cleanup\";\nimport { isFunction } from \"./utils\";\n\nexport const scope = (fn: () => void): (() => void) => {\n if (!isFunction(fn)) {\n throw new TypeError(\"scope(fn) expects a function\");\n }\n\n const bucket: Array<() => void> = [];\n const dispose = createAlienScope(() => __withCleanupBucket(bucket, fn));\n return __wrapDisposerWithCleanup(dispose, bucket);\n};\n","import { isObjectLike } from \"./utils\";\n\nconst DEV = ((): boolean => {\n const env = (globalThis as any)?.process?.env;\n const nodeEnv = env?.NODE_ENV;\n return nodeEnv !== \"production\";\n})();\n\nconst deepFreezeImpl = (value: unknown, seen: WeakSet<object>): void => {\n if (!isObjectLike(value)) return;\n if (Object.isFrozen(value)) return;\n if (seen.has(value)) return;\n seen.add(value);\n\n const keys = Object.keys(value);\n for (const k of keys) {\n deepFreezeImpl((value as any)[k], seen);\n }\n\n Object.freeze(value);\n};\n\nexport const freezeIfDev = <T>(value: T): T => {\n if (!DEV) return value;\n if (!isObjectLike(value)) return value;\n\n deepFreezeImpl(value, new WeakSet());\n return value;\n};\n\nexport const __isDevModeForTests = (): boolean => {\n return DEV;\n};\n","import { freezeIfDev } from \"./freeze\";\nimport { createSubscribe } from \"./subscribe\";\nimport { createAlienSignal, untrack } from \"./internals/alien.js\";\nimport { brandReadonly, brandWritable } from \"./internals/guards.js\";\nimport type { ReadonlySignal, ReadValue, Signal } from \"./types\";\nimport { isFunction } from \"./utils\";\n\nexport const createReadonlySignalFacade = <T>(impl: {\n value(): ReadValue<T>;\n peek(): ReadValue<T>;\n subscribe(fn: () => void): () => void;\n}): ReadonlySignal<T> => {\n return brandReadonly({\n value: impl.value,\n peek: impl.peek,\n subscribe: impl.subscribe,\n });\n};\n\nconst signalImpl = <T>(...args: [initial: T] | []): Signal<T> => {\n if (args.length === 0) {\n throw new TypeError(\"signal(initial) expects 1 argument\");\n }\n\n const initial = args[0] as T;\n const engine = createAlienSignal(freezeIfDev(initial));\n\n const readTracked = (): T => engine();\n const readUntracked = (): T => untrack(() => engine());\n\n const subscribe = createSubscribe(readTracked);\n\n const readonlyFacade = createReadonlySignalFacade<T>({\n value: () => readTracked() as ReadValue<T>,\n peek: () => readUntracked() as ReadValue<T>,\n subscribe,\n });\n\n const writable: Signal<T> = brandWritable({\n value: () => readTracked() as ReadValue<T>,\n peek: () => readUntracked() as ReadValue<T>,\n subscribe,\n set: (next: T) => {\n engine(freezeIfDev(next));\n },\n update: (fn: (prev: T) => T) => {\n const prev = readUntracked();\n const next = fn(prev);\n engine(freezeIfDev(next));\n },\n readonly: () => readonlyFacade,\n });\n\n return writable;\n};\n\nexport const signal = signalImpl as <T>(initial: T) => Signal<T>;\n","import { isBrandedReadonlySignal, isBrandedSignal } from \"./internals/guards.js\";\nimport type { ReadonlySignal, Signal } from \"./types\";\n\nexport const isSignal = (value: unknown): value is Signal<unknown> | ReadonlySignal<unknown> => {\n return isBrandedSignal(value);\n};\n\nexport const isReadonlySignal = (value: unknown): value is ReadonlySignal<unknown> => {\n return isBrandedReadonlySignal(value);\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@echojs-ecosystem/reactivity",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"check-types": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.typing.json --noEmit",
|
|
19
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.typing.json --noEmit",
|
|
20
|
+
"test:types": "tsc -p tsconfig.typing.json --noEmit",
|
|
21
|
+
"lint": "oxlint .",
|
|
22
|
+
"lint:fix": "oxlint . --fix",
|
|
23
|
+
"format": "oxfmt --check .",
|
|
24
|
+
"format:fix": "oxfmt .",
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"test:run": "vitest run",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"test:ui": "vitest ui"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"alien-signals": "^3.1.2"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@echojs-ecosystem/oxc-config": "workspace:*",
|
|
35
|
+
"oxlint": "^1.60.0",
|
|
36
|
+
"typescript": "5.9.2",
|
|
37
|
+
"vitest": "^4.1.4"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/echojs-ecosystem/echojs-core.git",
|
|
45
|
+
"directory": "packages/reactivity"
|
|
46
|
+
}
|
|
47
|
+
}
|