alien-signals
Advanced tools
Comparing version
{ | ||
"name": "alien-signals", | ||
"version": "2.0.0-alpha.0", | ||
"version": "2.0.0-alpha.1", | ||
"sideEffects": false, | ||
@@ -27,5 +27,5 @@ "license": "MIT", | ||
"files": [ | ||
"**/*.cjs", | ||
"**/*.mjs", | ||
"**/*.d.ts" | ||
"cjs/index.cjs", | ||
"esm/index.mjs", | ||
"types/*.d.ts" | ||
], | ||
@@ -39,5 +39,6 @@ "repository": { | ||
"build": "tsc && npm run build:esm && npm run build:cjs", | ||
"build:esm": "esbuild src/index.ts --bundle --format=esm --outfile=esm/index.mjs", | ||
"build:cjs": "esbuild src/index.ts --bundle --format=cjs --outfile=cjs/index.cjs", | ||
"build:esm": "esbuild src/index.ts --bundle --minify-whitespace --format=esm --outfile=esm/index.mjs", | ||
"build:cjs": "esbuild src/index.ts --bundle --minify-whitespace --format=cjs --outfile=cjs/index.cjs", | ||
"test": "vitest run", | ||
"lint": "tsslint --project tsconfig.json", | ||
"bench": "npm run build:esm && node --jitless --expose-gc benchs/propagate.mjs", | ||
@@ -47,2 +48,4 @@ "bench:memory": "npm run build:esm && node --expose-gc benchs/memoryUsage.mjs" | ||
"devDependencies": { | ||
"@tsslint/cli": "latest", | ||
"@tsslint/config": "latest", | ||
"esbuild": "latest", | ||
@@ -49,0 +52,0 @@ "mitata": "latest", |
@@ -33,6 +33,9 @@ <p align="center"> | ||
- [YanqingXu/alien-signals-in-lua](https://github.com/YanqingXu/alien-signals-in-lua): Lua implementation of alien-signals | ||
- [medz/alien-signals-dart](https://github.com/medz/alien-signals-dart): alien-signals Dart implementation of alien-signals | ||
- [medz/alien-signals-dart](https://github.com/medz/alien-signals-dart): Dart implementation of alien-signals | ||
- [delaneyj/alien-signals-go](https://github.com/delaneyj/alien-signals-go): Go implementation of alien-signals | ||
- [Rajaniraiyn/react-alien-signals](https://github.com/Rajaniraiyn/react-alien-signals): React bindings for the alien-signals API | ||
- [CCherry07/alien-deepsignals](https://github.com/CCherry07/alien-deepsignals): Use alien-signals with the interface of a plain JavaScript object | ||
- [hunghg255/reactjs-signal](https://github.com/hunghg255/reactjs-signal): Share Store State with Signal Pattern | ||
- [gn8-ai/universe-alien-signals](https://github.com/gn8-ai/universe-alien-signals): Enables simple use of the Alien Signals state management system in modern frontend frameworks | ||
- [Nicell/alien-signals-luau](https://github.com/Nicell/alien-signals-luau): Luau implementation of alien-signals | ||
@@ -68,3 +71,3 @@ ## Adoption | ||
```ts | ||
import { signal, effectScope } from 'alien-signals'; | ||
import { signal, effect, effectScope } from 'alien-signals'; | ||
@@ -77,6 +80,6 @@ const count = signal(1); | ||
}); // Console: Count in scope: 1 | ||
count(2); // Console: Count in scope: 2 | ||
}); | ||
count(2); // Console: Count in scope: 2 | ||
stopScope(); | ||
@@ -87,3 +90,3 @@ | ||
#### Creating Your Own Public API | ||
#### Creating Your Own Surface API | ||
@@ -111,21 +114,16 @@ You can reuse alien-signals’ core algorithm via `createReactiveSystem()` to build your own signal API. For implementation examples, see: | ||
if ( | ||
( | ||
!(subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed | SubscriberFlags.Propagated)) | ||
&& (sub.flags = subFlags | targetFlag | SubscriberFlags.Notified, true) | ||
) | ||
|| ( | ||
(subFlags & SubscriberFlags.Recursed) | ||
&& !(subFlags & SubscriberFlags.Tracking) | ||
&& (sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag | SubscriberFlags.Notified, true) | ||
) | ||
|| ( | ||
!(subFlags & SubscriberFlags.Propagated) | ||
&& isValidLink(link, sub) | ||
&& ( | ||
sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag | SubscriberFlags.Notified, | ||
(sub as Dependency).subs !== undefined | ||
) | ||
) | ||
) { | ||
let shouldNotify = false; | ||
if (!(subFlags & (SubscriberFlags.Tracking | SubscriberFlags.Recursed | SubscriberFlags.Propagated))) { | ||
sub.flags = subFlags | targetFlag | SubscriberFlags.Notified; | ||
shouldNotify = true; | ||
} else if ((subFlags & SubscriberFlags.Recursed) && !(subFlags & SubscriberFlags.Tracking)) { | ||
sub.flags = (subFlags & ~SubscriberFlags.Recursed) | targetFlag | SubscriberFlags.Notified; | ||
shouldNotify = true; | ||
} else if (!(subFlags & SubscriberFlags.Propagated) && isValidLink(current, sub)) { | ||
sub.flags = subFlags | SubscriberFlags.Recursed | targetFlag | SubscriberFlags.Notified; | ||
shouldNotify = (sub as Dependency).subs !== undefined; | ||
} | ||
if (shouldNotify) { | ||
const subSubs = (sub as Dependency).subs; | ||
@@ -139,9 +137,9 @@ if (subSubs !== undefined) { | ||
); | ||
} else if (subFlags & SubscriberFlags.Effect) { | ||
} | ||
if (subFlags & SubscriberFlags.Effect) { | ||
if (queuedEffectsTail !== undefined) { | ||
queuedEffectsTail.depsTail!.nextDep = sub.deps; | ||
queuedEffectsTail = queuedEffectsTail.linked = { target: sub, linked: undefined }; | ||
} else { | ||
queuedEffects = sub; | ||
queuedEffectsTail = queuedEffects = { target: sub, linked: undefined }; | ||
} | ||
queuedEffectsTail = sub; | ||
} | ||
@@ -152,7 +150,6 @@ } else if (!(subFlags & (SubscriberFlags.Tracking | targetFlag))) { | ||
if (queuedEffectsTail !== undefined) { | ||
queuedEffectsTail.depsTail!.nextDep = sub.deps; | ||
queuedEffectsTail = queuedEffectsTail.linked = { target: sub, linked: undefined }; | ||
} else { | ||
queuedEffects = sub; | ||
queuedEffectsTail = queuedEffects = { target: sub, linked: undefined }; | ||
} | ||
queuedEffectsTail = sub; | ||
} | ||
@@ -181,3 +178,3 @@ } else if ( | ||
if ((depFlags & (SubscriberFlags.Computed | SubscriberFlags.Dirty)) === (SubscriberFlags.Computed | SubscriberFlags.Dirty)) { | ||
if (updateComputed(dep)) { | ||
if (update(dep)) { | ||
const subs = dep.subs!; | ||
@@ -191,3 +188,3 @@ if (subs.nextSub !== undefined) { | ||
if (checkDirty(dep.deps!)) { | ||
if (updateComputed(dep)) { | ||
if (update(dep)) { | ||
const subs = dep.subs!; | ||
@@ -194,0 +191,0 @@ if (subs.nextSub !== undefined) { |
export * from './system.js'; | ||
interface WriteableSignal<T> { | ||
(): T; | ||
(value: T): void; | ||
} | ||
export declare let batchDepth: number; | ||
export declare function startBatch(): void; | ||
@@ -10,6 +7,12 @@ export declare function endBatch(): void; | ||
export declare function resumeTracking(): void; | ||
export declare function signal<T>(): WriteableSignal<T | undefined>; | ||
export declare function signal<T>(oldValue: T): WriteableSignal<T>; | ||
export declare function computed<T>(getter: (cachedValue?: T) => T): () => T; | ||
export declare function signal<T>(): { | ||
(): T | undefined; | ||
(value: T | undefined): void; | ||
}; | ||
export declare function signal<T>(initialValue: T): { | ||
(): T; | ||
(value: T): void; | ||
}; | ||
export declare function computed<T>(getter: (previousValue?: T) => T): () => T; | ||
export declare function effect<T>(fn: () => T): () => void; | ||
export declare function effectScope<T>(fn: () => T): () => void; |
export interface Dependency { | ||
version?: number; | ||
subs: Link | undefined; | ||
@@ -12,3 +11,2 @@ subsTail: Link | undefined; | ||
export interface Link { | ||
version: number | undefined; | ||
dep: Dependency | (Dependency & Subscriber); | ||
@@ -18,114 +16,35 @@ sub: Subscriber | (Dependency & Subscriber); | ||
nextSub: Link | undefined; | ||
prevDep: Link | undefined; | ||
nextDep: Link | undefined; | ||
} | ||
export declare const enum SubscriberFlags { | ||
None = 0, | ||
Computed = 1, | ||
Effect = 2, | ||
Tracking = 4, | ||
Recursed = 8, | ||
Mutable = 1, | ||
Watching = 2, | ||
BlockPropagation = 4, | ||
IsPropagationBlocked = 8, | ||
Dirty = 16, | ||
Pending = 32, | ||
Cold = 64, | ||
Propagated = 48 | ||
Pending = 32 | ||
} | ||
export declare function createReactiveSystem({ computed: { update: updateComputed, onUnwatched: onUnwatchedComputed, }, effect: { notify: notifyEffect, }, }: { | ||
computed: { | ||
/** | ||
* Updates the computed subscriber's value and returns whether it changed. | ||
* | ||
* This function should be called when a computed subscriber is marked as Dirty. | ||
* The computed subscriber's getter function is invoked, and its value is updated. | ||
* If the value changes, the new value is stored, and the function returns `true`. | ||
* | ||
* @param computed - The computed subscriber to update. | ||
* @returns `true` if the computed subscriber's value changed; otherwise `false`. | ||
*/ | ||
update(computed: Dependency & Subscriber): boolean; | ||
onUnwatched?: (computed: Dependency & Subscriber) => void; | ||
}; | ||
effect: { | ||
/** | ||
* Handles effect notifications by processing the specified `effect`. | ||
* | ||
* When an `effect` first receives any of the following flags: | ||
* - `Dirty` | ||
* - `PendingComputed` | ||
* - `PendingEffect` | ||
* this method will process them and return `true` if the flags are successfully handled. | ||
* If not fully handled, future changes to these flags will trigger additional calls | ||
* until the method eventually returns `true`. | ||
*/ | ||
notify(effect: Subscriber): void; | ||
}; | ||
}): { | ||
export declare function createReactiveSystem({ update, notify, unwatched, }: { | ||
/** | ||
* Links a given dependency and subscriber if they are not already linked. | ||
* Updates the computed subscriber's value and returns whether it changed. | ||
* | ||
* @param dep - The dependency to be linked. | ||
* @param sub - The subscriber that depends on this dependency. | ||
* @returns The newly created link object if the two are not already linked; otherwise `undefined`. | ||
*/ | ||
link(dep: Dependency, sub: Subscriber): Link | undefined; | ||
/** | ||
* Traverses and marks subscribers starting from the provided link. | ||
* This function should be called when a computed subscriber is marked as Dirty. | ||
* The computed subscriber's getter function is invoked, and its value is updated. | ||
* If the value changes, the new value is stored, and the function returns `true`. | ||
* | ||
* It sets flags (e.g., Dirty, PendingComputed, PendingEffect) on each subscriber | ||
* to indicate which ones require re-computation or effect processing. | ||
* This function should be called after a signal's value changes. | ||
* | ||
* @param link - The starting link from which propagation begins. | ||
* @param sub - The computed subscriber to update. | ||
* @returns `true` if the computed subscriber's value changed; otherwise `false`. | ||
*/ | ||
propagate(link: Link): void; | ||
/** | ||
* Prepares the given subscriber to track new dependencies. | ||
* | ||
* It resets the subscriber's internal pointers (e.g., depsTail) and | ||
* sets its flags to indicate it is now tracking dependency links. | ||
* | ||
* @param sub - The subscriber to start tracking. | ||
*/ | ||
startTracking(sub: Subscriber): void; | ||
/** | ||
* Concludes tracking of dependencies for the specified subscriber. | ||
* | ||
* It clears or unlinks any tracked dependency information, then | ||
* updates the subscriber's flags to indicate tracking is complete. | ||
* | ||
* @param sub - The subscriber whose tracking is ending. | ||
*/ | ||
endTracking(sub: Subscriber): void; | ||
/** | ||
* Recursively checks and updates all computed subscribers marked as pending. | ||
* | ||
* It traverses the linked structure using a stack mechanism. For each computed | ||
* subscriber in a pending state, updateComputed is called and shallowPropagate | ||
* is triggered if a value changes. Returns whether any updates occurred. | ||
* | ||
* @param link - The starting link representing a sequence of pending computeds. | ||
* @returns `true` if a computed was updated, otherwise `false`. | ||
*/ | ||
checkDirty(link: Link): boolean; | ||
/** | ||
* Ensures all pending internal effects for the given subscriber are processed. | ||
* | ||
* This should be called after an effect decides not to re-run itself but may still | ||
* have dependencies flagged with PendingEffect. If the subscriber is flagged with | ||
* PendingEffect, this function clears that flag and invokes `notifyEffect` on any | ||
* related dependencies marked as Effect and Propagated, processing pending effects. | ||
* | ||
* @param sub - The subscriber which may have pending effects. | ||
* @param flags - The current flags on the subscriber to check. | ||
*/ | ||
processPendingInnerEffects(sub: Subscriber): void; | ||
/** | ||
* Processes queued effect notifications after a batch operation finishes. | ||
* | ||
* Iterates through all queued effects, calling notifyEffect on each. | ||
* If an effect remains partially handled, its flags are updated, and future | ||
* notifications may be triggered until fully handled. | ||
*/ | ||
processEffectNotifications(): void; | ||
warming: (sub: Subscriber & Dependency) => void; | ||
cooling: (sub: Subscriber & Dependency) => void; | ||
update(sub: Dependency & Subscriber): boolean; | ||
notify(sub: Subscriber, nextSub: Link | undefined): void; | ||
unwatched(sub: Dependency): void; | ||
}): { | ||
link: (dep: Dependency, sub: Subscriber) => Link | undefined; | ||
unlink: (link: Link, sub?: Subscriber) => Link | undefined; | ||
propagate: (current: Link) => void; | ||
shallowPropagate: (link: Link) => void; | ||
checkDirty: (current: Link) => boolean; | ||
startTracking: (sub: Subscriber) => void; | ||
endTracking: (sub: Subscriber) => void; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
30836
-45.39%7
40%7
-41.67%135
-91.1%198
-1.49%1
Infinity%