@valentin30/signal 0.1.0 → 1.0.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/.prettierrc +21 -0
- package/package.json +86 -8
- package/src/core/computed.ts +68 -0
- package/src/core/contracts/consumer.ts +3 -0
- package/src/core/contracts/source.ts +7 -0
- package/src/core/interfaces/consumers.ts +8 -0
- package/src/core/interfaces/sources.ts +8 -0
- package/src/core/interfaces/value.ts +5 -0
- package/src/core/signal.ts +45 -0
- package/src/modules/common/contracts/addable.ts +3 -0
- package/src/modules/common/contracts/disposable.ts +3 -0
- package/src/modules/common/contracts/subscriber.ts +6 -0
- package/src/modules/common/types/callable.ts +1 -0
- package/src/modules/common/types/callback.ts +1 -0
- package/src/modules/common/utils/compare.ts +3 -0
- package/src/modules/common/utils/swap.ts +23 -0
- package/src/modules/consumers/factory.ts +26 -0
- package/src/modules/event/channel.ts +82 -0
- package/src/modules/event/effect.ts +45 -0
- package/src/modules/event/notifier.ts +38 -0
- package/src/modules/node/factory.ts +51 -0
- package/src/modules/node/index.ts +13 -0
- package/src/modules/node/source.ts +11 -0
- package/src/modules/scheduler/dispatch.ts +3 -0
- package/src/modules/scheduler/runner.ts +47 -0
- package/src/modules/sources/dynamic.ts +73 -0
- package/src/modules/sources/static.ts +49 -0
- package/src/modules/value/factory.ts +29 -0
- package/src/packages/builder/consumers.ts +11 -0
- package/src/packages/builder/node.ts +16 -0
- package/src/packages/builder/sources.ts +18 -0
- package/src/packages/builder/value.ts +11 -0
- package/src/packages/builder.ts +4 -0
- package/src/packages/core/computed.ts +15 -0
- package/src/packages/core/context.ts +5 -0
- package/src/packages/core/scheduler.ts +5 -0
- package/src/packages/core/signal.ts +12 -0
- package/src/packages/core.ts +2 -0
- package/src/packages/event/channel.ts +17 -0
- package/src/packages/event/effect.ts +15 -0
- package/src/packages/event/notifier.ts +14 -0
- package/src/packages/event.ts +3 -0
- package/src/packages/react/use-computed.ts +9 -0
- package/src/packages/react/use-read.ts +23 -0
- package/src/packages/react/use-signal.ts +9 -0
- package/src/packages/react.ts +3 -0
- package/src/runtime/context.ts +36 -0
- package/src/runtime/scheduler.ts +75 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +53 -0
- package/vitest.config.ts +14 -0
- package/dist/index.d.mts +0 -194
- package/dist/index.d.ts +0 -194
- package/dist/index.js +0 -372
- package/dist/index.mjs +0 -342
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
2
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
3
|
+
import { Sources } from '@valentin30/signal/core/interfaces/sources'
|
|
4
|
+
import { Addable } from '@valentin30/signal/modules/common/contracts/addable'
|
|
5
|
+
import { Callable } from '@valentin30/signal/modules/common/types/callable'
|
|
6
|
+
import { unsafe_swap } from '@valentin30/signal/modules/common/utils/swap'
|
|
7
|
+
|
|
8
|
+
type Track = <Target extends Addable<Source>, Args extends any[], ReturnType>(
|
|
9
|
+
target: Target,
|
|
10
|
+
callback: Callable<Args, ReturnType>,
|
|
11
|
+
...args: Args
|
|
12
|
+
) => ReturnType
|
|
13
|
+
|
|
14
|
+
export function DynamicSourcesFactory(track: Track) {
|
|
15
|
+
class DynamicSources<T> implements Sources<T>, Addable<Source> {
|
|
16
|
+
private current: Map<Source, number>
|
|
17
|
+
|
|
18
|
+
private previous: Map<Source, number>
|
|
19
|
+
|
|
20
|
+
private consumer: Consumer | null
|
|
21
|
+
|
|
22
|
+
private readonly computation: () => T
|
|
23
|
+
|
|
24
|
+
constructor(computation: () => T) {
|
|
25
|
+
this.consumer = null
|
|
26
|
+
this.current = new Map<Source, number>()
|
|
27
|
+
this.previous = new Map<Source, number>()
|
|
28
|
+
this.computation = computation
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public link(consumer: Consumer): void {
|
|
32
|
+
if (this.consumer !== null) return
|
|
33
|
+
this.consumer = consumer
|
|
34
|
+
for (const source of this.current.keys()) source.link(consumer)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public unlink(consumer: Consumer): void {
|
|
38
|
+
if (this.consumer === null || this.consumer !== consumer) return
|
|
39
|
+
this.consumer = null
|
|
40
|
+
for (const source of this.current.keys()) source.unlink(consumer)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public changed(): boolean {
|
|
44
|
+
for (const [source, version] of this.current) {
|
|
45
|
+
if (source.version() !== version) return true
|
|
46
|
+
}
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public compute(): T {
|
|
51
|
+
try {
|
|
52
|
+
unsafe_swap(this, 'previous', this, 'current')
|
|
53
|
+
const result = track(this, this.computation)
|
|
54
|
+
if (this.consumer !== null) {
|
|
55
|
+
for (const source of this.previous.keys()) source.unlink(this.consumer)
|
|
56
|
+
}
|
|
57
|
+
return result
|
|
58
|
+
} finally {
|
|
59
|
+
this.previous.clear()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public add(source: Source): void {
|
|
64
|
+
this.current.set(source, source.version())
|
|
65
|
+
this.previous.delete(source)
|
|
66
|
+
if (this.consumer !== null) source.link(this.consumer)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return function dynamic_sources<T>(computation: () => T): Sources<T> {
|
|
71
|
+
return new DynamicSources(computation)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
2
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
3
|
+
import { Sources } from '@valentin30/signal/core/interfaces/sources'
|
|
4
|
+
|
|
5
|
+
function version(source: Source): number {
|
|
6
|
+
return source.version()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function StaticSourcesFactory() {
|
|
10
|
+
class StaticSources<T> implements Sources<T> {
|
|
11
|
+
private readonly sources: Source[]
|
|
12
|
+
|
|
13
|
+
private readonly versioning: number[]
|
|
14
|
+
|
|
15
|
+
private readonly computation: () => T
|
|
16
|
+
|
|
17
|
+
constructor(computation: () => T, sources: Source[]) {
|
|
18
|
+
this.sources = sources
|
|
19
|
+
this.versioning = sources.map(version)
|
|
20
|
+
this.computation = computation
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public link(consumer: Consumer): void {
|
|
24
|
+
for (const source of this.sources) source.link(consumer)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public unlink(consumer: Consumer): void {
|
|
28
|
+
for (const source of this.sources) source.unlink(consumer)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public changed(): boolean {
|
|
32
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
33
|
+
if (this.versioning[i] !== this.sources[i].version()) return true
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public compute(): T {
|
|
39
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
40
|
+
this.versioning[i] = this.sources[i].version()
|
|
41
|
+
}
|
|
42
|
+
return this.computation()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return function static_sources<T>(computation: () => T, sources: Source[]): Sources<T> {
|
|
47
|
+
return new StaticSources(computation, sources)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Value } from '@valentin30/signal/core/interfaces/value'
|
|
2
|
+
|
|
3
|
+
export function ValueFactory(compare: <T>(a: T, b: T) => boolean) {
|
|
4
|
+
class V<T> implements Value<T> {
|
|
5
|
+
private value: T
|
|
6
|
+
|
|
7
|
+
constructor(value: T) {
|
|
8
|
+
this.value = value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public read(): T {
|
|
12
|
+
return this.value
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public write(value: T): boolean {
|
|
16
|
+
if (this.compare(this.read(), value)) return false
|
|
17
|
+
this.value = value
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public compare(a: T, b: T): boolean {
|
|
22
|
+
return compare(a, b)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return function v<T>(value: T): Value<T> {
|
|
27
|
+
return new V(value)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Consumers } from '@valentin30/signal/core/interfaces/consumers'
|
|
2
|
+
import { ConsumersFactory } from '@valentin30/signal/modules/consumers/factory'
|
|
3
|
+
|
|
4
|
+
const factory = ConsumersFactory()
|
|
5
|
+
|
|
6
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
7
|
+
export type { Consumers } from '@valentin30/signal/core/interfaces/consumers'
|
|
8
|
+
|
|
9
|
+
export function consumers(): Consumers {
|
|
10
|
+
return factory()
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Node } from '@valentin30/signal/modules/node'
|
|
2
|
+
import { NodeFactory } from '@valentin30/signal/modules/node/factory'
|
|
3
|
+
import { NodeSource } from '@valentin30/signal/modules/node/source'
|
|
4
|
+
import { register } from '@valentin30/signal/runtime/context'
|
|
5
|
+
|
|
6
|
+
const factory = NodeFactory(register)
|
|
7
|
+
|
|
8
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
9
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
10
|
+
export type { Value } from '@valentin30/signal/core/interfaces/value'
|
|
11
|
+
export type { Node } from '@valentin30/signal/modules/node'
|
|
12
|
+
export type { NodeSource } from '@valentin30/signal/modules/node/source'
|
|
13
|
+
|
|
14
|
+
export function node<T>(source: NodeSource<T>): Node<T> {
|
|
15
|
+
return factory(source)
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
2
|
+
import { Sources } from '@valentin30/signal/core/interfaces/sources'
|
|
3
|
+
|
|
4
|
+
import { DynamicSourcesFactory } from '@valentin30/signal/modules/sources/dynamic'
|
|
5
|
+
import { StaticSourcesFactory } from '@valentin30/signal/modules/sources/static'
|
|
6
|
+
import { track } from '@valentin30/signal/runtime/context'
|
|
7
|
+
|
|
8
|
+
const static_sources = StaticSourcesFactory()
|
|
9
|
+
const dynamic_sources = DynamicSourcesFactory(track)
|
|
10
|
+
|
|
11
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
12
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
13
|
+
export type { Sources } from '@valentin30/signal/core/interfaces/sources'
|
|
14
|
+
|
|
15
|
+
export function sources<T>(compute: () => T, dependencies?: Source[]): Sources<T> {
|
|
16
|
+
if (dependencies) return static_sources(compute, dependencies)
|
|
17
|
+
return dynamic_sources(compute)
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Value } from '@valentin30/signal/core/interfaces/value'
|
|
2
|
+
import { strict_compare } from '@valentin30/signal/modules/common/utils/compare'
|
|
3
|
+
import { ValueFactory } from '@valentin30/signal/modules/value/factory'
|
|
4
|
+
|
|
5
|
+
const factory = ValueFactory(strict_compare)
|
|
6
|
+
|
|
7
|
+
export type { Value } from '@valentin30/signal/core/interfaces/value'
|
|
8
|
+
|
|
9
|
+
export function value<T>(value: T): Value<T> {
|
|
10
|
+
return factory(value)
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Computed } from '@valentin30/signal/core/computed'
|
|
2
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
3
|
+
import { Node } from '@valentin30/signal/modules/node'
|
|
4
|
+
import { consumers } from '@valentin30/signal/packages/builder/consumers'
|
|
5
|
+
import { node } from '@valentin30/signal/packages/builder/node'
|
|
6
|
+
import { sources } from '@valentin30/signal/packages/builder/sources'
|
|
7
|
+
import { value } from '@valentin30/signal/packages/builder/value'
|
|
8
|
+
|
|
9
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
10
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
11
|
+
export type { Node } from '@valentin30/signal/modules/node'
|
|
12
|
+
|
|
13
|
+
export function computed<T>(compute: () => T, deps?: Source[]): Node<T> {
|
|
14
|
+
return node(new Computed(value(<T>undefined), sources(compute, deps), consumers()))
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
2
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
3
|
+
export type { Addable } from '@valentin30/signal/modules/common/contracts/addable'
|
|
4
|
+
export type { Callable } from '@valentin30/signal/modules/common/types/callable'
|
|
5
|
+
export { register, scope, track, untrack } from '@valentin30/signal/runtime/context'
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { Callable } from '@valentin30/signal/modules/common/types/callable'
|
|
2
|
+
export type { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
3
|
+
export type { Dispatch } from '@valentin30/signal/modules/scheduler/dispatch'
|
|
4
|
+
export type { Runner } from '@valentin30/signal/modules/scheduler/runner'
|
|
5
|
+
export { batch, dequeue, enqueue, flush, schedule } from '@valentin30/signal/runtime/scheduler'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Signal } from '@valentin30/signal/core/signal'
|
|
2
|
+
import { consumers } from '@valentin30/signal/packages/builder/consumers'
|
|
3
|
+
import { Node, node } from '@valentin30/signal/packages/builder/node'
|
|
4
|
+
import { value } from '@valentin30/signal/packages/builder/value'
|
|
5
|
+
|
|
6
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
7
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
8
|
+
export type { Node } from '@valentin30/signal/modules/node'
|
|
9
|
+
|
|
10
|
+
export function signal<T>(initial: T): Node<T> {
|
|
11
|
+
return node(new Signal(value(initial), consumers()))
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
2
|
+
import { Channel } from '@valentin30/signal/modules/event/channel'
|
|
3
|
+
import { ChannelFactory } from '@valentin30/signal/modules/event/channel'
|
|
4
|
+
import { dequeue, enqueue } from '@valentin30/signal/runtime/scheduler'
|
|
5
|
+
|
|
6
|
+
const factory = ChannelFactory(enqueue, dequeue)
|
|
7
|
+
|
|
8
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
9
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
10
|
+
export type { Channel } from '@valentin30/signal/modules/event/channel'
|
|
11
|
+
export type { Disposable } from '@valentin30/signal/modules/common/contracts/disposable'
|
|
12
|
+
export type { Subscriber } from '@valentin30/signal/modules/common/contracts/subscriber'
|
|
13
|
+
export type { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
14
|
+
|
|
15
|
+
export function channel(...sources: Source[]): Channel {
|
|
16
|
+
return factory(...sources)
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
2
|
+
import { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
3
|
+
import { EffectFactory } from '@valentin30/signal/modules/event/effect'
|
|
4
|
+
import { sources } from '@valentin30/signal/packages/builder/sources'
|
|
5
|
+
import { dequeue, enqueue } from '@valentin30/signal/runtime/scheduler'
|
|
6
|
+
|
|
7
|
+
const factory = EffectFactory(enqueue, dequeue)
|
|
8
|
+
|
|
9
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
10
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
11
|
+
export type { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
12
|
+
|
|
13
|
+
export function effect(callback: () => Callback | void, dependencies?: Source[]): Callback {
|
|
14
|
+
return factory(sources(callback, dependencies))
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
2
|
+
import { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
3
|
+
import { NotifierFactory } from '@valentin30/signal/modules/event/notifier'
|
|
4
|
+
import { dequeue, enqueue } from '@valentin30/signal/runtime/scheduler'
|
|
5
|
+
|
|
6
|
+
const factory = NotifierFactory(enqueue, dequeue)
|
|
7
|
+
|
|
8
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
9
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
10
|
+
export type { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
11
|
+
|
|
12
|
+
export function notifier(source: Source, callback: Callback): Callback {
|
|
13
|
+
return factory(source, callback)
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { computed, Node, Source } from '@valentin30/signal/packages/core/computed'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
export type { Consumer, Node, Source } from '@valentin30/signal/packages/core/computed'
|
|
5
|
+
|
|
6
|
+
export function useComputed<T>(compute: () => T, deps?: Source[]): Node<T> {
|
|
7
|
+
const [result] = useState<Node<T>>(() => computed(compute, deps))
|
|
8
|
+
return result
|
|
9
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Node } from '@valentin30/signal/modules/node'
|
|
2
|
+
import { notifier } from '@valentin30/signal/packages/event/notifier'
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export type { Consumer } from '@valentin30/signal/core/contracts/consumer'
|
|
6
|
+
export type { Source } from '@valentin30/signal/core/contracts/source'
|
|
7
|
+
export type { Node } from '@valentin30/signal/modules/node'
|
|
8
|
+
|
|
9
|
+
export function useRead<T>(source: Node<T>): T
|
|
10
|
+
export function useRead<T>(source: Node<T> | null | undefined): T | null
|
|
11
|
+
export function useRead<T>(source: Node<T> | null | undefined): T | null {
|
|
12
|
+
const [value, setValue] = useState<T>(() => {
|
|
13
|
+
if (!source) return null as T
|
|
14
|
+
return source.read()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!source) return
|
|
19
|
+
return notifier(source, () => setValue(source.read()))
|
|
20
|
+
}, [source])
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Node, signal } from '@valentin30/signal/packages/core/signal'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
export type { Consumer, Node, Source } from '@valentin30/signal/packages/core/signal'
|
|
5
|
+
|
|
6
|
+
export function useSignal<T>(initial: T): Node<T> {
|
|
7
|
+
const [result] = useState<Node<T>>(() => signal(initial))
|
|
8
|
+
return result
|
|
9
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Source } from '@valentin30/signal/core/contracts/source'
|
|
2
|
+
import { Callable } from '@valentin30/signal/modules/common/types/callable'
|
|
3
|
+
import { Addable } from '@valentin30/signal/modules/common/contracts/addable'
|
|
4
|
+
|
|
5
|
+
let context: Addable<Source> | null = null
|
|
6
|
+
|
|
7
|
+
export function register(source: Source): void {
|
|
8
|
+
if (context === null) return
|
|
9
|
+
context.add(source)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function scope<Target extends Addable<Source>, Args extends any[], ReturnType>(
|
|
13
|
+
target: Target | null,
|
|
14
|
+
callback: Callable<Args, ReturnType>,
|
|
15
|
+
...args: Args
|
|
16
|
+
): ReturnType {
|
|
17
|
+
const current = context
|
|
18
|
+
context = target
|
|
19
|
+
try {
|
|
20
|
+
return callback(...args)
|
|
21
|
+
} finally {
|
|
22
|
+
context = current
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function track<Target extends Addable<Source>, Args extends any[], ReturnType>(
|
|
27
|
+
target: Target,
|
|
28
|
+
callback: Callable<Args, ReturnType>,
|
|
29
|
+
...args: Args
|
|
30
|
+
): ReturnType {
|
|
31
|
+
return scope(target, callback, ...args)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function untrack<ReturnType, Args extends any[]>(callback: Callable<Args, ReturnType>, ...args: Args): ReturnType {
|
|
35
|
+
return scope(null, callback, ...args)
|
|
36
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Callable } from '@valentin30/signal/modules/common/types/callable'
|
|
2
|
+
import { Callback } from '@valentin30/signal/modules/common/types/callback'
|
|
3
|
+
import { swap } from '@valentin30/signal/modules/common/utils/swap'
|
|
4
|
+
import { Runner } from '@valentin30/signal/modules/scheduler/runner'
|
|
5
|
+
|
|
6
|
+
let depth = 0
|
|
7
|
+
let is_running = false
|
|
8
|
+
const lookup = new Set<Callback>()
|
|
9
|
+
const runners = {
|
|
10
|
+
current: new Set<Runner>(),
|
|
11
|
+
pending: new Set<Runner>(),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let qmt: (callback: Callback) => void
|
|
15
|
+
if (typeof queueMicrotask === 'function') qmt = queueMicrotask
|
|
16
|
+
else qmt = (callback: Callback) => Promise.resolve().then(callback)
|
|
17
|
+
|
|
18
|
+
export function schedule(): void {
|
|
19
|
+
if (is_running || depth !== 0) return
|
|
20
|
+
is_running = true
|
|
21
|
+
qmt(flush)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function flush(): void {
|
|
25
|
+
is_running = false
|
|
26
|
+
|
|
27
|
+
if (runners.pending.size === 0) return
|
|
28
|
+
swap(runners, 'current', runners, 'pending')
|
|
29
|
+
runners.pending.clear()
|
|
30
|
+
|
|
31
|
+
for (const runner of runners.current) runner.run(dispatch)
|
|
32
|
+
|
|
33
|
+
lookup.clear()
|
|
34
|
+
runners.current.clear()
|
|
35
|
+
|
|
36
|
+
if (runners.pending.size === 0) return
|
|
37
|
+
schedule()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function enqueue(runner: Runner): void {
|
|
41
|
+
if (!runners.pending.has(runner) && !runners.current.has(runner)) {
|
|
42
|
+
runners.pending.add(runner)
|
|
43
|
+
}
|
|
44
|
+
schedule()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function dequeue(runner: Runner): void {
|
|
48
|
+
runners.pending.delete(runner)
|
|
49
|
+
runners.current.delete(runner)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function dispatch(callback: Callback): void {
|
|
53
|
+
if (lookup.has(callback)) return
|
|
54
|
+
lookup.add(callback)
|
|
55
|
+
try {
|
|
56
|
+
callback()
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(error)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function batch<Args extends any[], ReturnType>(
|
|
63
|
+
callback: Callable<Args, Promise<ReturnType>>,
|
|
64
|
+
...args: Args
|
|
65
|
+
): Promise<ReturnType> {
|
|
66
|
+
try {
|
|
67
|
+
depth++
|
|
68
|
+
return await callback(...args)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw error
|
|
71
|
+
} finally {
|
|
72
|
+
if (depth > 0) depth--
|
|
73
|
+
schedule()
|
|
74
|
+
}
|
|
75
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "esnext",
|
|
4
|
+
"target": "es2020",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"stripInternal": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"baseUrl": "src",
|
|
11
|
+
"paths": {
|
|
12
|
+
"@valentin30/signal/*": ["*"]
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineConfig, Options } from 'tsup'
|
|
2
|
+
|
|
3
|
+
const options: Options = {
|
|
4
|
+
format: ['esm'],
|
|
5
|
+
target: 'es2020',
|
|
6
|
+
outDir: 'dist',
|
|
7
|
+
clean: false,
|
|
8
|
+
splitting: true,
|
|
9
|
+
treeshake: true,
|
|
10
|
+
skipNodeModulesBundle: true,
|
|
11
|
+
external: ['react', 'react-dom'],
|
|
12
|
+
tsconfig: 'tsconfig.build.json',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function config(...names: string[]) {
|
|
16
|
+
return defineConfig([js(...names), ...names.map(n => dts(n))])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function js(...names: string[]): Options {
|
|
20
|
+
const entry = names.reduce((acc, name) => ({ ...acc, [name]: 'src/packages/' + name + '.ts' }), {})
|
|
21
|
+
return { entry, dts: false, ...options }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dts(...names: string[]): Options {
|
|
25
|
+
const entry = names.reduce((acc, name) => ({ ...acc, [name]: 'src/packages/' + name + '.ts' }), {})
|
|
26
|
+
return { entry, dts: { only: true, entry }, ...options }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default config(
|
|
30
|
+
//
|
|
31
|
+
'core',
|
|
32
|
+
'core/signal',
|
|
33
|
+
'core/computed',
|
|
34
|
+
'core/context',
|
|
35
|
+
'core/scheduler',
|
|
36
|
+
//
|
|
37
|
+
'react',
|
|
38
|
+
'react/use-signal',
|
|
39
|
+
'react/use-computed',
|
|
40
|
+
'react/use-read',
|
|
41
|
+
//
|
|
42
|
+
'event',
|
|
43
|
+
'event/channel',
|
|
44
|
+
'event/effect',
|
|
45
|
+
'event/notifier',
|
|
46
|
+
//
|
|
47
|
+
'builder',
|
|
48
|
+
'builder/value',
|
|
49
|
+
'builder/node',
|
|
50
|
+
'builder/sources',
|
|
51
|
+
'builder/consumers',
|
|
52
|
+
//
|
|
53
|
+
)
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
globals: true,
|
|
7
|
+
environment: 'node',
|
|
8
|
+
},
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
'@valentin30/signal': path.resolve(__dirname, 'src'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
})
|