All files / src utils.ts

100% Statements 52/52
100% Branches 29/29
100% Functions 6/6
100% Lines 52/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111    2x     2x                   2x 69x 69x 69x 69x 63x 1x 62x 62x 87x 87x 62x 63x 69x         2x                     2x 6x 1x 1x 6x 1x 1x 4x 6x 1x 1x 6x 9x 2x 2x 9x 1x 1x                         2x                         2x             2x 120x 120x 137x 137x 137x 1x 1x 137x 120x 1x 1x 120x   2x 473x  
import { type Writable, type Readable, type ReadableLike } from "./typings";
 
export const BRAND = /* @__PURE__ */ Symbol.for("@embra/reactivity");
export type BRAND = typeof BRAND;
 
export const UNIQUE_VALUE: unique symbol = /* @__PURE__ */ Symbol();
 
export type UNIQUE_VALUE = typeof UNIQUE_VALUE;
 
/**
 * Remove the given subscriber.
 * Remove all if no subscriber provided.
 * @param $
 * @param subscriber
 */
export const unsubscribe = (
  $: Iterable<Readable> | Readable | null | undefined,
  subscriber?: (...args: any[]) => any,
): void => {
  if ($) {
    if (isReadable($)) {
      $.unsubscribe(subscriber);
    } else {
      for (const v of $) {
        v.unsubscribe(subscriber);
      }
    }
  }
};
 
/**
 * `Object.is`
 */
export const strictEqual = Object.is;
 
/**
 * Shallow compare two arrays.
 * @param arrA - any value
 * @param arrB - any value
 * @returns `false` if any of:
 *          1. one of arrA or arrB is an array and the other is not
 *          2. arrA and arrB have different lengths
 *          3. arrA and arrB have different values at any index
 */
export const arrayShallowEqual = (arrA: any, arrB: any): boolean => {
  if (strictEqual(arrA, arrB)) {
    return true;
  }
  if (!Array.isArray(arrA) || !Array.isArray(arrB)) {
    return false;
  }
  const len = arrA.length;
  if (arrB.length !== len) {
    return false;
  }
  for (let i = 0; i < len; i++) {
    if (!strictEqual(arrA[i], arrB[i])) {
      return false;
    }
  }
  return true;
};
 
export interface IsReadable {
  ($: unknown): $ is Readable;
  ($: any): $ is Readable;
  <T extends Readable>($: T): $ is T extends Readable ? T : never;
}
 
/**
 * Checks if $ is is a Readable. A Writable is also a Readable.
 *
 * @returns `true` if $ is Readable.
 */
export const isReadable: IsReadable = ($: unknown): $ is Readable => ($ as Readable | undefined)?.[BRAND] === BRAND;
 
export interface IsWritable {
  ($: unknown): $ is Writable;
  ($: any): $ is Writable;
  <T extends Writable>($: T): $ is T extends Writable ? T : never;
}
 
/**
 * Checks if $ is is a Writable.
 *
 * @returns `true` if $ is Readable.
 */
export const isWritable: IsWritable = ($: unknown): $ is Writable => isReadable($) && !!($ as Writable).set;
 
interface InvokeEach {
  (iterable: Iterable<() => any>): void;
  <T>(iterable: Iterable<(value: T) => any>, value: T): void;
}
 
export const invokeEach: InvokeEach = <T>(iterable: Iterable<(value?: T) => any>, value?: T) => {
  let error: unknown = UNIQUE_VALUE;
  for (const fn of iterable) {
    try {
      fn(value);
    } catch (e) {
      error = e;
    }
  }
  if (error !== UNIQUE_VALUE) {
    throw error;
  }
};
 
export const getReadable = <T = any>($: ReadableLike<T> | any): Readable<T> | undefined =>
  isReadable($) ? $ : isReadable($?.$) ? $.$ : undefined;