declare module 'datascript' {
  type Db = string & {_opaque: typeof Db};
  const Db: unique symbol;

  type Conn = string & {_opaque: typeof Conn};
  const Conn: unique symbol;

  interface Iterable<T> {
    next(): { value: null, done: true } | { value: T, done: false }
  }

  interface Entity {
    get(x: Attr): Value
    key_set(): Attr[];
    value_set(): Value[];
    entry_set(): [Attr, Value][];
    forEach(f: (v: Value, a: Attr) => undefined): undefined;
    keys(): Iterable<Attr>
    values(): Iterable<Value>
    entries(): Iterable<[Attr, Value]>
  }

  type Attr = string
  type Value = any
  type Datom = [number, Attr, Value, number];
  type TxReport = {
    db_before: Db
    db_after: Db
    tx_data: Tx[]
    tempids: Record<string, number>,
    tx_meta: TxMeta
  };
  type TxMeta = any;

  export type Schema = any;
  export type Tx = any;

  export function empty_db() : Db;
  export function empty_db(schema: Schema) : Db;
  
  export function init_db(datoms: Datom[]): Db;
  export function init_db(datoms: Datom[], schema: Schema): Db;

  export function serializable(db: Db): any;
  export function from_serializable(x: any): Db;
  export function q(query: string, ...inputs: any[]): any;
  export function pull(db: Db, selector: any, eid: number): any;
  export function pull_many(db: Db, selector: any, eid: number[]): any[];
  export function db_with(db: Db, tx_data: Tx[]): Db;
  export function entity(db: Db, eid: number): Entity | null;
  export function touch(db: Db): Db;
  export function entity_db(entity: Entity): Db;
  export function filter(db: Db, pred: (db: Db, datom: Datom) => boolean): Db;
  export function is_filtered(db: Db) : boolean;
  export function create_conn() : Conn;
  export function create_conn(schema: Schema): Conn;
  export function conn_from_db(db: Db): Conn;
  export function conn_from_datoms(datoms: Datom[]): Conn;
  export function db(conn: Conn): Db;
  export function transact(conn: Conn, tx_data: Tx[]): TxReport;
  export function transact(conn: Conn, tx_data: Tx[], tx_meta: any): TxReport;
  export function reset_conn(conn: Conn, db: Db): TxReport;
  export function reset_conn(conn: Conn, db: Db, tx_meta: TxMeta): TxReport;
  export function listen(conn: Conn, callback: (tx_report: TxReport) => undefined): undefined;
  export function listen(conn: Conn, key: string, callback: (tx_report: TxReport) => undefined): undefined;
  export function unlisten(conn: Conn, key: string): undefined;
  export function resolve_tempid(db: Db, tempids: Record<string, number>, tempid: string): number;
  type Index = ":eavt" | ":aevt" | ":avet"
  export function datoms(db: Db, index: Index, ...c: any[]): Datom[];
  export function seek_datoms(db: Db, index: Index, ...c: any[]): Datom[];
  export function index_range(db: Db, attr: string, start: Value, end: Value): Datom[];
  export function squuid(): string;
  export function squuid(msec: number): string;
  export function squuid_time_millis(uuid: string): number;
}