signalium 0.2.1 → 0.2.2
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 +6 -0
- package/dist/signals.d.ts +1 -0
- package/dist/signals.js +49 -2
- package/package.json +1 -1
- package/src/__tests__/async.test.ts +1 -1
- package/src/signals.ts +61 -4
    
        package/CHANGELOG.md
    CHANGED
    
    
    
        package/dist/signals.d.ts
    CHANGED
    
    
    
        package/dist/signals.js
    CHANGED
    
    | @@ -10,6 +10,41 @@ const SUBSCRIPTIONS = new WeakMap(); | |
| 10 10 | 
             
            const ACTIVE_ASYNCS = new WeakMap();
         | 
| 11 11 | 
             
            const WAITING = Symbol();
         | 
| 12 12 | 
             
            let linkPool;
         | 
| 13 | 
            +
            const checkForCircularLinks = (link) => {
         | 
| 14 | 
            +
                if (!link)
         | 
| 15 | 
            +
                    return;
         | 
| 16 | 
            +
                for (const key of ['nextDep', 'nextSub', 'prevSub', 'nextDirty']) {
         | 
| 17 | 
            +
                    let currentLink = link?.[key];
         | 
| 18 | 
            +
                    while (currentLink !== undefined) {
         | 
| 19 | 
            +
                        if (currentLink === link) {
         | 
| 20 | 
            +
                            throw new Error(`Circular link detected via ${key}. This is a bug, please report it to the Signalium maintainers.`);
         | 
| 21 | 
            +
                        }
         | 
| 22 | 
            +
                        currentLink = currentLink[key];
         | 
| 23 | 
            +
                    }
         | 
| 24 | 
            +
                }
         | 
| 25 | 
            +
            };
         | 
| 26 | 
            +
            const typeToString = (type) => {
         | 
| 27 | 
            +
                switch (type) {
         | 
| 28 | 
            +
                    case 0 /* SignalType.Computed */:
         | 
| 29 | 
            +
                        return 'Computed';
         | 
| 30 | 
            +
                    case 1 /* SignalType.Subscription */:
         | 
| 31 | 
            +
                        return 'Subscription';
         | 
| 32 | 
            +
                    case 2 /* SignalType.Async */:
         | 
| 33 | 
            +
                        return 'Async';
         | 
| 34 | 
            +
                    case 3 /* SignalType.Watcher */:
         | 
| 35 | 
            +
                        return 'Watcher';
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
            };
         | 
| 38 | 
            +
            const printComputed = (computed) => {
         | 
| 39 | 
            +
                const type = typeToString(computed._type);
         | 
| 40 | 
            +
                return `ComputedSignal<${type}:${computed.id}>`;
         | 
| 41 | 
            +
            };
         | 
| 42 | 
            +
            const printLink = (link) => {
         | 
| 43 | 
            +
                const sub = link.sub.deref();
         | 
| 44 | 
            +
                const subStr = sub === undefined ? 'undefined' : printComputed(sub);
         | 
| 45 | 
            +
                const depStr = printComputed(link.dep);
         | 
| 46 | 
            +
                return `Link<${link.id}> sub(${subStr}) -> dep(${depStr})`;
         | 
| 47 | 
            +
            };
         | 
| 13 48 | 
             
            function linkNewDep(dep, sub, nextDep, depsTail, ord) {
         | 
| 14 49 | 
             
                let newLink;
         | 
| 15 50 | 
             
                if (linkPool !== undefined) {
         | 
| @@ -22,6 +57,7 @@ function linkNewDep(dep, sub, nextDep, depsTail, ord) { | |
| 22 57 | 
             
                }
         | 
| 23 58 | 
             
                else {
         | 
| 24 59 | 
             
                    newLink = {
         | 
| 60 | 
            +
                        id: id++,
         | 
| 25 61 | 
             
                        dep,
         | 
| 26 62 | 
             
                        sub: sub._ref,
         | 
| 27 63 | 
             
                        ord,
         | 
| @@ -175,6 +211,8 @@ export class ComputedSignal { | |
| 175 211 | 
             
                        }
         | 
| 176 212 | 
             
                        this._check(CURRENT_IS_WATCHED && !prevTracked);
         | 
| 177 213 | 
             
                        CURRENT_DEP_TAIL = newLink ?? linkNewDep(this, CURRENT_CONSUMER, nextDep, CURRENT_DEP_TAIL, ord);
         | 
| 214 | 
            +
                        if (process.env.NODE_ENV !== 'production')
         | 
| 215 | 
            +
                            checkForCircularLinks(CURRENT_DEP_TAIL);
         | 
| 178 216 | 
             
                        CURRENT_DEP_TAIL.version = this._version;
         | 
| 179 217 | 
             
                        CURRENT_SEEN.add(this);
         | 
| 180 218 | 
             
                    }
         | 
| @@ -197,6 +235,8 @@ export class ComputedSignal { | |
| 197 235 | 
             
                        }
         | 
| 198 236 | 
             
                        else {
         | 
| 199 237 | 
             
                            let link = this._deps;
         | 
| 238 | 
            +
                            if (process.env.NODE_ENV !== 'production')
         | 
| 239 | 
            +
                                checkForCircularLinks(link);
         | 
| 200 240 | 
             
                            while (link !== undefined) {
         | 
| 201 241 | 
             
                                const dep = link.dep;
         | 
| 202 242 | 
             
                                if (link.version !== dep._check(true)) {
         | 
| @@ -206,7 +246,6 @@ export class ComputedSignal { | |
| 206 246 | 
             
                                link = link.nextDep;
         | 
| 207 247 | 
             
                            }
         | 
| 208 248 | 
             
                        }
         | 
| 209 | 
            -
                        this._resetDirty();
         | 
| 210 249 | 
             
                    }
         | 
| 211 250 | 
             
                    if (state === 0 /* SignalState.Clean */) {
         | 
| 212 251 | 
             
                        return this._version;
         | 
| @@ -269,8 +308,8 @@ export class ComputedSignal { | |
| 269 308 | 
             
                                        value.isPending = false;
         | 
| 270 309 | 
             
                                        value.isError = true;
         | 
| 271 310 | 
             
                                        this._version++;
         | 
| 311 | 
            +
                                        break;
         | 
| 272 312 | 
             
                                    }
         | 
| 273 | 
            -
                                    break;
         | 
| 274 313 | 
             
                                }
         | 
| 275 314 | 
             
                                if (CURRENT_IS_WAITING) {
         | 
| 276 315 | 
             
                                    if (!value.isPending) {
         | 
| @@ -372,13 +411,19 @@ export class ComputedSignal { | |
| 372 411 | 
             
                        }
         | 
| 373 412 | 
             
                        else {
         | 
| 374 413 | 
             
                            dirty.nextSub = oldHead;
         | 
| 414 | 
            +
                            dirty.prevSub = undefined;
         | 
| 375 415 | 
             
                            oldHead.prevSub = dirty;
         | 
| 376 416 | 
             
                            dep._subs = dirty;
         | 
| 377 417 | 
             
                        }
         | 
| 418 | 
            +
                        if (process.env.NODE_ENV !== 'production') {
         | 
| 419 | 
            +
                            checkForCircularLinks(this._dirtyDep);
         | 
| 420 | 
            +
                        }
         | 
| 378 421 | 
             
                        let nextDirty = dirty.nextDirty;
         | 
| 379 422 | 
             
                        dirty.nextDirty = undefined;
         | 
| 380 423 | 
             
                        dirty = nextDirty;
         | 
| 381 424 | 
             
                    }
         | 
| 425 | 
            +
                    if (process.env.NODE_ENV !== 'production')
         | 
| 426 | 
            +
                        checkForCircularLinks(this._dirtyDep);
         | 
| 382 427 | 
             
                }
         | 
| 383 428 | 
             
                _dirty() {
         | 
| 384 429 | 
             
                    if (this._type === 1 /* SignalType.Subscription */) {
         | 
| @@ -397,6 +442,8 @@ export class ComputedSignal { | |
| 397 442 | 
             
                }
         | 
| 398 443 | 
             
                _dirtyConsumers() {
         | 
| 399 444 | 
             
                    let link = this._subs;
         | 
| 445 | 
            +
                    if (process.env.NODE_ENV !== 'production')
         | 
| 446 | 
            +
                        checkForCircularLinks(link);
         | 
| 400 447 | 
             
                    while (link !== undefined) {
         | 
| 401 448 | 
             
                        const consumer = link.sub.deref();
         | 
| 402 449 | 
             
                        if (consumer === undefined) {
         | 
    
        package/package.json
    CHANGED
    
    
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            import { describe, expect, test } from 'vitest';
         | 
| 2 | 
            -
            import { state,  | 
| 2 | 
            +
            import { state, asyncComputed } from './utils/instrumented.js';
         | 
| 3 3 | 
             
            import { AsyncResult } from '../signals';
         | 
| 4 4 |  | 
| 5 5 | 
             
            const sleep = (ms = 0) => new Promise(r => setTimeout(r, ms));
         | 
    
        package/src/signals.ts
    CHANGED
    
    | @@ -61,6 +61,7 @@ const enum SignalState { | |
| 61 61 | 
             
            const WAITING = Symbol();
         | 
| 62 62 |  | 
| 63 63 | 
             
            interface Link {
         | 
| 64 | 
            +
              id: number;
         | 
| 64 65 | 
             
              sub: WeakRef<ComputedSignal<any>>;
         | 
| 65 66 | 
             
              dep: ComputedSignal<any>;
         | 
| 66 67 | 
             
              ord: number;
         | 
| @@ -75,6 +76,51 @@ interface Link { | |
| 75 76 |  | 
| 76 77 | 
             
            let linkPool: Link | undefined;
         | 
| 77 78 |  | 
| 79 | 
            +
            const checkForCircularLinks = (link: Link | undefined) => {
         | 
| 80 | 
            +
              if (!link) return;
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              for (const key of ['nextDep', 'nextSub', 'prevSub', 'nextDirty'] as const) {
         | 
| 83 | 
            +
                let currentLink: Link | undefined = link?.[key];
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                while (currentLink !== undefined) {
         | 
| 86 | 
            +
                  if (currentLink === link) {
         | 
| 87 | 
            +
                    throw new Error(
         | 
| 88 | 
            +
                      `Circular link detected via ${key}. This is a bug, please report it to the Signalium maintainers.`,
         | 
| 89 | 
            +
                    );
         | 
| 90 | 
            +
                  }
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  currentLink = currentLink[key];
         | 
| 93 | 
            +
                }
         | 
| 94 | 
            +
              }
         | 
| 95 | 
            +
            };
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            const typeToString = (type: SignalType) => {
         | 
| 98 | 
            +
              switch (type) {
         | 
| 99 | 
            +
                case SignalType.Computed:
         | 
| 100 | 
            +
                  return 'Computed';
         | 
| 101 | 
            +
                case SignalType.Subscription:
         | 
| 102 | 
            +
                  return 'Subscription';
         | 
| 103 | 
            +
                case SignalType.Async:
         | 
| 104 | 
            +
                  return 'Async';
         | 
| 105 | 
            +
                case SignalType.Watcher:
         | 
| 106 | 
            +
                  return 'Watcher';
         | 
| 107 | 
            +
              }
         | 
| 108 | 
            +
            };
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            const printComputed = (computed: ComputedSignal<any>) => {
         | 
| 111 | 
            +
              const type = typeToString(computed._type);
         | 
| 112 | 
            +
             | 
| 113 | 
            +
              return `ComputedSignal<${type}:${computed.id}>`;
         | 
| 114 | 
            +
            };
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            const printLink = (link: Link) => {
         | 
| 117 | 
            +
              const sub = link.sub.deref();
         | 
| 118 | 
            +
              const subStr = sub === undefined ? 'undefined' : printComputed(sub);
         | 
| 119 | 
            +
              const depStr = printComputed(link.dep);
         | 
| 120 | 
            +
             | 
| 121 | 
            +
              return `Link<${link.id}> sub(${subStr}) -> dep(${depStr})`;
         | 
| 122 | 
            +
            };
         | 
| 123 | 
            +
             | 
| 78 124 | 
             
            function linkNewDep(
         | 
| 79 125 | 
             
              dep: ComputedSignal<any>,
         | 
| 80 126 | 
             
              sub: ComputedSignal<any>,
         | 
| @@ -93,6 +139,7 @@ function linkNewDep( | |
| 93 139 | 
             
                newLink.ord = ord;
         | 
| 94 140 | 
             
              } else {
         | 
| 95 141 | 
             
                newLink = {
         | 
| 142 | 
            +
                  id: id++,
         | 
| 96 143 | 
             
                  dep,
         | 
| 97 144 | 
             
                  sub: sub._ref,
         | 
| 98 145 | 
             
                  ord,
         | 
| @@ -288,6 +335,8 @@ export class ComputedSignal<T> { | |
| 288 335 |  | 
| 289 336 | 
             
                  CURRENT_DEP_TAIL = newLink ?? linkNewDep(this, CURRENT_CONSUMER, nextDep, CURRENT_DEP_TAIL, ord);
         | 
| 290 337 |  | 
| 338 | 
            +
                  if (process.env.NODE_ENV !== 'production') checkForCircularLinks(CURRENT_DEP_TAIL);
         | 
| 339 | 
            +
             | 
| 291 340 | 
             
                  CURRENT_DEP_TAIL.version = this._version;
         | 
| 292 341 | 
             
                  CURRENT_SEEN!.add(this);
         | 
| 293 342 | 
             
                } else {
         | 
| @@ -314,6 +363,8 @@ export class ComputedSignal<T> { | |
| 314 363 | 
             
                  } else {
         | 
| 315 364 | 
             
                    let link = this._deps;
         | 
| 316 365 |  | 
| 366 | 
            +
                    if (process.env.NODE_ENV !== 'production') checkForCircularLinks(link);
         | 
| 367 | 
            +
             | 
| 317 368 | 
             
                    while (link !== undefined) {
         | 
| 318 369 | 
             
                      const dep = link.dep;
         | 
| 319 370 |  | 
| @@ -325,8 +376,6 @@ export class ComputedSignal<T> { | |
| 325 376 | 
             
                      link = link.nextDep;
         | 
| 326 377 | 
             
                    }
         | 
| 327 378 | 
             
                  }
         | 
| 328 | 
            -
             | 
| 329 | 
            -
                  this._resetDirty();
         | 
| 330 379 | 
             
                }
         | 
| 331 380 |  | 
| 332 381 | 
             
                if (state === SignalState.Clean) {
         | 
| @@ -403,9 +452,8 @@ export class ComputedSignal<T> { | |
| 403 452 | 
             
                          value.isPending = false;
         | 
| 404 453 | 
             
                          value.isError = true;
         | 
| 405 454 | 
             
                          this._version++;
         | 
| 455 | 
            +
                          break;
         | 
| 406 456 | 
             
                        }
         | 
| 407 | 
            -
             | 
| 408 | 
            -
                        break;
         | 
| 409 457 | 
             
                      }
         | 
| 410 458 |  | 
| 411 459 | 
             
                      if (CURRENT_IS_WAITING) {
         | 
| @@ -527,14 +575,21 @@ export class ComputedSignal<T> { | |
| 527 575 | 
             
                    dirty.prevSub = undefined;
         | 
| 528 576 | 
             
                  } else {
         | 
| 529 577 | 
             
                    dirty.nextSub = oldHead;
         | 
| 578 | 
            +
                    dirty.prevSub = undefined;
         | 
| 530 579 | 
             
                    oldHead.prevSub = dirty;
         | 
| 531 580 | 
             
                    dep._subs = dirty;
         | 
| 532 581 | 
             
                  }
         | 
| 533 582 |  | 
| 583 | 
            +
                  if (process.env.NODE_ENV !== 'production') {
         | 
| 584 | 
            +
                    checkForCircularLinks(this._dirtyDep);
         | 
| 585 | 
            +
                  }
         | 
| 586 | 
            +
             | 
| 534 587 | 
             
                  let nextDirty = dirty.nextDirty;
         | 
| 535 588 | 
             
                  dirty.nextDirty = undefined;
         | 
| 536 589 | 
             
                  dirty = nextDirty;
         | 
| 537 590 | 
             
                }
         | 
| 591 | 
            +
             | 
| 592 | 
            +
                if (process.env.NODE_ENV !== 'production') checkForCircularLinks(this._dirtyDep);
         | 
| 538 593 | 
             
              }
         | 
| 539 594 |  | 
| 540 595 | 
             
              _dirty() {
         | 
| @@ -556,6 +611,8 @@ export class ComputedSignal<T> { | |
| 556 611 | 
             
              _dirtyConsumers() {
         | 
| 557 612 | 
             
                let link = this._subs;
         | 
| 558 613 |  | 
| 614 | 
            +
                if (process.env.NODE_ENV !== 'production') checkForCircularLinks(link);
         | 
| 615 | 
            +
             | 
| 559 616 | 
             
                while (link !== undefined) {
         | 
| 560 617 | 
             
                  const consumer = link.sub.deref();
         | 
| 561 618 |  |