feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024-present Johnson Chu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+76
View File
@@ -0,0 +1,76 @@
# alien-signals
Project Status: **Preview**
The goal of `alien-signals` is to create a Signal library with the lowest overhead.
We have set the following scheduling logic constraints:
1. Based on Push-Pull
2. No dynamic objects
3. No use of Array/Set/Map
4. No recursion calls
5. Class properties must be fewer than 10 (https://v8.dev/blog/fast-properties)
Experimental results have shown that with these constraints, it is possible to achieve excellent performance for a Signal library without using sophisticated scheduling strategies. The overall performance of `alien-signals` is approximately 400% that of Vue 3.4's reactivity system.
For more detailed performance comparisons, please visit: https://github.com/transitive-bullshit/js-reactivity-benchmark
## Motivation
To achieve high-performance code generation in https://github.com/vuejs/language-tools, I needed to write some on-demand computed logic using Signals, but I couldn't find a low-cost Signal library that satisfied me.
In the past, I accumulated some knowledge of reactivity systems in https://github.com/vuejs/core/pull/5912, so I attempted to develop `alien-signals` with the goal of creating a Signal library with minimal memory usage and excellent performance.
Since Vue 3.5 switched to a Pull reactivity system in https://github.com/vuejs/core/pull/10397, I continued to research the Push-Pull reactivity system here. It is worth mentioning that I was inspired by the doubly-linked concept, but `alien-signals` does not use a similar implementation.
## Usage
### Basic
```ts
import { signal, computed, effect } from 'alien-signals';
const count = signal(1);
const doubleCount = computed(() => count.get() * 2);
effect(() => {
console.log(`Count is: ${count.get()}`);
}); // Console: Count is: 1
console.log(doubleCount.get()); // 2
count.set(2); // Console: Count is: 2
console.log(doubleCount.get()); // 4
```
### Effect Scope
```ts
import { signal, effectScope } from 'alien-signals';
const count = signal(1);
const scope = effectScope();
scope.run(() => {
effect(() => {
console.log(`Count in scope: ${count.get()}`);
}); // Console: Count in scope: 1
count.set(2); // Console: Count in scope: 2
});
scope.stop();
count.set(3); // No console output
```
## Roadmap
| Version | Savings |
|---------|-----------------------------------------------------------------------------------------------|
| 0.3 | Correctly schedule child effect scopes |
| 0.2 | Correctly schedule computed side effects |
| 0.1 | Correctly schedule inner effect callbacks |
| 0.0 | Add APIs: `signal()`, `computed()`, `effect()`, `effectScope()`, `startBatch()`, `endBatch()` |
+19
View File
@@ -0,0 +1,19 @@
import { DirtyLevels, IComputed, Link } from './system.js';
export interface ISignal<T = any> {
get(): T;
}
export declare function computed<T>(getter: (cachedValue?: T) => T): ISignal<T>;
export declare class Computed<T = any> implements IComputed {
getter: (cachedValue?: T) => T;
cachedValue: T | undefined;
subs: Link | undefined;
subsTail: Link | undefined;
deps: Link | undefined;
depsTail: Link | undefined;
trackId: number;
dirtyLevel: DirtyLevels;
canPropagate: boolean;
constructor(getter: (cachedValue?: T) => T);
get(): T;
update(): void;
}
+60
View File
@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Computed = void 0;
exports.computed = computed;
const system_js_1 = require("./system.js");
function computed(getter) {
return new Computed(getter);
}
class Computed {
constructor(getter) {
this.getter = getter;
this.cachedValue = undefined;
// Dependency
this.subs = undefined;
this.subsTail = undefined;
// Subscriber
this.deps = undefined;
this.depsTail = undefined;
this.trackId = 0;
this.dirtyLevel = 3 /* DirtyLevels.Dirty */;
this.canPropagate = false;
}
get() {
let dirtyLevel = this.dirtyLevel;
if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
system_js_1.Subscriber.resolveMaybeDirty(this);
dirtyLevel = this.dirtyLevel;
}
if (dirtyLevel >= 3 /* DirtyLevels.Dirty */) {
this.update();
}
const activeTrackId = system_js_1.System.activeTrackId;
if (activeTrackId !== 0) {
const subsTail = this.subsTail;
if (subsTail === undefined || subsTail.trackId !== activeTrackId) {
system_js_1.Dependency.link(this, system_js_1.System.activeSub);
}
}
return this.cachedValue;
}
update() {
const prevSub = system_js_1.Subscriber.startTrack(this);
const oldValue = this.cachedValue;
let newValue;
try {
newValue = this.getter(oldValue);
}
finally {
system_js_1.Subscriber.endTrack(this, prevSub);
}
if (oldValue !== newValue) {
this.cachedValue = newValue;
const subs = this.subs;
if (subs !== undefined) {
system_js_1.Dependency.propagate(subs);
}
}
}
}
exports.Computed = Computed;
+17
View File
@@ -0,0 +1,17 @@
import { DirtyLevels, IEffect, Link } from './system.js';
export declare function effect(fn: () => void): Effect<void>;
export declare class Effect<T = any> implements IEffect {
fn: () => T;
nextNotify: IEffect | undefined;
subs: Link | undefined;
subsTail: Link | undefined;
deps: Link | undefined;
depsTail: Link | undefined;
trackId: number;
dirtyLevel: DirtyLevels;
canPropagate: boolean;
constructor(fn: () => T);
notify(): void;
run(): T;
stop(): void;
}
+78
View File
@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Effect = void 0;
exports.effect = effect;
const effectScope_js_1 = require("./effectScope.js");
const system_js_1 = require("./system.js");
function effect(fn) {
const e = new Effect(fn);
e.run();
return e;
}
class Effect {
constructor(fn) {
this.fn = fn;
this.nextNotify = undefined;
// Dependency
this.subs = undefined;
this.subsTail = undefined;
// Subscriber
this.deps = undefined;
this.depsTail = undefined;
this.trackId = 0;
this.dirtyLevel = 3 /* DirtyLevels.Dirty */;
this.canPropagate = false;
const activeTrackId = system_js_1.System.activeTrackId;
if (activeTrackId !== 0) {
system_js_1.Dependency.link(this, system_js_1.System.activeSub);
return;
}
if (effectScope_js_1.activeEffectScope !== undefined) {
const subsTail = this.subsTail;
if (subsTail === undefined || subsTail.trackId !== effectScope_js_1.activeEffectScope.trackId) {
system_js_1.Dependency.link(this, effectScope_js_1.activeEffectScope);
}
}
}
notify() {
let dirtyLevel = this.dirtyLevel;
if (dirtyLevel > 0 /* DirtyLevels.None */) {
if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
system_js_1.Subscriber.resolveMaybeDirty(this);
dirtyLevel = this.dirtyLevel;
}
if (dirtyLevel === 3 /* DirtyLevels.Dirty */) {
this.run();
}
else {
this.dirtyLevel = 0 /* DirtyLevels.None */;
let link = this.deps;
while (link !== undefined) {
const dep = link.dep;
if ('notify' in dep) {
dep.notify();
}
link = link.nextDep;
}
}
}
}
run() {
const prevSub = system_js_1.Subscriber.startTrack(this);
try {
return this.fn();
}
finally {
system_js_1.Subscriber.endTrack(this, prevSub);
}
}
stop() {
if (this.deps !== undefined) {
system_js_1.Subscriber.clearTrack(this.deps);
this.deps = undefined;
this.depsTail = undefined;
}
this.dirtyLevel = 3 /* DirtyLevels.Dirty */;
}
}
exports.Effect = Effect;
+13
View File
@@ -0,0 +1,13 @@
import { DirtyLevels, Link, Subscriber } from './system.js';
export declare let activeEffectScope: EffectScope | undefined;
export declare function effectScope(): EffectScope;
export declare class EffectScope implements Subscriber {
deps: Link | undefined;
depsTail: Link | undefined;
trackId: number;
dirtyLevel: DirtyLevels;
canPropagate: boolean;
notify(): void;
run<T>(fn: () => T): T;
stop(): void;
}
+53
View File
@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EffectScope = exports.activeEffectScope = void 0;
exports.effectScope = effectScope;
const system_js_1 = require("./system.js");
exports.activeEffectScope = undefined;
function effectScope() {
return new EffectScope();
}
class EffectScope {
constructor() {
// Subscriber
this.deps = undefined;
this.depsTail = undefined;
this.trackId = -(++system_js_1.System.lastTrackId);
this.dirtyLevel = 0 /* DirtyLevels.None */;
this.canPropagate = false;
}
notify() {
if (this.dirtyLevel !== 0 /* DirtyLevels.None */) {
this.dirtyLevel = 0 /* DirtyLevels.None */;
let link = this.deps;
while (link !== undefined) {
const dep = link.dep;
if ('notify' in dep) {
dep.notify();
}
link = link.nextDep;
}
}
}
run(fn) {
const prevSub = exports.activeEffectScope;
exports.activeEffectScope = this;
this.trackId = Math.abs(this.trackId);
try {
return fn();
}
finally {
exports.activeEffectScope = prevSub;
this.trackId = -Math.abs(this.trackId);
}
}
stop() {
if (this.deps !== undefined) {
system_js_1.Subscriber.clearTrack(this.deps);
this.deps = undefined;
this.depsTail = undefined;
}
this.dirtyLevel = 0 /* DirtyLevels.None */;
}
}
exports.EffectScope = EffectScope;
+6
View File
@@ -0,0 +1,6 @@
export * from './computed.js';
export * from './effect.js';
export * from './effectScope.js';
export * from './signal.js';
export * from './system.js';
export * as Unstable from './unstable/index.js';
+23
View File
@@ -0,0 +1,23 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Unstable = void 0;
__exportStar(require("./computed.js"), exports);
__exportStar(require("./effect.js"), exports);
__exportStar(require("./effectScope.js"), exports);
__exportStar(require("./signal.js"), exports);
__exportStar(require("./system.js"), exports);
exports.Unstable = require("./unstable/index.js");
+15
View File
@@ -0,0 +1,15 @@
import { ISignal } from './computed.js';
import { Dependency, Link } from './system.js';
export interface IWritableSignal<T = any> extends ISignal<T> {
set(value: T): void;
}
export declare function signal<T>(): Signal<T | undefined>;
export declare function signal<T>(oldValue: T): Signal<T>;
export declare class Signal<T = any> implements Dependency {
currentValue: T;
subs: Link | undefined;
subsTail: Link | undefined;
constructor(currentValue: T);
get(): NonNullable<T>;
set(value: T): void;
}
+37
View File
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Signal = void 0;
exports.signal = signal;
const system_js_1 = require("./system.js");
function signal(oldValue) {
return new Signal(oldValue);
}
class Signal {
constructor(currentValue) {
this.currentValue = currentValue;
// Dependency
this.subs = undefined;
this.subsTail = undefined;
}
get() {
const activeTrackId = system_js_1.System.activeTrackId;
if (activeTrackId !== 0) {
const subsTail = this.subsTail;
if (subsTail === undefined || subsTail.trackId !== activeTrackId) {
system_js_1.Dependency.link(this, system_js_1.System.activeSub);
}
}
return this.currentValue;
}
set(value) {
if (this.currentValue !== (this.currentValue = value)) {
const subs = this.subs;
if (subs !== undefined) {
(0, system_js_1.startBatch)();
system_js_1.Dependency.propagate(subs);
(0, system_js_1.endBatch)();
}
}
}
}
exports.Signal = Signal;
+69
View File
@@ -0,0 +1,69 @@
export interface IEffect extends Dependency, Subscriber {
nextNotify: IEffect | undefined;
notify(): void;
}
export interface IComputed extends Dependency, Subscriber {
update(): void;
}
export interface Dependency {
subs: Link | undefined;
subsTail: Link | undefined;
}
export interface Subscriber {
trackId: number;
canPropagate: boolean;
dirtyLevel: DirtyLevels;
deps: Link | undefined;
depsTail: Link | undefined;
}
export interface Link {
dep: Dependency | IComputed | IEffect;
sub: Subscriber | IComputed | IEffect;
trackId: number;
prevSub: Link | undefined;
nextSub: Link | undefined;
nextDep: Link | undefined;
}
export declare const enum DirtyLevels {
None = 0,
SideEffectsOnly = 1,
MaybeDirty = 2,
Dirty = 3
}
export declare namespace System {
let activeSub: Subscriber | undefined;
let activeTrackId: number;
let batchDepth: number;
let lastTrackId: number;
let queuedEffects: IEffect | undefined;
let queuedEffectsTail: IEffect | undefined;
}
export declare function startBatch(): void;
export declare function endBatch(): void;
export declare namespace Link {
function get(dep: Dependency, sub: Subscriber, nextDep: Link | undefined): Link;
function release(link: Link): void;
}
export declare namespace Dependency {
/**
* @deprecated Use `startTrack` instead.
*/
function linkSubscriber(dep: Dependency, sub: Subscriber): void;
function link(dep: Dependency, sub: Subscriber): void;
function propagate(subs: Link): void;
}
export declare namespace Subscriber {
function resolveMaybeDirty(sub: IComputed | IEffect, depth?: number): void;
function resolveMaybeDirtyNonRecursive(sub: IComputed | IEffect): void;
/**
* @deprecated Use `startTrack` instead.
*/
function startTrackDependencies(sub: Subscriber): Subscriber | undefined;
/**
* @deprecated Use `endTrack` instead.
*/
function endTrackDependencies(sub: Subscriber, prevSub: Subscriber | undefined): void;
function startTrack(sub: Subscriber): Subscriber | undefined;
function endTrack(sub: Subscriber, prevSub: Subscriber | undefined): void;
function clearTrack(link: Link): void;
}
+402
View File
@@ -0,0 +1,402 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Subscriber = exports.Dependency = exports.Link = exports.System = void 0;
exports.startBatch = startBatch;
exports.endBatch = endBatch;
var System;
(function (System) {
System.activeSub = undefined;
System.activeTrackId = 0;
System.batchDepth = 0;
System.lastTrackId = 0;
System.queuedEffects = undefined;
System.queuedEffectsTail = undefined;
})(System || (exports.System = System = {}));
function startBatch() {
System.batchDepth++;
}
function endBatch() {
System.batchDepth--;
if (System.batchDepth === 0) {
while (System.queuedEffects !== undefined) {
const effect = System.queuedEffects;
const queuedNext = System.queuedEffects.nextNotify;
if (queuedNext !== undefined) {
System.queuedEffects.nextNotify = undefined;
System.queuedEffects = queuedNext;
}
else {
System.queuedEffects = undefined;
System.queuedEffectsTail = undefined;
}
effect.notify();
}
}
}
var Link;
(function (Link) {
let pool = undefined;
function get(dep, sub, nextDep) {
if (pool !== undefined) {
const newLink = pool;
pool = newLink.nextDep;
newLink.nextDep = nextDep;
newLink.dep = dep;
newLink.sub = sub;
newLink.trackId = sub.trackId;
return newLink;
}
else {
return {
dep,
sub,
trackId: sub.trackId,
nextDep: nextDep,
prevSub: undefined,
nextSub: undefined,
};
}
}
Link.get = get;
function release(link) {
const dep = link.dep;
const nextSub = link.nextSub;
const prevSub = link.prevSub;
if (nextSub !== undefined) {
nextSub.prevSub = prevSub;
}
if (prevSub !== undefined) {
prevSub.nextSub = nextSub;
}
if (nextSub === undefined) {
dep.subsTail = prevSub;
}
if (prevSub === undefined) {
dep.subs = nextSub;
}
// @ts-expect-error
link.dep = undefined;
// @ts-expect-error
link.sub = undefined;
link.prevSub = undefined;
link.nextSub = undefined;
link.nextDep = pool;
pool = link;
}
Link.release = release;
})(Link || (exports.Link = Link = {}));
var Dependency;
(function (Dependency) {
const system = System;
/**
* @deprecated Use `startTrack` instead.
*/
function linkSubscriber(dep, sub) {
return link(dep, sub);
}
Dependency.linkSubscriber = linkSubscriber;
function link(dep, sub) {
const depsTail = sub.depsTail;
const old = depsTail !== undefined
? depsTail.nextDep
: sub.deps;
if (old === undefined || old.dep !== dep) {
const newLink = Link.get(dep, sub, old);
if (depsTail === undefined) {
sub.deps = newLink;
}
else {
depsTail.nextDep = newLink;
}
if (dep.subs === undefined) {
dep.subs = newLink;
}
else {
const oldTail = dep.subsTail;
newLink.prevSub = oldTail;
oldTail.nextSub = newLink;
}
sub.depsTail = newLink;
dep.subsTail = newLink;
}
else {
old.trackId = sub.trackId;
sub.depsTail = old;
}
}
Dependency.link = link;
function propagate(subs) {
let link = subs;
let dep = subs.dep;
let dirtyLevel = 3 /* DirtyLevels.Dirty */;
let remainingQuantity = 0;
do {
if (link !== undefined) {
const sub = link.sub;
const subTrackId = sub.trackId;
if (subTrackId > 0) {
if (subTrackId === link.trackId) {
const subDirtyLevel = sub.dirtyLevel;
if (subDirtyLevel < dirtyLevel) {
sub.dirtyLevel = dirtyLevel;
if (subDirtyLevel === 0 /* DirtyLevels.None */) {
sub.canPropagate = true;
if ('subs' in sub && sub.subs !== undefined) {
sub.depsTail.nextDep = link;
dep = sub;
link = sub.subs;
if ('notify' in sub) {
dirtyLevel = 1 /* DirtyLevels.SideEffectsOnly */;
}
else {
dirtyLevel = 2 /* DirtyLevels.MaybeDirty */;
}
remainingQuantity++;
continue;
}
}
}
}
}
else if (subTrackId === -link.trackId) {
const subDirtyLevel = sub.dirtyLevel;
const notDirty = subDirtyLevel === 0 /* DirtyLevels.None */;
if (subDirtyLevel < dirtyLevel) {
sub.dirtyLevel = dirtyLevel;
}
if (notDirty || sub.canPropagate) {
if (!notDirty) {
sub.canPropagate = false;
}
if ('subs' in sub && sub.subs !== undefined) {
sub.depsTail.nextDep = link;
dep = sub;
link = sub.subs;
if ('notify' in sub) {
dirtyLevel = 1 /* DirtyLevels.SideEffectsOnly */;
}
else {
dirtyLevel = 2 /* DirtyLevels.MaybeDirty */;
}
remainingQuantity++;
continue;
}
else if ('notify' in sub) {
const queuedEffectsTail = system.queuedEffectsTail;
if (queuedEffectsTail !== undefined) {
queuedEffectsTail.nextNotify = sub;
}
else {
system.queuedEffects = sub;
}
system.queuedEffectsTail = sub;
}
}
}
link = link.nextSub;
continue;
}
if (remainingQuantity !== 0) {
const depsTail = dep.depsTail;
const prevLink = depsTail.nextDep;
const prevSub = prevLink.sub;
depsTail.nextDep = undefined;
dep = prevLink.dep;
link = prevLink.nextSub;
remainingQuantity--;
if (remainingQuantity === 0) {
dirtyLevel = 3 /* DirtyLevels.Dirty */;
}
else if ('notify' in dep) {
dirtyLevel = 1 /* DirtyLevels.SideEffectsOnly */;
}
else {
dirtyLevel = 2 /* DirtyLevels.MaybeDirty */;
}
if ('notify' in prevSub) {
const queuedEffectsTail = system.queuedEffectsTail;
if (queuedEffectsTail !== undefined) {
queuedEffectsTail.nextNotify = prevSub;
}
else {
system.queuedEffects = prevSub;
}
system.queuedEffectsTail = prevSub;
}
continue;
}
break;
} while (true);
}
Dependency.propagate = propagate;
})(Dependency || (exports.Dependency = Dependency = {}));
var Subscriber;
(function (Subscriber) {
const system = System;
function resolveMaybeDirty(sub, depth = 0) {
let link = sub.deps;
while (link !== undefined) {
const dep = link.dep;
if ('update' in dep) {
let dirtyLevel = dep.dirtyLevel;
if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
if (depth >= 4) {
resolveMaybeDirtyNonRecursive(dep);
}
else {
resolveMaybeDirty(dep, depth + 1);
}
dirtyLevel = dep.dirtyLevel;
}
if (dirtyLevel === 3 /* DirtyLevels.Dirty */) {
dep.update();
if (sub.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
break;
}
}
}
link = link.nextDep;
}
if (sub.dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
sub.dirtyLevel = 0 /* DirtyLevels.None */;
}
}
Subscriber.resolveMaybeDirty = resolveMaybeDirty;
function resolveMaybeDirtyNonRecursive(sub) {
let link = sub.deps;
let remaining = 0;
do {
if (link !== undefined) {
const dep = link.dep;
if ('update' in dep) {
const depDirtyLevel = dep.dirtyLevel;
if (depDirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
dep.subs.prevSub = link;
sub = dep;
link = dep.deps;
remaining++;
continue;
}
else if (depDirtyLevel === 3 /* DirtyLevels.Dirty */) {
dep.update();
if (sub.dirtyLevel === 3 /* DirtyLevels.Dirty */) {
if (remaining !== 0) {
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
sub.update();
subSubs.prevSub = undefined;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
break;
}
}
}
link = link.nextDep;
continue;
}
const dirtyLevel = sub.dirtyLevel;
if (dirtyLevel === 2 /* DirtyLevels.MaybeDirty */) {
sub.dirtyLevel = 0 /* DirtyLevels.None */;
if (remaining !== 0) {
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
subSubs.prevSub = undefined;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
}
else if (remaining !== 0) {
if (dirtyLevel === 3 /* DirtyLevels.Dirty */) {
sub.update();
}
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
subSubs.prevSub = undefined;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
break;
} while (true);
}
Subscriber.resolveMaybeDirtyNonRecursive = resolveMaybeDirtyNonRecursive;
/**
* @deprecated Use `startTrack` instead.
*/
function startTrackDependencies(sub) {
return startTrack(sub);
}
Subscriber.startTrackDependencies = startTrackDependencies;
/**
* @deprecated Use `endTrack` instead.
*/
function endTrackDependencies(sub, prevSub) {
return endTrack(sub, prevSub);
}
Subscriber.endTrackDependencies = endTrackDependencies;
function startTrack(sub) {
const newTrackId = system.lastTrackId + 1;
const prevSub = system.activeSub;
system.activeSub = sub;
system.activeTrackId = newTrackId;
system.lastTrackId = newTrackId;
sub.depsTail = undefined;
sub.trackId = newTrackId;
sub.dirtyLevel = 0 /* DirtyLevels.None */;
return prevSub;
}
Subscriber.startTrack = startTrack;
function endTrack(sub, prevSub) {
if (prevSub !== undefined) {
system.activeSub = prevSub;
system.activeTrackId = prevSub.trackId;
}
else {
system.activeSub = undefined;
system.activeTrackId = 0;
}
const depsTail = sub.depsTail;
if (depsTail !== undefined) {
if (depsTail.nextDep !== undefined) {
clearTrack(depsTail.nextDep);
depsTail.nextDep = undefined;
}
}
else if (sub.deps !== undefined) {
clearTrack(sub.deps);
sub.deps = undefined;
}
sub.trackId = -sub.trackId;
}
Subscriber.endTrack = endTrack;
function clearTrack(link) {
do {
const dep = link.dep;
const nextDep = link.nextDep;
Link.release(link);
if (dep.subs === undefined && 'deps' in dep) {
if ('notify' in dep) {
dep.dirtyLevel = 0 /* DirtyLevels.None */;
}
else {
dep.dirtyLevel = 3 /* DirtyLevels.Dirty */;
}
if (dep.deps !== undefined) {
link = dep.deps;
dep.depsTail.nextDep = nextDep;
dep.deps = undefined;
dep.depsTail = undefined;
continue;
}
}
link = nextDep;
} while (link !== undefined);
}
Subscriber.clearTrack = clearTrack;
})(Subscriber || (exports.Subscriber = Subscriber = {}));
@@ -0,0 +1,2 @@
import { ISignal } from '../index.js';
export declare function computedArray<I, O>(arr: ISignal<I[]>, getGetter: (item: ISignal<I>, index: number) => () => O): readonly Readonly<O>[];
+43
View File
@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.computedArray = computedArray;
const index_js_1 = require("../index.js");
function computedArray(arr, getGetter) {
const length = (0, index_js_1.computed)(() => arr.get().length);
const keys = (0, index_js_1.computed)(() => {
const keys = [];
for (let i = 0; i < length.get(); i++) {
keys.push(String(i));
}
return keys;
});
const items = (0, index_js_1.computed)((array) => {
array ??= [];
while (array.length < length.get()) {
const index = array.length;
const item = (0, index_js_1.computed)(() => arr.get()[index]);
array.push((0, index_js_1.computed)(getGetter(item, index)));
}
if (array.length > length.get()) {
array.length = length.get();
}
return array;
});
return new Proxy({}, {
get(_, p, receiver) {
if (p === 'length') {
return length.get();
}
if (typeof p === 'string' && !isNaN(Number(p))) {
return items.get()[Number(p)]?.get();
}
return Reflect.get(items.get(), p, receiver);
},
has(_, p) {
return Reflect.has(items.get(), p);
},
ownKeys() {
return keys.get();
},
});
}
@@ -0,0 +1,2 @@
import { ISignal } from '../index.js';
export declare function computedSet<T>(source: ISignal<Set<T>>): ISignal<Set<T>>;
+13
View File
@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.computedSet = computedSet;
const index_js_1 = require("../index.js");
function computedSet(source) {
return (0, index_js_1.computed)((oldValue) => {
const newValue = source.get();
if (oldValue?.size === newValue.size && [...oldValue].every(c => newValue.has(c))) {
return oldValue;
}
return newValue;
});
}
@@ -0,0 +1,6 @@
import { Computed, ISignal } from '../index.js';
export declare function equalityComputed<T>(getter: () => T): ISignal<T>;
export declare class EqualityComputed<T = any> extends Computed<T> {
constructor(getter: () => T);
equals(a: any, b: any): boolean;
}
@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EqualityComputed = void 0;
exports.equalityComputed = equalityComputed;
const index_js_1 = require("../index.js");
function equalityComputed(getter) {
return new EqualityComputed(getter);
}
class EqualityComputed extends index_js_1.Computed {
constructor(getter) {
super(oldValue => {
const newValue = getter();
if (this.equals(oldValue, newValue)) {
return oldValue;
}
return newValue;
});
}
equals(a, b) {
if (a === b) {
return true;
}
if (a === null || b === null || typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!this.equals(a[i], b[i])) {
return false;
}
}
return true;
}
if (!Array.isArray(a) && !Array.isArray(b)) {
for (const key in a) {
if (a.hasOwnProperty(key)) {
if (!b.hasOwnProperty(key) || !this.equals(a[key], b[key])) {
return false;
}
}
}
for (const key in b) {
if (b.hasOwnProperty(key) && !a.hasOwnProperty(key)) {
return false;
}
}
return true;
}
return false; // One is array and the other is not
}
return false;
}
}
exports.EqualityComputed = EqualityComputed;
+3
View File
@@ -0,0 +1,3 @@
export * from './computedArray.js';
export * from './computedSet.js';
export * from './equalityComputed.js';
+19
View File
@@ -0,0 +1,19 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./computedArray.js"), exports);
__exportStar(require("./computedSet.js"), exports);
__exportStar(require("./equalityComputed.js"), exports);
+726
View File
@@ -0,0 +1,726 @@
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/system.ts
var DirtyLevels = /* @__PURE__ */ ((DirtyLevels2) => {
DirtyLevels2[DirtyLevels2["None"] = 0] = "None";
DirtyLevels2[DirtyLevels2["SideEffectsOnly"] = 1] = "SideEffectsOnly";
DirtyLevels2[DirtyLevels2["MaybeDirty"] = 2] = "MaybeDirty";
DirtyLevels2[DirtyLevels2["Dirty"] = 3] = "Dirty";
return DirtyLevels2;
})(DirtyLevels || {});
var System;
((System2) => {
System2.activeSub = void 0;
System2.activeTrackId = 0;
System2.batchDepth = 0;
System2.lastTrackId = 0;
System2.queuedEffects = void 0;
System2.queuedEffectsTail = void 0;
})(System || (System = {}));
function startBatch() {
System.batchDepth++;
}
function endBatch() {
System.batchDepth--;
if (System.batchDepth === 0) {
while (System.queuedEffects !== void 0) {
const effect2 = System.queuedEffects;
const queuedNext = System.queuedEffects.nextNotify;
if (queuedNext !== void 0) {
System.queuedEffects.nextNotify = void 0;
System.queuedEffects = queuedNext;
} else {
System.queuedEffects = void 0;
System.queuedEffectsTail = void 0;
}
effect2.notify();
}
}
}
var Link;
((Link6) => {
let pool = void 0;
function get(dep, sub, nextDep) {
if (pool !== void 0) {
const newLink = pool;
pool = newLink.nextDep;
newLink.nextDep = nextDep;
newLink.dep = dep;
newLink.sub = sub;
newLink.trackId = sub.trackId;
return newLink;
} else {
return {
dep,
sub,
trackId: sub.trackId,
nextDep,
prevSub: void 0,
nextSub: void 0
};
}
}
Link6.get = get;
function release(link) {
const dep = link.dep;
const nextSub = link.nextSub;
const prevSub = link.prevSub;
if (nextSub !== void 0) {
nextSub.prevSub = prevSub;
}
if (prevSub !== void 0) {
prevSub.nextSub = nextSub;
}
if (nextSub === void 0) {
dep.subsTail = prevSub;
}
if (prevSub === void 0) {
dep.subs = nextSub;
}
link.dep = void 0;
link.sub = void 0;
link.prevSub = void 0;
link.nextSub = void 0;
link.nextDep = pool;
pool = link;
}
Link6.release = release;
})(Link || (Link = {}));
var Dependency;
((Dependency2) => {
const system = System;
function linkSubscriber(dep, sub) {
return link(dep, sub);
}
Dependency2.linkSubscriber = linkSubscriber;
function link(dep, sub) {
const depsTail = sub.depsTail;
const old = depsTail !== void 0 ? depsTail.nextDep : sub.deps;
if (old === void 0 || old.dep !== dep) {
const newLink = Link.get(dep, sub, old);
if (depsTail === void 0) {
sub.deps = newLink;
} else {
depsTail.nextDep = newLink;
}
if (dep.subs === void 0) {
dep.subs = newLink;
} else {
const oldTail = dep.subsTail;
newLink.prevSub = oldTail;
oldTail.nextSub = newLink;
}
sub.depsTail = newLink;
dep.subsTail = newLink;
} else {
old.trackId = sub.trackId;
sub.depsTail = old;
}
}
Dependency2.link = link;
function propagate(subs) {
let link2 = subs;
let dep = subs.dep;
let dirtyLevel = 3 /* Dirty */;
let remainingQuantity = 0;
do {
if (link2 !== void 0) {
const sub = link2.sub;
const subTrackId = sub.trackId;
if (subTrackId > 0) {
if (subTrackId === link2.trackId) {
const subDirtyLevel = sub.dirtyLevel;
if (subDirtyLevel < dirtyLevel) {
sub.dirtyLevel = dirtyLevel;
if (subDirtyLevel === 0 /* None */) {
sub.canPropagate = true;
if ("subs" in sub && sub.subs !== void 0) {
sub.depsTail.nextDep = link2;
dep = sub;
link2 = sub.subs;
if ("notify" in sub) {
dirtyLevel = 1 /* SideEffectsOnly */;
} else {
dirtyLevel = 2 /* MaybeDirty */;
}
remainingQuantity++;
continue;
}
}
}
}
} else if (subTrackId === -link2.trackId) {
const subDirtyLevel = sub.dirtyLevel;
const notDirty = subDirtyLevel === 0 /* None */;
if (subDirtyLevel < dirtyLevel) {
sub.dirtyLevel = dirtyLevel;
}
if (notDirty || sub.canPropagate) {
if (!notDirty) {
sub.canPropagate = false;
}
if ("subs" in sub && sub.subs !== void 0) {
sub.depsTail.nextDep = link2;
dep = sub;
link2 = sub.subs;
if ("notify" in sub) {
dirtyLevel = 1 /* SideEffectsOnly */;
} else {
dirtyLevel = 2 /* MaybeDirty */;
}
remainingQuantity++;
continue;
} else if ("notify" in sub) {
const queuedEffectsTail = system.queuedEffectsTail;
if (queuedEffectsTail !== void 0) {
queuedEffectsTail.nextNotify = sub;
} else {
system.queuedEffects = sub;
}
system.queuedEffectsTail = sub;
}
}
}
link2 = link2.nextSub;
continue;
}
if (remainingQuantity !== 0) {
const depsTail = dep.depsTail;
const prevLink = depsTail.nextDep;
const prevSub = prevLink.sub;
depsTail.nextDep = void 0;
dep = prevLink.dep;
link2 = prevLink.nextSub;
remainingQuantity--;
if (remainingQuantity === 0) {
dirtyLevel = 3 /* Dirty */;
} else if ("notify" in dep) {
dirtyLevel = 1 /* SideEffectsOnly */;
} else {
dirtyLevel = 2 /* MaybeDirty */;
}
if ("notify" in prevSub) {
const queuedEffectsTail = system.queuedEffectsTail;
if (queuedEffectsTail !== void 0) {
queuedEffectsTail.nextNotify = prevSub;
} else {
system.queuedEffects = prevSub;
}
system.queuedEffectsTail = prevSub;
}
continue;
}
break;
} while (true);
}
Dependency2.propagate = propagate;
})(Dependency || (Dependency = {}));
var Subscriber;
((Subscriber2) => {
const system = System;
function resolveMaybeDirty(sub, depth = 0) {
let link = sub.deps;
while (link !== void 0) {
const dep = link.dep;
if ("update" in dep) {
let dirtyLevel = dep.dirtyLevel;
if (dirtyLevel === 2 /* MaybeDirty */) {
if (depth >= 4) {
resolveMaybeDirtyNonRecursive(dep);
} else {
resolveMaybeDirty(dep, depth + 1);
}
dirtyLevel = dep.dirtyLevel;
}
if (dirtyLevel === 3 /* Dirty */) {
dep.update();
if (sub.dirtyLevel === 3 /* Dirty */) {
break;
}
}
}
link = link.nextDep;
}
if (sub.dirtyLevel === 2 /* MaybeDirty */) {
sub.dirtyLevel = 0 /* None */;
}
}
Subscriber2.resolveMaybeDirty = resolveMaybeDirty;
function resolveMaybeDirtyNonRecursive(sub) {
let link = sub.deps;
let remaining = 0;
do {
if (link !== void 0) {
const dep = link.dep;
if ("update" in dep) {
const depDirtyLevel = dep.dirtyLevel;
if (depDirtyLevel === 2 /* MaybeDirty */) {
dep.subs.prevSub = link;
sub = dep;
link = dep.deps;
remaining++;
continue;
} else if (depDirtyLevel === 3 /* Dirty */) {
dep.update();
if (sub.dirtyLevel === 3 /* Dirty */) {
if (remaining !== 0) {
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
sub.update();
subSubs.prevSub = void 0;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
break;
}
}
}
link = link.nextDep;
continue;
}
const dirtyLevel = sub.dirtyLevel;
if (dirtyLevel === 2 /* MaybeDirty */) {
sub.dirtyLevel = 0 /* None */;
if (remaining !== 0) {
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
subSubs.prevSub = void 0;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
} else if (remaining !== 0) {
if (dirtyLevel === 3 /* Dirty */) {
sub.update();
}
const subSubs = sub.subs;
const prevLink = subSubs.prevSub;
subSubs.prevSub = void 0;
sub = prevLink.sub;
link = prevLink.nextDep;
remaining--;
continue;
}
break;
} while (true);
}
Subscriber2.resolveMaybeDirtyNonRecursive = resolveMaybeDirtyNonRecursive;
function startTrackDependencies(sub) {
return startTrack(sub);
}
Subscriber2.startTrackDependencies = startTrackDependencies;
function endTrackDependencies(sub, prevSub) {
return endTrack(sub, prevSub);
}
Subscriber2.endTrackDependencies = endTrackDependencies;
function startTrack(sub) {
const newTrackId = system.lastTrackId + 1;
const prevSub = system.activeSub;
system.activeSub = sub;
system.activeTrackId = newTrackId;
system.lastTrackId = newTrackId;
sub.depsTail = void 0;
sub.trackId = newTrackId;
sub.dirtyLevel = 0 /* None */;
return prevSub;
}
Subscriber2.startTrack = startTrack;
function endTrack(sub, prevSub) {
if (prevSub !== void 0) {
system.activeSub = prevSub;
system.activeTrackId = prevSub.trackId;
} else {
system.activeSub = void 0;
system.activeTrackId = 0;
}
const depsTail = sub.depsTail;
if (depsTail !== void 0) {
if (depsTail.nextDep !== void 0) {
clearTrack(depsTail.nextDep);
depsTail.nextDep = void 0;
}
} else if (sub.deps !== void 0) {
clearTrack(sub.deps);
sub.deps = void 0;
}
sub.trackId = -sub.trackId;
}
Subscriber2.endTrack = endTrack;
function clearTrack(link) {
do {
const dep = link.dep;
const nextDep = link.nextDep;
Link.release(link);
if (dep.subs === void 0 && "deps" in dep) {
if ("notify" in dep) {
dep.dirtyLevel = 0 /* None */;
} else {
dep.dirtyLevel = 3 /* Dirty */;
}
if (dep.deps !== void 0) {
link = dep.deps;
dep.depsTail.nextDep = nextDep;
dep.deps = void 0;
dep.depsTail = void 0;
continue;
}
}
link = nextDep;
} while (link !== void 0);
}
Subscriber2.clearTrack = clearTrack;
})(Subscriber || (Subscriber = {}));
// src/computed.ts
function computed(getter) {
return new Computed(getter);
}
var Computed = class {
constructor(getter) {
this.getter = getter;
this.cachedValue = void 0;
// Dependency
this.subs = void 0;
this.subsTail = void 0;
// Subscriber
this.deps = void 0;
this.depsTail = void 0;
this.trackId = 0;
this.dirtyLevel = 3 /* Dirty */;
this.canPropagate = false;
}
get() {
let dirtyLevel = this.dirtyLevel;
if (dirtyLevel === 2 /* MaybeDirty */) {
Subscriber.resolveMaybeDirty(this);
dirtyLevel = this.dirtyLevel;
}
if (dirtyLevel >= 3 /* Dirty */) {
this.update();
}
const activeTrackId = System.activeTrackId;
if (activeTrackId !== 0) {
const subsTail = this.subsTail;
if (subsTail === void 0 || subsTail.trackId !== activeTrackId) {
Dependency.link(this, System.activeSub);
}
}
return this.cachedValue;
}
update() {
const prevSub = Subscriber.startTrack(this);
const oldValue = this.cachedValue;
let newValue;
try {
newValue = this.getter(oldValue);
} finally {
Subscriber.endTrack(this, prevSub);
}
if (oldValue !== newValue) {
this.cachedValue = newValue;
const subs = this.subs;
if (subs !== void 0) {
Dependency.propagate(subs);
}
}
}
};
// src/effectScope.ts
var activeEffectScope = void 0;
function effectScope() {
return new EffectScope();
}
var EffectScope = class {
constructor() {
// Subscriber
this.deps = void 0;
this.depsTail = void 0;
this.trackId = -++System.lastTrackId;
this.dirtyLevel = 0 /* None */;
this.canPropagate = false;
}
notify() {
if (this.dirtyLevel !== 0 /* None */) {
this.dirtyLevel = 0 /* None */;
let link = this.deps;
while (link !== void 0) {
const dep = link.dep;
if ("notify" in dep) {
dep.notify();
}
link = link.nextDep;
}
}
}
run(fn) {
const prevSub = activeEffectScope;
activeEffectScope = this;
this.trackId = Math.abs(this.trackId);
try {
return fn();
} finally {
activeEffectScope = prevSub;
this.trackId = -Math.abs(this.trackId);
}
}
stop() {
if (this.deps !== void 0) {
Subscriber.clearTrack(this.deps);
this.deps = void 0;
this.depsTail = void 0;
}
this.dirtyLevel = 0 /* None */;
}
};
// src/effect.ts
function effect(fn) {
const e = new Effect(fn);
e.run();
return e;
}
var Effect = class {
constructor(fn) {
this.fn = fn;
this.nextNotify = void 0;
// Dependency
this.subs = void 0;
this.subsTail = void 0;
// Subscriber
this.deps = void 0;
this.depsTail = void 0;
this.trackId = 0;
this.dirtyLevel = 3 /* Dirty */;
this.canPropagate = false;
const activeTrackId = System.activeTrackId;
if (activeTrackId !== 0) {
Dependency.link(this, System.activeSub);
return;
}
if (activeEffectScope !== void 0) {
const subsTail = this.subsTail;
if (subsTail === void 0 || subsTail.trackId !== activeEffectScope.trackId) {
Dependency.link(this, activeEffectScope);
}
}
}
notify() {
let dirtyLevel = this.dirtyLevel;
if (dirtyLevel > 0 /* None */) {
if (dirtyLevel === 2 /* MaybeDirty */) {
Subscriber.resolveMaybeDirty(this);
dirtyLevel = this.dirtyLevel;
}
if (dirtyLevel === 3 /* Dirty */) {
this.run();
} else {
this.dirtyLevel = 0 /* None */;
let link = this.deps;
while (link !== void 0) {
const dep = link.dep;
if ("notify" in dep) {
dep.notify();
}
link = link.nextDep;
}
}
}
}
run() {
const prevSub = Subscriber.startTrack(this);
try {
return this.fn();
} finally {
Subscriber.endTrack(this, prevSub);
}
}
stop() {
if (this.deps !== void 0) {
Subscriber.clearTrack(this.deps);
this.deps = void 0;
this.depsTail = void 0;
}
this.dirtyLevel = 3 /* Dirty */;
}
};
// src/signal.ts
function signal(oldValue) {
return new Signal(oldValue);
}
var Signal = class {
constructor(currentValue) {
this.currentValue = currentValue;
// Dependency
this.subs = void 0;
this.subsTail = void 0;
}
get() {
const activeTrackId = System.activeTrackId;
if (activeTrackId !== 0) {
const subsTail = this.subsTail;
if (subsTail === void 0 || subsTail.trackId !== activeTrackId) {
Dependency.link(this, System.activeSub);
}
}
return this.currentValue;
}
set(value) {
if (this.currentValue !== (this.currentValue = value)) {
const subs = this.subs;
if (subs !== void 0) {
startBatch();
Dependency.propagate(subs);
endBatch();
}
}
}
};
// src/unstable/index.ts
var unstable_exports = {};
__export(unstable_exports, {
EqualityComputed: () => EqualityComputed,
computedArray: () => computedArray,
computedSet: () => computedSet,
equalityComputed: () => equalityComputed
});
// src/unstable/computedArray.ts
function computedArray(arr, getGetter) {
const length = computed(() => arr.get().length);
const keys = computed(
() => {
const keys2 = [];
for (let i = 0; i < length.get(); i++) {
keys2.push(String(i));
}
return keys2;
}
);
const items = computed(
(array) => {
array ??= [];
while (array.length < length.get()) {
const index = array.length;
const item = computed(() => arr.get()[index]);
array.push(computed(getGetter(item, index)));
}
if (array.length > length.get()) {
array.length = length.get();
}
return array;
}
);
return new Proxy({}, {
get(_, p, receiver) {
if (p === "length") {
return length.get();
}
if (typeof p === "string" && !isNaN(Number(p))) {
return items.get()[Number(p)]?.get();
}
return Reflect.get(items.get(), p, receiver);
},
has(_, p) {
return Reflect.has(items.get(), p);
},
ownKeys() {
return keys.get();
}
});
}
// src/unstable/computedSet.ts
function computedSet(source) {
return computed(
(oldValue) => {
const newValue = source.get();
if (oldValue?.size === newValue.size && [...oldValue].every((c) => newValue.has(c))) {
return oldValue;
}
return newValue;
}
);
}
// src/unstable/equalityComputed.ts
function equalityComputed(getter) {
return new EqualityComputed(getter);
}
var EqualityComputed = class extends Computed {
constructor(getter) {
super((oldValue) => {
const newValue = getter();
if (this.equals(oldValue, newValue)) {
return oldValue;
}
return newValue;
});
}
equals(a, b) {
if (a === b) {
return true;
}
if (a === null || b === null || typeof a !== typeof b) {
return false;
}
if (typeof a === "object") {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!this.equals(a[i], b[i])) {
return false;
}
}
return true;
}
if (!Array.isArray(a) && !Array.isArray(b)) {
for (const key in a) {
if (a.hasOwnProperty(key)) {
if (!b.hasOwnProperty(key) || !this.equals(a[key], b[key])) {
return false;
}
}
}
for (const key in b) {
if (b.hasOwnProperty(key) && !a.hasOwnProperty(key)) {
return false;
}
}
return true;
}
return false;
}
return false;
}
};
export {
Computed,
Dependency,
DirtyLevels,
Effect,
EffectScope,
Link,
Signal,
Subscriber,
System,
unstable_exports as Unstable,
activeEffectScope,
computed,
effect,
effectScope,
endBatch,
signal,
startBatch
};
+45
View File
@@ -0,0 +1,45 @@
{
"name": "alien-signals",
"version": "0.2.2",
"license": "MIT",
"packageManager": "pnpm@9.12.0",
"type": "commonjs",
"types": "./cjs/index.d.ts",
"exports": {
".": {
"types": "./cjs/index.d.ts",
"import": "./esm/index.mjs",
"require": "./cjs/index.js"
},
"./cjs": {
"types": "./cjs/index.d.ts",
"import": "./cjs/index.js",
"require": "./cjs/index.js"
},
"./esm": {
"types": "./cjs/index.d.ts",
"import": "./esm/index.mjs",
"require": "./esm/index.mjs"
}
},
"files": [
"**/*.js",
"**/*.mjs",
"**/*.d.ts"
],
"repository": {
"type": "git",
"url": "git+https://github.com/johnsoncodehk/signals.git"
},
"scripts": {
"prepublishOnly": "npm run build",
"build": "tsc && esbuild src/index.ts --bundle --format=esm --outfile=esm/index.mjs",
"test": "vitest run"
},
"devDependencies": {
"esbuild": "latest",
"vite": "latest",
"vitest": "latest",
"typescript": "latest"
}
}