signalium 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/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # signalium
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 03b2d2b: Initial release
8
+
9
+ ### Patch Changes
10
+
11
+ - a472569: Fix release and build, add linting
@@ -0,0 +1,3 @@
1
+ export type FlushCallback = () => Promise<void>;
2
+ export declare let scheduleWatchers: (flushWatchers: FlushCallback) => void;
3
+ export declare let scheduleDisconnects: (flushDisconnects: FlushCallback) => void;
package/dist/config.js ADDED
@@ -0,0 +1,19 @@
1
+ let currentWatcherFlush = null;
2
+ let currentDisconnectFlush = null;
3
+ const idleCallback = typeof requestIdleCallback === 'function' ? requestIdleCallback : (cb) => setTimeout(cb, 0);
4
+ export let scheduleWatchers = flushWatchers => {
5
+ if (currentWatcherFlush !== null)
6
+ return;
7
+ currentWatcherFlush = setTimeout(() => {
8
+ currentWatcherFlush = null;
9
+ flushWatchers();
10
+ }, 0);
11
+ };
12
+ export let scheduleDisconnects = flushDisconnects => {
13
+ if (currentDisconnectFlush !== null)
14
+ return;
15
+ currentDisconnectFlush = idleCallback(() => {
16
+ currentDisconnectFlush = null;
17
+ flushDisconnects();
18
+ });
19
+ };
@@ -0,0 +1,2 @@
1
+ export type { Signal, WriteableSignal, SignalCompute, SignalAsyncCompute, SignalSubscribe, SignalEquals, SignalOptions, SignalOptionsWithInit, SignalSubscription, SignalWatcherEffect, AsyncPending, AsyncReady, AsyncResult, } from './signals.js';
2
+ export { state, computed, asyncComputed, subscription, watcher } from './signals.js';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { state, computed, asyncComputed, subscription, watcher } from './signals.js';
@@ -0,0 +1,3 @@
1
+ import type { ComputedSignal } from './signals.js';
2
+ export declare const scheduleWatcher: (watcher: ComputedSignal<any>) => void;
3
+ export declare const scheduleDisconnect: (disconnect: ComputedSignal<any>) => void;
@@ -0,0 +1,36 @@
1
+ import { scheduleWatchers, scheduleDisconnects } from './config.js';
2
+ let PENDING_FLUSH_WATCHERS = null;
3
+ const PENDING_WATCHERS = [];
4
+ const PENDING_DISCONNECTS = new Map();
5
+ export const scheduleWatcher = (watcher) => {
6
+ PENDING_WATCHERS.push(watcher);
7
+ if (PENDING_FLUSH_WATCHERS === null) {
8
+ let resolve;
9
+ const promise = new Promise(r => {
10
+ resolve = r;
11
+ });
12
+ PENDING_FLUSH_WATCHERS = { promise, resolve: resolve };
13
+ }
14
+ scheduleWatchers(flushWatchers);
15
+ };
16
+ const flushWatchers = async () => {
17
+ PENDING_FLUSH_WATCHERS.resolve();
18
+ PENDING_FLUSH_WATCHERS = null;
19
+ let watcher;
20
+ while ((watcher = PENDING_WATCHERS.shift())) {
21
+ watcher._check();
22
+ }
23
+ PENDING_WATCHERS.length = 0;
24
+ };
25
+ export const scheduleDisconnect = (disconnect) => {
26
+ const current = PENDING_DISCONNECTS.get(disconnect) ?? 0;
27
+ PENDING_DISCONNECTS.set(disconnect, current + 1);
28
+ scheduleDisconnects(flushDisconnects);
29
+ };
30
+ const flushDisconnects = async () => {
31
+ await PENDING_FLUSH_WATCHERS?.promise;
32
+ for (const [signal, count] of PENDING_DISCONNECTS) {
33
+ signal._disconnect(count);
34
+ }
35
+ PENDING_DISCONNECTS.clear();
36
+ };
@@ -0,0 +1,103 @@
1
+ declare const enum SignalType {
2
+ Computed = 0,
3
+ Subscription = 1,
4
+ Async = 2,
5
+ Watcher = 3
6
+ }
7
+ export interface Signal<T = unknown> {
8
+ get(): T;
9
+ }
10
+ export interface WriteableSignal<T> extends Signal<T> {
11
+ set(value: T): void;
12
+ }
13
+ export type SignalCompute<T> = (prev: T | undefined) => T;
14
+ export type SignalAsyncCompute<T> = (prev: T | undefined) => T | Promise<T>;
15
+ export type SignalWatcherEffect = () => void;
16
+ export type SignalEquals<T> = (prev: T, next: T) => boolean;
17
+ export type SignalSubscription = {
18
+ update?(): void;
19
+ unsubscribe?(): void;
20
+ };
21
+ export type SignalSubscribe<T> = (get: () => T, set: (value: T) => void) => SignalSubscription | undefined | void;
22
+ export interface SignalOptions<T> {
23
+ equals?: SignalEquals<T>;
24
+ }
25
+ export interface SignalOptionsWithInit<T> extends SignalOptions<T> {
26
+ initValue: T;
27
+ }
28
+ declare const enum SignalState {
29
+ Clean = 0,
30
+ MaybeDirty = 1,
31
+ Dirty = 2
32
+ }
33
+ interface Link {
34
+ sub: WeakRef<ComputedSignal<any>>;
35
+ dep: ComputedSignal<any>;
36
+ ord: number;
37
+ version: number;
38
+ nextDep: Link | undefined;
39
+ nextSub: Link | undefined;
40
+ prevSub: Link | undefined;
41
+ nextDirty: Link | undefined;
42
+ }
43
+ export declare function endTrack(sub: ComputedSignal<any>, shouldDisconnect: boolean): void;
44
+ export declare class ComputedSignal<T> {
45
+ id: number;
46
+ _type: SignalType;
47
+ _subs: Link | undefined;
48
+ _subsTail: Link | undefined;
49
+ _deps: Link | undefined;
50
+ _dirtyDep: Link | undefined;
51
+ _state: SignalState;
52
+ _version: number;
53
+ _connectedCount: number;
54
+ _currentValue: T | AsyncResult<T> | undefined;
55
+ _compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T> | undefined;
56
+ _equals: SignalEquals<T>;
57
+ _ref: WeakRef<ComputedSignal<T>>;
58
+ constructor(type: SignalType, compute: SignalCompute<T> | SignalAsyncCompute<T> | SignalSubscribe<T> | undefined, equals?: SignalEquals<T>, initValue?: T);
59
+ get(): T | AsyncResult<T>;
60
+ _check(shouldWatch?: boolean): number;
61
+ _run(wasConnected: boolean, isConnected: boolean, shouldConnect: boolean): void;
62
+ _resetDirty(): void;
63
+ _dirty(): void;
64
+ _dirtyConsumers(): void;
65
+ _disconnect(count?: number): void;
66
+ }
67
+ export interface AsyncPending {
68
+ result: undefined;
69
+ error: unknown;
70
+ isPending: boolean;
71
+ isReady: false;
72
+ isError: boolean;
73
+ isSuccess: boolean;
74
+ }
75
+ export interface AsyncReady<T> {
76
+ result: T;
77
+ error: unknown;
78
+ isPending: boolean;
79
+ isReady: true;
80
+ isError: boolean;
81
+ isSuccess: boolean;
82
+ }
83
+ export type AsyncResult<T> = AsyncPending | AsyncReady<T>;
84
+ declare class StateSignal<T> implements StateSignal<T> {
85
+ private _value;
86
+ private _equals;
87
+ private _consumers;
88
+ constructor(_value: T, _equals?: SignalEquals<T>);
89
+ get(): T;
90
+ set(value: T): void;
91
+ }
92
+ export declare function state<T>(initialValue: T, opts?: SignalOptions<T>): StateSignal<T>;
93
+ export declare function computed<T>(compute: (prev: T | undefined) => T, opts?: SignalOptions<T>): Signal<T>;
94
+ export declare function asyncComputed<T>(compute: (prev: T | undefined) => Promise<T>, opts?: SignalOptions<T>): Signal<AsyncResult<T>>;
95
+ export declare function asyncComputed<T>(compute: (prev: T | undefined) => Promise<T>, opts: SignalOptionsWithInit<T>): Signal<AsyncReady<T>>;
96
+ export declare function subscription<T>(subscribe: SignalSubscribe<T>, opts?: SignalOptions<T>): Signal<T | undefined>;
97
+ export declare function subscription<T>(subscribe: SignalSubscribe<T>, opts: SignalOptionsWithInit<T>): Signal<T>;
98
+ export interface Watcher {
99
+ disconnect(): void;
100
+ }
101
+ export declare function watcher(fn: () => void): Watcher;
102
+ export declare function untrack<T = void>(fn: () => T): T;
103
+ export {};
@@ -0,0 +1,474 @@
1
+ import { scheduleDisconnect, scheduleWatcher } from './scheduling.js';
2
+ let CURRENT_CONSUMER;
3
+ let CURRENT_DEP_TAIL;
4
+ let CURRENT_ORD = 0;
5
+ let CURRENT_IS_WATCHED = false;
6
+ let CURRENT_SEEN;
7
+ let id = 0;
8
+ const SUBSCRIPTIONS = new WeakMap();
9
+ let linkPool;
10
+ function linkNewDep(dep, sub, nextDep, depsTail, ord) {
11
+ let newLink;
12
+ if (linkPool !== undefined) {
13
+ newLink = linkPool;
14
+ linkPool = newLink.nextDep;
15
+ newLink.nextDep = nextDep;
16
+ newLink.dep = dep;
17
+ newLink.sub = sub._ref;
18
+ newLink.ord = ord;
19
+ }
20
+ else {
21
+ newLink = {
22
+ dep,
23
+ sub: sub._ref,
24
+ ord,
25
+ version: 0,
26
+ nextDep,
27
+ nextDirty: undefined,
28
+ prevSub: undefined,
29
+ nextSub: undefined,
30
+ };
31
+ }
32
+ if (depsTail === undefined) {
33
+ sub._deps = newLink;
34
+ }
35
+ else {
36
+ depsTail.nextDep = newLink;
37
+ }
38
+ if (dep._subs === undefined) {
39
+ dep._subs = newLink;
40
+ }
41
+ else {
42
+ const oldTail = dep._subsTail;
43
+ newLink.prevSub = oldTail;
44
+ oldTail.nextSub = newLink;
45
+ }
46
+ dep._subsTail = newLink;
47
+ return newLink;
48
+ }
49
+ function poolLink(link) {
50
+ const dep = link.dep;
51
+ const nextSub = link.nextSub;
52
+ const prevSub = link.prevSub;
53
+ if (nextSub !== undefined) {
54
+ nextSub.prevSub = prevSub;
55
+ link.nextSub = undefined;
56
+ }
57
+ else {
58
+ dep._subsTail = prevSub;
59
+ }
60
+ if (prevSub !== undefined) {
61
+ prevSub.nextSub = nextSub;
62
+ link.prevSub = undefined;
63
+ }
64
+ else {
65
+ dep._subs = nextSub;
66
+ }
67
+ // @ts-expect-error - override to pool the value
68
+ link.dep = undefined;
69
+ // @ts-expect-error - override to pool the value
70
+ link.sub = undefined;
71
+ link.nextDep = linkPool;
72
+ linkPool = link;
73
+ link.prevSub = undefined;
74
+ }
75
+ export function endTrack(sub, shouldDisconnect) {
76
+ if (CURRENT_DEP_TAIL !== undefined) {
77
+ if (CURRENT_DEP_TAIL.nextDep !== undefined) {
78
+ clearTrack(CURRENT_DEP_TAIL.nextDep, shouldDisconnect);
79
+ CURRENT_DEP_TAIL.nextDep = undefined;
80
+ }
81
+ }
82
+ else if (sub._deps !== undefined) {
83
+ clearTrack(sub._deps, shouldDisconnect);
84
+ sub._deps = undefined;
85
+ }
86
+ }
87
+ function clearTrack(link, shouldDisconnect) {
88
+ do {
89
+ const nextDep = link.nextDep;
90
+ if (shouldDisconnect) {
91
+ scheduleDisconnect(link.dep);
92
+ }
93
+ poolLink(link);
94
+ link = nextDep;
95
+ } while (link !== undefined);
96
+ }
97
+ // const registry = new FinalizationRegistry(poolLink);
98
+ export class ComputedSignal {
99
+ id = id++;
100
+ _type;
101
+ _subs;
102
+ _subsTail;
103
+ _deps;
104
+ _dirtyDep;
105
+ _state = 2 /* SignalState.Dirty */;
106
+ _version = 0;
107
+ _connectedCount;
108
+ _currentValue;
109
+ _compute;
110
+ _equals;
111
+ _ref = new WeakRef(this);
112
+ constructor(type, compute, equals, initValue) {
113
+ this._type = type;
114
+ this._compute = compute;
115
+ this._equals = equals ?? ((a, b) => a === b);
116
+ this._currentValue = initValue;
117
+ this._connectedCount = type === 3 /* SignalType.Watcher */ ? 1 : 0;
118
+ }
119
+ get() {
120
+ let prevTracked = false;
121
+ if (CURRENT_CONSUMER !== undefined && this._type !== 3 /* SignalType.Watcher */ && !CURRENT_SEEN.has(this)) {
122
+ const ord = CURRENT_ORD++;
123
+ const nextDep = CURRENT_DEP_TAIL === undefined ? CURRENT_CONSUMER._deps : CURRENT_DEP_TAIL.nextDep;
124
+ let newLink = nextDep;
125
+ while (newLink !== undefined) {
126
+ if (newLink.dep === this) {
127
+ prevTracked = true;
128
+ if (CURRENT_DEP_TAIL === undefined) {
129
+ CURRENT_CONSUMER._deps = newLink;
130
+ }
131
+ else {
132
+ CURRENT_DEP_TAIL.nextDep = newLink;
133
+ }
134
+ newLink.ord = ord;
135
+ newLink.nextDirty = undefined;
136
+ if (this._subs === undefined) {
137
+ this._subs = newLink;
138
+ }
139
+ break;
140
+ }
141
+ newLink = newLink.nextDep;
142
+ }
143
+ this._check(CURRENT_IS_WATCHED && !prevTracked);
144
+ CURRENT_DEP_TAIL = newLink ?? linkNewDep(this, CURRENT_CONSUMER, nextDep, CURRENT_DEP_TAIL, ord);
145
+ CURRENT_DEP_TAIL.version = this._version;
146
+ CURRENT_SEEN.add(this);
147
+ }
148
+ else {
149
+ this._check();
150
+ }
151
+ return this._currentValue;
152
+ }
153
+ _check(shouldWatch = false) {
154
+ let state = this._state;
155
+ let connectedCount = this._connectedCount;
156
+ const wasConnected = connectedCount > 0;
157
+ const shouldConnect = shouldWatch && !wasConnected;
158
+ if (shouldWatch) {
159
+ this._connectedCount = connectedCount = connectedCount + 1;
160
+ }
161
+ if (shouldConnect) {
162
+ if (this._type === 1 /* SignalType.Subscription */) {
163
+ state = 2 /* SignalState.Dirty */;
164
+ }
165
+ else {
166
+ let link = this._deps;
167
+ while (link !== undefined) {
168
+ const dep = link.dep;
169
+ if (link.version !== dep._check(true)) {
170
+ state = 2 /* SignalState.Dirty */;
171
+ break;
172
+ }
173
+ link = link.nextDep;
174
+ }
175
+ }
176
+ this._resetDirty();
177
+ }
178
+ if (state === 0 /* SignalState.Clean */) {
179
+ return this._version;
180
+ }
181
+ if (state === 1 /* SignalState.MaybeDirty */) {
182
+ let dirty = this._dirtyDep;
183
+ while (dirty !== undefined) {
184
+ const dep = dirty.dep;
185
+ if (dirty.version !== dep._check()) {
186
+ state = 2 /* SignalState.Dirty */;
187
+ break;
188
+ }
189
+ dirty = dirty.nextDirty;
190
+ }
191
+ }
192
+ if (state === 2 /* SignalState.Dirty */) {
193
+ this._run(wasConnected, connectedCount > 0, shouldConnect);
194
+ }
195
+ else {
196
+ this._resetDirty();
197
+ }
198
+ this._state = 0 /* SignalState.Clean */;
199
+ this._dirtyDep = undefined;
200
+ return this._version;
201
+ }
202
+ _run(wasConnected, isConnected, shouldConnect) {
203
+ const { _type: type } = this;
204
+ const prevConsumer = CURRENT_CONSUMER;
205
+ const prevOrd = CURRENT_ORD;
206
+ const prevSeen = CURRENT_SEEN;
207
+ const prevDepTail = CURRENT_DEP_TAIL;
208
+ const prevIsWatched = CURRENT_IS_WATCHED;
209
+ try {
210
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
211
+ CURRENT_CONSUMER = this;
212
+ CURRENT_ORD = 0;
213
+ CURRENT_SEEN = new WeakSet();
214
+ CURRENT_DEP_TAIL = undefined;
215
+ CURRENT_IS_WATCHED = isConnected;
216
+ switch (type) {
217
+ case 0 /* SignalType.Computed */: {
218
+ const prevValue = this._currentValue;
219
+ const nextValue = this._compute(prevValue);
220
+ if (!this._equals(prevValue, nextValue)) {
221
+ this._currentValue = nextValue;
222
+ this._version++;
223
+ }
224
+ break;
225
+ }
226
+ case 2 /* SignalType.Async */: {
227
+ const value = this._currentValue ??
228
+ (this._currentValue = {
229
+ result: undefined,
230
+ error: undefined,
231
+ isPending: true,
232
+ isReady: false,
233
+ isError: false,
234
+ isSuccess: false,
235
+ });
236
+ const nextValue = this._compute(value?.result);
237
+ if ('then' in nextValue) {
238
+ const currentVersion = ++this._version;
239
+ nextValue.then(result => {
240
+ if (currentVersion !== this._version) {
241
+ return;
242
+ }
243
+ value.result = result;
244
+ value.isReady = true;
245
+ value.isPending = false;
246
+ value.isSuccess = true;
247
+ this._version++;
248
+ this._dirtyConsumers();
249
+ }, error => {
250
+ if (currentVersion !== this._version) {
251
+ return;
252
+ }
253
+ value.error = error;
254
+ value.isPending = false;
255
+ value.isError = true;
256
+ this._version++;
257
+ this._dirtyConsumers();
258
+ });
259
+ value.isPending = true;
260
+ value.isError = false;
261
+ value.isSuccess = false;
262
+ }
263
+ else {
264
+ value.result = nextValue;
265
+ value.isReady = true;
266
+ value.isPending = false;
267
+ value.isSuccess = true;
268
+ value.isError = false;
269
+ this._version++;
270
+ }
271
+ break;
272
+ }
273
+ case 1 /* SignalType.Subscription */: {
274
+ if (shouldConnect) {
275
+ const subscription = this._compute(() => this._currentValue, value => {
276
+ if (this._equals(value, this._currentValue)) {
277
+ return;
278
+ }
279
+ this._currentValue = value;
280
+ this._version++;
281
+ this._dirtyConsumers();
282
+ });
283
+ SUBSCRIPTIONS.set(this, subscription);
284
+ }
285
+ else {
286
+ const subscription = SUBSCRIPTIONS.get(this);
287
+ subscription?.update?.();
288
+ }
289
+ break;
290
+ }
291
+ default: {
292
+ this._compute();
293
+ }
294
+ }
295
+ }
296
+ finally {
297
+ endTrack(this, wasConnected);
298
+ CURRENT_CONSUMER = prevConsumer;
299
+ CURRENT_SEEN = prevSeen;
300
+ CURRENT_DEP_TAIL = prevDepTail;
301
+ CURRENT_ORD = prevOrd;
302
+ CURRENT_IS_WATCHED = prevIsWatched;
303
+ }
304
+ }
305
+ _resetDirty() {
306
+ let dirty = this._dirtyDep;
307
+ while (dirty !== undefined) {
308
+ const dep = dirty.dep;
309
+ const oldHead = dep._subs;
310
+ if (oldHead === undefined) {
311
+ dep._subs = dirty;
312
+ dirty.nextSub = undefined;
313
+ dirty.prevSub = undefined;
314
+ }
315
+ else {
316
+ dirty.nextSub = oldHead;
317
+ oldHead.prevSub = dirty;
318
+ dep._subs = dirty;
319
+ }
320
+ let nextDirty = dirty.nextDirty;
321
+ dirty.nextDirty = undefined;
322
+ dirty = nextDirty;
323
+ }
324
+ }
325
+ _dirty() {
326
+ if (this._type === 1 /* SignalType.Subscription */) {
327
+ if (this._connectedCount > 0) {
328
+ scheduleWatcher(this);
329
+ }
330
+ // else do nothing, only schedule if connected
331
+ }
332
+ else if (this._type === 3 /* SignalType.Watcher */) {
333
+ scheduleWatcher(this);
334
+ }
335
+ else {
336
+ this._dirtyConsumers();
337
+ }
338
+ this._subs = undefined;
339
+ }
340
+ _dirtyConsumers() {
341
+ let link = this._subs;
342
+ while (link !== undefined) {
343
+ const consumer = link.sub.deref();
344
+ if (consumer === undefined) {
345
+ const nextSub = link.nextSub;
346
+ poolLink(link);
347
+ link = nextSub;
348
+ continue;
349
+ }
350
+ const state = consumer._state;
351
+ if (state === 2 /* SignalState.Dirty */) {
352
+ const nextSub = link.nextSub;
353
+ link = nextSub;
354
+ continue;
355
+ }
356
+ if (state === 1 /* SignalState.MaybeDirty */) {
357
+ let dirty = consumer._dirtyDep;
358
+ const ord = link.ord;
359
+ if (dirty.ord > ord) {
360
+ consumer._dirtyDep = link;
361
+ link.nextDirty = dirty;
362
+ }
363
+ else {
364
+ let nextDirty = dirty.nextDirty;
365
+ while (nextDirty !== undefined && nextDirty.ord < ord) {
366
+ dirty = nextDirty;
367
+ nextDirty = dirty.nextDirty;
368
+ }
369
+ link.nextDirty = nextDirty;
370
+ dirty.nextDirty = link;
371
+ }
372
+ }
373
+ else {
374
+ // consumer._dirtyQueueLength = dirtyQueueLength + 2;
375
+ consumer._state = 1 /* SignalState.MaybeDirty */;
376
+ consumer._dirtyDep = link;
377
+ link.nextDirty = undefined;
378
+ consumer._dirty();
379
+ }
380
+ link = link.nextSub;
381
+ }
382
+ }
383
+ _disconnect(count = 1) {
384
+ this._connectedCount -= count;
385
+ if (this._connectedCount > 0) {
386
+ return;
387
+ }
388
+ else if (this._connectedCount < 0) {
389
+ throw new Error('Signal disconnect count cannot be negative');
390
+ }
391
+ if (this._type === 1 /* SignalType.Subscription */) {
392
+ const subscription = SUBSCRIPTIONS.get(this);
393
+ if (subscription !== undefined) {
394
+ subscription.unsubscribe?.();
395
+ SUBSCRIPTIONS.delete(this);
396
+ }
397
+ }
398
+ let link = this._deps;
399
+ while (link !== undefined) {
400
+ const dep = link.dep;
401
+ dep._disconnect();
402
+ link = link.nextDep;
403
+ }
404
+ }
405
+ }
406
+ class StateSignal {
407
+ _value;
408
+ _equals;
409
+ _consumers = [];
410
+ constructor(_value, _equals = (a, b) => a === b) {
411
+ this._value = _value;
412
+ this._equals = _equals;
413
+ }
414
+ get() {
415
+ if (CURRENT_CONSUMER !== undefined) {
416
+ this._consumers.push(CURRENT_CONSUMER._ref);
417
+ }
418
+ return this._value;
419
+ }
420
+ set(value) {
421
+ if (this._equals(value, this._value)) {
422
+ return;
423
+ }
424
+ this._value = value;
425
+ const { _consumers: consumers } = this;
426
+ for (const consumerRef of consumers) {
427
+ const consumer = consumerRef.deref();
428
+ if (consumer === undefined) {
429
+ continue;
430
+ }
431
+ consumer._state = 2 /* SignalState.Dirty */;
432
+ consumer._dirty();
433
+ }
434
+ consumers.length = 0;
435
+ }
436
+ }
437
+ export function state(initialValue, opts) {
438
+ return new StateSignal(initialValue, opts?.equals);
439
+ }
440
+ export function computed(compute, opts) {
441
+ return new ComputedSignal(0 /* SignalType.Computed */, compute, opts?.equals);
442
+ }
443
+ export function asyncComputed(compute, opts) {
444
+ return new ComputedSignal(2 /* SignalType.Async */, compute, opts?.equals, opts?.initValue);
445
+ }
446
+ export function subscription(subscribe, opts) {
447
+ return new ComputedSignal(1 /* SignalType.Subscription */, subscribe, opts?.equals, opts?.initValue);
448
+ }
449
+ export function watcher(fn) {
450
+ const watcher = new ComputedSignal(3 /* SignalType.Watcher */, fn);
451
+ scheduleWatcher(watcher);
452
+ return {
453
+ disconnect() {
454
+ scheduleDisconnect(watcher);
455
+ },
456
+ };
457
+ }
458
+ export function untrack(fn) {
459
+ const prevConsumer = CURRENT_CONSUMER;
460
+ const prevOrd = CURRENT_ORD;
461
+ const prevIsWatched = CURRENT_IS_WATCHED;
462
+ try {
463
+ CURRENT_CONSUMER = undefined;
464
+ // LAST_CONSUMED = undefined;
465
+ CURRENT_ORD = 0;
466
+ CURRENT_IS_WATCHED = false;
467
+ return fn();
468
+ }
469
+ finally {
470
+ CURRENT_CONSUMER = prevConsumer;
471
+ CURRENT_ORD = prevOrd;
472
+ CURRENT_IS_WATCHED = prevIsWatched;
473
+ }
474
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "signalium",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "repository": "https://github.com/pzuraq/signalium",
6
+ "description": "Chain-reactivity at critical mass",
7
+ "exports": {
8
+ ".": {
9
+ "import": {
10
+ "development": "./src/index.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "types": {
14
+ "development": "./src/index.ts",
15
+ "default": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "scripts": {
21
+ "dev": "vitest",
22
+ "test": "vitest run",
23
+ "build": "tsc"
24
+ },
25
+ "author": "",
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "vite": "^5.4.8",
29
+ "vitest": "^2.1.2"
30
+ }
31
+ }