code:
/**
 * This file provides type definitions for use with the Flow type checker.
 *
 * An important caveat when using these definitions is that the types for
 * `Collection.Keyed`, `Collection.Indexed`, `Seq.Keyed`, and so on are stubs.
 * When referring to those types, you can get the proper definitions by
 * importing the types `KeyedCollection`, `IndexedCollection`, `KeyedSeq`, etc.
 * For example,
 *
 *     import { Seq } from 'immutable'
 *     import type { IndexedCollection, IndexedSeq } from 'immutable'
 *
 *     const someSeq: IndexedSeq<number> = Seq.Indexed.of(1, 2, 3)
 *
 *     function takesASeq<T, TS: IndexedCollection<T>>(iter: TS): TS {
 *       return iter.butLast()
 *     }
 *
 *     takesASeq(someSeq)
 *
 * @flow strict
 */

// Helper type that represents plain objects allowed as arguments to
// some constructors and functions.
type PlainObjInput<K, V> = { +[key: K]: V, __proto__: null };

type K<T> = $Keys<T>;

// Helper types to extract the "keys" and "values" use by the *In() methods.
type $KeyOf<C> = $Call<
  (<K>(?_Collection<K, mixed>) => K) &
    (<T>(?$ReadOnlyArray<T>) => number) &
    (<T>(?RecordInstance<T> | T) => $Keys<T>) &
    (<T: Object>(T) => $Keys<T>),
  C
>;

type $ValOf<C, K = $KeyOf<C>> = $Call<
  (<V>(?_Collection<any, V>) => V) &
    (<T>(?$ReadOnlyArray<T>) => T) &
    (<T, K: $Keys<T>>(?RecordInstance<T> | T, K) => $ElementType<T, K>) &
    (<T: Object>(T) => $Values<T>),
  C,
  K
>;

type $IterableOf<C> = $Call<
  (<V: Array<any> | IndexedCollection<any> | SetCollection<any>>(
    V
  ) => Iterable<$ValOf<V>>) &
    (<
      V:
        | KeyedCollection<any, any>
        | RecordInstance<any>
        | PlainObjInput<any, any>
    >(
      V
    ) => Iterable<[$KeyOf<V>, $ValOf<V>]>),
  C
>;

const PairSorting: $ReadOnly<{ LeftThenRight: number, RightThenLeft: number }> =
  {
    LeftThenRight: -1,
    RightThenLeft: +1,
  };

type Comparator<T> = (left: T, right: T) => number;

declare class _Collection<K, +V> implements ValueObject {
  equals(other: mixed): boolean;
  hashCode(): number;
  get(key: K, ..._: []): V | void;
  get<NSV>(key: K, notSetValue: NSV): V | NSV;
  has(key: K): boolean;
  includes(value: V): boolean;
  contains(value: V): boolean;
  first<NSV>(notSetValue?: NSV): V | NSV;
  last<NSV>(notSetValue?: NSV): V | NSV;

  hasIn(keyPath: Iterable<mixed>): boolean;

  getIn(keyPath: [], notSetValue?: mixed): this;
  getIn<NSV>(keyPath: [K], notSetValue: NSV): V | NSV;
  getIn<NSV, K2: $KeyOf<V>>(
    keyPath: [K, K2],
    notSetValue: NSV
  ): $ValOf<V, K2> | NSV;
  getIn<NSV, K2: $KeyOf<V>, K3: $KeyOf<$ValOf<V, K2>>>(
    keyPath: [K, K2, K3],
    notSetValue: NSV
  ): $ValOf<$ValOf<V, K2>, K3> | NSV;
  getIn<
    NSV,
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4],
    notSetValue: NSV
  ): $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4> | NSV;
  getIn<
    NSV,
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5],
    notSetValue: NSV
  ): $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5> | NSV;

  update<U>(updater: (value: this) => U): U;

  toJS(): Array<any> | { [key: string]: mixed };
  toJSON(): Array<V> | { [key: string]: V };
  toArray(): Array<V> | Array<[K, V]>;
  toObject(): { [key: string]: V };
  toMap(): Map<K, V>;
  toOrderedMap(): OrderedMap<K, V>;
  toSet(): Set<V>;
  toOrderedSet(): OrderedSet<V>;
  toList(): List<V>;
  toStack(): Stack<V>;
  toSeq(): Seq<K, V>;
  toKeyedSeq(): KeyedSeq<K, V>;
  toIndexedSeq(): IndexedSeq<V>;
  toSetSeq(): SetSeq<V>;

  keys(): Iterator<K>;
  values(): Iterator<V>;
  entries(): Iterator<[K, V]>;

  keySeq(): IndexedSeq<K>;
  valueSeq(): IndexedSeq<V>;
  entrySeq(): IndexedSeq<[K, V]>;

  reverse(): this;
  sort(comparator?: Comparator<V>): this;

  sortBy<C>(
    comparatorValueMapper: (value: V, key: K, iter: this) => C,
    comparator?: Comparator<C>
  ): this;

  groupBy<G>(
    grouper: (value: V, key: K, iter: this) => G,
    context?: mixed
  ): KeyedSeq<G, this>;

  forEach(
    sideEffect: (value: V, key: K, iter: this) => any,
    context?: mixed
  ): number;

  slice(begin?: number, end?: number): this;
  rest(): this;
  butLast(): this;
  skip(amount: number): this;
  skipLast(amount: number): this;
  skipWhile(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): this;
  skipUntil(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): this;
  take(amount: number): this;
  takeLast(amount: number): this;
  takeWhile(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): this;
  takeUntil(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): this;

  filterNot(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): this;

  reduce<R>(
    reducer: (reduction: R, value: V, key: K, iter: this) => R,
    initialReduction: R,
    context?: mixed
  ): R;
  reduce<R>(reducer: (reduction: V | R, value: V, key: K, iter: this) => R): R;

  reduceRight<R>(
    reducer: (reduction: R, value: V, key: K, iter: this) => R,
    initialReduction: R,
    context?: mixed
  ): R;
  reduceRight<R>(
    reducer: (reduction: V | R, value: V, key: K, iter: this) => R
  ): R;

  every(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): boolean;
  some(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): boolean;
  join(separator?: string): string;
  isEmpty(): boolean;
  count(
    predicate?: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): number;
  countBy<G>(
    grouper: (value: V, key: K, iter: this) => G,
    context?: mixed
  ): Map<G, number>;

  find<NSV>(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed,
    notSetValue?: NSV
  ): V | NSV;
  findLast<NSV>(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed,
    notSetValue?: NSV
  ): V | NSV;

  findEntry(predicate: (value: V, key: K, iter: this) => mixed): [K, V] | void;
  findLastEntry(
    predicate: (value: V, key: K, iter: this) => mixed
  ): [K, V] | void;

  findKey(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): K | void;
  findLastKey(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): K | void;

  keyOf(searchValue: V): K | void;
  lastKeyOf(searchValue: V): K | void;

  max(comparator?: Comparator<V>): V;
  maxBy<C>(
    comparatorValueMapper: (value: V, key: K, iter: this) => C,
    comparator?: Comparator<C>
  ): V;
  min(comparator?: Comparator<V>): V;
  minBy<C>(
    comparatorValueMapper: (value: V, key: K, iter: this) => C,
    comparator?: Comparator<C>
  ): V;

  isSubset(iter: Iterable<V>): boolean;
  isSuperset(iter: Iterable<V>): boolean;
}

declare function isImmutable(
  maybeImmutable: mixed
): boolean %checks(maybeImmutable instanceof Collection);
declare function isCollection(
  maybeCollection: mixed
): boolean %checks(maybeCollection instanceof Collection);
declare function isKeyed(
  maybeKeyed: mixed
): boolean %checks(maybeKeyed instanceof KeyedCollection);
declare function isIndexed(
  maybeIndexed: mixed
): boolean %checks(maybeIndexed instanceof IndexedCollection);
declare function isAssociative(
  maybeAssociative: mixed
): boolean %checks(maybeAssociative instanceof KeyedCollection ||
  maybeAssociative instanceof IndexedCollection);
declare function isOrdered(
  maybeOrdered: mixed
): boolean %checks(maybeOrdered instanceof IndexedCollection ||
  maybeOrdered instanceof OrderedMap ||
  maybeOrdered instanceof OrderedSet);
declare function isValueObject(maybeValue: mixed): boolean;

declare function isSeq(maybeSeq: any): boolean %checks(maybeSeq instanceof Seq);
declare function isList(maybeList: any): boolean %checks(maybeList instanceof
  List);
declare function isMap(maybeMap: any): boolean %checks(maybeMap instanceof Map);
declare function isOrderedMap(
  maybeOrderedMap: any
): boolean %checks(maybeOrderedMap instanceof OrderedMap);
declare function isStack(maybeStack: any): boolean %checks(maybeStack instanceof
  Stack);
declare function isSet(maybeSet: any): boolean %checks(maybeSet instanceof Set);
declare function isOrderedSet(
  maybeOrderedSet: any
): boolean %checks(maybeOrderedSet instanceof OrderedSet);
declare function isRecord(
  maybeRecord: any
): boolean %checks(maybeRecord instanceof Record);

declare interface ValueObject {
  equals(other: mixed): boolean;
  hashCode(): number;
}

declare class Collection<K, +V> extends _Collection<K, V> {
  static Keyed: typeof KeyedCollection;
  static Indexed: typeof IndexedCollection;
  static Set: typeof SetCollection;

  static isCollection: typeof isCollection;
  static isKeyed: typeof isKeyed;
  static isIndexed: typeof isIndexed;
  static isAssociative: typeof isAssociative;
  static isOrdered: typeof isOrdered;
}

declare class KeyedCollection<K, +V> extends Collection<K, V> {
  static <K, V>(
    values?: Iterable<[K, V]> | PlainObjInput<K, V>
  ): KeyedCollection<K, V>;

  toJS(): { [key: string]: mixed };
  toJSON(): { [key: string]: V };
  toArray(): Array<[K, V]>;
  @@iterator(): Iterator<[K, V]>;
  toSeq(): KeyedSeq<K, V>;
  flip(): KeyedCollection<V, K>;

  concat<KC, VC>(
    ...iters: Array<Iterable<[KC, VC]> | PlainObjInput<KC, VC>>
  ): KeyedCollection<K | KC, V | VC>;

  filter(predicate: typeof Boolean): KeyedCollection<K, $NonMaybeType<V>>;
  filter(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): KeyedCollection<K, V>;

  partition(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: V, key: K, iter: this) => M,
    context?: mixed
  ): KeyedCollection<K, M>;

  mapKeys<M>(
    mapper: (key: K, value: V, iter: this) => M,
    context?: mixed
  ): KeyedCollection<M, V>;

  mapEntries<KM, VM>(
    mapper: (entry: [K, V], index: number, iter: this) => [KM, VM],
    context?: mixed
  ): KeyedCollection<KM, VM>;

  flatMap<KM, VM>(
    mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]>,
    context?: mixed
  ): KeyedCollection<KM, VM>;

  flatten(depth?: number): KeyedCollection<any, any>;
  flatten(shallow?: boolean): KeyedCollection<any, any>;
}

Collection.Keyed = KeyedCollection;

declare class IndexedCollection<+T> extends Collection<number, T> {
  static <T>(iter?: Iterable<T>): IndexedCollection<T>;

  toJS(): Array<mixed>;
  toJSON(): Array<T>;
  toArray(): Array<T>;
  @@iterator(): Iterator<T>;
  toSeq(): IndexedSeq<T>;
  fromEntrySeq<K, V>(): KeyedSeq<K, V>;
  interpose(separator: T): this;
  interleave(...collections: Iterable<T>[]): this;
  splice(index: number, removeNum: number, ...values: T[]): this;

  zip<A>(a: Iterable<A>, ..._: []): IndexedCollection<[T, A]>;
  zip<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): IndexedCollection<[T, A, B]>;
  zip<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedCollection<[T, A, B, C]>;
  zip<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedCollection<[T, A, B, C, D]>;
  zip<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedCollection<[T, A, B, C, D, E]>;

  zipAll<A>(a: Iterable<A>, ..._: []): IndexedCollection<[T | void, A | void]>;
  zipAll<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): IndexedCollection<[T | void, A | void, B | void]>;
  zipAll<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedCollection<[T | void, A | void, B | void, C | void]>;
  zipAll<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedCollection<[T | void, A | void, B | void, C | void, D | void]>;
  zipAll<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedCollection<
    [T | void, A | void, B | void, C | void, D | void, E | void]
  >;

  zipWith<A, R>(
    zipper: (value: T, a: A) => R,
    a: Iterable<A>,
    ..._: []
  ): IndexedCollection<R>;
  zipWith<A, B, R>(
    zipper: (value: T, a: A, b: B) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): IndexedCollection<R>;
  zipWith<A, B, C, R>(
    zipper: (value: T, a: A, b: B, c: C) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedCollection<R>;
  zipWith<A, B, C, D, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedCollection<R>;
  zipWith<A, B, C, D, E, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedCollection<R>;

  indexOf(searchValue: T): number;
  lastIndexOf(searchValue: T): number;
  findIndex(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): number;
  findLastIndex(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): number;

  concat<C>(...iters: Array<Iterable<C> | C>): IndexedCollection<T | C>;

  filter(predicate: typeof Boolean): IndexedCollection<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): IndexedCollection<T>;

  partition(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, index: number, iter: this) => M,
    context?: mixed
  ): IndexedCollection<M>;

  flatMap<M>(
    mapper: (value: T, index: number, iter: this) => Iterable<M>,
    context?: mixed
  ): IndexedCollection<M>;

  flatten(depth?: number): IndexedCollection<any>;
  flatten(shallow?: boolean): IndexedCollection<any>;
}

declare class SetCollection<+T> extends Collection<T, T> {
  static <T>(iter?: Iterable<T>): SetCollection<T>;

  toJS(): Array<mixed>;
  toJSON(): Array<T>;
  toArray(): Array<T>;
  @@iterator(): Iterator<T>;
  toSeq(): SetSeq<T>;

  concat<U>(...collections: Iterable<U>[]): SetCollection<T | U>;

  // `filter`, `map` and `flatMap` cannot be defined further up the hierarchy,
  // because the implementation for `KeyedCollection` allows the value type to
  // change without constraining the key type. That does not work for
  // `SetCollection` - the value and key types *must* match.
  filter(predicate: typeof Boolean): SetCollection<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): SetCollection<T>;

  partition(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, value: T, iter: this) => M,
    context?: mixed
  ): SetCollection<M>;

  flatMap<M>(
    mapper: (value: T, value: T, iter: this) => Iterable<M>,
    context?: mixed
  ): SetCollection<M>;

  flatten(depth?: number): SetCollection<any>;
  flatten(shallow?: boolean): SetCollection<any>;
}

declare function isSeq(maybeSeq: mixed): boolean %checks(maybeSeq instanceof
  Seq);
declare class Seq<K, +V> extends _Collection<K, V> {
  static Keyed: typeof KeyedSeq;
  static Indexed: typeof IndexedSeq;
  static Set: typeof SetSeq;

  static <K, V>(values: KeyedSeq<K, V>): KeyedSeq<K, V>;
  static <T>(values: SetSeq<T>): SetSeq<K, V>;
  static <T>(values: Iterable<T>): IndexedSeq<T>;
  static <K, V>(values?: PlainObjInput<K, V>): KeyedSeq<K, V>;

  static isSeq: typeof isSeq;

  size: number | void;
  cacheResult(): this;
  toSeq(): this;
}

declare class KeyedSeq<K, +V> extends Seq<K, V> mixins KeyedCollection<K, V> {
  static <K, V>(
    values?: Iterable<[K, V]> | PlainObjInput<K, V>
  ): KeyedSeq<K, V>;

  // Override specialized return types
  flip(): KeyedSeq<V, K>;

  concat<KC, VC>(
    ...iters: Array<Iterable<[KC, VC]> | PlainObjInput<KC, VC>>
  ): KeyedSeq<K | KC, V | VC>;

  filter(predicate: typeof Boolean): KeyedSeq<K, $NonMaybeType<V>>;
  filter(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): KeyedSeq<K, V>;

  partition(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: V, key: K, iter: this) => M,
    context?: mixed
  ): KeyedSeq<K, M>;

  mapKeys<M>(
    mapper: (key: K, value: V, iter: this) => M,
    context?: mixed
  ): KeyedSeq<M, V>;

  mapEntries<KM, VM>(
    mapper: (entry: [K, V], index: number, iter: this) => [KM, VM],
    context?: mixed
  ): KeyedSeq<KM, VM>;

  flatMap<KM, VM>(
    mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]>,
    context?: mixed
  ): KeyedSeq<KM, VM>;

  flatten(depth?: number): KeyedSeq<any, any>;
  flatten(shallow?: boolean): KeyedSeq<any, any>;
}

declare class IndexedSeq<+T>
  extends Seq<number, T>
  mixins IndexedCollection<T>
{
  static <T>(values?: Iterable<T>): IndexedSeq<T>;

  static of<T>(...values: T[]): IndexedSeq<T>;

  // Override specialized return types

  concat<C>(...iters: Array<Iterable<C> | C>): IndexedSeq<T | C>;

  filter(predicate: typeof Boolean): IndexedSeq<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): IndexedSeq<T>;

  partition(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, index: number, iter: this) => M,
    context?: mixed
  ): IndexedSeq<M>;

  flatMap<M>(
    mapper: (value: T, index: number, iter: this) => Iterable<M>,
    context?: mixed
  ): IndexedSeq<M>;

  flatten(depth?: number): IndexedSeq<any>;
  flatten(shallow?: boolean): IndexedSeq<any>;

  zip<A>(a: Iterable<A>, ..._: []): IndexedSeq<[T, A]>;
  zip<A, B>(a: Iterable<A>, b: Iterable<B>, ..._: []): IndexedSeq<[T, A, B]>;
  zip<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedSeq<[T, A, B, C]>;
  zip<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedSeq<[T, A, B, C, D]>;
  zip<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedSeq<[T, A, B, C, D, E]>;

  zipAll<A>(a: Iterable<A>, ..._: []): IndexedSeq<[T | void, A | void]>;
  zipAll<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): IndexedSeq<[T | void, A | void, B | void]>;
  zipAll<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedSeq<[T | void, A | void, B | void, C | void]>;
  zipAll<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedSeq<[T | void, A | void, B | void, C | void, D | void]>;
  zipAll<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedSeq<[T | void, A | void, B | void, C | void, D | void, E | void]>;

  zipWith<A, R>(
    zipper: (value: T, a: A) => R,
    a: Iterable<A>,
    ..._: []
  ): IndexedSeq<R>;
  zipWith<A, B, R>(
    zipper: (value: T, a: A, b: B) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): IndexedSeq<R>;
  zipWith<A, B, C, R>(
    zipper: (value: T, a: A, b: B, c: C) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): IndexedSeq<R>;
  zipWith<A, B, C, D, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): IndexedSeq<R>;
  zipWith<A, B, C, D, E, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): IndexedSeq<R>;
}

declare class SetSeq<+T> extends Seq<T, T> mixins SetCollection<T> {
  static <T>(values?: Iterable<T>): SetSeq<T>;

  static of<T>(...values: T[]): SetSeq<T>;

  // Override specialized return types

  concat<U>(...collections: Iterable<U>[]): SetSeq<T | U>;

  filter(predicate: typeof Boolean): SetSeq<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): SetSeq<T>;

  partition(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, value: T, iter: this) => M,
    context?: mixed
  ): SetSeq<M>;

  flatMap<M>(
    mapper: (value: T, value: T, iter: this) => Iterable<M>,
    context?: mixed
  ): SetSeq<M>;

  flatten(depth?: number): SetSeq<any>;
  flatten(shallow?: boolean): SetSeq<any>;
}

declare class UpdatableInCollection<K, +V> {
  setIn<S>(keyPath: [], value: S): S;
  setIn(keyPath: [K], value: V): this;
  setIn<K2: $KeyOf<V>, S: $ValOf<V, K2>>(keyPath: [K, K2], value: S): this;
  setIn<K2: $KeyOf<V>, K3: $KeyOf<$ValOf<V, K2>>, S: $ValOf<$ValOf<V, K2>, K3>>(
    keyPath: [K, K2, K3],
    value: S
  ): this;
  setIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    value: S
  ): this;
  setIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    value: S
  ): this;

  deleteIn(keyPath: []): void;
  deleteIn(keyPath: [K]): this;
  deleteIn<K2: $KeyOf<V>>(keyPath: [K, K2]): this;
  deleteIn<K2: $KeyOf<V>, K3: $KeyOf<$ValOf<V, K2>>>(
    keyPath: [K, K2, K3]
  ): this;
  deleteIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4]
  ): this;
  deleteIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5]
  ): this;

  removeIn(keyPath: []): void;
  removeIn(keyPath: [K]): this;
  removeIn<K2: $KeyOf<V>>(keyPath: [K, K2]): this;
  removeIn<K2: $KeyOf<V>, K3: $KeyOf<$ValOf<V, K2>>>(
    keyPath: [K, K2, K3]
  ): this;
  removeIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4]
  ): this;
  removeIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5]
  ): this;

  updateIn<U>(keyPath: [], notSetValue: mixed, updater: (value: this) => U): U;
  updateIn<U>(keyPath: [], updater: (value: this) => U): U;
  updateIn<NSV>(keyPath: [K], notSetValue: NSV, updater: (value: V) => V): this;
  updateIn(keyPath: [K], updater: (value: V) => V): this;
  updateIn<NSV, K2: $KeyOf<V>, S: $ValOf<V, K2>>(
    keyPath: [K, K2],
    notSetValue: NSV,
    updater: (value: $ValOf<V, K2> | NSV) => S
  ): this;
  updateIn<K2: $KeyOf<V>, S: $ValOf<V, K2>>(
    keyPath: [K, K2],
    updater: (value: $ValOf<V, K2>) => S
  ): this;
  updateIn<
    NSV,
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    S: $ValOf<$ValOf<V, K2>, K3>
  >(
    keyPath: [K, K2, K3],
    notSetValue: NSV,
    updater: (value: $ValOf<$ValOf<V, K2>, K3> | NSV) => S
  ): this;
  updateIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    S: $ValOf<$ValOf<V, K2>, K3>
  >(
    keyPath: [K, K2, K3],
    updater: (value: $ValOf<$ValOf<V, K2>, K3>) => S
  ): this;
  updateIn<
    NSV,
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    notSetValue: NSV,
    updater: (value: $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4> | NSV) => S
  ): this;
  updateIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    updater: (value: $ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>) => S
  ): this;
  updateIn<
    NSV,
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    notSetValue: NSV,
    updater: (
      value: $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5> | NSV
    ) => S
  ): this;
  updateIn<
    K2: $KeyOf<V>,
    K3: $KeyOf<$ValOf<V, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<V, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    updater: (value: $ValOf<$ValOf<$ValOf<$ValOf<V, K2>, K3>, K4>, K5>) => S
  ): this;
}

declare function isList(maybeList: mixed): boolean %checks(maybeList instanceof
  List);
declare class List<+T>
  extends IndexedCollection<T>
  mixins UpdatableInCollection<number, T>
{
  static (collection?: Iterable<T>): List<T>;

  static of<T>(...values: T[]): List<T>;

  static isList: typeof isList;

  size: number;

  set<U>(index: number, value: U): List<T | U>;
  delete(index: number): this;
  remove(index: number): this;
  insert<U>(index: number, value: U): List<T | U>;
  clear(): this;
  push<U>(...values: U[]): List<T | U>;
  pop(): this;
  unshift<U>(...values: U[]): List<T | U>;
  shift(): this;

  update<U>(updater: (value: this) => U): U;
  update<U>(index: number, updater: (value: T) => U): List<T | U>;
  update<U>(
    index: number,
    notSetValue: U,
    updater: (value: T) => U
  ): List<T | U>;

  merge<U>(...collections: Iterable<U>[]): List<T | U>;

  setSize(size: number): this;

  mergeIn(keyPath: Iterable<mixed>, ...collections: Iterable<mixed>[]): this;
  mergeDeepIn(
    keyPath: Iterable<mixed>,
    ...collections: Iterable<mixed>[]
  ): this;

  withMutations(mutator: (mutable: this) => mixed): this;
  asMutable(): this;
  wasAltered(): boolean;
  asImmutable(): this;

  // Override specialized return types

  concat<C>(...iters: Array<Iterable<C> | C>): List<T | C>;

  filter(predicate: typeof Boolean): List<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): List<T>;

  partition(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, index: number, iter: this) => M,
    context?: mixed
  ): List<M>;

  flatMap<M>(
    mapper: (value: T, index: number, iter: this) => Iterable<M>,
    context?: mixed
  ): List<M>;

  flatten(depth?: number): List<any>;
  flatten(shallow?: boolean): List<any>;

  zip<A>(a: Iterable<A>, ..._: []): List<[T, A]>;
  zip<A, B>(a: Iterable<A>, b: Iterable<B>, ..._: []): List<[T, A, B]>;
  zip<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): List<[T, A, B, C]>;
  zip<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): List<[T, A, B, C, D]>;
  zip<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): List<[T, A, B, C, D, E]>;

  zipAll<A>(a: Iterable<A>, ..._: []): List<[T | void, A | void]>;
  zipAll<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): List<[T | void, A | void, B | void]>;
  zipAll<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): List<[T | void, A | void, B | void, C | void]>;
  zipAll<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): List<[T | void, A | void, B | void, C | void, D | void]>;
  zipAll<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): List<[T | void, A | void, B | void, C | void, D | void, E | void]>;

  zipWith<A, R>(
    zipper: (value: T, a: A) => R,
    a: Iterable<A>,
    ..._: []
  ): List<R>;
  zipWith<A, B, R>(
    zipper: (value: T, a: A, b: B) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): List<R>;
  zipWith<A, B, C, R>(
    zipper: (value: T, a: A, b: B, c: C) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): List<R>;
  zipWith<A, B, C, D, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): List<R>;
  zipWith<A, B, C, D, E, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): List<R>;
}

declare function isMap(maybeMap: mixed): boolean %checks(maybeMap instanceof
  Map);
declare class Map<K, +V>
  extends KeyedCollection<K, V>
  mixins UpdatableInCollection<K, V>
{
  static <K, V>(values?: Iterable<[K, V]> | PlainObjInput<K, V>): Map<K, V>;

  static isMap: typeof isMap;

  size: number;

  set<K_, V_>(key: K_, value: V_): Map<K | K_, V | V_>;
  delete(key: K): this;
  remove(key: K): this;
  clear(): this;

  deleteAll(keys: Iterable<K>): Map<K, V>;
  removeAll(keys: Iterable<K>): Map<K, V>;

  update<U>(updater: (value: this) => U): U;
  update<V_>(key: K, updater: (value: V) => V_): Map<K, V | V_>;
  update<V_>(
    key: K,
    notSetValue: V_,
    updater: (value: V) => V_
  ): Map<K, V | V_>;

  merge<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): Map<K | K_, V | V_>;
  concat<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): Map<K | K_, V | V_>;

  mergeWith<K_, W, X>(
    merger: (oldVal: V, newVal: W, key: K) => X,
    ...collections: (Iterable<[K_, W]> | PlainObjInput<K_, W>)[]
  ): Map<K | K_, V | W | X>;

  mergeDeep<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): Map<K | K_, V | V_>;

  mergeDeepWith<K_, V_>(
    merger: (oldVal: any, newVal: any, key: any) => mixed,
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): Map<K | K_, V | V_>;

  mergeIn(
    keyPath: Iterable<mixed>,
    ...collections: (Iterable<mixed> | PlainObjInput<mixed, mixed>)[]
  ): this;
  mergeDeepIn(
    keyPath: Iterable<mixed>,
    ...collections: (Iterable<mixed> | PlainObjInput<mixed, mixed>)[]
  ): this;

  withMutations(mutator: (mutable: this) => mixed): this;
  asMutable(): this;
  wasAltered(): boolean;
  asImmutable(): this;

  // Override specialized return types

  flip(): Map<V, K>;

  filter(predicate: typeof Boolean): Map<K, $NonMaybeType<V>>;
  filter(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): Map<K, V>;

  partition(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: V, key: K, iter: this) => M,
    context?: mixed
  ): Map<K, M>;

  mapKeys<M>(
    mapper: (key: K, value: V, iter: this) => M,
    context?: mixed
  ): Map<M, V>;

  mapEntries<KM, VM>(
    mapper: (entry: [K, V], index: number, iter: this) => [KM, VM],
    context?: mixed
  ): Map<KM, VM>;

  flatMap<KM, VM>(
    mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]>,
    context?: mixed
  ): Map<KM, VM>;

  flatten(depth?: number): Map<any, any>;
  flatten(shallow?: boolean): Map<any, any>;
}

declare function isOrderedMap(
  maybeOrderedMap: mixed
): boolean %checks(maybeOrderedMap instanceof OrderedMap);
declare class OrderedMap<K, +V>
  extends Map<K, V>
  mixins UpdatableInCollection<K, V>
{
  static <K, V>(
    values?: Iterable<[K, V]> | PlainObjInput<K, V>
  ): OrderedMap<K, V>;

  static isOrderedMap: typeof isOrderedMap;

  size: number;

  set<K_, V_>(key: K_, value: V_): OrderedMap<K | K_, V | V_>;
  delete(key: K): this;
  remove(key: K): this;
  clear(): this;

  update<U>(updater: (value: this) => U): U;
  update<V_>(key: K, updater: (value: V) => V_): OrderedMap<K, V | V_>;
  update<V_>(
    key: K,
    notSetValue: V_,
    updater: (value: V) => V_
  ): OrderedMap<K, V | V_>;

  merge<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): OrderedMap<K | K_, V | V_>;
  concat<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): OrderedMap<K | K_, V | V_>;

  mergeWith<K_, W, X>(
    merger: (oldVal: V, newVal: W, key: K) => X,
    ...collections: (Iterable<[K_, W]> | PlainObjInput<K_, W>)[]
  ): OrderedMap<K | K_, V | W | X>;

  mergeDeep<K_, V_>(
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): OrderedMap<K | K_, V | V_>;

  mergeDeepWith<K_, V_>(
    merger: (oldVal: any, newVal: any, key: any) => mixed,
    ...collections: (Iterable<[K_, V_]> | PlainObjInput<K_, V_>)[]
  ): OrderedMap<K | K_, V | V_>;

  mergeIn(
    keyPath: Iterable<mixed>,
    ...collections: (Iterable<mixed> | PlainObjInput<mixed, mixed>)[]
  ): this;
  mergeDeepIn(
    keyPath: Iterable<mixed>,
    ...collections: (Iterable<mixed> | PlainObjInput<mixed, mixed>)[]
  ): this;

  withMutations(mutator: (mutable: this) => mixed): this;
  asMutable(): this;
  wasAltered(): boolean;
  asImmutable(): this;

  // Override specialized return types

  flip(): OrderedMap<V, K>;

  filter(predicate: typeof Boolean): OrderedMap<K, $NonMaybeType<V>>;
  filter(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): OrderedMap<K, V>;

  partition(
    predicate: (value: V, key: K, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: V, key: K, iter: this) => M,
    context?: mixed
  ): OrderedMap<K, M>;

  mapKeys<M>(
    mapper: (key: K, value: V, iter: this) => M,
    context?: mixed
  ): OrderedMap<M, V>;

  mapEntries<KM, VM>(
    mapper: (entry: [K, V], index: number, iter: this) => [KM, VM],
    context?: mixed
  ): OrderedMap<KM, VM>;

  flatMap<KM, VM>(
    mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]>,
    context?: mixed
  ): OrderedMap<KM, VM>;

  flatten(depth?: number): OrderedMap<any, any>;
  flatten(shallow?: boolean): OrderedMap<any, any>;
}

declare function isSet(maybeSet: mixed): boolean %checks(maybeSet instanceof
  Set);
declare class Set<+T> extends SetCollection<T> {
  static <T>(values?: Iterable<T>): Set<T>;

  static of<T>(...values: T[]): Set<T>;
  static fromKeys<T>(
    values: Iterable<[T, mixed]> | PlainObjInput<T, mixed>
  ): Set<T>;

  static intersect(sets: Iterable<Iterable<T>>): Set<T>;
  static union(sets: Iterable<Iterable<T>>): Set<T>;

  static isSet: typeof isSet;

  size: number;

  add<U>(value: U): Set<T | U>;
  delete(value: T): this;
  remove(value: T): this;
  clear(): this;
  union<U>(...collections: Iterable<U>[]): Set<T | U>;
  merge<U>(...collections: Iterable<U>[]): Set<T | U>;
  concat<U>(...collections: Iterable<U>[]): Set<T | U>;
  intersect<U>(...collections: Iterable<U>[]): Set<T & U>;
  subtract(...collections: Iterable<mixed>[]): this;

  withMutations(mutator: (mutable: this) => mixed): this;
  asMutable(): this;
  wasAltered(): boolean;
  asImmutable(): this;

  // Override specialized return types

  filter(predicate: typeof Boolean): Set<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): Set<T>;

  partition(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, value: T, iter: this) => M,
    context?: mixed
  ): Set<M>;

  flatMap<M>(
    mapper: (value: T, value: T, iter: this) => Iterable<M>,
    context?: mixed
  ): Set<M>;

  flatten(depth?: number): Set<any>;
  flatten(shallow?: boolean): Set<any>;
}

// Overrides except for `isOrderedSet` are for specialized return types
declare function isOrderedSet(
  maybeOrderedSet: mixed
): boolean %checks(maybeOrderedSet instanceof OrderedSet);
declare class OrderedSet<+T> extends Set<T> {
  static <T>(values?: Iterable<T>): OrderedSet<T>;

  static of<T>(...values: T[]): OrderedSet<T>;
  static fromKeys<T>(
    values: Iterable<[T, mixed]> | PlainObjInput<T, mixed>
  ): OrderedSet<T>;

  static isOrderedSet: typeof isOrderedSet;

  size: number;

  add<U>(value: U): OrderedSet<T | U>;
  union<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
  merge<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
  concat<U>(...collections: Iterable<U>[]): OrderedSet<T | U>;
  intersect<U>(...collections: Iterable<U>[]): OrderedSet<T & U>;

  filter(predicate: typeof Boolean): OrderedSet<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): OrderedSet<T>;

  partition(
    predicate: (value: T, value: T, iter: this) => mixed,
    context?: mixed
  ): [this, this];

  map<M>(
    mapper: (value: T, value: T, iter: this) => M,
    context?: mixed
  ): OrderedSet<M>;

  flatMap<M>(
    mapper: (value: T, value: T, iter: this) => Iterable<M>,
    context?: mixed
  ): OrderedSet<M>;

  flatten(depth?: number): OrderedSet<any>;
  flatten(shallow?: boolean): OrderedSet<any>;

  zip<A>(a: Iterable<A>, ..._: []): OrderedSet<[T, A]>;
  zip<A, B>(a: Iterable<A>, b: Iterable<B>, ..._: []): OrderedSet<[T, A, B]>;
  zip<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): OrderedSet<[T, A, B, C]>;
  zip<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): OrderedSet<[T, A, B, C, D]>;
  zip<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): OrderedSet<[T, A, B, C, D, E]>;

  zipAll<A>(a: Iterable<A>, ..._: []): OrderedSet<[T | void, A | void]>;
  zipAll<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): OrderedSet<[T | void, A | void, B | void]>;
  zipAll<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): OrderedSet<[T | void, A | void, B | void, C | void]>;
  zipAll<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): OrderedSet<[T | void, A | void, B | void, C | void, D | void]>;
  zipAll<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): OrderedSet<[T | void, A | void, B | void, C | void, D | void, E | void]>;

  zipWith<A, R>(
    zipper: (value: T, a: A) => R,
    a: Iterable<A>,
    ..._: []
  ): OrderedSet<R>;
  zipWith<A, B, R>(
    zipper: (value: T, a: A, b: B) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): OrderedSet<R>;
  zipWith<A, B, C, R>(
    zipper: (value: T, a: A, b: B, c: C) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): OrderedSet<R>;
  zipWith<A, B, C, D, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): OrderedSet<R>;
  zipWith<A, B, C, D, E, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): OrderedSet<R>;
}

declare function isStack(
  maybeStack: mixed
): boolean %checks(maybeStack instanceof Stack);
declare class Stack<+T> extends IndexedCollection<T> {
  static <T>(collection?: Iterable<T>): Stack<T>;

  static isStack(maybeStack: mixed): boolean;
  static of<T>(...values: T[]): Stack<T>;

  static isStack: typeof isStack;

  size: number;

  peek(): T;
  clear(): this;
  unshift<U>(...values: U[]): Stack<T | U>;
  unshiftAll<U>(iter: Iterable<U>): Stack<T | U>;
  shift(): this;
  push<U>(...values: U[]): Stack<T | U>;
  pushAll<U>(iter: Iterable<U>): Stack<T | U>;
  pop(): this;

  withMutations(mutator: (mutable: this) => mixed): this;
  asMutable(): this;
  wasAltered(): boolean;
  asImmutable(): this;

  // Override specialized return types

  concat<C>(...iters: Array<Iterable<C> | C>): Stack<T | C>;

  filter(predicate: typeof Boolean): Stack<$NonMaybeType<T>>;
  filter(
    predicate: (value: T, index: number, iter: this) => mixed,
    context?: mixed
  ): Stack<T>;

  map<M>(
    mapper: (value: T, index: number, iter: this) => M,
    context?: mixed
  ): Stack<M>;

  flatMap<M>(
    mapper: (value: T, index: number, iter: this) => Iterable<M>,
    context?: mixed
  ): Stack<M>;

  flatten(depth?: number): Stack<any>;
  flatten(shallow?: boolean): Stack<any>;

  zip<A>(a: Iterable<A>, ..._: []): Stack<[T, A]>;
  zip<A, B>(a: Iterable<A>, b: Iterable<B>, ..._: []): Stack<[T, A, B]>;
  zip<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): Stack<[T, A, B, C]>;
  zip<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): Stack<[T, A, B, C, D]>;
  zip<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): Stack<[T, A, B, C, D, E]>;

  zipAll<A>(a: Iterable<A>, ..._: []): Stack<[T | void, A | void]>;
  zipAll<A, B>(
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): Stack<[T | void, A | void, B | void]>;
  zipAll<A, B, C>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): Stack<[T | void, A | void, B | void, C | void]>;
  zipAll<A, B, C, D>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): Stack<[T | void, A | void, B | void, C | void, D | void]>;
  zipAll<A, B, C, D, E>(
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): Stack<[T | void, A | void, B | void, C | void, D | void, E | void]>;

  zipWith<A, R>(
    zipper: (value: T, a: A) => R,
    a: Iterable<A>,
    ..._: []
  ): Stack<R>;
  zipWith<A, B, R>(
    zipper: (value: T, a: A, b: B) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    ..._: []
  ): Stack<R>;
  zipWith<A, B, C, R>(
    zipper: (value: T, a: A, b: B, c: C) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    ..._: []
  ): Stack<R>;
  zipWith<A, B, C, D, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    ..._: []
  ): Stack<R>;
  zipWith<A, B, C, D, E, R>(
    zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R,
    a: Iterable<A>,
    b: Iterable<B>,
    c: Iterable<C>,
    d: Iterable<D>,
    e: Iterable<E>,
    ..._: []
  ): Stack<R>;
}

declare function Range(
  start?: number,
  end?: number,
  step?: number
): IndexedSeq<number>;
declare function Repeat<T>(value: T, times?: number): IndexedSeq<T>;

// The type of a Record factory function.
type RecordFactory<Values: Object> = Class<RecordInstance<Values>>;

// The type of runtime Record instances.
type RecordOf<Values: Object> = RecordInstance<Values> & $ReadOnly<Values>;

// The values of a Record instance.
type _RecordValues<T, R: RecordInstance<T> | T> = R;
type RecordValues<R> = _RecordValues<*, R>;

declare function isRecord(
  maybeRecord: any
): boolean %checks(maybeRecord instanceof RecordInstance);
declare class Record {
  static <Values: Object>(spec: Values, name?: string): typeof RecordInstance;
  constructor<Values: Object>(
    spec: Values,
    name?: string
  ): typeof RecordInstance;

  static isRecord: typeof isRecord;

  static getDescriptiveName(record: RecordInstance<any>): string;
}

declare class RecordInstance<T: Object = Object> {
  static (values?: Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>): RecordOf<T>;
  // Note: a constructor can only create an instance of RecordInstance<T>,
  // it's encouraged to not use `new` when creating Records.
  constructor(values?: Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>): void;

  size: number;

  has(key: string): boolean;

  get<K: $Keys<T>>(key: K, ..._: []): $ElementType<T, K>;
  get<K: $Keys<T>, NSV>(key: K, notSetValue: NSV): $ElementType<T, K> | NSV;

  hasIn(keyPath: Iterable<mixed>): boolean;

  getIn(keyPath: [], notSetValue?: mixed): this & $ReadOnly<T>;
  getIn<K: $Keys<T>>(keyPath: [K], notSetValue?: mixed): $ElementType<T, K>;
  getIn<NSV, K: $Keys<T>, K2: $KeyOf<$ValOf<T, K>>>(
    keyPath: [K, K2],
    notSetValue: NSV
  ): $ValOf<$ValOf<T, K>, K2> | NSV;
  getIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>
  >(
    keyPath: [K, K2, K3],
    notSetValue: NSV
  ): $ValOf<$ValOf<$ValOf<T, K>, K2>, K3> | NSV;
  getIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4],
    notSetValue: NSV
  ): $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4> | NSV;
  getIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5],
    notSetValue: NSV
  ): $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5> | NSV;

  equals(other: any): boolean;
  hashCode(): number;

  set<K: $Keys<T>>(key: K, value: $ElementType<T, K>): this & $ReadOnly<T>;
  update<K: $Keys<T>>(
    key: K,
    updater: (value: $ElementType<T, K>) => $ElementType<T, K>
  ): this & $ReadOnly<T>;
  merge(
    ...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
  ): this & $ReadOnly<T>;
  mergeDeep(
    ...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
  ): this & $ReadOnly<T>;

  mergeWith(
    merger: (oldVal: $ValOf<T>, newVal: $ValOf<T>, key: $Keys<T>) => $ValOf<T>,
    ...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
  ): this & $ReadOnly<T>;
  mergeDeepWith(
    merger: (oldVal: any, newVal: any, key: any) => any,
    ...collections: Array<Iterable<[$Keys<T>, $ValOf<T>]> | $Shape<T>>
  ): this & $ReadOnly<T>;

  delete<K: $Keys<T>>(key: K): this & $ReadOnly<T>;
  remove<K: $Keys<T>>(key: K): this & $ReadOnly<T>;
  clear(): this & $ReadOnly<T>;

  setIn<S>(keyPath: [], value: S): S;
  setIn<K: $Keys<T>, S: $ValOf<T, K>>(
    keyPath: [K],
    value: S
  ): this & $ReadOnly<T>;
  setIn<K: $Keys<T>, K2: $KeyOf<$ValOf<T, K>>, S: $ValOf<$ValOf<T, K>, K2>>(
    keyPath: [K, K2],
    value: S
  ): this & $ReadOnly<T>;
  setIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    S: $ValOf<$ValOf<$ValOf<T, K>, K2>, K3>
  >(
    keyPath: [K, K2, K3],
    value: S
  ): this & $ReadOnly<T>;
  setIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    value: S
  ): this & $ReadOnly<T>;
  setIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    value: S
  ): this & $ReadOnly<T>;

  deleteIn(keyPath: []): void;
  deleteIn<K: $Keys<T>>(keyPath: [K]): this & $ReadOnly<T>;
  deleteIn<K: $Keys<T>, K2: $KeyOf<$ValOf<T, K>>>(
    keyPath: [K, K2]
  ): this & $ReadOnly<T>;
  deleteIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>
  >(
    keyPath: [K, K2, K3]
  ): this & $ReadOnly<T>;
  deleteIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4]
  ): this & $ReadOnly<T>;
  deleteIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5]
  ): this & $ReadOnly<T>;

  removeIn(keyPath: []): void;
  removeIn<K: $Keys<T>>(keyPath: [K]): this & $ReadOnly<T>;
  removeIn<K: $Keys<T>, K2: $KeyOf<$ValOf<T, K>>>(
    keyPath: [K, K2]
  ): this & $ReadOnly<T>;
  removeIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>
  >(
    keyPath: [K, K2, K3]
  ): this & $ReadOnly<T>;
  removeIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>
  >(
    keyPath: [K, K2, K3, K4]
  ): this & $ReadOnly<T>;
  removeIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>
  >(
    keyPath: [K, K2, K3, K4, K5]
  ): this & $ReadOnly<T>;

  updateIn<U>(
    keyPath: [],
    notSetValue: mixed,
    updater: (value: this & T) => U
  ): U;
  updateIn<U>(keyPath: [], updater: (value: this & T) => U): U;
  updateIn<NSV, K: $Keys<T>, S: $ValOf<T, K>>(
    keyPath: [K],
    notSetValue: NSV,
    updater: (value: $ValOf<T, K>) => S
  ): this & $ReadOnly<T>;
  updateIn<K: $Keys<T>, S: $ValOf<T, K>>(
    keyPath: [K],
    updater: (value: $ValOf<T, K>) => S
  ): this & $ReadOnly<T>;
  updateIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    S: $ValOf<$ValOf<T, K>, K2>
  >(
    keyPath: [K, K2],
    notSetValue: NSV,
    updater: (value: $ValOf<$ValOf<T, K>, K2> | NSV) => S
  ): this & $ReadOnly<T>;
  updateIn<K: $Keys<T>, K2: $KeyOf<$ValOf<T, K>>, S: $ValOf<$ValOf<T, K>, K2>>(
    keyPath: [K, K2],
    updater: (value: $ValOf<$ValOf<T, K>, K2>) => S
  ): this & $ReadOnly<T>;
  updateIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    S: $ValOf<$ValOf<$ValOf<T, K>, K2>, K3>
  >(
    keyPath: [K, K2, K3],
    notSetValue: NSV,
    updater: (value: $ValOf<$ValOf<$ValOf<T, K>, K2>, K3> | NSV) => S
  ): this & $ReadOnly<T>;
  updateIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    S: $ValOf<$ValOf<$ValOf<T, K>, K2>, K3>
  >(
    keyPath: [K, K2, K3],
    updater: (value: $ValOf<$ValOf<$ValOf<T, K>, K2>, K3>) => S
  ): this & $ReadOnly<T>;
  updateIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    notSetValue: NSV,
    updater: (
      value: $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4> | NSV
    ) => S
  ): this & $ReadOnly<T>;
  updateIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>
  >(
    keyPath: [K, K2, K3, K4],
    updater: (value: $ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>) => S
  ): this & $ReadOnly<T>;
  updateIn<
    NSV,
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    notSetValue: NSV,
    updater: (
      value: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5> | NSV
    ) => S
  ): this & $ReadOnly<T>;
  updateIn<
    K: $Keys<T>,
    K2: $KeyOf<$ValOf<T, K>>,
    K3: $KeyOf<$ValOf<$ValOf<T, K>, K2>>,
    K4: $KeyOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>>,
    K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>>,
    S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5>
  >(
    keyPath: [K, K2, K3, K4, K5],
    updater: (
      value: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<T, K>, K2>, K3>, K4>, K5>
    ) => S
  ): this & $ReadOnly<T>;

  mergeIn(
    keyPath: Iterable<mixed>,
    ...collections: Array<any>
  ): this & $ReadOnly<T>;
  mergeDeepIn(
    keyPath: Iterable<mixed>,
    ...collections: Array<any>
  ): this & $ReadOnly<T>;

  toSeq(): KeyedSeq<$Keys<T>, any>;

  toJS(): { [key: $Keys<T>]: mixed };
  toJSON(): T;
  toObject(): T;

  withMutations(mutator: (mutable: this & T) => mixed): this & $ReadOnly<T>;
  asMutable(): this & $ReadOnly<T>;
  wasAltered(): boolean;
  asImmutable(): this & $ReadOnly<T>;

  @@iterator(): Iterator<[$Keys<T>, $ValOf<T>]>;
}

declare function fromJS(
  jsValue: mixed,
  reviver?: (
    key: string | number,
    sequence: KeyedCollection<string, mixed> | IndexedCollection<mixed>,
    path?: Array<string | number>
  ) => mixed
): Collection<mixed, mixed>;

declare function is(first: mixed, second: mixed): boolean;
declare function hash(value: mixed): number;

declare function get<C: Object, K: $Keys<C>>(
  collection: C,
  key: K,
  notSetValue: mixed
): $ValOf<C, K>;
declare function get<C, K: $KeyOf<C>, NSV>(
  collection: C,
  key: K,
  notSetValue: NSV
): $ValOf<C, K> | NSV;

declare function has(collection: Object, key: mixed): boolean;
declare function remove<C>(collection: C, key: $KeyOf<C>): C;
declare function set<C, K: $KeyOf<C>, V: $ValOf<C, K>>(
  collection: C,
  key: K,
  value: V
): C;
declare function update<C, K: $KeyOf<C>, V: $ValOf<C, K>, NSV>(
  collection: C,
  key: K,
  notSetValue: NSV,
  updater: ($ValOf<C, K> | NSV) => V
): C;
declare function update<C, K: $KeyOf<C>, V: $ValOf<C, K>>(
  collection: C,
  key: K,
  updater: ($ValOf<C, K>) => V
): C;

declare function getIn<C>(collection: C, keyPath: [], notSetValue?: mixed): C;
declare function getIn<C, K: $KeyOf<C>, NSV>(
  collection: C,
  keyPath: [K],
  notSetValue: NSV
): $ValOf<C, K> | NSV;
declare function getIn<C, K: $KeyOf<C>, K2: $KeyOf<$ValOf<C, K>>, NSV>(
  collection: C,
  keyPath: [K, K2],
  notSetValue: NSV
): $ValOf<$ValOf<C, K>, K2> | NSV;
declare function getIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3],
  notSetValue: NSV
): $ValOf<$ValOf<$ValOf<C, K>, K2>, K3> | NSV;
declare function getIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3, K4],
  notSetValue: NSV
): $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4> | NSV;
declare function getIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3, K4, K5],
  notSetValue: NSV
): $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5> | NSV;

declare function hasIn(collection: Object, keyPath: Iterable<mixed>): boolean;

declare function removeIn<C>(collection: C, keyPath: []): void;
declare function removeIn<C, K: $KeyOf<C>>(collection: C, keyPath: [K]): C;
declare function removeIn<C, K: $KeyOf<C>, K2: $KeyOf<$ValOf<C, K>>>(
  collection: C,
  keyPath: [K, K2]
): C;
declare function removeIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>
>(
  collection: C,
  keyPath: [K, K2, K3]
): C;
declare function removeIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>
>(
  collection: C,
  keyPath: [K, K2, K3, K4]
): C;
declare function removeIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>>
>(
  collection: C,
  keyPath: [K, K2, K3, K4, K5]
): C;

declare function setIn<S>(collection: Object, keyPath: [], value: S): S;
declare function setIn<C, K: $KeyOf<C>, S: $ValOf<C, K>>(
  collection: C,
  keyPath: [K],
  value: S
): C;
declare function setIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  S: $ValOf<$ValOf<C, K>, K2>
>(
  collection: C,
  keyPath: [K, K2],
  value: S
): C;
declare function setIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  S: $ValOf<$ValOf<$ValOf<C, K>, K2>, K3>
>(
  collection: C,
  keyPath: [K, K2, K3],
  value: S
): C;
declare function setIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>
>(
  collection: C,
  keyPath: [K, K2, K3, K4],
  value: S
): C;
declare function setIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5>
>(
  collection: C,
  keyPath: [K, K2, K3, K4, K5],
  value: S
): C;

declare function updateIn<C, S>(
  collection: C,
  keyPath: [],
  notSetValue: mixed,
  updater: (value: C) => S
): S;
declare function updateIn<C, S>(
  collection: C,
  keyPath: [],
  updater: (value: C) => S
): S;
declare function updateIn<C, K: $KeyOf<C>, S: $ValOf<C, K>, NSV>(
  collection: C,
  keyPath: [K],
  notSetValue: NSV,
  updater: (value: $ValOf<C, K> | NSV) => S
): C;
declare function updateIn<C, K: $KeyOf<C>, S: $ValOf<C, K>>(
  collection: C,
  keyPath: [K],
  updater: (value: $ValOf<C, K>) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  S: $ValOf<$ValOf<C, K>, K2>,
  NSV
>(
  collection: C,
  keyPath: [K, K2],
  notSetValue: NSV,
  updater: (value: $ValOf<$ValOf<C, K>, K2> | NSV) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  S: $ValOf<$ValOf<C, K>, K2>
>(
  collection: C,
  keyPath: [K, K2],
  updater: (value: $ValOf<$ValOf<C, K>, K2>) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  S: $ValOf<$ValOf<$ValOf<C, K>, K2>, K3>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3],
  notSetValue: NSV,
  updater: (value: $ValOf<$ValOf<$ValOf<C, K>, K2>, K3> | NSV) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  S: $ValOf<$ValOf<$ValOf<C, K>, K2>, K3>
>(
  collection: C,
  keyPath: [K, K2, K3],
  updater: (value: $ValOf<$ValOf<$ValOf<C, K>, K2>, K3>) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3, K4],
  notSetValue: NSV,
  updater: (value: $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4> | NSV) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>
>(
  collection: C,
  keyPath: [K, K2, K3, K4],
  updater: (value: $ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5>,
  NSV
>(
  collection: C,
  keyPath: [K, K2, K3, K4, K5],
  notSetValue: NSV,
  updater: (
    value: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5> | NSV
  ) => S
): C;
declare function updateIn<
  C,
  K: $KeyOf<C>,
  K2: $KeyOf<$ValOf<C, K>>,
  K3: $KeyOf<$ValOf<$ValOf<C, K>, K2>>,
  K4: $KeyOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>>,
  K5: $KeyOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>>,
  S: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5>
>(
  collection: C,
  keyPath: [K, K2, K3, K4, K5],
  updater: (
    value: $ValOf<$ValOf<$ValOf<$ValOf<$ValOf<C, K>, K2>, K3>, K4>, K5>
  ) => S
): C;

declare function merge<C>(
  collection: C,
  ...collections: Array<
    | $IterableOf<C>
    | $Shape<RecordValues<C>>
    | PlainObjInput<$KeyOf<C>, $ValOf<C>>
  >
): C;
declare function mergeWith<C>(
  merger: (oldVal: $ValOf<C>, newVal: $ValOf<C>, key: $KeyOf<C>) => $ValOf<C>,
  collection: C,
  ...collections: Array<
    | $IterableOf<C>
    | $Shape<RecordValues<C>>
    | PlainObjInput<$KeyOf<C>, $ValOf<C>>
  >
): C;
declare function mergeDeep<C>(
  collection: C,
  ...collections: Array<
    | $IterableOf<C>
    | $Shape<RecordValues<C>>
    | PlainObjInput<$KeyOf<C>, $ValOf<C>>
  >
): C;
declare function mergeDeepWith<C>(
  merger: (oldVal: any, newVal: any, key: any) => mixed,
  collection: C,
  ...collections: Array<
    | $IterableOf<C>
    | $Shape<RecordValues<C>>
    | PlainObjInput<$KeyOf<C>, $ValOf<C>>
  >
): C;

export {
  Collection,
  Seq,
  List,
  Map,
  OrderedMap,
  OrderedSet,
  Range,
  Repeat,
  Record,
  Set,
  Stack,
  fromJS,
  is,
  hash,
  isImmutable,
  isCollection,
  isKeyed,
  isIndexed,
  isAssociative,
  isOrdered,
  isRecord,
  isValueObject,
  get,
  has,
  remove,
  set,
  update,
  getIn,
  hasIn,
  removeIn,
  setIn,
  updateIn,
  merge,
  mergeWith,
  mergeDeep,
  mergeDeepWith,
};

export default {
  Collection,
  Seq,

  List,
  Map,
  OrderedMap,
  OrderedSet,
  PairSorting,
  Range,
  Repeat,
  Record,
  Set,
  Stack,

  fromJS,
  is,
  hash,

  isImmutable,
  isCollection,
  isKeyed,
  isIndexed,
  isAssociative,
  isOrdered,
  isRecord,
  isValueObject,

  get,
  has,
  remove,
  set,
  update,
  getIn,
  hasIn,
  removeIn,
  setIn,
  updateIn,
  merge,
  mergeWith,
  mergeDeep,
  mergeDeepWith,
};

export type {
  Comparator,
  KeyedCollection,
  IndexedCollection,
  SetCollection,
  KeyedSeq,
  IndexedSeq,
  SetSeq,
  RecordFactory,
  RecordOf,
  RecordInstance,
  ValueObject,
  $KeyOf,
  $ValOf,
};


immutable

description: Immutable Data Collections

license: MIT

type: git

files: dist README.md LICENSE

keywords: immutable persistent lazy data datastructure functional collection stateless sequence iteration

license: mit


code:
/**
Import a module while bypassing the cache.

@example
```
// foo.js
let i = 0;
module.exports = () => ++i;

// index.js
import importFresh = require('import-fresh');

require('./foo')();
//=> 1

require('./foo')();
//=> 2

importFresh('./foo')();
//=> 1

importFresh('./foo')();
//=> 1

const foo = importFresh<typeof import('./foo')>('./foo');
```
*/
declare function importFresh<T>(moduleId: string): T;

export = importFresh;


code:
'use strict';
const path = require('path');
const resolveFrom = require('resolve-from');
const parentModule = require('parent-module');

module.exports = moduleId => {
	if (typeof moduleId !== 'string') {
		throw new TypeError('Expected a string');
	}

	const parentPath = parentModule(__filename);

	const cwd = parentPath ? path.dirname(parentPath) : __dirname;
	const filePath = resolveFrom(cwd, moduleId);

	const oldModule = require.cache[filePath];
	// Delete itself from module parent
	if (oldModule && oldModule.parent) {
		let i = oldModule.parent.children.length;

		while (i--) {
			if (oldModule.parent.children[i].id === filePath) {
				oldModule.parent.children.splice(i, 1);
			}
		}
	}

	delete require.cache[filePath]; // Delete module from cache

	const parent = require.cache[parentPath]; // If `filePath` and `parentPath` are the same, cache will already be deleted so we won't get a memory leak in next step

	return parent === undefined ? require(filePath) : parent.require(filePath); // In case cache doesn't have parent, fall back to normal require
};


import-fresh

description: Import a module while bypassing the cache

repository: sindresorhus/import-fresh

test: xo && ava && tsd

heapdump: node heapdump.js

keywords: require cache uncache uncached module fresh bypass


code:
# import-fresh

> Import a module while bypassing the [cache](https://nodejs.org/api/modules.html#modules_caching)

Useful for testing purposes when you need to freshly import a module.

## Install

```
$ npm install import-fresh
```

## Usage

```js
// foo.js
let i = 0;
module.exports = () => ++i;
```

```js
const importFresh = require('import-fresh');

require('./foo')();
//=> 1

require('./foo')();
//=> 2

importFresh('./foo')();
//=> 1

importFresh('./foo')();
//=> 1
```

## import-fresh for enterprise

Available as part of the Tidelift Subscription.

The maintainers of import-fresh and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-import-fresh?utm_source=npm-import-fresh&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

## Related

- [clear-module](https://github.com/sindresorhus/clear-module) - Clear a module from the import cache
- [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path
- [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory
- [import-lazy](https://github.com/sindresorhus/import-lazy) - Import modules lazily


code:
iMurmurHash.js
==============

An incremental implementation of the MurmurHash3 (32-bit) hashing algorithm for JavaScript based on [Gary Court's implementation](https://github.com/garycourt/murmurhash-js) with [kazuyukitanimura's modifications](https://github.com/kazuyukitanimura/murmurhash-js).

This version works significantly faster than the non-incremental version if you need to hash many small strings into a single hash, since string concatenation (to build the single string to pass the non-incremental version) is fairly costly. In one case tested, using the incremental version was about 50% faster than concatenating 5-10 strings and then hashing.

Installation
------------

To use iMurmurHash in the browser, [download the latest version](https://raw.github.com/jensyt/imurmurhash-js/master/imurmurhash.min.js) and include it as a script on your site.

```html
<script type="text/javascript" src="/scripts/imurmurhash.min.js"></script>
<script>
// Your code here, access iMurmurHash using the global object MurmurHash3
</script>
```

---

To use iMurmurHash in Node.js, install the module using NPM:

```bash
npm install imurmurhash
```

Then simply include it in your scripts:

```javascript
MurmurHash3 = require('imurmurhash');
```

Quick Example
-------------

```javascript
// Create the initial hash
var hashState = MurmurHash3('string');

// Incrementally add text
hashState.hash('more strings');
hashState.hash('even more strings');

// All calls can be chained if desired
hashState.hash('and').hash('some').hash('more');

// Get a result
hashState.result();
// returns 0xe4ccfe6b
```

Functions
---------

### MurmurHash3 ([string], [seed])
Get a hash state object, optionally initialized with the given _string_ and _seed_. _Seed_ must be a positive integer if provided. Calling this function without the `new` keyword will return a cached state object that has been reset. This is safe to use as long as the object is only used from a single thread and no other hashes are created while operating on this one. If this constraint cannot be met, you can use `new` to create a new state object. For example:

```javascript
// Use the cached object, calling the function again will return the same
// object (but reset, so the current state would be lost)
hashState = MurmurHash3();
...

// Create a new object that can be safely used however you wish. Calling the
// function again will simply return a new state object, and no state loss
// will occur, at the cost of creating more objects.
hashState = new MurmurHash3();
```

Both methods can be mixed however you like if you have different use cases.

---

### MurmurHash3.prototype.hash (string)
Incrementally add _string_ to the hash. This can be called as many times as you want for the hash state object, including after a call to `result()`. Returns `this` so calls can be chained.

---

### MurmurHash3.prototype.result ()
Get the result of the hash as a 32-bit positive integer. This performs the tail and finalizer portions of the algorithm, but does not store the result in the state object. This means that it is perfectly safe to get results and then continue adding strings via `hash`.

```javascript
// Do the whole string at once
MurmurHash3('this is a test string').result();
// 0x70529328

// Do part of the string, get a result, then the other part
var m = MurmurHash3('this is a');
m.result();
// 0xbfc4f834
m.hash(' test string').result();
// 0x70529328 (same as above)
```

---

### MurmurHash3.prototype.reset ([seed])
Reset the state object for reuse, optionally using the given _seed_ (defaults to 0 like the constructor). Returns `this` so calls can be chained.

---

License (MIT)
-------------
Copyright (c) 2013 Gary Court, Jens Taylor

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


code:
/**
 * @preserve
 * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013)
 *
 * @author Jens Taylor
 * @see http://github.com/homebrewing/brauhaus-diff
 * @author Gary Court
 * @see http://github.com/garycourt/murmurhash-js
 * @author Austin Appleby
 * @see http://sites.google.com/site/murmurhash/
 */
(function(){
    var cache;

    // Call this function without `new` to use the cached object (good for
    // single-threaded environments), or with `new` to create a new object.
    //
    // @param {string} key A UTF-16 or ASCII string
    // @param {number} seed An optional positive integer
    // @return {object} A MurmurHash3 object for incremental hashing
    function MurmurHash3(key, seed) {
        var m = this instanceof MurmurHash3 ? this : cache;
        m.reset(seed)
        if (typeof key === 'string' && key.length > 0) {
            m.hash(key);
        }

        if (m !== this) {
            return m;
        }
    };

    // Incrementally add a string to this hash
    //
    // @param {string} key A UTF-16 or ASCII string
    // @return {object} this
    MurmurHash3.prototype.hash = function(key) {
        var h1, k1, i, top, len;

        len = key.length;
        this.len += len;

        k1 = this.k1;
        i = 0;
        switch (this.rem) {
            case 0: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) : 0;
            case 1: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 8 : 0;
            case 2: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 16 : 0;
            case 3:
                k1 ^= len > i ? (key.charCodeAt(i) & 0xff) << 24 : 0;
                k1 ^= len > i ? (key.charCodeAt(i++) & 0xff00) >> 8 : 0;
        }

        this.rem = (len + this.rem) & 3; // & 3 is same as % 4
        len -= this.rem;
        if (len > 0) {
            h1 = this.h1;
            while (1) {
                k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
                k1 = (k1 << 15) | (k1 >>> 17);
                k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;

                h1 ^= k1;
                h1 = (h1 << 13) | (h1 >>> 19);
                h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff;

                if (i >= len) {
                    break;
                }

                k1 = ((key.charCodeAt(i++) & 0xffff)) ^
                     ((key.charCodeAt(i++) & 0xffff) << 8) ^
                     ((key.charCodeAt(i++) & 0xffff) << 16);
                top = key.charCodeAt(i++);
                k1 ^= ((top & 0xff) << 24) ^
                      ((top & 0xff00) >> 8);
            }

            k1 = 0;
            switch (this.rem) {
                case 3: k1 ^= (key.charCodeAt(i + 2) & 0xffff) << 16;
                case 2: k1 ^= (key.charCodeAt(i + 1) & 0xffff) << 8;
                case 1: k1 ^= (key.charCodeAt(i) & 0xffff);
            }

            this.h1 = h1;
        }

        this.k1 = k1;
        return this;
    };

    // Get the result of this hash
    //
    // @return {number} The 32-bit hash
    MurmurHash3.prototype.result = function() {
        var k1, h1;

        k1 = this.k1;
        h1 = this.h1;

        if (k1 > 0) {
            k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff;
            k1 = (k1 << 15) | (k1 >>> 17);
            k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff;
            h1 ^= k1;
        }

        h1 ^= this.len;

        h1 ^= h1 >>> 16;
        h1 = (h1 * 0xca6b + (h1 & 0xffff) * 0x85eb0000) & 0xffffffff;
        h1 ^= h1 >>> 13;
        h1 = (h1 * 0xae35 + (h1 & 0xffff) * 0xc2b20000) & 0xffffffff;
        h1 ^= h1 >>> 16;

        return h1 >>> 0;
    };

    // Reset the hash object for reuse
    //
    // @param {number} seed An optional positive integer
    MurmurHash3.prototype.reset = function(seed) {
        this.h1 = typeof seed === 'number' ? seed : 0;
        this.rem = this.k1 = this.len = 0;
        return this;
    };

    // A cached object to use. This can be safely used if you're in a single-
    // threaded environment, otherwise you need to create new hashes to use.
    cache = new MurmurHash3();

    if (typeof(module) != 'undefined') {
        module.exports = MurmurHash3;
    } else {
        this.MurmurHash3 = MurmurHash3;
    }
}());


imurmurhash

description: An incremental implementation of MurmurHash3

keywords: murmur murmurhash murmurhash3 hash incremental


code:
# inflight

Add callbacks to requests in flight to avoid async duplication

## USAGE

```javascript
var inflight = require('inflight')

// some request that does some stuff
function req(key, callback) {
  // key is any random string.  like a url or filename or whatever.
  //
  // will return either a falsey value, indicating that the
  // request for this key is already in flight, or a new callback
  // which when called will call all callbacks passed to inflightk
  // with the same key
  callback = inflight(key, callback)

  // If we got a falsey value back, then there's already a req going
  if (!callback) return

  // this is where you'd fetch the url or whatever
  // callback is also once()-ified, so it can safely be assigned
  // to multiple events etc.  First call wins.
  setTimeout(function() {
    callback(null, key)
  }, 100)
}

// only assigns a single setTimeout
// when it dings, all cbs get called
req('foo', cb1)
req('foo', cb2)
req('foo', cb3)
req('foo', cb4)
```


code:
var wrappy = require('wrappy')
var reqs = Object.create(null)
var once = require('once')

module.exports = wrappy(inflight)

function inflight (key, cb) {
  if (reqs[key]) {
    reqs[key].push(cb)
    return null
  } else {
    reqs[key] = [cb]
    return makeres(key)
  }
}

function makeres (key) {
  return once(function RES () {
    var cbs = reqs[key]
    var len = cbs.length
    var args = slice(arguments)

    // XXX It's somewhat ambiguous whether a new callback added in this
    // pass should be queued for later execution if something in the
    // list of callbacks throws, or if it should just be discarded.
    // However, it's such an edge case that it hardly matters, and either
    // choice is likely as surprising as the other.
    // As it happens, we do go ahead and schedule it for later execution.
    try {
      for (var i = 0; i < len; i++) {
        cbs[i].apply(null, args)
      }
    } finally {
      if (cbs.length > len) {
        // added more in the interim.
        // de-zalgo, just in case, but don't call again.
        cbs.splice(0, len)
        process.nextTick(function () {
          RES.apply(null, args)
        })
      } else {
        delete reqs[key]
      }
    }
  })
}

function slice (args) {
  var length = args.length
  var array = []

  for (var i = 0; i < length; i++) array[i] = args[i]
  return array
}


inflight

description: Add callbacks to requests in flight to avoid async duplication

test: tap test.js --100

author: Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)

license: ISC


code:
Browser-friendly inheritance fully compatible with standard node.js
[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).

This package exports standard `inherits` from node.js `util` module in
node environment, but also provides alternative browser-friendly
implementation through [browser
field](https://gist.github.com/shtylman/4339901). Alternative
implementation is a literal copy of standard one located in standalone
module to avoid requiring of `util`. It also has a shim for old
browsers with no `Object.create` support.

While keeping you sure you are using standard `inherits`
implementation in node.js environment, it allows bundlers such as
[browserify](https://github.com/substack/node-browserify) to not
include full `util` package to your client code if all you need is
just `inherits` function. It worth, because browser shim for `util`
package is large and `inherits` is often the single function you need
from it.

It's recommended to use this package instead of
`require('util').inherits` for any code that has chances to be used
not only in node.js but in browser too.

## usage

```js
var inherits = require('inherits');
// then use exactly as the standard one
```

## note on version ~1.0

Version ~1.0 had completely different motivation and is not compatible
neither with 2.0 nor with standard node.js `inherits`.

If you are using version ~1.0 and planning to switch to ~2.0, be
careful:

* new version uses `super_` instead of `super` for referencing
  superclass
* new version overwrites current prototype while old one preserves any
  existing fields on it


code:
try {
  var util = require('util');
  /* istanbul ignore next */
  if (typeof util.inherits !== 'function') throw '';
  module.exports = util.inherits;
} catch (e) {
  /* istanbul ignore next */
  module.exports = require('./inherits_browser.js');
}


code:
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false,
          writable: true,
          configurable: true
        }
      })
    }
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      var TempCtor = function () {}
      TempCtor.prototype = superCtor.prototype
      ctor.prototype = new TempCtor()
      ctor.prototype.constructor = ctor
    }
  }
}


inherits

description: Browser-friendly inheritance fully compatible with standard node.js inherits()

keywords: inheritance class klass oop object-oriented inherits browser browserify

test: tap


code:
# ipaddr.js — an IPv6 and IPv4 address manipulation library

[![Build Status](https://github.com/whitequark/ipaddr.js/workflows/CI%20Tests/badge.svg)](https://github.com/whitequark/ipaddr.js/actions?query=workflow%3A%22CI+Tests%22)

ipaddr.js is a small (1.9K minified and gzipped) library for manipulating
IP addresses in JavaScript environments. It runs on both CommonJS runtimes
(e.g. [nodejs]) and in a web browser.

ipaddr.js allows you to verify and parse string representation of an IP
address, match it against a CIDR range or range list, determine if it falls
into some reserved ranges (examples include loopback and private ranges),
and convert between IPv4 and IPv4-mapped IPv6 addresses.

[nodejs]: http://nodejs.org

## Installation

`npm install ipaddr.js`

or

`bower install ipaddr.js`

## Older Node support

Use 2.x release for nodejs versions 10+.
Use the 1.x release for versions of nodejs older than 10.

## API

ipaddr.js defines one object in the global scope: `ipaddr`. In CommonJS,
it is exported from the module:

```js
const ipaddr = require('ipaddr.js');
```

The API consists of several global methods and two classes: ipaddr.IPv6 and ipaddr.IPv4.

### Global methods

There are four global methods defined: `ipaddr.isValid`, `ipaddr.isValidCIDR`,
`ipaddr.parse`, and `ipaddr.process`. All of them receive a string as a single
parameter.

The `ipaddr.isValid` method returns `true` if the address is a valid IPv4 or
IPv6 address, and `false` otherwise. It does not throw any exceptions.

The `ipaddr.isValidCIDR` method returns `true` if the address is a valid IPv4 or
IPv6 address in CIDR notation, and `false` otherwise. It does not throw any exceptions.

The `ipaddr.parse` method returns an object representing the IP address,
or throws an `Error` if the passed string is not a valid representation of an
IP address.

The `ipaddr.process` method works just like the `ipaddr.parse` one, but it
automatically converts IPv4-mapped IPv6 addresses to their IPv4 counterparts
before returning. It is useful when you have a Node.js instance listening
on an IPv6 socket, and the `net.ivp6.bindv6only` sysctl parameter (or its
equivalent on non-Linux OS) is set to 0. In this case, you can accept IPv4
connections on your IPv6-only socket, but the remote address will be mangled.
Use `ipaddr.process` method to automatically demangle it.

### Object representation

Parsing methods return an object which descends from `ipaddr.IPv6` or
`ipaddr.IPv4`. These objects share some properties, but most of them differ.

#### Shared properties

One can determine the type of address by calling `addr.kind()`. It will return
either `"ipv6"` or `"ipv4"`.

An address can be converted back to its string representation with `addr.toString()`.
Note that this method:
 * does not return the original string used to create the object (in fact, there is
   no way of getting that string)
 * returns a compact representation (when it is applicable)

A `match(range, bits)` method can be used to check if the address falls into a
certain CIDR range. Note that an address can be (obviously) matched only against an address of the same type.

For example:

```js
const addr  = ipaddr.parse('2001:db8:1234::1');
const range = ipaddr.parse('2001:db8::');

addr.match(range, 32); // => true
```

Alternatively, `match` can also be called as `match([range, bits])`. In this way, it can be used together with the `parseCIDR(string)` method, which parses an IP address together with a CIDR range.

For example:

```js
const addr = ipaddr.parse('2001:db8:1234::1');

addr.match(ipaddr.parseCIDR('2001:db8::/32')); // => true
```

A `range()` method returns one of predefined names for several special ranges defined by IP protocols. The exact names (and their respective CIDR ranges) can be looked up in the source: [IPv6 ranges] and [IPv4 ranges]. Some common ones include `"unicast"` (the default one) and `"reserved"`.

You can match against your own range list by using
`ipaddr.subnetMatch(address, rangeList, defaultName)` method. It can work with a mix of IPv6 or IPv4 addresses, and accepts a name-to-subnet map as the range list. For example:

```js
const rangeList = {
  documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ],
  tunnelProviders: [
    [ ipaddr.parse('2001:470::'), 32 ], // he.net
    [ ipaddr.parse('2001:5c0::'), 32 ]  // freenet6
  ]
};
ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown'); // => "tunnelProviders"
```

The addresses can be converted to their byte representation with `toByteArray()`. (Actually, JavaScript mostly does not know about byte buffers. They are emulated with arrays of numbers, each in range of 0..255.)

```js
const bytes = ipaddr.parse('2a00:1450:8007::68').toByteArray(); // ipv6.google.com
bytes // => [42, 0x00, 0x14, 0x50, 0x80, 0x07, 0x00, <zeroes...>, 0x00, 0x68 ]
```

The `ipaddr.IPv4` and `ipaddr.IPv6` objects have some methods defined, too. All of them have the same interface for both protocols, and are similar to global methods.

`ipaddr.IPvX.isValid(string)` can be used to check if the string is a valid address for particular protocol, and `ipaddr.IPvX.parse(string)` is the error-throwing parser.

`ipaddr.IPvX.isValid(string)` uses the same format for parsing as the POSIX `inet_ntoa` function, which accepts unusual formats like `0xc0.168.1.1` or `0x10000000`. The function `ipaddr.IPv4.isValidFourPartDecimal(string)` validates the IPv4 address and also ensures that it is written in four-part decimal format.

[IPv6 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L530
[IPv4 ranges]: https://github.com/whitequark/ipaddr.js/blob/master/lib/ipaddr.js#L182

#### IPv6 properties

Sometimes you will want to convert IPv6 not to a compact string representation (with the `::` substitution); the `toNormalizedString()` method will return an address where all zeroes are explicit.

For example:

```js
const addr = ipaddr.parse('2001:0db8::0001');
addr.toString(); // => '2001:db8::1'
addr.toNormalizedString(); // => '2001:db8:0:0:0:0:0:1'
```

The `isIPv4MappedAddress()` method will return `true` if this address is an IPv4-mapped
one, and `toIPv4Address()` will return an IPv4 object address.

To access the underlying binary representation of the address, use `addr.parts`.

```js
const addr = ipaddr.parse('2001:db8:10::1234:DEAD');
addr.parts // => [0x2001, 0xdb8, 0x10, 0, 0, 0, 0x1234, 0xdead]
```

A IPv6 zone index can be accessed via `addr.zoneId`:

```js
const addr = ipaddr.parse('2001:db8::%eth0');
addr.zoneId // => 'eth0'
```

#### IPv4 properties

`toIPv4MappedAddress()` will return a corresponding IPv4-mapped IPv6 address.

To access the underlying representation of the address, use `addr.octets`.

```js
const addr = ipaddr.parse('192.168.1.1');
addr.octets // => [192, 168, 1, 1]
```

`prefixLengthFromSubnetMask()` will return a CIDR prefix length for a valid IPv4 netmask or
null if the netmask is not valid.

```js
ipaddr.IPv4.parse('255.255.255.240').prefixLengthFromSubnetMask() == 28
ipaddr.IPv4.parse('255.192.164.0').prefixLengthFromSubnetMask()  == null
```

`subnetMaskFromPrefixLength()` will return an IPv4 netmask for a valid CIDR prefix length.

```js
ipaddr.IPv4.subnetMaskFromPrefixLength(24) == '255.255.255.0'
ipaddr.IPv4.subnetMaskFromPrefixLength(29) == '255.255.255.248'
```

`broadcastAddressFromCIDR()` will return the broadcast address for a given IPv4 interface and netmask in CIDR notation.
```js
ipaddr.IPv4.broadcastAddressFromCIDR('172.0.0.1/24') == '172.0.0.255'
```
`networkAddressFromCIDR()` will return the network address for a given IPv4 interface and netmask in CIDR notation.
```js
ipaddr.IPv4.networkAddressFromCIDR('172.0.0.1/24') == '172.0.0.0'
```

#### Conversion

IPv4 and IPv6 can be converted bidirectionally to and from network byte order (MSB) byte arrays.

The `fromByteArray()` method will take an array and create an appropriate IPv4 or IPv6 object
if the input satisfies the requirements. For IPv4 it has to be an array of four 8-bit values,
while for IPv6 it has to be an array of sixteen 8-bit values.

For example:
```js
const addr = ipaddr.fromByteArray([0x7f, 0, 0, 1]);
addr.toString(); // => '127.0.0.1'
```

or

```js
const addr = ipaddr.fromByteArray([0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
addr.toString(); // => '2001:db8::1'
```

Both objects also offer a `toByteArray()` method, which returns an array in network byte order (MSB).

For example:
```js
const addr = ipaddr.parse('127.0.0.1');
addr.toByteArray(); // => [0x7f, 0, 0, 1]
```

or

```js
const addr = ipaddr.parse('2001:db8::1');
addr.toByteArray(); // => [0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
```