@rlabs-inc/signals 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/dist/index.js ADDED
@@ -0,0 +1,707 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
6
+ var __toCommonJS = (from) => {
7
+ var entry = __moduleCache.get(from), desc;
8
+ if (entry)
9
+ return entry;
10
+ entry = __defProp({}, "__esModule", { value: true });
11
+ if (from && typeof from === "object" || typeof from === "function")
12
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
+ get: () => from[key],
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ }));
16
+ __moduleCache.set(from, entry);
17
+ return entry;
18
+ };
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true,
24
+ configurable: true,
25
+ set: (newValue) => all[name] = () => newValue
26
+ });
27
+ };
28
+
29
+ // src/index.ts
30
+ var exports_src = {};
31
+ __export(exports_src, {
32
+ watch: () => watch,
33
+ untrack: () => untrack,
34
+ toRaw: () => toRaw,
35
+ state: () => state,
36
+ signal: () => signal,
37
+ shallowEquals: () => shallowEquals,
38
+ readonly: () => readonly,
39
+ reactiveArray: () => reactiveArray,
40
+ peek: () => peek,
41
+ isReactive: () => isReactive,
42
+ effectScope: () => effectScope,
43
+ effect: () => effect,
44
+ derived: () => derived,
45
+ defaultEquals: () => defaultEquals,
46
+ batch: () => batch,
47
+ ReactiveSet: () => ReactiveSet,
48
+ ReactiveMap: () => ReactiveMap
49
+ });
50
+ module.exports = __toCommonJS(exports_src);
51
+ var activeReaction = null;
52
+ var reactionStack = [];
53
+ var batchDepth = 0;
54
+ var pendingReactions = new Set;
55
+ var untracking = false;
56
+ var proxyToSignal = new WeakMap;
57
+ var rawToProxy = new WeakMap;
58
+ var defaultEquals = (a, b) => Object.is(a, b);
59
+ var shallowEquals = (a, b) => {
60
+ if (Object.is(a, b))
61
+ return true;
62
+ if (typeof a !== "object" || typeof b !== "object")
63
+ return false;
64
+ if (a === null || b === null)
65
+ return false;
66
+ const keysA = Object.keys(a);
67
+ const keysB = Object.keys(b);
68
+ if (keysA.length !== keysB.length)
69
+ return false;
70
+ for (const key of keysA) {
71
+ if (!Object.is(a[key], b[key]))
72
+ return false;
73
+ }
74
+ return true;
75
+ };
76
+ function signal(initial, options) {
77
+ const internal = {
78
+ v: initial,
79
+ reactions: new Set,
80
+ equals: options?.equals ?? defaultEquals
81
+ };
82
+ return {
83
+ get value() {
84
+ track(internal);
85
+ return internal.v;
86
+ },
87
+ set value(newValue) {
88
+ if (!internal.equals(internal.v, newValue)) {
89
+ internal.v = newValue;
90
+ trigger(internal);
91
+ }
92
+ }
93
+ };
94
+ }
95
+ function effect(fn) {
96
+ const reaction = {
97
+ execute: () => {
98
+ if (reaction.cleanup) {
99
+ reaction.cleanup();
100
+ reaction.cleanup = undefined;
101
+ }
102
+ for (const dep of reaction.deps) {
103
+ dep.reactions.delete(reaction);
104
+ }
105
+ reaction.deps.clear();
106
+ reactionStack.push(reaction);
107
+ const prevReaction = activeReaction;
108
+ activeReaction = reaction;
109
+ try {
110
+ const cleanup = fn();
111
+ if (typeof cleanup === "function") {
112
+ reaction.cleanup = cleanup;
113
+ }
114
+ } finally {
115
+ activeReaction = prevReaction;
116
+ reactionStack.pop();
117
+ }
118
+ },
119
+ deps: new Set,
120
+ active: true
121
+ };
122
+ reaction.execute();
123
+ return () => {
124
+ if (!reaction.active)
125
+ return;
126
+ reaction.active = false;
127
+ if (reaction.cleanup) {
128
+ reaction.cleanup();
129
+ }
130
+ for (const dep of reaction.deps) {
131
+ dep.reactions.delete(reaction);
132
+ }
133
+ reaction.deps.clear();
134
+ };
135
+ }
136
+ function derived(fn, options) {
137
+ const internal = {
138
+ v: undefined,
139
+ reactions: new Set,
140
+ equals: options?.equals ?? defaultEquals,
141
+ fn,
142
+ dirty: true
143
+ };
144
+ const deps = new Set;
145
+ const markDirty = () => {
146
+ if (!internal.dirty) {
147
+ internal.dirty = true;
148
+ trigger(internal);
149
+ }
150
+ };
151
+ const recompute = () => {
152
+ for (const dep of deps) {
153
+ dep.reactions.delete(derivedReaction);
154
+ }
155
+ deps.clear();
156
+ const prevReaction = activeReaction;
157
+ activeReaction = derivedReaction;
158
+ try {
159
+ const newValue = fn();
160
+ internal.v = newValue;
161
+ } finally {
162
+ activeReaction = prevReaction;
163
+ }
164
+ internal.dirty = false;
165
+ };
166
+ const derivedReaction = {
167
+ execute: markDirty,
168
+ deps,
169
+ active: true
170
+ };
171
+ return {
172
+ get value() {
173
+ track(internal);
174
+ if (internal.dirty) {
175
+ recompute();
176
+ }
177
+ return internal.v;
178
+ }
179
+ };
180
+ }
181
+ derived.by = derived;
182
+ function batch(fn) {
183
+ batchDepth++;
184
+ try {
185
+ return fn();
186
+ } finally {
187
+ batchDepth--;
188
+ if (batchDepth === 0) {
189
+ flushPending();
190
+ }
191
+ }
192
+ }
193
+ function untrack(fn) {
194
+ const prev = untracking;
195
+ untracking = true;
196
+ try {
197
+ return fn();
198
+ } finally {
199
+ untracking = prev;
200
+ }
201
+ }
202
+ function track(signal2) {
203
+ if (activeReaction && !untracking) {
204
+ activeReaction.deps.add(signal2);
205
+ signal2.reactions.add(activeReaction);
206
+ }
207
+ }
208
+ function trigger(signal2) {
209
+ const reactions = [...signal2.reactions];
210
+ for (const reaction of reactions) {
211
+ if (!reaction.active)
212
+ continue;
213
+ if ("dirty" in reaction) {
214
+ reaction.dirty = true;
215
+ }
216
+ if (batchDepth > 0) {
217
+ pendingReactions.add(reaction);
218
+ } else {
219
+ reaction.execute();
220
+ }
221
+ }
222
+ }
223
+ function flushPending() {
224
+ const reactions = [...pendingReactions];
225
+ pendingReactions.clear();
226
+ for (const reaction of reactions) {
227
+ if (reaction.active) {
228
+ reaction.execute();
229
+ }
230
+ }
231
+ }
232
+ var REACTIVE_MARKER = Symbol("reactive");
233
+ function state(initial) {
234
+ return createDeepReactive(initial);
235
+ }
236
+ function shouldProxy(value) {
237
+ if (value === null || typeof value !== "object")
238
+ return false;
239
+ if (value[REACTIVE_MARKER])
240
+ return false;
241
+ const proto = Object.getPrototypeOf(value);
242
+ return proto === Object.prototype || proto === Array.prototype || proto === null;
243
+ }
244
+ function createDeepReactive(target) {
245
+ const existing = rawToProxy.get(target);
246
+ if (existing)
247
+ return existing;
248
+ const internal = {
249
+ v: target,
250
+ reactions: new Set,
251
+ equals: defaultEquals
252
+ };
253
+ const propSignals = new Map;
254
+ const getPropSignal = (prop) => {
255
+ let sig = propSignals.get(prop);
256
+ if (!sig) {
257
+ sig = {
258
+ v: target[prop],
259
+ reactions: new Set,
260
+ equals: defaultEquals
261
+ };
262
+ propSignals.set(prop, sig);
263
+ }
264
+ return sig;
265
+ };
266
+ const proxy = new Proxy(target, {
267
+ get(target2, prop, receiver) {
268
+ if (prop === REACTIVE_MARKER)
269
+ return true;
270
+ const value = Reflect.get(target2, prop, receiver);
271
+ if (Array.isArray(target2) && typeof value === "function") {
272
+ track(internal);
273
+ return value.bind(proxy);
274
+ }
275
+ const sig = getPropSignal(prop);
276
+ track(sig);
277
+ if (shouldProxy(value)) {
278
+ const existingProxy = rawToProxy.get(value);
279
+ if (existingProxy)
280
+ return existingProxy;
281
+ return createDeepReactive(value);
282
+ }
283
+ return value;
284
+ },
285
+ set(target2, prop, value, receiver) {
286
+ const oldValue = target2[prop];
287
+ const rawValue = value?.[REACTIVE_MARKER] ? proxyToRaw.get(value) ?? value : value;
288
+ if (Object.is(oldValue, rawValue))
289
+ return true;
290
+ const result = Reflect.set(target2, prop, rawValue, receiver);
291
+ if (result) {
292
+ const sig = getPropSignal(prop);
293
+ sig.v = rawValue;
294
+ trigger(sig);
295
+ if (Array.isArray(target2)) {
296
+ internal.v = target2;
297
+ trigger(internal);
298
+ }
299
+ }
300
+ return result;
301
+ },
302
+ deleteProperty(target2, prop) {
303
+ const hadKey = prop in target2;
304
+ const result = Reflect.deleteProperty(target2, prop);
305
+ if (result && hadKey) {
306
+ const sig = propSignals.get(prop);
307
+ if (sig) {
308
+ sig.v = undefined;
309
+ trigger(sig);
310
+ }
311
+ trigger(internal);
312
+ }
313
+ return result;
314
+ },
315
+ has(target2, prop) {
316
+ if (prop === REACTIVE_MARKER)
317
+ return true;
318
+ track(internal);
319
+ return Reflect.has(target2, prop);
320
+ },
321
+ ownKeys(target2) {
322
+ track(internal);
323
+ return Reflect.ownKeys(target2);
324
+ }
325
+ });
326
+ rawToProxy.set(target, proxy);
327
+ proxyToRaw.set(proxy, target);
328
+ proxyToSignal.set(proxy, internal);
329
+ return proxy;
330
+ }
331
+ var proxyToRaw = new WeakMap;
332
+ function toRaw(proxy) {
333
+ if (proxy && typeof proxy === "object" && proxy[REACTIVE_MARKER]) {
334
+ return proxyToRaw.get(proxy) ?? proxy;
335
+ }
336
+ return proxy;
337
+ }
338
+ function isReactive(value) {
339
+ return value !== null && typeof value === "object" && value[REACTIVE_MARKER] === true;
340
+ }
341
+
342
+ class ReactiveMap extends Map {
343
+ #keySignals = new Map;
344
+ #version = { v: 0, reactions: new Set, equals: defaultEquals };
345
+ #size = { v: 0, reactions: new Set, equals: defaultEquals };
346
+ constructor(entries) {
347
+ super();
348
+ if (entries) {
349
+ for (const [key, value] of entries) {
350
+ super.set(key, value);
351
+ }
352
+ this.#size.v = super.size;
353
+ }
354
+ }
355
+ #getKeySignal(key) {
356
+ let sig = this.#keySignals.get(key);
357
+ if (!sig) {
358
+ sig = { v: 0, reactions: new Set, equals: defaultEquals };
359
+ this.#keySignals.set(key, sig);
360
+ }
361
+ return sig;
362
+ }
363
+ get size() {
364
+ track(this.#size);
365
+ return super.size;
366
+ }
367
+ has(key) {
368
+ const sig = this.#getKeySignal(key);
369
+ track(sig);
370
+ return super.has(key);
371
+ }
372
+ get(key) {
373
+ const sig = this.#getKeySignal(key);
374
+ track(sig);
375
+ return super.get(key);
376
+ }
377
+ set(key, value) {
378
+ const isNew = !super.has(key);
379
+ const oldValue = super.get(key);
380
+ super.set(key, value);
381
+ const sig = this.#getKeySignal(key);
382
+ if (isNew) {
383
+ this.#size.v = super.size;
384
+ trigger(this.#size);
385
+ this.#version.v++;
386
+ trigger(this.#version);
387
+ sig.v++;
388
+ trigger(sig);
389
+ } else if (!Object.is(oldValue, value)) {
390
+ sig.v++;
391
+ trigger(sig);
392
+ }
393
+ return this;
394
+ }
395
+ delete(key) {
396
+ const had = super.has(key);
397
+ const result = super.delete(key);
398
+ if (had) {
399
+ const sig = this.#keySignals.get(key);
400
+ if (sig) {
401
+ sig.v = -1;
402
+ trigger(sig);
403
+ this.#keySignals.delete(key);
404
+ }
405
+ this.#size.v = super.size;
406
+ trigger(this.#size);
407
+ this.#version.v++;
408
+ trigger(this.#version);
409
+ }
410
+ return result;
411
+ }
412
+ clear() {
413
+ if (super.size === 0)
414
+ return;
415
+ for (const sig of this.#keySignals.values()) {
416
+ sig.v = -1;
417
+ trigger(sig);
418
+ }
419
+ super.clear();
420
+ this.#keySignals.clear();
421
+ this.#size.v = 0;
422
+ trigger(this.#size);
423
+ this.#version.v++;
424
+ trigger(this.#version);
425
+ }
426
+ forEach(callbackfn, thisArg) {
427
+ track(this.#version);
428
+ super.forEach(callbackfn, thisArg);
429
+ }
430
+ keys() {
431
+ track(this.#version);
432
+ return super.keys();
433
+ }
434
+ values() {
435
+ track(this.#version);
436
+ return super.values();
437
+ }
438
+ entries() {
439
+ track(this.#version);
440
+ return super.entries();
441
+ }
442
+ [Symbol.iterator]() {
443
+ return this.entries();
444
+ }
445
+ }
446
+
447
+ class ReactiveSet extends Set {
448
+ #itemSignals = new Map;
449
+ #version = { v: 0, reactions: new Set, equals: defaultEquals };
450
+ #size = { v: 0, reactions: new Set, equals: defaultEquals };
451
+ constructor(values) {
452
+ super();
453
+ if (values) {
454
+ for (const value of values) {
455
+ super.add(value);
456
+ }
457
+ this.#size.v = super.size;
458
+ }
459
+ }
460
+ #getItemSignal(item) {
461
+ let sig = this.#itemSignals.get(item);
462
+ if (!sig) {
463
+ sig = { v: super.has(item), reactions: new Set, equals: defaultEquals };
464
+ this.#itemSignals.set(item, sig);
465
+ }
466
+ return sig;
467
+ }
468
+ get size() {
469
+ track(this.#size);
470
+ return super.size;
471
+ }
472
+ has(value) {
473
+ const sig = this.#getItemSignal(value);
474
+ track(sig);
475
+ return super.has(value);
476
+ }
477
+ add(value) {
478
+ if (!super.has(value)) {
479
+ super.add(value);
480
+ const sig = this.#getItemSignal(value);
481
+ sig.v = true;
482
+ trigger(sig);
483
+ this.#size.v = super.size;
484
+ trigger(this.#size);
485
+ this.#version.v++;
486
+ trigger(this.#version);
487
+ }
488
+ return this;
489
+ }
490
+ delete(value) {
491
+ const had = super.has(value);
492
+ const result = super.delete(value);
493
+ if (had) {
494
+ const sig = this.#itemSignals.get(value);
495
+ if (sig) {
496
+ sig.v = false;
497
+ trigger(sig);
498
+ this.#itemSignals.delete(value);
499
+ }
500
+ this.#size.v = super.size;
501
+ trigger(this.#size);
502
+ this.#version.v++;
503
+ trigger(this.#version);
504
+ }
505
+ return result;
506
+ }
507
+ clear() {
508
+ if (super.size === 0)
509
+ return;
510
+ for (const sig of this.#itemSignals.values()) {
511
+ sig.v = false;
512
+ trigger(sig);
513
+ }
514
+ super.clear();
515
+ this.#itemSignals.clear();
516
+ this.#size.v = 0;
517
+ trigger(this.#size);
518
+ this.#version.v++;
519
+ trigger(this.#version);
520
+ }
521
+ forEach(callbackfn, thisArg) {
522
+ track(this.#version);
523
+ super.forEach(callbackfn, thisArg);
524
+ }
525
+ keys() {
526
+ track(this.#version);
527
+ return super.keys();
528
+ }
529
+ values() {
530
+ track(this.#version);
531
+ return super.values();
532
+ }
533
+ entries() {
534
+ track(this.#version);
535
+ return super.entries();
536
+ }
537
+ [Symbol.iterator]() {
538
+ return this.values();
539
+ }
540
+ }
541
+ function reactiveArray(initial = []) {
542
+ const internal = {
543
+ v: 0,
544
+ reactions: new Set,
545
+ equals: defaultEquals
546
+ };
547
+ const indexSignals = new Map;
548
+ const lengthSignal = {
549
+ v: initial.length,
550
+ reactions: new Set,
551
+ equals: defaultEquals
552
+ };
553
+ const getIndexSignal = (index) => {
554
+ let sig = indexSignals.get(index);
555
+ if (!sig) {
556
+ sig = { v: initial[index], reactions: new Set, equals: defaultEquals };
557
+ indexSignals.set(index, sig);
558
+ }
559
+ return sig;
560
+ };
561
+ const array = [...initial];
562
+ const proxy = new Proxy(array, {
563
+ get(target, prop, receiver) {
564
+ if (prop === "length") {
565
+ track(lengthSignal);
566
+ return target.length;
567
+ }
568
+ const index = typeof prop === "string" ? parseInt(prop, 10) : NaN;
569
+ if (!isNaN(index)) {
570
+ const sig = getIndexSignal(index);
571
+ track(sig);
572
+ return target[index];
573
+ }
574
+ const value = Reflect.get(target, prop, receiver);
575
+ if (typeof value === "function") {
576
+ return function(...args) {
577
+ const method = prop;
578
+ if (["indexOf", "includes", "find", "findIndex", "some", "every", "forEach", "map", "filter", "reduce", "reduceRight", "join", "toString"].includes(method)) {
579
+ track(internal);
580
+ return value.apply(target, args);
581
+ }
582
+ const prevLength = target.length;
583
+ const result = value.apply(target, args);
584
+ const newLength = target.length;
585
+ if (newLength !== prevLength) {
586
+ lengthSignal.v = newLength;
587
+ trigger(lengthSignal);
588
+ }
589
+ if (["push", "pop", "shift", "unshift", "splice", "sort", "reverse", "fill", "copyWithin"].includes(method)) {
590
+ internal.v++;
591
+ trigger(internal);
592
+ for (let i = 0;i < Math.max(prevLength, newLength); i++) {
593
+ const sig = indexSignals.get(i);
594
+ if (sig && !Object.is(sig.v, target[i])) {
595
+ sig.v = target[i];
596
+ trigger(sig);
597
+ }
598
+ }
599
+ }
600
+ return result;
601
+ };
602
+ }
603
+ return value;
604
+ },
605
+ set(target, prop, value, receiver) {
606
+ if (prop === "length") {
607
+ const oldLength = target.length;
608
+ target.length = value;
609
+ if (oldLength !== value) {
610
+ lengthSignal.v = value;
611
+ trigger(lengthSignal);
612
+ internal.v++;
613
+ trigger(internal);
614
+ }
615
+ return true;
616
+ }
617
+ const index = typeof prop === "string" ? parseInt(prop, 10) : NaN;
618
+ if (!isNaN(index)) {
619
+ const oldValue = target[index];
620
+ if (!Object.is(oldValue, value)) {
621
+ target[index] = value;
622
+ const sig = getIndexSignal(index);
623
+ sig.v = value;
624
+ trigger(sig);
625
+ if (index >= lengthSignal.v) {
626
+ lengthSignal.v = target.length;
627
+ trigger(lengthSignal);
628
+ }
629
+ internal.v++;
630
+ trigger(internal);
631
+ }
632
+ return true;
633
+ }
634
+ return Reflect.set(target, prop, value, receiver);
635
+ }
636
+ });
637
+ return proxy;
638
+ }
639
+ function effectScope() {
640
+ const disposers = [];
641
+ let active = true;
642
+ return {
643
+ run(fn) {
644
+ if (!active)
645
+ return;
646
+ const originalEffect = effect;
647
+ globalThis.__signalEffect = effect;
648
+ const wrappedEffect = (effectFn) => {
649
+ const dispose = originalEffect(effectFn);
650
+ disposers.push(dispose);
651
+ return dispose;
652
+ };
653
+ const prevEffect = globalThis.__effectOverride;
654
+ globalThis.__effectOverride = wrappedEffect;
655
+ try {
656
+ return fn();
657
+ } finally {
658
+ globalThis.__effectOverride = prevEffect;
659
+ }
660
+ },
661
+ stop() {
662
+ if (!active)
663
+ return;
664
+ active = false;
665
+ for (const dispose of disposers) {
666
+ dispose();
667
+ }
668
+ disposers.length = 0;
669
+ },
670
+ get active() {
671
+ return active;
672
+ }
673
+ };
674
+ }
675
+ function watch(source, callback, options) {
676
+ let oldValue;
677
+ let cleanup;
678
+ let first = true;
679
+ return effect(() => {
680
+ const newValue = source();
681
+ if (first) {
682
+ first = false;
683
+ oldValue = newValue;
684
+ if (options?.immediate) {
685
+ cleanup = callback(newValue, undefined);
686
+ }
687
+ return;
688
+ }
689
+ if (!Object.is(newValue, oldValue)) {
690
+ if (typeof cleanup === "function") {
691
+ cleanup();
692
+ }
693
+ cleanup = callback(newValue, oldValue);
694
+ oldValue = newValue;
695
+ }
696
+ });
697
+ }
698
+ function readonly(sig) {
699
+ return {
700
+ get value() {
701
+ return sig.value;
702
+ }
703
+ };
704
+ }
705
+ function peek(fn) {
706
+ return untrack(fn);
707
+ }