about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-06-03 20:42:16 +0200
committerbptato <nincsnevem662@gmail.com>2024-06-03 21:15:44 +0200
commit3aa8f1e0694d1606c3f3795f8b83e8a82caacd3e (patch)
tree9708d4599360116a96e4aa7f983eea387e8437c6 /src
parent3e12a95ab34e120fb958ba0eeebaada5def7cd11 (diff)
downloadchawan-3aa8f1e0694d1606c3f3795f8b83e8a82caacd3e.tar.gz
Move JS wrapper into Monoucha
Operation "modularize Chawan somewhat" part 3
Diffstat (limited to 'src')
-rw-r--r--src/bindings/constcharp.nim6
-rw-r--r--src/bindings/libregexp.nim30
-rw-r--r--src/bindings/libunicode.nim44
-rw-r--r--src/bindings/quickjs.nim573
-rw-r--r--src/config/chapath.nim8
-rw-r--r--src/config/config.nim17
-rw-r--r--src/html/chadombuilder.nim19
-rw-r--r--src/html/dom.nim22
-rw-r--r--src/html/env.nim11
-rw-r--r--src/html/event.nim14
-rw-r--r--src/html/formdata.nim8
-rw-r--r--src/html/script.nim2
-rw-r--r--src/html/xmlhttprequest.nim7
-rw-r--r--src/io/promise.nim12
-rw-r--r--src/io/urlfilter.nim2
-rw-r--r--src/js/base64.nim6
-rw-r--r--src/js/console.nim4
-rw-r--r--src/js/domexception.nim7
-rw-r--r--src/js/encoding.nim10
-rw-r--r--src/js/fromjs.nim445
-rw-r--r--src/js/intl.nim8
-rw-r--r--src/js/javascript.nim1719
-rw-r--r--src/js/jscolor.nim11
-rw-r--r--src/js/jserror.nim59
-rw-r--r--src/js/jsmodule.nim8
-rw-r--r--src/js/jsopaque.nim128
-rw-r--r--src/js/jspropenumlist.nim42
-rw-r--r--src/js/jsregex.nim150
-rw-r--r--src/js/jstypes.nim43
-rw-r--r--src/js/jsutils.nim9
-rw-r--r--src/js/timeout.nim2
-rw-r--r--src/js/tojs.nim399
-rw-r--r--src/loader/headers.nim9
-rw-r--r--src/loader/loader.nim5
-rw-r--r--src/loader/request.nim11
-rw-r--r--src/loader/response.nim14
-rw-r--r--src/local/client.nim18
-rw-r--r--src/local/container.nim8
-rw-r--r--src/local/lineedit.nim4
-rw-r--r--src/local/pager.nim19
-rw-r--r--src/local/select.nim2
-rw-r--r--src/main.nim11
-rw-r--r--src/server/buffer.nim12
-rw-r--r--src/types/blob.nim6
-rw-r--r--src/types/cookie.nim6
-rw-r--r--src/types/formdata.nim2
-rw-r--r--src/types/url.nim7
-rw-r--r--src/utils/luwrap.nim5
-rw-r--r--src/utils/regexutils.nim64
-rw-r--r--src/version.nim2
50 files changed, 234 insertions, 3796 deletions
diff --git a/src/bindings/constcharp.nim b/src/bindings/constcharp.nim
deleted file mode 100644
index f1753147..00000000
--- a/src/bindings/constcharp.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-type
-  cstringConstImpl {.importc: "const char*".} = cstring
-  cstringConst* = distinct cstringConstImpl
-
-proc `[]`*(s: cstringConst; i: int): char = cstring(s)[i]
-proc `$`*(s: cstringConst): string {.borrow.}
diff --git a/src/bindings/libregexp.nim b/src/bindings/libregexp.nim
deleted file mode 100644
index fe603908..00000000
--- a/src/bindings/libregexp.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-type
-  LREFlag* {.size: sizeof(cint).} = enum
-    LRE_FLAG_GLOBAL = "g"
-    LRE_FLAG_IGNORECASE = "i"
-    LRE_FLAG_MULTILINE = "m"
-    LRE_FLAG_DOTALL = "s"
-    LRE_FLAG_UNICODE = "u"
-    LRE_FLAG_STICKY = "y"
-
-  LREFlags* = set[LREFlag]
-
-func toCInt*(flags: LREFlags): cint =
-  cint(cast[uint8](flags))
-
-func toLREFlags*(flags: cint): LREFlags =
-  cast[LREFlags](flags)
-
-{.passc: "-Ilib/".}
-
-{.push header: "quickjs/libregexp.h", importc.}
-proc lre_compile*(plen: ptr cint; error_msg: cstring; error_msg_size: cint;
-  buf: cstring; buf_len: csize_t; re_flags: cint; opaque: pointer): ptr uint8
-
-proc lre_exec*(capture: ptr ptr uint8; bc_buf, cbuf: ptr uint8;
-  cindex, clen, cbuf_type: cint; opaque: pointer): cint
-
-proc lre_get_capture_count*(bc_buf: ptr uint8): cint
-
-proc lre_get_flags*(bc_buf: ptr uint8): cint
-{.pop.}
diff --git a/src/bindings/libunicode.nim b/src/bindings/libunicode.nim
deleted file mode 100644
index 18d146fe..00000000
--- a/src/bindings/libunicode.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-type
-  DynBufReallocFunc = proc(opaque, p: pointer; size: csize_t): pointer {.cdecl.}
-
-  CharRange* = object
-    len*: cint # in points, always even
-    size*: cint
-    points*: ptr uint32 # points sorted by increasing value
-    mem_opaque*: pointer
-    realloc_func*: DynBufReallocFunc
-
-  UnicodeNormalizationEnum* {.size: sizeof(cint).} = enum
-    UNICODE_NFC, UNICODE_NFD, UNICODE_NKFC, UNICODE_NKFD
-
-{.passc: "-Ilib/".}
-
-{.push header: "quickjs/libunicode.h", importc.}
-
-proc cr_init*(cr: ptr CharRange; mem_opaque: pointer;
-  realloc_func: DynBufReallocFunc)
-
-proc cr_free*(cr: ptr CharRange)
-
-proc unicode_normalize*(pdst: ptr ptr uint32; src: ptr uint32; src_len: cint;
-  n_type: UnicodeNormalizationEnum; opaque: pointer;
-  realloc_func: DynBufReallocFunc): cint
-
-proc unicode_script*(cr: ptr CharRange; script_name: cstring; is_ext: cint):
-  cint
-proc unicode_prop*(cr: ptr CharRange; prop_name: cstring): cint
-proc unicode_general_category*(cr: ptr CharRange; gc_name: cstring): cint
-
-const LRE_CC_RES_LEN_MAX* = 3
-
-# conv_type:
-# 0 = to upper
-# 1 = to lower
-# 2 = case folding
-# res must be an array of LRE_CC_RES_LEN_MAX
-proc lre_case_conv*(res: ptr UncheckedArray[uint32]; c: uint32;
-  conv_type: cint): cint
-
-proc lre_is_space*(c: uint32): cint
-
-{.pop.}
diff --git a/src/bindings/quickjs.nim b/src/bindings/quickjs.nim
deleted file mode 100644
index 20dd048b..00000000
--- a/src/bindings/quickjs.nim
+++ /dev/null
@@ -1,573 +0,0 @@
-import bindings/constcharp
-
-const qjsheader = "quickjs/quickjs.h"
-
-{.passc: "-Ilib/".}
-{.passl: "-Llib/ -lquickjs -lm -lpthread".}
-
-const                         ##  all tags with a reference count are negative
-  JS_TAG_FIRST* = -10           ##  first negative tag
-  JS_TAG_BIG_INT* = -10
-  JS_TAG_BIG_FLOAT* = -9
-  JS_TAG_SYMBOL* = -8
-  JS_TAG_STRING* = -7
-  JS_TAG_SHAPE* = -6            ##  used internally during GC
-  JS_TAG_ASYNC_FUNCTION* = -5   ##  used internally during GC
-  JS_TAG_VAR_REF* = -4          ##  used internally during GC
-  JS_TAG_MODULE* = -3           ##  used internally
-  JS_TAG_FUNCTION_BYTECODE* = -2 ##  used internally
-  JS_TAG_OBJECT* = -1
-  JS_TAG_INT* = 0
-  JS_TAG_BOOL* = 1
-  JS_TAG_NULL* = 2
-  JS_TAG_UNDEFINED* = 3
-  JS_TAG_UNINITIALIZED* = 4
-  JS_TAG_CATCH_OFFSET* = 5
-  JS_TAG_EXCEPTION* = 6
-  JS_TAG_FLOAT64* = 7           ##  any larger tag is FLOAT64 if JS_NAN_BOXING
-
-when sizeof(int) < sizeof(int64):
-  {.passc: "-DJS_NAN_BOXING".}
-  type
-    JSValue* {.importc, header: qjsheader.} = distinct uint64
-
-  template JS_VALUE_GET_TAG*(v: untyped): int32 =
-    cast[int32](cast[uint64](v) shr 32)
-
-  template JS_VALUE_GET_PTR*(v: untyped): pointer =
-    cast[pointer](v)
-
-  template JS_MKVAL*(t, val: untyped): JSValue =
-    JSValue((cast[uint64](int64(t)) shl 32) or uint32(val))
-
-  template JS_MKPTR*(t, p: untyped): JSValue =
-    JSValue((cast[uint64](int64(t)) shl 32) or cast[uint](p))
-
-  proc `==`*(a, b: JSValue): bool {.borrow.}
-else:
-  type
-    JSValueUnion* {.importc, header: qjsheader, union.} = object
-      int32*: int32
-      float64*: float64
-      `ptr`*: pointer
-    JSValue* {.importc, header: qjsheader.} = object
-      u*: JSValueUnion
-      tag*: int64
-
-  template JS_VALUE_GET_TAG*(v: untyped): int32 =
-    cast[int32](v.tag)
-
-  template JS_VALUE_GET_PTR*(v: untyped): pointer =
-    cast[pointer](v.u)
-
-  template JS_MKVAL*(t, val: untyped): JSValue =
-    JSValue(u: JSValueUnion(`int32`: val), tag: t)
-
-  template JS_MKPTR*(t, p: untyped): JSValue =
-    JSValue(u: JSValueUnion(`ptr`: p), tag: t)
-
-type
-  JSRuntimeT {.importc: "JSRuntime", header: qjsheader,
-    incompleteStruct.} = object
-  JSContextT {.importc: "JSContext", header: qjsheader,
-    incompleteStruct.} = object
-  JSModuleDefT {.importc: "JSModuleDef", header: qjsheader,
-    incompleteStruct.} = object
-
-  JSRuntime* = ptr JSRuntimeT
-  JSContext* = ptr JSContextT
-  JSModuleDef* = ptr JSModuleDefT
-  JSCFunction* = proc(ctx: JSContext; this_val: JSValue; argc: cint;
-      argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.}
-  JSCFunctionData* = proc(ctx: JSContext; this_val: JSValue; argc: cint;
-    argv: ptr UncheckedArray[JSValue]; magic: cint;
-    func_data: ptr UncheckedArray[JSValue]): JSValue {.cdecl.}
-  JSGetterFunction* = proc(ctx: JSContext; this_val: JSValue): JSValue {.cdecl.}
-  JSSetterFunction* = proc(ctx: JSContext; this_val, val: JSValue):
-    JSValue {.cdecl.}
-  JSGetterMagicFunction* = proc(ctx: JSContext; this_val: JSValue; magic: cint):
-    JSValue {.cdecl.}
-  JSSetterMagicFunction* = proc(ctx: JSContext; this_val, val: JSValue;
-    magic: cint): JSValue {.cdecl.}
-  JSInterruptHandler* = proc(rt: JSRuntime; opaque: pointer): cint {.cdecl.}
-  JSClassID* = uint32
-  JSAtom* = distinct uint32
-  JSClassFinalizer* = proc(rt: JSRuntime; val: JSValue) {.cdecl.}
-  JSClassCheckDestroy* = proc(rt: JSRuntime; val: JSValue): JS_BOOL {.cdecl.}
-  JSClassGCMark* = proc(rt: JSRuntime; val: JSValue; mark_func: JS_MarkFunc)
-    {.cdecl.}
-  JS_MarkFunc* = proc(rt: JSRuntime; gp: ptr JSGCObjectHeader) {.cdecl.}
-  JSModuleNormalizeFunc* = proc(ctx: JSContext; module_base_name,
-    module_name: cstringConst; opaque: pointer): cstring {.cdecl.}
-  JSModuleLoaderFunc* = proc(ctx: JSContext; module_name: cstringConst,
-    opaque: pointer): JSModuleDef {.cdecl.}
-  JSJobFunc* = proc(ctx: JSContext; argc: cint;
-    argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.}
-  JSGCObjectHeader* {.importc, header: qjsheader.} = object
-  JSFreeArrayBufferDataFunc* = proc(rt: JSRuntime; opaque, p: pointer) {.cdecl.}
-
-  JSPropertyDescriptor* {.importc, header: qjsheader.} = object
-    flags*: cint
-    value*: JSValue
-    getter*: JSValue
-    setter*: JSValue
-
-  JSClassExoticMethods* {.importc, header: qjsheader.} =  object
-    get_own_property*: proc(ctx: JSContext; desc: ptr JSPropertyDescriptor;
-      obj: JSValue; prop: JSAtom): cint {.cdecl.}
-    get_own_property_names*: proc(ctx: JSContext;
-      ptab: ptr ptr UncheckedArray[JSPropertyEnum]; plen: ptr uint32;
-      obj: JSValue): cint {.cdecl.}
-    delete_property*: proc(ctx: JSContext; obj: JSValue; prop: JSAtom): cint
-      {.cdecl.}
-    define_own_property*: proc(ctx: JSContext; this_obj: JSValue; prop: JSAtom;
-      val, getter, setter: JSValue; flags: cint): cint {.cdecl.}
-    has_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom): cint
-      {.cdecl.}
-    get_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom;
-      receiver: JSValue; flags: cint): JSValue {.cdecl.}
-    set_property*: proc(ctx: JSContext; obj: JSValue; atom: JSAtom;
-      value, receiver: JSValue; flags: cint): cint {.cdecl.}
-
-  JSClassExoticMethodsConst* {.importc: "const JSClassExoticMethods *",
-    header: qjsheader.} = ptr JSClassExoticMethods
-
-  JSRuntimeCleanUpFunc* {.importc.} = proc(rt: JSRuntime) {.cdecl.}
-
-  JSClassDef* {.importc, header: qjsheader.} = object
-    class_name*: cstring
-    finalizer*: JSClassFinalizer
-    gc_mark*: JSClassGCMark
-    call*: pointer
-    exotic*: JSClassExoticMethodsConst
-    can_destroy*: JSClassCheckDestroy
-
-  JSClassDefConst* {.importc: "const JSClassDef *",
-    header: qjsheader.} = ptr JSClassDef
-
-  JSMemoryUsage* = object
-    malloc_size*, malloc_limit*, memory_used_size*: int64
-    malloc_count*: int64
-    memory_used_count*: int64
-    atom_count*, atom_size*: int64
-    str_count*, str_size*: int64
-    obj_count*, obj_size*: int64
-    prop_count*, prop_size*: int64
-    shape_count*, shape_size*: int64
-    js_func_count*, js_func_size*, js_func_code_size*: int64
-    js_func_pc2line_count*, js_func_pc2line_size*: int64
-    c_func_count*, array_count*: int64
-    fast_array_count*, fast_array_elements*: int64
-    binary_object_count*, binary_object_size*: int64
-
-  JSCFunctionEnum* {.size: sizeof(uint8).} = enum
-    JS_CFUNC_generic, JS_CFUNC_generic_magic, JS_CFUNC_constructor,
-    JS_CFUNC_constructor_magic, JS_CFUNC_constructor_or_func,
-    JS_CFUNC_constructor_or_func_magic, JS_CFUNC_f_f, JS_CFUNC_f_f_f,
-    JS_CFUNC_getter, JS_CFUNC_setter, JS_CFUNC_getter_magic,
-    JS_CFUNC_setter_magic, JS_CFUNC_iterator_next
-
-  JSCFunctionType* {.importc, union.} = object
-    generic*: JSCFunction
-    getter*: JSGetterFunction
-    setter*: JSSetterFunction
-    getter_magic*: JSGetterMagicFunction
-    setter_magic*: JSSetterMagicFunction
-
-  JSCFunctionListEntryFunc = object
-    length*: uint8
-    cproto*: JSCFunctionEnum
-    cfunc*: JSCFunctionType
-
-  JSCFunctionListEntryGetSet = object
-    get*: JSCFunctionType
-    set*: JSCFunctionType
-
-  JSCFunctionListEntryAlias = object
-    name: cstring
-    base: cint
-
-  JSCFunctionListEntryPropList = object
-    tab: ptr UncheckedArray[JSCFunctionListEntry]
-    len: cint
-
-  JSCFunctionListEntryU* {.union.} = object
-    `func`* {.importc: "func".}: JSCFunctionListEntryFunc
-    getset: JSCFunctionListEntryGetSet
-    alias: JSCFunctionListEntryAlias
-    prop_list: JSCFunctionListEntryPropList
-    str: cstring
-    i32: int32
-    i64: int64
-    f64: cdouble
-
-  JSCFunctionListEntry* {.importc.} = object
-    name*: cstring
-    prop_flags*: uint8
-    def_type*: uint8
-    magic*: int16
-    u* {.importc.}: JSCFunctionListEntryU
-
-  JSRefCountHeader* {.importc.} = object
-    ref_count* {.importc.}: cint
-
-  JS_BOOL* = distinct cint
-
-  JSPropertyEnum* {.importc.} = object
-    is_enumerable*: JS_BOOL
-    atom*: JSAtom
-
-  JSClassEnum* {.size: sizeof(uint32).} = enum
-    JS_CLASS_OBJECT = 1
-    JS_CLASS_ARRAY
-    JS_CLASS_ERROR
-
-  JSMallocState* {.importc.} = object
-    malloc_count*: csize_t
-    malloc_size*: csize_t
-    malloc_limit*: csize_t
-    opaque*: pointer
-
-  JSMallocFunctions* {.importc.} = object
-    js_malloc*: proc(s: ptr JSMallocState; size: csize_t): pointer {.cdecl.}
-    js_free*: proc(s: ptr JSMallocState; p: pointer) {.cdecl.}
-    js_realloc*: proc(s: ptr JSMallocState; p: pointer; size: csize_t): pointer
-      {.cdecl.}
-    js_malloc_usable_size*: proc(p: pointer) {.cdecl.}
-
-func `==`*(a, b: JSAtom): bool {.borrow.}
-
-converter toBool*(js: JS_BOOL): bool {.inline.} =
-  cast[cint](js) != 0
-
-converter toJSBool*(b: bool): JS_BOOL {.inline.} =
-  cast[JS_BOOL](cint(b))
-
-converter toJSClassID*(e: JSClassEnum): JSClassID {.inline.} =
-  JSClassID(e)
-
-const
-  JS_NULL* = JS_MKVAL(JS_TAG_NULL, 0)
-  JS_UNDEFINED* = JS_MKVAL(JS_TAG_UNDEFINED, 0)
-  JS_FALSE* = JS_MKVAL(JS_TAG_BOOL, 0)
-  JS_TRUE* = JS_MKVAL(JS_TAG_BOOL, 1)
-  JS_EXCEPTION* = JS_MKVAL(JS_TAG_EXCEPTION, 0)
-  JS_UNINITIALIZED* = JS_MKVAL(JS_TAG_UNINITIALIZED, 0)
-
-const
-  JS_EVAL_TYPE_GLOBAL* = (0 shl 0) ##  global code (default)
-  JS_EVAL_TYPE_MODULE* = (1 shl 0) ##  module code
-  JS_EVAL_TYPE_DIRECT* = (2 shl 0) ##  direct call (internal use)
-  JS_EVAL_TYPE_INDIRECT* = (3 shl 0) ##  indirect call (internal use)
-  JS_EVAL_TYPE_MASK* = (3 shl 0)
-  JS_EVAL_FLAG_SHEBANG* = (1 shl 2) ##  skip first line beginning with '#!'
-  JS_EVAL_FLAG_STRICT* = (1 shl 3) ##  force 'strict' mode
-  JS_EVAL_FLAG_STRIP* = (1 shl 4) ##  force 'strip' mode
-  JS_EVAL_FLAG_COMPILE_ONLY* = (1 shl 5) ##  internal use
-
-const
-  JS_DEF_CFUNC* = 0
-  JS_DEF_CGETSET* = 1
-  JS_DEF_CGETSET_MAGIC* = 2
-  JS_DEF_PROP_STRING* = 3
-  JS_DEF_PROP_INT32* = 4
-  JS_DEF_PROP_INT64* = 5
-  JS_DEF_PROP_DOUBLE* = 6
-  JS_DEF_PROP_UNDEFINED* = 7
-  JS_DEF_OBJECT* = 8
-  JS_DEF_ALIAS* = 9
-
-const
-  JS_PROP_CONFIGURABLE* = (1 shl 0)
-  JS_PROP_WRITABLE* = (1 shl 1)
-  JS_PROP_ENUMERABLE* = (1 shl 2)
-  JS_PROP_C_W_E* = (JS_PROP_CONFIGURABLE or JS_PROP_WRITABLE or
-    JS_PROP_ENUMERABLE)
-  JS_PROP_LENGTH* = (1 shl 3) # used internally in Arrays
-  JS_PROP_TMASK* = (3 shl 4) # mask for NORMAL, GETSET, VARREF, AUTOINIT
-  JS_PROP_NORMAL* = (0 shl 4)
-  JS_PROP_GETSET* = (1 shl 4)
-  JS_PROP_VARREF* = (2 shl 4) # used internally
-  JS_PROP_AUTOINIT* = (3 shl 4) # used internally
-  JS_PROP_THROW* = (1 shl 14)
-
-const
-  JS_GPN_STRING_MASK* = (1 shl 0)
-  JS_GPN_SYMBOL_MASK* = (1 shl 1)
-  JS_GPN_PRIVATE_MASK* = (1 shl 2)
-  JS_GPN_ENUM_ONLY* = (1 shl 3)
-  JS_GPN_SET_ENUM* = (1 shl 4)
-
-const
-  JS_PARSE_JSON_EXT* = (1 shl 0)
-
-template JS_CFUNC_DEF*(n: string; len: uint8; func1: JSCFunction):
-    JSCFunctionListEntry =
-  JSCFunctionListEntry(name: cstring(n),
-                       prop_flags: JS_PROP_WRITABLE or JS_PROP_CONFIGURABLE,
-                       def_type: JS_DEF_CFUNC,
-                       u: JSCFunctionListEntryU(
-                         `func`: JSCFunctionListEntryFunc(
-                           length: len,
-                           cproto: JS_CFUNC_generic,
-                           cfunc: JSCFunctionType(generic: func1))))
-
-template JS_CFUNC_DEF_NOCONF*(n: string; len: uint8; func1: JSCFunction):
-    JSCFunctionListEntry =
-  JSCFunctionListEntry(name: cstring(n),
-                       prop_flags: JS_PROP_ENUMERABLE,
-                       def_type: JS_DEF_CFUNC,
-                       u: JSCFunctionListEntryU(
-                         `func`: JSCFunctionListEntryFunc(
-                           length: len,
-                           cproto: JS_CFUNC_generic,
-                           cfunc: JSCFunctionType(generic: func1))))
-
-template JS_CGETSET_DEF*(n: string; fgetter: JSGetterFunction;
-    fsetter: JSSetterFunction): JSCFunctionListEntry =
-  JSCFunctionListEntry(name: cstring(n),
-                       prop_flags: JS_PROP_CONFIGURABLE,
-                       def_type: JS_DEF_CGETSET,
-                       u: JSCFunctionListEntryU(
-                         getset: JSCFunctionListEntryGetSet(
-                           get: JSCFunctionType(getter: fgetter),
-                           set: JSCFunctionType(setter: fsetter))))
-
-template JS_CGETSET_DEF_NOCONF*(n: string; fgetter: JSGetterFunction;
-    fsetter: JSSetterFunction): JSCFunctionListEntry =
-  JSCFunctionListEntry(name: cstring(n),
-                       prop_flags: JS_PROP_ENUMERABLE,
-                       def_type: JS_DEF_CGETSET,
-                       u: JSCFunctionListEntryU(
-                         getset: JSCFunctionListEntryGetSet(
-                           get: JSCFunctionType(getter: fgetter),
-                           set: JSCFunctionType(setter: fsetter))))
-
-template JS_CGETSET_MAGIC_DEF*(n: string; fgetter, fsetter: typed;
-    m: int16): JSCFunctionListEntry =
-  JSCFunctionListEntry(name: cstring(n),
-                       prop_flags: JS_PROP_CONFIGURABLE,
-                       def_type: JS_DEF_CGETSET_MAGIC,
-                       magic: m,
-                       u: JSCFunctionListEntryU(
-                         getset: JSCFunctionListEntryGetSet(
-                           get: JSCFunctionType(getter_magic: fgetter),
-                           set: JSCFunctionType(setter_magic: fsetter))))
-
-{.push header: qjsheader, importc, cdecl.}
-
-proc JS_NewRuntime*(): JSRuntime
-proc JS_NewRuntime2*(mf: ptr JSMallocFunctions; opaque: pointer): JSRuntime
-proc JS_FreeRuntime*(rt: JSRuntime)
-proc JS_GetRuntime*(ctx: JSContext): JSRuntime
-
-proc JS_ComputeMemoryUsage*(rt: JSRuntime; s: ptr JSMemoryUsage)
-proc JS_RunGC*(rt: JSRuntime)
-
-proc JS_NewContext*(rt: JSRuntime): JSContext
-proc JS_NewContextRaw*(rt: JSRuntime): JSContext
-proc JS_FreeContext*(ctx: JSContext)
-
-proc JS_GetGlobalObject*(ctx: JSContext): JSValue
-proc JS_IsInstanceOf*(ctx: JSContext; val: JSValue; obj: JSValue): cint
-
-proc JS_NewArray*(ctx: JSContext): JSValue
-proc JS_NewObject*(ctx: JSContext): JSValue
-proc JS_NewObjectClass*(ctx: JSContext; class_id: JSClassID): JSValue
-proc JS_NewObjectProto*(ctx: JSContext; proto: JSValue): JSValue
-proc JS_NewObjectProtoClass*(ctx: JSContext; proto: JSValue;
-  class_id: JSClassID): JSValue
-proc JS_NewPromiseCapability*(ctx: JSContext;
-  resolving_funcs: ptr UncheckedArray[JSValue]): JSValue
-proc JS_SetOpaque*(obj: JSValue; opaque: pointer)
-proc JS_GetOpaque*(obj: JSValue; class_id: JSClassID): pointer
-proc JS_GetOpaque2*(ctx: JSContext; obj: JSValue; class_id: JSClassID): pointer
-proc JS_GetClassID*(obj: JSValue): JSClassID
-
-proc JS_ParseJSON*(ctx: JSContext; buf: cstring; buf_len: csize_t;
-  filename: cstring): JSValue
-proc JS_ParseJSON2*(ctx: JSContext; buf: cstring; buf_len: csize_t;
-  filename: cstring; flags: cint): JSValue
-
-proc JS_NewArrayBuffer*(ctx: JSContext; buf: ptr UncheckedArray[uint8];
-  len: csize_t; free_func: JSFreeArrayBufferDataFunc; opaque: pointer;
-  is_shared: JS_BOOL): JSValue
-proc JS_GetArrayBuffer*(ctx: JSContext; psize: ptr csize_t; obj: JSValue):
-  ptr uint8
-proc JS_GetTypedArrayBuffer*(ctx: JSContext; obj: JSValue;
-  pbyte_offset, pbyte_length, pbytes_per_element: ptr csize_t): JSValue
-
-proc JS_NewClassID*(pclass_id: ptr JSClassID): JSClassID
-proc JS_NewClass*(rt: JSRuntime; class_id: JSClassID;
-  class_def: ptr JSClassDef): cint
-proc JS_IsRegisteredClass*(rt: JSRuntime; class_id: JSClassID): cint
-proc JS_SetClassProto*(ctx: JSContext; class_id: JSClassID; obj: JSValue)
-proc JS_GetClassProto*(ctx: JSContext; class_id: JSClassID): JSValue
-proc JS_SetConstructor*(ctx: JSContext; func_obj: JSValue; proto: JSValue)
-proc JS_SetPrototype*(ctx: JSContext; obj: JSValue; proto_val: JSValue): cint
-proc JS_GetPrototype*(ctx: JSContext; val: JSValue): JSValue
-
-proc JS_NewBool*(ctx: JSContext; val: JS_BOOL): JSValue
-proc JS_NewInt32*(ctx: JSContext; val: int32): JSValue
-proc JS_NewCatchOffset*(ctx: JSContext; val: int32): JSValue
-proc JS_NewInt64*(ctx: JSContext; val: int64): JSValue
-proc JS_NewUint32*(ctx: JSContext; val: uint32): JSValue
-proc JS_NewBigInt64*(ctx: JSContext; val: int64): JSValue
-proc JS_NewBigUInt64*(ctx: JSContext; val: uint64): JSValue
-proc JS_NewFloat64*(ctx: JSContext; val: cdouble): JSValue
-
-const JS_ATOM_NULL* = JSAtom(0)
-
-proc JS_NewAtomLen*(ctx: JSContext; str: cstring; len: csize_t): JSAtom
-proc JS_NewAtomUInt32*(ctx: JSContext; u: uint32): JSAtom
-proc JS_ValueToAtom*(ctx: JSContext; val: JSValue): JSAtom
-proc JS_AtomToValue*(ctx: JSContext; atom: JSAtom): JSValue
-proc JS_AtomToCString*(ctx: JSContext; atom: JSAtom): cstring
-proc JS_FreeAtom*(ctx: JSContext; atom: JSAtom)
-proc JS_FreeAtomRT*(rt: JSRuntime; atom: JSAtom)
-
-proc JS_NewCFunction2*(ctx: JSContext; cfunc: JSCFunction; name: cstring;
-  length: cint; proto: JSCFunctionEnum; magic: cint): JSValue
-proc JS_NewCFunctionData*(ctx: JSContext; cfunc: JSCFunctionData;
-  length, magic, data_len: cint; data: ptr UncheckedArray[JSValue]): JSValue
-proc JS_NewCFunction*(ctx: JSContext; cfunc: JSCFunction; name: cstring;
-  length: cint): JSValue
-
-proc JS_NewString*(ctx: JSContext; str: cstring): JSValue
-proc JS_NewStringLen*(ctx: JSContext; str: cstring; len1: csize_t): JSValue
-proc JS_NewAtomString*(ctx: JSContext; str: cstring): JSValue
-proc JS_ToString*(ctx: JSContext; val: JSValue): JSValue
-
-proc JS_SetProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom;
-  val: JSValue): cint
-proc JS_SetPropertyUint32*(ctx: JSContext; this_obj: JSValue; idx: uint32;
-  val: JSValue): cint
-proc JS_SetPropertyInt64*(ctx: JSContext; this_obj: JSValue; idx: int64;
-  val: JSValue): cint
-proc JS_SetPropertyStr*(ctx: JSContext; this_obj: JSValue; prop: cstring;
-  val: JSValue): cint
-proc JS_SetPropertyFunctionList*(ctx: JSContext; obj: JSValue;
-  tab: ptr JSCFunctionListEntry; len: cint)
-proc JS_GetProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom): JSValue
-proc JS_GetPropertyStr*(ctx: JSContext; this_obj: JSValue; prop: cstring):
-  JSValue
-proc JS_GetPropertyUint32*(ctx: JSContext; this_obj: JSValue; idx: uint32):
-  JSValue
-proc JS_GetOwnPropertyNames*(ctx: JSContext;
-    ptab: ptr ptr UncheckedArray[JSPropertyEnum]; plen: ptr uint32;
-    obj: JSValue; flags: cint): cint
-
-proc JS_GetOwnProperty*(ctx: JSContext; desc: ptr JSPropertyDescriptor;
-  obj: JSValue; prop: JSAtom): cint
-proc JS_Call*(ctx: JSContext; func_obj, this_obj: JSValue; argc: cint;
-  argv: ptr UncheckedArray[JSValue]): JSValue
-proc JS_NewObjectFromCtor*(ctx: JSContext; ctor: JSValue;
-  class_id: JSClassID): JSValue
-proc JS_Invoke*(ctx: JSContext; this_obj: JSValue; atom: JSAtom; argc: cint;
-  argv: ptr UncheckedArray[JSValue]): JSValue
-proc JS_CallConstructor*(ctx: JSContext; func_obj: JSValue; argc: cint;
-  argv: ptr UncheckedArray[JSValue]): JSValue
-
-proc JS_DefineProperty*(ctx: JSContext; this_obj: JSValue; prop: JSAtom;
-  val, getter, setter: JSValue; flags: cint): cint
-proc JS_DefinePropertyValue*(ctx: JSContext; this_obj: JSValue; prop: JSAtom;
-  val: JSValue; flags: cint): cint
-proc JS_DefinePropertyValueUint32*(ctx: JSContext; this_obj: JSValue;
-  idx: uint32; val: JSValue; flags: cint): cint
-proc JS_DefinePropertyValueStr*(ctx: JSContext; this_obj: JSValue;
-  prop: cstring; val: JSValue; flags: cint): cint
-proc JS_DefinePropertyValueGetSet*(ctx: JSContext; this_obj: JSValue;
-  prop: JSAtom; getter, setter: JSValue; flags: cint): cint
-
-proc JS_FreeValue*(ctx: JSContext; v: JSValue)
-proc JS_FreeValueRT*(rt: JSRuntime; v: JSValue)
-proc JS_DupValue*(ctx: JSContext; v: JSValue): JSValue
-
-proc JS_ToBool*(ctx: JSContext; val: JSValue): cint # return -1 for JS_EXCEPTION
-proc JS_ToInt32*(ctx: JSContext; pres: ptr int32; val: JSValue): cint
-proc JS_ToUint32*(ctx: JSContext; pres: ptr uint32; val: JSValue): cint
-proc JS_ToInt64*(ctx: JSContext; pres: ptr int64; val: JSValue): cint
-proc JS_ToIndex*(ctx: JSContext; plen: ptr uint64; val: JSValue): cint
-proc JS_ToFloat64*(ctx: JSContext; pres: ptr float64; val: JSValue): cint
-# return an exception if 'val' is a Number
-proc JS_ToBigInt64*(ctx: JSContext; pres: ptr int64; val: JSValue): cint
-# same as JS_ToInt64 but allow BigInt
-proc JS_ToInt64Ext*(ctx: JSContext; pres: ptr int64; val: JSValue): cint
-
-proc JS_ToCStringLen*(ctx: JSContext; plen: ptr csize_t; val1: JSValue): cstring
-proc JS_ToCString*(ctx: JSContext; val1: JSValue): cstring
-proc JS_FreeCString*(ctx: JSContext, `ptr`: cstring)
-
-proc JS_NewNarrowStringLen*(ctx: JSContext; s: cstring; len: csize_t): JSValue
-proc JS_IsStringWideChar*(str: JSValue): JS_BOOL
-proc JS_GetNarrowStringBuffer*(str: JSValue): ptr UncheckedArray[uint8]
-proc JS_GetStringLength*(str: JSValue): uint32
-
-proc JS_Eval*(ctx: JSContext; input: cstring; input_len: csize_t;
-  filename: cstring; eval_flags: cint): JSValue
-proc JS_EvalFunction*(ctx: JSContext; val: JSValue): JSValue
-proc JS_SetInterruptHandler*(rt: JSRuntime; cb: JSInterruptHandler;
-  opaque: pointer)
-proc JS_SetCanBlock*(rt: JSRuntime; can_block: JS_BOOL)
-proc JS_SetIsHTMLDDA*(ctx: JSContext; obj: JSValue)
-
-proc JS_IsNumber*(v: JSValue): JS_BOOL
-proc JS_IsBigInt*(v: JSValue): JS_BOOL
-proc JS_IsBigFloat*(v: JSValue): JS_BOOL
-proc JS_IsBigDecimal*(v: JSValue): JS_BOOL
-proc JS_IsBool*(v: JSValue): JS_BOOL
-proc JS_IsNull*(v: JSValue): JS_BOOL
-proc JS_IsUndefined*(v: JSValue): JS_BOOL
-proc JS_IsException*(v: JSValue): JS_BOOL
-proc JS_IsUninitialized*(v: JSValue): JS_BOOL
-proc JS_IsString*(v: JSValue): JS_BOOL
-proc JS_IsSymbol*(v: JSValue): JS_BOOL
-proc JS_IsObject*(v: JSValue): JS_BOOL
-
-proc JS_IsFunction*(ctx: JSContext; val: JSValue): JS_BOOL
-proc JS_IsArray*(ctx: JSContext; v: JSValue): cint
-
-proc JS_Throw*(ctx: JSContext; obj: JSValue): JSValue
-proc JS_GetException*(ctx: JSContext): JSValue
-proc JS_IsError*(ctx: JSContext; v: JSValue): JS_BOOL
-proc JS_SetUncatchableError*(ctx: JSContext; val: JSValue; flag: JS_BOOL)
-proc JS_ResetUncatchableError*(ctx: JSContext)
-proc JS_NewError*(ctx: JSContext): JSValue
-proc JS_ThrowSyntaxError*(ctx: JSContext; fmt: cstring): JSValue {.varargs,
-  discardable.}
-proc JS_ThrowTypeError*(ctx: JSContext; fmt: cstring): JSValue {.varargs,
-  discardable.}
-proc JS_ThrowReferenceError*(ctx: JSContext; fmt: cstring): JSValue {.varargs,
-  discardable.}
-proc JS_ThrowRangeError*(ctx: JSContext; fmt: cstring): JSValue {.varargs,
-  discardable.}
-proc JS_ThrowInternalError*(ctx: JSContext; fmt: cstring): JSValue {.varargs,
-  discardable.}
-
-proc JS_SetModuleLoaderFunc*(rt: JSRuntime;
-  module_normalize: JSModuleNormalizeFunc; module_loader: JSModuleLoaderFunc;
-  opaque: pointer)
-proc JS_GetImportMeta*(ctx: JSContext; m: JSModuleDef): JSValue
-proc JS_GetModuleName*(ctx: JSContext; m: JSModuleDef): JSAtom
-
-proc JS_EnqueueJob*(ctx: JSContext; job_func: JSJobFunc; argc: cint;
-  argv: ptr UncheckedArray[JSValue]): cint
-proc JS_IsJobPending*(rt: JSRuntime): JS_BOOL
-proc JS_ExecutePendingJob*(rt: JSRuntime; pctx: ptr JSContext): cint
-
-proc JS_GetRuntimeOpaque*(rt: JSRuntime): pointer
-proc JS_SetRuntimeOpaque*(rt: JSRuntime; p: pointer)
-proc JS_SetRuntimeCleanUpFunc*(rt: JSRuntime;
-  cleanup_func: JSRuntimeCleanUpFunc)
-
-proc JS_SetContextOpaque*(ctx: JSContext; opaque: pointer)
-proc JS_GetContextOpaque*(ctx: JSContext): pointer
-
-proc js_malloc*(ctx: JSContext; size: csize_t): pointer
-proc js_mallocz*(ctx: JSContext; size: csize_t): pointer
-proc js_realloc*(ctx: JSContext; p: pointer; size: csize_t): pointer
-proc js_free_rt*(rt: JSRuntime; p: pointer)
-proc js_free*(ctx: JSContext; p: pointer)
-
-proc js_strdup*(ctx: JSContext; str: cstring): cstring
-{.pop.}
diff --git a/src/config/chapath.nim b/src/config/chapath.nim
index d9ba3c50..8397cb3d 100644
--- a/src/config/chapath.nim
+++ b/src/config/chapath.nim
@@ -2,10 +2,10 @@ import std/options
 import std/os
 import std/posix
 
-import js/fromjs
-import js/javascript
-import js/jserror
-import js/tojs
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/tojs
 import types/opt
 import utils/twtstr
 
diff --git a/src/config/config.nim b/src/config/config.nim
index 063c32fc..35ca1a17 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -4,26 +4,27 @@ import std/streams
 import std/strutils
 import std/tables
 
-import bindings/quickjs
 import config/chapath
 import config/mailcap
 import config/mimetypes
 import config/toml
-import js/jserror
-import js/fromjs
-import js/javascript
 import js/jscolor
-import js/jstypes
-import js/jspropenumlist
-import js/jsregex
-import js/tojs
 import loader/headers
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jspropenumlist
+import monoucha/jsregex
+import monoucha/jstypes
+import monoucha/quickjs
+import monoucha/tojs
 import types/cell
 import types/color
 import types/cookie
 import types/opt
 import types/urimethodmap
 import types/url
+import utils/regexutils
 import utils/twtstr
 
 import chagashi/charset
diff --git a/src/html/chadombuilder.nim b/src/html/chadombuilder.nim
index 3c5b2c12..708b127c 100644
--- a/src/html/chadombuilder.nim
+++ b/src/html/chadombuilder.nim
@@ -1,19 +1,19 @@
 import std/deques
 import std/options
+import std/tables
 
+import chagashi/charset
+import chame/htmlparser
+import chame/tags
 import html/catom
 import html/dom
 import html/enums
-import js/jserror
-import js/fromjs
-import js/javascript
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import types/opt
 import types/url
 
-import chagashi/charset
-
-import chame/htmlparser
-import chame/tags
-
 export htmlparser.ParseResult
 
 # DOMBuilder implementation for Chawan.
@@ -260,6 +260,9 @@ proc parseHTMLFragment*(element: Element; s: string): seq[Node] =
   builder.finish()
   return root.childList
 
+# Forward declaration hack
+domParseHTMLFragment = parseHTMLFragment
+
 proc newHTML5ParserWrapper*(window: Window; url: URL; factory: CAtomFactory;
     confidence: CharsetConfidence; charset: Charset): HTML5ParserWrapper =
   let opts = HTML5ParserOpts[Node, CAtom](scripting: window.settings.scripting)
diff --git a/src/html/dom.nim b/src/html/dom.nim
index af839cea..6eb1079f 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -22,19 +22,20 @@ import io/dynstream
 import io/promise
 import js/console
 import js/domexception
-import js/fromjs
-import js/javascript
-import js/jserror
-import js/jsopaque
-import js/jspropenumlist
-import js/jsutils
 import js/timeout
-import js/tojs
 import loader/loader
 import loader/request
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jsopaque
+import monoucha/jspropenumlist
+import monoucha/jsutils
+import monoucha/tojs
 import types/blob
 import types/color
 import types/matrix
+import types/opt
 import types/referrer
 import types/url
 import types/vector
@@ -4189,11 +4190,14 @@ proc toBlob(ctx: JSContext; this: HTMLCanvasElement; callback: JSValue;
   JS_FreeValue(ctx, res)
   return JS_UNDEFINED
 
-import html/chadombuilder
+# Forward declaration hack
+var domParseHTMLFragment*: proc(element: Element; s: string): seq[Node]
+  {.nimcall.}
+
 # https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm
 proc fragmentParsingAlgorithm*(element: Element; s: string): DocumentFragment =
   #TODO xml
-  let newChildren = parseHTMLFragment(element, s)
+  let newChildren = domParseHTMLFragment(element, s)
   let fragment = element.document.newDocumentFragment()
   for child in newChildren:
     fragment.append(child)
diff --git a/src/html/env.nim b/src/html/env.nim
index 76aa2007..fadcd52c 100644
--- a/src/html/env.nim
+++ b/src/html/env.nim
@@ -1,6 +1,5 @@
 import std/selectors
 
-import bindings/quickjs
 import html/catom
 import html/chadombuilder
 import html/dom
@@ -14,17 +13,19 @@ import js/base64
 import js/console
 import js/domexception
 import js/encoding
-import js/jserror
 import js/intl
-import js/javascript
-import js/jstypes
 import js/timeout
-import js/tojs
 import loader/headers
 import loader/loader
 import loader/request
 import loader/response
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jstypes
+import monoucha/quickjs
+import monoucha/tojs
 import types/blob
+import types/opt
 import types/url
 import types/winattrs
 
diff --git a/src/html/event.nim b/src/html/event.nim
index 7ae024db..fd818048 100644
--- a/src/html/event.nim
+++ b/src/html/event.nim
@@ -2,13 +2,13 @@ import std/math
 import std/options
 import std/times
 
-import bindings/quickjs
-import js/jserror
-import js/fromjs
-import js/javascript
-import js/jstypes
-import js/jsutils
-import js/tojs
+import monoucha/quickjs
+import monoucha/jserror
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jstypes
+import monoucha/jsutils
+import monoucha/tojs
 import types/opt
 
 type
diff --git a/src/html/formdata.nim b/src/html/formdata.nim
index 7f099acd..df839d3f 100644
--- a/src/html/formdata.nim
+++ b/src/html/formdata.nim
@@ -1,3 +1,4 @@
+import chame/tags
 import html/catom
 import html/dom
 import html/enums
@@ -5,14 +6,13 @@ import io/dynstream
 import io/posixstream
 import js/base64
 import js/domexception
-import js/javascript
-import js/tojs
+import monoucha/javascript
+import monoucha/tojs
 import types/blob
 import types/formdata
+import types/opt
 import utils/twtstr
 
-import chame/tags
-
 proc constructEntryList*(form: HTMLFormElement; submitter: Element = nil;
     encoding = "UTF-8"): seq[FormDataEntry]
 
diff --git a/src/html/script.nim b/src/html/script.nim
index c0962b13..59ada2f0 100644
--- a/src/html/script.nim
+++ b/src/html/script.nim
@@ -1,4 +1,4 @@
-import js/javascript
+import monoucha/javascript
 import types/referrer
 import types/url
 
diff --git a/src/html/xmlhttprequest.nim b/src/html/xmlhttprequest.nim
index 33f1e61c..92065ab4 100644
--- a/src/html/xmlhttprequest.nim
+++ b/src/html/xmlhttprequest.nim
@@ -1,15 +1,16 @@
 import std/options
 import std/strutils
 
-import bindings/quickjs
 import html/dom
 import html/event
 import js/domexception
-import js/fromjs
-import js/javascript
 import loader/headers
 import loader/request
 import loader/response
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/quickjs
+import types/opt
 import types/url
 
 type
diff --git a/src/io/promise.nim b/src/io/promise.nim
index 6e57791b..183091cb 100644
--- a/src/io/promise.nim
+++ b/src/io/promise.nim
@@ -1,11 +1,11 @@
 import std/tables
 
-import bindings/quickjs
-import js/jserror
-import js/javascript
-import js/jsutils
-import js/jsopaque
-import js/tojs
+import monoucha/quickjs
+import monoucha/jserror
+import monoucha/javascript
+import monoucha/jsutils
+import monoucha/jsopaque
+import monoucha/tojs
 import types/opt
 
 type
diff --git a/src/io/urlfilter.nim b/src/io/urlfilter.nim
index 4d1d2404..e86e0e6b 100644
--- a/src/io/urlfilter.nim
+++ b/src/io/urlfilter.nim
@@ -1,6 +1,6 @@
 import std/options
 
-import js/jsregex
+import monoucha/jsregex
 import types/url
 
 #TODO add denyhost/s for blocklists
diff --git a/src/js/base64.nim b/src/js/base64.nim
index a1693aa3..85844e09 100644
--- a/src/js/base64.nim
+++ b/src/js/base64.nim
@@ -1,7 +1,7 @@
-import bindings/quickjs
+import monoucha/quickjs
 import js/domexception
-import js/javascript
-import js/jstypes
+import monoucha/javascript
+import monoucha/jstypes
 import types/opt
 import utils/twtstr
 
diff --git a/src/js/console.nim b/src/js/console.nim
index 712f228c..0de66162 100644
--- a/src/js/console.nim
+++ b/src/js/console.nim
@@ -1,6 +1,6 @@
 import io/dynstream
-import js/jserror
-import js/javascript
+import monoucha/jserror
+import monoucha/javascript
 
 type Console* = ref object
   err*: DynStream
diff --git a/src/js/domexception.nim b/src/js/domexception.nim
index 902cd618..68e313e9 100644
--- a/src/js/domexception.nim
+++ b/src/js/domexception.nim
@@ -1,8 +1,9 @@
 import std/tables
 
-import bindings/quickjs
-import js/jserror
-import js/javascript
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/quickjs
+import types/opt
 
 const NamesTable = {
   "IndexSizeError": 1u16,
diff --git a/src/js/encoding.nim b/src/js/encoding.nim
index edf0cf97..ccadce16 100644
--- a/src/js/encoding.nim
+++ b/src/js/encoding.nim
@@ -1,13 +1,13 @@
-import bindings/quickjs
-import js/jserror
-import js/javascript
-import js/jstypes
-
 import chagashi/charset
 import chagashi/decoder
 import chagashi/decodercore
 import chagashi/validator
 import chagashi/validatorcore
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jstypes
+import monoucha/quickjs
+import types/opt
 
 type
   JSTextEncoder = ref object
diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim
deleted file mode 100644
index 055ae19e..00000000
--- a/src/js/fromjs.nim
+++ /dev/null
@@ -1,445 +0,0 @@
-import std/macros
-import std/options
-import std/tables
-import std/unicode
-
-import bindings/quickjs
-import js/jserror
-import js/jstypes
-import js/jsopaque
-import types/opt
-import utils/twtstr
-
-proc fromJS*[T](ctx: JSContext; val: JSValue): JSResult[T]
-
-func isInstanceOfNonGlobal(ctx: JSContext; val: JSValue; class: string): bool =
-  let ctxOpaque = ctx.getOpaque()
-  var classid = JS_GetClassID(val)
-  let tclassid = ctxOpaque.creg[class]
-  var found = false
-  while true:
-    if classid == tclassid:
-      found = true
-      break
-    ctxOpaque.parents.withValue(classid, val):
-      classid = val[]
-    do:
-      classid = 0 # not defined by Chawan; assume parent is Object.
-    if classid == 0:
-      break
-  return found
-
-func isInstanceOfGlobal(ctx: JSContext; val: JSValue; class: string): bool =
-  let ctxOpaque = ctx.getOpaque()
-  #TODO gparent only works for a single level. (But this is not really a
-  # problem right now, because our global objects have at most one inheritance
-  # level.)
-  if ctx.isGlobal(class) or ctxOpaque.creg[class] == ctxOpaque.gparent:
-    # undefined -> global
-    if JS_IsUndefined(val):
-      return true
-    if JS_IsObject(val):
-      let global = JS_GetGlobalObject(ctx)
-      let p0 = JS_VALUE_GET_PTR(global)
-      let p1 = JS_VALUE_GET_PTR(val)
-      JS_FreeValue(ctx, global)
-      if p0 == p1:
-        return true
-  return false
-
-func isInstanceOf*(ctx: JSContext; val: JSValue; class: string): bool =
-  return ctx.isInstanceOfGlobal(val, class) or
-    ctx.isInstanceOfNonGlobal(val, class)
-
-func toString(ctx: JSContext; val: JSValue): Opt[string] =
-  var plen: csize_t
-  let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring
-  if outp != nil:
-    var ret = newString(plen)
-    if plen != 0:
-      prepareMutation(ret)
-      copyMem(addr ret[0], outp, plen)
-    result = ok(ret)
-    JS_FreeCString(ctx, outp)
-
-func fromJSString(ctx: JSContext; val: JSValue): JSResult[string] =
-  var plen: csize_t
-  let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring
-  if outp == nil:
-    return err()
-  var ret = newString(plen)
-  if plen != 0:
-    prepareMutation(ret)
-    copyMem(addr ret[0], outp, plen)
-  JS_FreeCString(ctx, outp)
-  return ok(ret)
-
-func fromJSInt[T: SomeInteger](ctx: JSContext; val: JSValue):
-    JSResult[T] =
-  when T is int:
-    # Always int32, so we don't risk 32-bit only breakage.
-    # If int64 is needed, specify it explicitly.
-    var ret: int32
-    if JS_ToInt32(ctx, addr ret, val) < 0:
-      return err()
-    return ok(int(ret))
-  elif T is int32:
-    var ret: int32
-    if JS_ToInt32(ctx, addr ret, val) < 0:
-      return err()
-    return ok(ret)
-  elif T is int64:
-    var ret: int64
-    if JS_ToInt64(ctx, addr ret, val) < 0:
-      return err()
-    return ok(ret)
-  elif T is uint32:
-    var ret: uint32
-    if JS_ToUint32(ctx, addr ret, val) < 0:
-      return err()
-    return ok(ret)
-  else:
-    static:
-      error($T & " cannot be converted to JS automatically")
-
-proc fromJSFloat64(ctx: JSContext; val: JSValue): JSResult[float64] =
-  var f64: float64
-  if JS_ToFloat64(ctx, addr f64, val) < 0:
-    return err()
-  return ok(f64)
-
-macro fromJSTupleBody(a: tuple) =
-  let len = a.getType().len - 1
-  let done = ident("done")
-  result = newStmtList(quote do:
-    var `done`: bool)
-  for i in 0..<len:
-    result.add(quote do:
-      let next = JS_Call(ctx, nextMethod, it, 0, nil)
-      if JS_IsException(next):
-        return err()
-      defer: JS_FreeValue(ctx, next)
-      let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone])
-      if JS_IsException(doneVal):
-        return err()
-      defer: JS_FreeValue(ctx, doneVal)
-      `done` = ?fromJS[bool](ctx, doneVal)
-      if `done`:
-        return errTypeError("Too few arguments in sequence (got " & $`i` &
-          ", expected " & $`len` & ")")
-      let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue])
-      if JS_IsException(valueVal):
-        return err()
-      defer: JS_FreeValue(ctx, valueVal)
-      `a`[`i`] = ?fromJS[typeof(`a`[`i`])](ctx, valueVal)
-    )
-    if i == len - 1:
-      result.add(quote do:
-        let next = JS_Call(ctx, nextMethod, it, 0, nil)
-        if JS_IsException(next):
-          return err()
-        defer: JS_FreeValue(ctx, next)
-        let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone])
-        `done` = ?fromJS[bool](ctx, doneVal)
-        var i = `i`
-        # we're emulating a sequence, so we must query all remaining parameters
-        # too:
-        while not `done`:
-          inc i
-          let next = JS_Call(ctx, nextMethod, it, 0, nil)
-          if JS_IsException(next):
-            return err()
-          defer: JS_FreeValue(ctx, next)
-          let doneVal = JS_GetProperty(ctx, next,
-            ctx.getOpaque().strRefs[jstDone])
-          if JS_IsException(doneVal):
-            return err()
-          defer: JS_FreeValue(ctx, doneVal)
-          `done` = ?fromJS[bool](ctx, doneVal)
-          if `done`:
-            let msg = "Too many arguments in sequence (got " & $i &
-              ", expected " & $`len` & ")"
-            return err(newTypeError(msg))
-          JS_FreeValue(ctx, JS_GetProperty(ctx, next,
-            ctx.getOpaque().strRefs[jstValue]))
-      )
-
-proc fromJSTuple[T: tuple](ctx: JSContext; val: JSValue): JSResult[T] =
-  let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator])
-  if JS_IsException(itprop):
-    return err()
-  defer: JS_FreeValue(ctx, itprop)
-  let it = JS_Call(ctx, itprop, val, 0, nil)
-  if JS_IsException(it):
-    return err()
-  defer: JS_FreeValue(ctx, it)
-  let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext])
-  if JS_IsException(nextMethod):
-    return err()
-  defer: JS_FreeValue(ctx, nextMethod)
-  var x: T
-  fromJSTupleBody(x)
-  return ok(x)
-
-proc fromJSSeq[T](ctx: JSContext; val: JSValue): JSResult[seq[T]] =
-  let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator])
-  if JS_IsException(itprop):
-    return err()
-  defer: JS_FreeValue(ctx, itprop)
-  let it = JS_Call(ctx, itprop, val, 0, nil)
-  if JS_IsException(it):
-    return err()
-  defer: JS_FreeValue(ctx, it)
-  let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext])
-  if JS_IsException(nextMethod):
-    return err()
-  defer: JS_FreeValue(ctx, nextMethod)
-  var s = newSeq[T]()
-  while true:
-    let next = JS_Call(ctx, nextMethod, it, 0, nil)
-    if JS_IsException(next):
-      return err()
-    defer: JS_FreeValue(ctx, next)
-    let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone])
-    if JS_IsException(doneVal):
-      return err()
-    defer: JS_FreeValue(ctx, doneVal)
-    let done = ?fromJS[bool](ctx, doneVal)
-    if done:
-      break
-    let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue])
-    if JS_IsException(valueVal):
-      return err()
-    defer: JS_FreeValue(ctx, valueVal)
-    let genericRes = ?fromJS[typeof(s[0])](ctx, valueVal)
-    s.add(genericRes)
-  return ok(s)
-
-proc fromJSSet[T](ctx: JSContext; val: JSValue): JSResult[set[T]] =
-  let itprop = JS_GetProperty(ctx, val, ctx.getOpaque().symRefs[jsyIterator])
-  if JS_IsException(itprop):
-    return err()
-  defer: JS_FreeValue(ctx, itprop)
-  let it = JS_Call(ctx, itprop, val, 0, nil)
-  if JS_IsException(it):
-    return err()
-  defer: JS_FreeValue(ctx, it)
-  let nextMethod = JS_GetProperty(ctx, it, ctx.getOpaque().strRefs[jstNext])
-  if JS_IsException(nextMethod):
-    return err()
-  defer: JS_FreeValue(ctx, nextMethod)
-  var s: set[T]
-  while true:
-    let next = JS_Call(ctx, nextMethod, it, 0, nil)
-    if JS_IsException(next):
-      return err()
-    defer: JS_FreeValue(ctx, next)
-    let doneVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstDone])
-    if JS_IsException(doneVal):
-      return err()
-    defer: JS_FreeValue(ctx, doneVal)
-    let done = ?fromJS[bool](ctx, doneVal)
-    if done:
-      break
-    let valueVal = JS_GetProperty(ctx, next, ctx.getOpaque().strRefs[jstValue])
-    if JS_IsException(valueVal):
-      return err()
-    defer: JS_FreeValue(ctx, valueVal)
-    let genericRes = ?fromJS[T](ctx, valueVal)
-    s.incl(genericRes)
-  return ok(s)
-
-proc fromJSTable[A, B](ctx: JSContext; val: JSValue): JSResult[Table[A, B]] =
-  if not JS_IsObject(val):
-    return err(newTypeError("object expected"))
-  var ptab: ptr UncheckedArray[JSPropertyEnum]
-  var plen: uint32
-  let flags = cint(JS_GPN_STRING_MASK)
-  if JS_GetOwnPropertyNames(ctx, addr ptab, addr plen, val, flags) == -1:
-    # exception
-    return err()
-  defer:
-    for i in 0 ..< plen:
-      JS_FreeAtom(ctx, ptab[i].atom)
-    js_free(ctx, ptab)
-  var res = Table[A, B]()
-  for i in 0 ..< plen:
-    let atom = ptab[i].atom
-    let k = JS_AtomToValue(ctx, atom)
-    defer: JS_FreeValue(ctx, k)
-    let kn = ?fromJS[A](ctx, k)
-    let v = JS_GetProperty(ctx, val, atom)
-    defer: JS_FreeValue(ctx, v)
-    let vn = ?fromJS[B](ctx, v)
-    res[kn] = vn
-  return ok(res)
-
-template optionType[T](o: type Option[T]): auto =
-  T
-
-# Option vs Opt:
-# Option is for nullable types, e.g. if you want to return either a string
-# or null. (This is rather pointless for anything else.)
-# Opt is for passing down exceptions received up in the chain.
-# So e.g. none(T) translates to JS_NULL, but err() translates to JS_EXCEPTION.
-proc fromJSOption[T](ctx: JSContext; val: JSValue): JSResult[Option[T]] =
-  if JS_IsNull(val):
-    return ok(none(T))
-  let res = ?fromJS[T](ctx, val)
-  return ok(option(res))
-
-proc fromJSBool(ctx: JSContext; val: JSValue): JSResult[bool] =
-  let ret = JS_ToBool(ctx, val)
-  if ret == -1: # exception
-    return err()
-  if ret == 0:
-    return ok(false)
-  return ok(true)
-
-proc fromJSEnum[T: enum](ctx: JSContext; val: JSValue): JSResult[T] =
-  if JS_IsException(val):
-    return err()
-  let s = ?toString(ctx, val)
-  let r = strictParseEnum[T](s)
-  if r.isSome:
-    return ok(r.get)
-  return errTypeError("`" & s & "' is not a valid value for enumeration " & $T)
-
-proc fromJSPObj0(ctx: JSContext; val: JSValue; t: string):
-    JSResult[pointer] =
-  if JS_IsException(val):
-    return err(nil)
-  if JS_IsNull(val):
-    return ok(nil)
-  if ctx.isInstanceOfGlobal(val, t):
-    return ok(?getGlobalOpaque0(ctx, val))
-  if not JS_IsObject(val):
-    return err(newTypeError("Value is not an object"))
-  if not isInstanceOfNonGlobal(ctx, val, t):
-    return errTypeError(t & " expected")
-  let classid = JS_GetClassID(val)
-  let op = JS_GetOpaque(val, classid)
-  return ok(op)
-
-proc fromJSObject[T: ref object](ctx: JSContext; val: JSValue): JSResult[T] =
-  return ok(cast[T](?fromJSPObj0(ctx, val, $T)))
-
-proc fromJSVoid(ctx: JSContext; val: JSValue): JSResult[void] =
-  if JS_IsException(val):
-    return err()
-  return ok()
-
-proc fromJSDict[T: JSDict](ctx: JSContext; val: JSValue): JSResult[T] =
-  if not JS_IsUndefined(val) and not JS_IsNull(val) and not JS_IsObject(val):
-    return err(newTypeError("Dictionary is not an object"))
-  #TODO throw on missing required values
-  var d = T()
-  if JS_IsObject(val):
-    for k, v in d.fieldPairs:
-      let esm = JS_GetPropertyStr(ctx, val, k)
-      if not JS_IsUndefined(esm):
-        v = ?fromJS[typeof(v)](ctx, esm)
-      when v isnot JSValue:
-        JS_FreeValue(ctx, esm)
-  return ok(d)
-
-proc fromJSArrayBuffer(ctx: JSContext; val: JSValue): JSResult[JSArrayBuffer] =
-  var len: csize_t
-  let p = JS_GetArrayBuffer(ctx, addr len, val)
-  if p == nil:
-    return err()
-  let abuf = JSArrayBuffer(
-    len: len,
-    p: cast[ptr UncheckedArray[uint8]](p)
-  )
-  return ok(abuf)
-
-proc fromJSArrayBufferView(ctx: JSContext; val: JSValue):
-    JSResult[JSArrayBufferView] =
-  var offset: csize_t
-  var nmemb: csize_t
-  var nsize: csize_t
-  let jsbuf = JS_GetTypedArrayBuffer(ctx, val, addr offset, addr nmemb,
-    addr nsize)
-  let abuf = ?fromJSArrayBuffer(ctx, jsbuf)
-  let view = JSArrayBufferView(
-    abuf: abuf,
-    offset: offset,
-    nmemb: nmemb,
-    nsize: nsize
-  )
-  return ok(view)
-
-type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict|
-  JSArrayBuffer|JSArrayBufferView|JSUint8Array))
-
-macro fromJS2(ctx: JSContext; val: JSValue; x: static string): untyped =
-  let id = ident("fromJS" & x)
-  return quote do:
-    `id`(`ctx`, `val`)
-
-proc fromJS*[T](ctx: JSContext; val: JSValue): JSResult[T] =
-  when T is string:
-    return fromJSString(ctx, val)
-  elif T is Option:
-    return fromJSOption[optionType(T)](ctx, val)
-  elif T is seq:
-    return fromJSSeq[typeof(result.get.items)](ctx, val)
-  elif T is set:
-    return fromJSSet[typeof(result.get.items)](ctx, val)
-  elif T is tuple:
-    return fromJSTuple[T](ctx, val)
-  elif T is bool:
-    return fromJSBool(ctx, val)
-  elif typeof(result).valType is Table:
-    return fromJSTable[typeof(result.get.keys),
-      typeof(result.get.values)](ctx, val)
-  elif T is SomeInteger:
-    return fromJSInt[T](ctx, val)
-  elif T is float64:
-    return fromJSFloat64(ctx, val)
-  elif T is enum:
-    return fromJSEnum[T](ctx, val)
-  elif T is JSValue:
-    return ok(val)
-  elif T is ref object:
-    return fromJSObject[T](ctx, val)
-  elif T is void:
-    return fromJSVoid(ctx, val)
-  elif T is JSDict:
-    return fromJSDict[T](ctx, val)
-  elif T is JSArrayBuffer:
-    return fromJSArrayBuffer(ctx, val)
-  elif T is JSArrayBufferView:
-    return fromJSArrayBufferView(ctx, val)
-  else:
-    return fromJS2(ctx, val, $T)
-
-const JS_ATOM_TAG_INT = 1u32 shl 31
-
-func JS_IsNumber*(v: JSAtom): JS_BOOL =
-  return (uint32(v) and JS_ATOM_TAG_INT) != 0
-
-func fromJS*[T: string|uint32|JSAtom](ctx: JSContext; atom: JSAtom): Opt[T] =
-  when T is JSAtom:
-    return ok(atom)
-  elif T is SomeNumber:
-    if JS_IsNumber(atom):
-      return ok(uint32(atom) and (not JS_ATOM_TAG_INT))
-    return err()
-  else:
-    let cs = JS_AtomToCString(ctx, atom)
-    if cs == nil:
-      return err()
-    let s = $cs
-    JS_FreeCString(ctx, cs)
-    return ok(s)
-
-proc fromJSPObj[T](ctx: JSContext; val: JSValue): JSResult[ptr T] =
-  return cast[JSResult[ptr T]](fromJSPObj0(ctx, val, $T))
-
-template fromJSP*[T](ctx: JSContext; val: JSValue): untyped =
-  when T is FromJSAllowedT:
-    fromJSPObj[T](ctx, val)
-  else:
-    fromJS[T](ctx, val)
diff --git a/src/js/intl.nim b/src/js/intl.nim
index 8be5a768..4ae264e4 100644
--- a/src/js/intl.nim
+++ b/src/js/intl.nim
@@ -1,9 +1,9 @@
 # Very minimal Intl module... TODO make it more complete
 
-import bindings/quickjs
-import js/javascript
-import js/jstypes
-import js/tojs
+import monoucha/quickjs
+import monoucha/javascript
+import monoucha/jstypes
+import monoucha/tojs
 
 type
   NumberFormat = ref object
diff --git a/src/js/javascript.nim b/src/js/javascript.nim
deleted file mode 100644
index cdf6b127..00000000
--- a/src/js/javascript.nim
+++ /dev/null
@@ -1,1719 +0,0 @@
-# JavaScript binding generator. Horrifying, I know. But it works!
-# Warning: Function overloading is currently not implemented. Though there is a
-# block dielabel:
-#   ...
-# around each bound function call, so it shouldn't be too difficult to get it
-# working. (This would involve generating JS functions in registerType.)
-# Now for the pragmas:
-# {.jsctor.} for constructors. These need no `this' value, and are bound as
-#   regular constructors in JS. They must return a ref object, which will have
-#   a JS counterpart too. (Other functions can return ref objects too, which
-#   will either use the existing JS counterpart, if exists, or create a new
-#   one. In other words: cross-language reference semantics work seamlessly.)
-# {.jsfunc.} is used for binding normal functions. Needs a `this' value, as all
-#   following pragmas. As mentioned before, overloading doesn't work but OR
-#   generics do. By default, the Nim function name is bound; if this is not
-#   desired, you can rename the function like this: {.jsfunc: "preferredName".}
-#   This also works for all other pragmas that define named functions in JS.
-# {.jsstfunc.} binds static functions. Unlike .jsfunc, it does not have a
-#   `this' value. A class name must be specified, e.g. {.jsstfunc: "URL".} to
-#   define on the URL class. To rename a static function, use the syntax
-#   "ClassName:funcName", e.g. "Response:error".
-# {.jsget.}, {.jsfget.} must be specified on object fields; these generate
-#   regular getter & setter functions.
-# {.jsufget, jsuffget, jsuffunc.} For fields with the [LegacyUnforgeable]
-#   WebIDL property.
-#   This makes it so a non-configurable/writable, but enumerable property
-#   is defined on the object when the *constructor* is called (i.e. NOT on
-#   the prototype.)
-# {.jsfget.} and {.jsfset.} for getters/setters. Note the `f'; bare jsget/jsset
-#   can only be used on object fields. (I initially wanted to use the same
-#   keyword, unfortunately that didn't work out.)
-# {.jsgetprop.} for property getters. Called when GetOwnProperty would return
-#   nothing. The key must be either a JSAtom, uint32 or string.  (Note that the
-#   string option copies.)
-# {.jssetprop.} for property setters. Called on SetProperty - in fact this
-#   is the set() method of Proxy, except it always returns true. Same rules as
-#   jsgetprop for keys.
-# {.jsdelprop.} for property deletion. It is like the deleteProperty method
-#   of Proxy. Must return true if deleted, false if not deleted.
-# {.jshasprop.} for overriding has_property. Must return a boolean.
-# {.jspropnames.} overrides get_own_property_names. Must return a
-#   JSPropertyEnumList object.
-
-import std/macros
-import std/options
-import std/sets
-import std/strutils
-import std/tables
-import std/unicode
-
-import js/jserror
-import js/fromjs
-import js/jsopaque
-import js/tojs
-import types/opt
-
-import bindings/quickjs
-
-export opt
-export options
-export tables
-
-export
-  JS_NULL, JS_UNDEFINED, JS_FALSE, JS_TRUE, JS_EXCEPTION, JS_UNINITIALIZED
-
-export
-  JS_EVAL_TYPE_GLOBAL,
-  JS_EVAL_TYPE_MODULE,
-  JS_EVAL_TYPE_DIRECT,
-  JS_EVAL_TYPE_INDIRECT,
-  JS_EVAL_TYPE_MASK,
-  JS_EVAL_FLAG_SHEBANG,
-  JS_EVAL_FLAG_STRICT,
-  JS_EVAL_FLAG_STRIP,
-  JS_EVAL_FLAG_COMPILE_ONLY
-
-export JSRuntime, JSContext, JSValue, JSClassID, JSAtom
-
-export
-  JS_GetGlobalObject, JS_FreeValue, JS_IsException, JS_GetPropertyStr,
-  JS_IsFunction, JS_NewCFunctionData, JS_Call, JS_DupValue, JS_IsUndefined
-
-when sizeof(int) < sizeof(int64):
-  export quickjs.`==`
-
-type
-  JSFunctionList = openArray[JSCFunctionListEntry]
-
-  BoundFunction = object
-    t: BoundFunctionType
-    name: string
-    id: NimNode
-    magic: uint16
-    unforgeable: bool
-    isstatic: bool
-    ctorBody: NimNode
-
-  BoundFunctionType = enum
-    bfFunction = "js_func"
-    bfConstructor = "js_ctor"
-    bfGetter = "js_get"
-    bfSetter = "js_set"
-    bfPropertyGet = "js_prop_get"
-    bfPropertySet = "js_prop_set"
-    bfPropertyDel = "js_prop_del"
-    bfPropertyHas = "js_prop_has"
-    bfPropertyNames = "js_prop_names"
-    bfFinalizer = "js_fin"
-
-var runtimes {.threadvar.}: seq[JSRuntime]
-
-proc bindMalloc(s: ptr JSMallocState; size: csize_t): pointer {.cdecl.} =
-  return alloc(size)
-
-proc bindFree(s: ptr JSMallocState; p: pointer) {.cdecl.} =
-  if p == nil:
-    return
-  dealloc(p)
-
-proc bindRealloc(s: ptr JSMallocState; p: pointer; size: csize_t): pointer
-    {.cdecl.} =
-  return realloc(p, size)
-
-proc jsRuntimeCleanUp(rt: JSRuntime) {.cdecl.} =
-  let rtOpaque = rt.getOpaque()
-  GC_unref(rtOpaque)
-  assert rtOpaque.destroying == nil
-  var ps: seq[pointer] = @[]
-  for p in rtOpaque.plist.values:
-    ps.add(p)
-  rtOpaque.plist.clear()
-  var unrefs: seq[proc() {.closure.}] = @[]
-  for (_, unref) in rtOpaque.refmap.values:
-    unrefs.add(unref)
-  rtOpaque.refmap.clear()
-  for unref in unrefs:
-    unref()
-  for p in ps:
-    #TODO maybe finalize?
-    let val = JS_MKPTR(JS_TAG_OBJECT, p)
-    JS_SetOpaque(val, nil)
-    JS_FreeValueRT(rt, val)
-  JS_RunGC(rt)
-
-proc newJSRuntime*(): JSRuntime =
-  var mf {.global.} = JSMallocFunctions(
-    js_malloc: bindMalloc,
-    js_free: bindFree,
-    js_realloc: bindRealloc,
-    js_malloc_usable_size: nil
-  )
-  let rt = JS_NewRuntime2(addr mf, nil)
-  let opaque = JSRuntimeOpaque()
-  GC_ref(opaque)
-  JS_SetRuntimeOpaque(rt, cast[pointer](opaque))
-  JS_SetRuntimeCleanUpFunc(rt, jsRuntimeCleanUp)
-  # Must be added after opaque is set, or there is a chance of
-  # nim_finalize_for_js dereferencing it (at the new call).
-  runtimes.add(rt)
-  return rt
-
-proc newJSContext*(rt: JSRuntime): JSContext =
-  let ctx = JS_NewContext(rt)
-  let opaque = newJSContextOpaque(ctx)
-  GC_ref(opaque)
-  JS_SetContextOpaque(ctx, cast[pointer](opaque))
-  return ctx
-
-func getClass*(ctx: JSContext; class: string): JSClassID =
-  # This function *should* never fail.
-  ctx.getOpaque().creg[class]
-
-func hasClass*(ctx: JSContext; class: type): bool =
-  return $class in ctx.getOpaque().creg
-
-proc free*(ctx: JSContext) =
-  var opaque = ctx.getOpaque()
-  if opaque != nil:
-    for a in opaque.symRefs:
-      JS_FreeAtom(ctx, a)
-    for a in opaque.strRefs:
-      JS_FreeAtom(ctx, a)
-    for v in opaque.valRefs:
-      JS_FreeValue(ctx, v)
-    for classid, v in opaque.ctors:
-      JS_FreeValue(ctx, v)
-    for v in opaque.errCtorRefs:
-      JS_FreeValue(ctx, v)
-    if opaque.globalUnref != nil:
-      opaque.globalUnref()
-    GC_unref(opaque)
-  JS_FreeContext(ctx)
-
-proc free*(rt: JSRuntime) =
-  JS_FreeRuntime(rt)
-  runtimes.del(runtimes.find(rt))
-
-proc setGlobal*[T](ctx: JSContext; obj: T) =
-  # Add JSValue reference.
-  let global = JS_GetGlobalObject(ctx)
-  let opaque = cast[pointer](obj)
-  ctx.setOpaque(global, opaque)
-  GC_ref(obj)
-  let rtOpaque = JS_GetRuntime(ctx).getOpaque()
-  ctx.getOpaque().globalUnref = proc() =
-    GC_unref(obj)
-    rtOpaque.plist.del(opaque)
-  JS_FreeValue(ctx, global)
-
-proc setInterruptHandler*(rt: JSRuntime; cb: JSInterruptHandler;
-    opaque: pointer = nil) =
-  JS_SetInterruptHandler(rt, cb, opaque)
-
-proc getExceptionMsg*(ctx: JSContext): string =
-  result = ""
-  let ex = JS_GetException(ctx)
-  let str = fromJS[string](ctx, ex)
-  if str.isSome:
-    result &= str.get & '\n'
-  let stack = JS_GetPropertyStr(ctx, ex, cstring("stack"));
-  if not JS_IsUndefined(stack):
-    result &= fromJS[string](ctx, stack).get("")
-  JS_FreeValue(ctx, stack)
-  JS_FreeValue(ctx, ex)
-
-proc getExceptionMsg*(ctx: JSContext; err: JSError): string =
-  if err != nil:
-    JS_FreeValue(ctx, ctx.toJS(err)) # note: this implicitly throws
-  return ctx.getExceptionMsg()
-
-# Returns early with err(JSContext) if an exception was thrown in a
-# context.
-proc runJSJobs*(rt: JSRuntime): Err[JSContext] =
-  while JS_IsJobPending(rt):
-    var ctx: JSContext
-    let r = JS_ExecutePendingJob(rt, addr ctx)
-    if r == -1:
-      return err(ctx)
-  ok()
-
-# Add all LegacyUnforgeable functions defined on the prototype chain to
-# the opaque.
-# Since every prototype has a list of all its ancestor's LegacyUnforgeable
-# functions, it is sufficient to simply merge the new list of new classes
-# with their parent's list to achieve this.
-proc addClassUnforgeable(ctx: JSContext; proto: JSValue;
-    classid, parent: JSClassID; ourUnforgeable: JSFunctionList) =
-  let ctxOpaque = ctx.getOpaque()
-  var merged = @ourUnforgeable
-  ctxOpaque.unforgeable.withValue(parent, uf):
-    merged.add(uf[])
-  if merged.len > 0:
-    ctxOpaque.unforgeable[classid] = merged
-    let ufp = addr ctxOpaque.unforgeable[classid][0]
-    JS_SetPropertyFunctionList(ctx, proto, ufp, cint(merged.len))
-
-func newJSClass*(ctx: JSContext; cdef: JSClassDefConst; tname: string;
-    nimt: pointer; ctor: JSCFunction; funcs: JSFunctionList; parent: JSClassID;
-    asglobal: bool; nointerface: bool; finalizer: JSFinalizerFunction;
-    namespace: JSValue; errid: Opt[JSErrorEnum];
-    unforgeable, staticfuns: JSFunctionList;
-    ishtmldda: bool): JSClassID {.discardable.} =
-  let rt = JS_GetRuntime(ctx)
-  discard JS_NewClassID(addr result)
-  var ctxOpaque = ctx.getOpaque()
-  var rtOpaque = rt.getOpaque()
-  if JS_NewClass(rt, result, cdef) != 0:
-    raise newException(Defect, "Failed to allocate JS class: " &
-      $cdef.class_name)
-  ctxOpaque.typemap[nimt] = result
-  ctxOpaque.creg[tname] = result
-  ctxOpaque.parents[result] = parent
-  if ishtmldda:
-    ctxOpaque.htmldda = result
-  if finalizer != nil:
-    rtOpaque.fins[result] = finalizer
-  let proto = if parent != 0:
-    let parentProto = JS_GetClassProto(ctx, parent)
-    let x = JS_NewObjectProtoClass(ctx, parentProto, parent)
-    JS_FreeValue(ctx, parentProto)
-    x
-  else:
-    JS_NewObject(ctx)
-  if funcs.len > 0:
-    # We avoid funcs being GC'ed by putting the list in rtOpaque.
-    # (QuickJS uses the pointer later.)
-    #TODO maybe put them in ctxOpaque instead?
-    rtOpaque.flist.add(@funcs)
-    JS_SetPropertyFunctionList(ctx, proto, addr rtOpaque.flist[^1][0],
-      cint(funcs.len))
-  #TODO check if this is an indexed property getter
-  if cdef.exotic != nil and cdef.exotic.get_own_property != nil:
-    let val = JS_DupValue(ctx, ctxOpaque.valRefs[jsvArrayPrototypeValues])
-    let itSym = ctxOpaque.symRefs[jsyIterator]
-    doAssert JS_SetProperty(ctx, proto, itSym, val) == 1
-  let news = JS_NewAtomString(ctx, cdef.class_name)
-  doAssert not JS_IsException(news)
-  ctx.definePropertyC(proto, ctxOpaque.symRefs[jsyToStringTag],
-    JS_DupValue(ctx, news))
-  JS_SetClassProto(ctx, result, proto)
-  ctx.addClassUnforgeable(proto, result, parent, unforgeable)
-  if asglobal:
-    let global = JS_GetGlobalObject(ctx)
-    assert ctxOpaque.gclaz == ""
-    ctxOpaque.gclaz = tname
-    ctxOpaque.gparent = parent
-    ctx.definePropertyC(global, ctxOpaque.symRefs[jsyToStringTag],
-      JS_DupValue(ctx, news))
-    if JS_SetPrototype(ctx, global, proto) != 1:
-      raise newException(Defect, "Failed to set global prototype: " &
-        $cdef.class_name)
-    # Global already exists, so set unforgeable functions here
-    ctxOpaque.unforgeable.withValue(result, uf):
-      JS_SetPropertyFunctionList(ctx, global, addr uf[][0], cint(uf[].len))
-    JS_FreeValue(ctx, global)
-  JS_FreeValue(ctx, news)
-  let jctor = JS_NewCFunction2(ctx, ctor, cstring($cdef.class_name), 0,
-    JS_CFUNC_constructor, 0)
-  if staticfuns.len > 0:
-    rtOpaque.flist.add(@staticfuns)
-    JS_SetPropertyFunctionList(ctx, jctor, addr rtOpaque.flist[^1][0],
-      cint(staticfuns.len))
-  JS_SetConstructor(ctx, jctor, proto)
-  if errid.isSome:
-    ctx.getOpaque().errCtorRefs[errid.get] = JS_DupValue(ctx, jctor)
-  ctxOpaque.ctors[result] = JS_DupValue(ctx, jctor)
-  if not nointerface:
-    if JS_IsNull(namespace):
-      let global = JS_GetGlobalObject(ctx)
-      ctx.definePropertyCW(global, $cdef.class_name, jctor)
-      JS_FreeValue(ctx, global)
-    else:
-      ctx.definePropertyCW(namespace, $cdef.class_name, jctor)
-  else:
-    JS_FreeValue(ctx, jctor)
-
-type FuncParam = tuple
-  name: string
-  t: NimNode
-  val: Option[NimNode]
-  generic: Option[NimNode]
-  isptr: bool
-
-func getMinArgs(params: seq[FuncParam]): int =
-  for i in 0..<params.len:
-    let it = params[i]
-    if it[2].isSome:
-      return i
-    let t = it.t
-    if t.kind == nnkBracketExpr:
-      if t.typeKind == varargs.getType().typeKind:
-        assert i == params.high, "Not even nim can properly handle this..."
-        return i
-  return params.len
-
-func fromJSP[T: string|uint32](ctx: JSContext; atom: JSAtom): Opt[T] =
-  return fromJS[T](ctx, atom)
-
-proc defineConsts*[T](ctx: JSContext; classid: JSClassID;
-    consts: static openArray[(string, T)]) =
-  let proto = ctx.getOpaque().ctors[classid]
-  for (k, v) in consts:
-    ctx.definePropertyE(proto, k, v)
-
-proc defineConsts*(ctx: JSContext; classid: JSClassID;
-    consts: typedesc[enum]; astype: typedesc) =
-  let proto = ctx.getOpaque().ctors[classid]
-  for e in consts:
-    ctx.definePropertyE(proto, $e, astype(e))
-
-type
-  JSFuncGenerator = ref object
-    t: BoundFunctionType
-    thisname: Option[string]
-    funcName: string
-    generics: Table[string, seq[NimNode]]
-    funcParams: seq[FuncParam]
-    passCtx: bool
-    thisType: string
-    thisTypeNode: NimNode
-    returnType: Option[NimNode]
-    newName: NimNode
-    newBranchList: seq[NimNode]
-    errval: NimNode # JS_EXCEPTION or -1
-    # die: didn't match parameters, but could still match other ones
-    dielabel: NimNode
-    jsFunCallLists: seq[NimNode]
-    jsFunCallList: NimNode
-    jsFunCall: NimNode
-    jsCallAndRet: NimNode
-    minArgs: int
-    actualMinArgs: int # minArgs without JSContext
-    i: int # nim parameters accounted for
-    j: int # js parameters accounted for (not including fix ones, e.g. `this')
-    unforgeable: bool
-    isstatic: bool
-
-var BoundFunctions {.compileTime.}: Table[string, seq[BoundFunction]]
-
-proc getGenerics(fun: NimNode): Table[string, seq[NimNode]] =
-  var node = fun.findChild(it.kind == nnkBracket)
-  if node.kind == nnkNilLit:
-    return # no bracket
-  node = node.findChild(it.kind == nnkGenericParams)
-  if node.kind == nnkNilLit:
-    return # no generics
-  node = node.findChild(it.kind == nnkIdentDefs)
-  var stack: seq[NimNode]
-  for i in countdown(node.len - 1, 0): stack.add(node[i])
-  var gen_name: NimNode
-  var gen_types: seq[NimNode]
-  template add_gen =
-    if gen_name != nil:
-      assert gen_types.len != 0
-      result[gen_name.strVal] = gen_types
-      gen_types.setLen(0)
-
-  while stack.len > 0:
-    let node = stack.pop()
-    case node.kind
-    of nnkIdent:
-      add_gen
-      gen_name = node
-    of nnkSym:
-      assert gen_name != nil
-      gen_types.add(node)
-    of nnkInfix:
-      assert node[0].eqIdent(ident("|")) or node[0].eqIdent(ident("or")),
-        "Only OR generics are supported."
-      for i in countdown(node.len - 1, 1): # except infix ident
-        stack.add(node[i])
-    of nnkBracketExpr:
-      gen_types.add(node)
-    else:
-      discard
-  add_gen
-
-proc getParams(fun: NimNode): seq[FuncParam] =
-  let formalParams = fun.findChild(it.kind == nnkFormalParams)
-  var funcParams: seq[FuncParam]
-  var returnType = none(NimNode)
-  if formalParams[0].kind != nnkEmpty:
-    returnType = some(formalParams[0])
-  for i in 1..<fun.params.len:
-    let it = formalParams[i]
-    let tt = it[^2]
-    var t: NimNode
-    if it[^2].kind != nnkEmpty:
-      t = `tt`
-    elif it[^1].kind != nnkEmpty:
-      let x = it[^1]
-      t = quote do:
-        typeof(`x`)
-    else:
-      error("?? " & treeRepr(it))
-    let isptr = t.kind == nnkVarTy
-    if t.kind in {nnkRefTy, nnkVarTy}:
-      t = t[0]
-    let val = if it[^1].kind != nnkEmpty:
-      let x = it[^1]
-      some(newPar(x))
-    else:
-      none(NimNode)
-    var g = none(NimNode)
-    for i in 0 ..< it.len - 2:
-      let name = $it[i]
-      funcParams.add((name, t, val, g, isptr))
-  funcParams
-
-proc getReturn(fun: NimNode): Option[NimNode] =
-  let formalParams = fun.findChild(it.kind == nnkFormalParams)
-  if formalParams[0].kind != nnkEmpty:
-    some(formalParams[0])
-  else:
-    none(NimNode)
-
-template getJSParams(): untyped =
-  [
-    (quote do: JSValue),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("this"), quote do: JSValue),
-    newIdentDefs(ident("argc"), quote do: cint),
-    newIdentDefs(ident("argv"), quote do: ptr UncheckedArray[JSValue])
-  ]
-
-template getJSGetterParams(): untyped =
-  [
-    (quote do: JSValue),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("this"), quote do: JSValue),
-  ]
-
-template getJSGetPropParams(): untyped =
-  [
-    (quote do: cint),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("desc"), quote do: ptr JSPropertyDescriptor),
-    newIdentDefs(ident("obj"), quote do: JSValue),
-    newIdentDefs(ident("prop"), quote do: JSAtom),
-  ]
-
-template getJSSetPropParams(): untyped =
-  [
-    (quote do: cint),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("obj"), quote do: JSValue),
-    newIdentDefs(ident("atom"), quote do: JSAtom),
-    newIdentDefs(ident("value"), quote do: JSValue),
-    newIdentDefs(ident("receiver"), quote do: JSValue),
-    newIdentDefs(ident("flags"), quote do: cint),
-  ]
-
-template getJSDelPropParams(): untyped =
-  [
-    (quote do: cint),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("obj"), quote do: JSValue),
-    newIdentDefs(ident("prop"), quote do: JSAtom),
-  ]
-
-template getJSHasPropParams(): untyped =
-  [
-    (quote do: cint),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("obj"), quote do: JSValue),
-    newIdentDefs(ident("atom"), quote do: JSAtom),
-  ]
-
-
-template getJSSetterParams(): untyped =
-  [
-    (quote do: JSValue),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("this"), quote do: JSValue),
-    newIdentDefs(ident("val"), quote do: JSValue),
-  ]
-
-template getJSPropNamesParams(): untyped =
-  [
-    (quote do: cint),
-    newIdentDefs(ident("ctx"), quote do: JSContext),
-    newIdentDefs(ident("ptab"), quote do: ptr JSPropertyEnumArray),
-    newIdentDefs(ident("plen"), quote do: ptr uint32),
-    newIdentDefs(ident("obj"), quote do: JSValue)
-  ]
-
-template fromJS_or_return*(t, ctx, val: untyped): untyped =
-  let x = fromJS[t](ctx, val)
-  if x.isNone:
-    return toJS(ctx, x.error)
-  x.get
-
-template fromJSP_or_return*(t, ctx, val: untyped): untyped =
-  let x = fromJSP[t](ctx, val)
-  if x.isNone:
-    return toJS(ctx, x.error)
-  x.get
-
-template fromJS_or_die*(t, ctx, val, dl: untyped): untyped =
-  let x = fromJSP[t](ctx, val)
-  if x.isNone:
-    break dl
-  x.get
-
-proc addParam2(gen: var JSFuncGenerator; s, t, val: NimNode;
-    fallback: NimNode = nil) =
-  let dl = gen.dielabel
-  let stmt = quote do:
-    fromJS_or_die(`t`, ctx, `val`, `dl`)
-  for i in 0..gen.jsFunCallLists.high:
-    if fallback == nil:
-      gen.jsFunCallLists[i].add(newLetStmt(s, stmt))
-    else:
-      let j = gen.j
-      gen.jsFunCallLists[i].add(newLetStmt(s, quote do:
-        if `j` < argc and not JS_IsUndefined(argv[`j`]):
-          `stmt`
-        else:
-          `fallback`))
-
-proc addValueParam(gen: var JSFuncGenerator; s, t: NimNode;
-    fallback: NimNode = nil) =
-  let j = gen.j
-  gen.addParam2(s, t, quote do: argv[`j`], fallback)
-
-proc addUnionParamBranch(gen: var JSFuncGenerator; query, newBranch: NimNode;
-    fallback: NimNode = nil) =
-  let i = gen.i
-  let query = if fallback == nil: query else:
-    quote do: (`i` < argc and `query`)
-  let newBranch = newStmtList(newBranch)
-  for i in 0..gen.jsFunCallLists.high:
-    var ifstmt = newIfStmt((query, newBranch))
-    let oldBranch = newStmtList()
-    ifstmt.add(newTree(nnkElse, oldBranch))
-    gen.jsFunCallLists[i].add(ifstmt)
-    gen.jsFunCallLists[i] = oldBranch
-  gen.newBranchList.add(newBranch)
-
-func isSequence*(ctx: JSContext; o: JSValue): bool =
-  if not JS_IsObject(o):
-    return false
-  let prop = JS_GetProperty(ctx, o, ctx.getOpaque().symRefs[jsyIterator])
-  # prop can't be exception (throws_ref_error is 0 and tag is object)
-  result = not JS_IsUndefined(prop)
-  JS_FreeValue(ctx, prop)
-
-proc addUnionParam0(gen: var JSFuncGenerator; tt, s, val: NimNode;
-    fallback: NimNode = nil) =
-  # Union types.
-  #TODO quite a few types are still missing.
-  let flattened = gen.generics[tt.strVal] # flattened member types
-  var tableg = none(NimNode)
-  var seqg = none(NimNode)
-  var numg = none(NimNode)
-  var objg = none(NimNode)
-  var hasString = false
-  var hasJSValue = false
-  var hasBoolean = false
-  let ev = gen.errval
-  let dl = gen.dielabel
-  for g in flattened:
-    if g.len > 0 and g[0] == Table.getType():
-      tableg = some(g)
-    elif g.typekind == ntySequence:
-      seqg = some(g)
-    elif g == string.getTypeInst():
-      hasString = true
-    elif g == JSValue.getTypeInst():
-      hasJSValue = true
-    elif g == bool.getTypeInst():
-      hasBoolean = true
-    elif g == int.getTypeInst(): #TODO should be SomeNumber
-      assert numg.isNone
-      numg = some(g)
-    elif g == uint32.getTypeInst(): #TODO should be SomeNumber
-      assert numg.isNone
-      numg = some(g)
-    elif g.getTypeInst().getTypeImpl().kind == nnkRefTy:
-      # Assume it's ref object.
-      objg = some(g)
-    else:
-      error("Type not supported yet")
-
-  # 5. If V is a platform object, then:
-  if objg.isSome:
-    let t = objg.get
-    let x = ident("x")
-    let query = quote do:
-      let `x` = fromJS[`t`](ctx, `val`)
-      `x`.isSome
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = `x`.get,
-      fallback)
-  # 10. If Type(V) is Object, then:
-  # Sequence:
-  if seqg.isSome:
-    let query = quote do:
-      isSequence(ctx, `val`)
-    let a = seqg.get[1]
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = fromJS_or_die(seq[`a`], ctx, `val`, `dl`),
-      fallback)
-  # Record:
-  if tableg.isSome:
-    let a = tableg.get[1]
-    let b = tableg.get[2]
-    let query = quote do:
-      JS_IsObject(`val`)
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = fromJS_or_die(Table[`a`, `b`], ctx, `val`, `dl`),
-      fallback)
-  # Object (JSObject variant):
-  #TODO non-JS objects (i.e. ref object)
-  if hasJSValue:
-    let query = quote do:
-      JS_IsObject(`val`)
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = fromJS_or_die(JSValue, ctx, `val`, `dl`),
-      fallback)
-  # 11. If Type(V) is Boolean, then:
-  if hasBoolean:
-    let query = quote do:
-      JS_IsBool(`val`)
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = fromJS_or_die(bool, ctx, `val`, `dl`),
-      fallback)
-  # 12. If Type(V) is Number, then:
-  if numg.isSome:
-    let ng = numg.get
-    let query = quote do:
-      JS_IsNumber(`val`)
-    gen.addUnionParamBranch(query, quote do:
-      let `s` = fromJS_or_die(`ng`, ctx, `val`, `dl`),
-      fallback)
-  # 14. If types includes a string type, then return the result of converting V
-  # to that type.
-  if hasString:
-    gen.addParam2(s, string.getType(), quote do: `val`, fallback)
-  # 16. If types includes a numeric type, then return the result of converting
-  # V to that numeric type.
-  elif numg.isSome:
-    gen.addParam2(s, numg.get.getType(), quote do: `val`, fallback)
-  # 17. If types includes boolean, then return the result of converting V to
-  # boolean.
-  elif hasBoolean:
-    gen.addParam2(s, bool.getType(), quote do: `val`, fallback)
-  # 19. Throw a TypeError.
-  else:
-    gen.addParam2(s, string.getType(), quote do:
-      if true:
-        discard JS_ThrowTypeError(ctx, "No match for union type")
-        return `ev`
-      JS_NULL, fallback)
-
-  for branch in gen.newBranchList:
-    gen.jsFunCallLists.add(branch)
-  gen.newBranchList.setLen(0)
-
-proc addUnionParam(gen: var JSFuncGenerator; tt, s: NimNode;
-    fallback: NimNode = nil) =
-  let j = gen.j
-  gen.addUnionParam0(tt, s, quote do: argv[`j`], fallback)
-
-proc addFixParam(gen: var JSFuncGenerator; name: string) =
-  var s = ident("arg_" & $gen.i)
-  let t = gen.funcParams[gen.i].t
-  let id = ident(name)
-  if t.typeKind == ntyGenericParam:
-    gen.addUnionParam0(t, s, id)
-  else:
-    gen.addParam2(s, t, id)
-  if gen.funcParams[gen.i].isptr:
-    s = quote do: `s`[]
-  gen.jsFunCall.add(s)
-  inc gen.i
-
-proc addRequiredParams(gen: var JSFuncGenerator) =
-  while gen.i < gen.minArgs:
-    var s = ident("arg_" & $gen.i)
-    let tt = gen.funcParams[gen.i].t
-    if tt.typeKind == ntyGenericParam:
-      gen.addUnionParam(tt, s)
-    else:
-      gen.addValueParam(s, tt)
-    if gen.funcParams[gen.i].isptr:
-      s = quote do: `s`[]
-    gen.jsFunCall.add(s)
-    inc gen.j
-    inc gen.i
-
-proc addOptionalParams(gen: var JSFuncGenerator) =
-  while gen.i < gen.funcParams.len:
-    let j = gen.j
-    var s = ident("arg_" & $gen.i)
-    let tt = gen.funcParams[gen.i].t
-    if tt.typeKind == varargs.getType().typeKind: # pray it's not a generic...
-      let vt = tt[1].getType()
-      for i in 0..gen.jsFunCallLists.high:
-        gen.jsFunCallLists[i].add(newLetStmt(s, quote do:
-          var valist: seq[`vt`] = @[]
-          for i in `j`..<argc:
-            let it = fromJS_or_return(`vt`, ctx, argv[i])
-            valist.add(it)
-          valist
-        ))
-    else:
-      if gen.funcParams[gen.i][2].isNone:
-        error("No fallback value. Maybe a non-optional parameter follows an " &
-          "optional parameter?")
-      let fallback = gen.funcParams[gen.i][2].get
-      if tt.typeKind == ntyGenericParam:
-        gen.addUnionParam(tt, s, fallback)
-      else:
-        gen.addValueParam(s, tt, fallback)
-    if gen.funcParams[gen.i].isptr:
-      s = quote do: `s`[]
-    gen.jsFunCall.add(s)
-    inc gen.j
-    inc gen.i
-
-proc finishFunCallList(gen: var JSFuncGenerator) =
-  for branch in gen.jsFunCallLists:
-    branch.add(gen.jsFunCall)
-
-var existingFuncs {.compileTime.}: HashSet[string]
-var jsDtors {.compileTime.}: HashSet[string]
-
-proc registerFunction(typ: string; nf: BoundFunction) =
-  BoundFunctions.withValue(typ, val):
-    val[].add(nf)
-  do:
-    BoundFunctions[typ] = @[nf]
-  existingFuncs.incl(nf.id.strVal)
-
-proc registerFunction(typ: string; t: BoundFunctionType; name: string;
-    id: NimNode; magic: uint16 = 0; uf = false; isstatic = false;
-    ctorBody: NimNode = nil) =
-  registerFunction(typ, BoundFunction(
-    t: t,
-    name: name,
-    id: id,
-    magic: magic,
-    unforgeable: uf,
-    isstatic: isstatic,
-    ctorBody: ctorBody
-  ))
-
-proc registerConstructor(gen: JSFuncGenerator; jsProc: NimNode) =
-  registerFunction(gen.thisType, gen.t, gen.funcName, gen.newName,
-    uf = gen.unforgeable, isstatic = gen.isstatic, ctorBody = jsProc)
-
-proc registerFunction(gen: JSFuncGenerator) =
-  registerFunction(gen.thisType, gen.t, gen.funcName, gen.newName,
-    uf = gen.unforgeable, isstatic = gen.isstatic)
-
-export JS_ThrowTypeError, JS_ThrowRangeError, JS_ThrowSyntaxError,
-       JS_ThrowInternalError, JS_ThrowReferenceError
-
-proc newJSProcBody(gen: var JSFuncGenerator; isva: bool): NimNode =
-  let tt = gen.thisType
-  let fn = gen.funcName
-  let ma = gen.actualMinArgs
-  result = newStmtList()
-  if isva:
-    result.add(quote do:
-      if argc < `ma`:
-        return JS_ThrowTypeError(ctx,
-          "At least %d arguments required, but only %d passed", cint(`ma`),
-          cint(argc))
-    )
-  if gen.thisname.isSome and not gen.isstatic:
-    let tn = ident(gen.thisname.get)
-    let ev = gen.errval
-    result.add(quote do:
-      if not ctx.isInstanceOf(`tn`, `tt`):
-        discard JS_ThrowTypeError(ctx,
-          "'%s' called on an object that is not an instance of %s", `fn`, `tt`)
-        return `ev`
-    )
-  result.add(gen.jsCallAndRet)
-
-proc newJSProc(gen: var JSFuncGenerator; params: openArray[NimNode];
-    isva = true): NimNode =
-  let jsBody = gen.newJSProcBody(isva)
-  let jsPragmas = newNimNode(nnkPragma).add(ident("cdecl"))
-  return newProc(gen.newName, params, jsBody, pragmas = jsPragmas)
-
-func getFuncName(fun: NimNode; jsname, staticName: string): string =
-  if jsname != "":
-    return jsname
-  if staticName != "":
-    let i = staticName.find('.')
-    if i != -1:
-      return staticName.substr(i + 1)
-  let x = $fun[0]
-  if x == "$":
-    # stringifier
-    return "toString"
-  return x
-
-func getErrVal(t: BoundFunctionType): NimNode =
-  if t in {bfPropertyGet, bfPropertySet, bfPropertyDel, bfPropertyHas,
-      bfPropertyNames}:
-    return quote do: cint(-1)
-  return quote do: JS_EXCEPTION
-
-proc addJSContext(gen: var JSFuncGenerator) =
-  if gen.funcParams.len > gen.i:
-    if gen.funcParams[gen.i].t.eqIdent(ident("JSContext")):
-      gen.passCtx = true
-      gen.jsFunCall.add(ident("ctx"))
-      inc gen.i
-    elif gen.funcParams[gen.i].t.eqIdent(ident("JSRuntime")):
-      inc gen.i # special case for finalizers that have a JSRuntime param
-
-proc addThisName(gen: var JSFuncGenerator; thisname: Option[string]) =
-  if thisname.isSome:
-    gen.thisTypeNode = gen.funcParams[gen.i].t
-    gen.thisType = $gen.funcParams[gen.i].t
-    gen.newName = ident($gen.t & "_" & gen.thisType & "_" & gen.funcName)
-  else:
-    let rt = gen.returnType.get
-    if rt.kind in {nnkRefTy, nnkPtrTy}:
-      gen.thisTypeNode = rt[0]
-      gen.thisType = rt[0].strVal
-    else:
-      if rt.kind == nnkBracketExpr:
-        gen.thisTypeNode = rt[1]
-        gen.thisType = rt[1].strVal
-      else:
-        gen.thisTypeNode = rt
-        gen.thisType = rt.strVal
-    gen.newName = ident($gen.t & "_" & gen.funcName)
-
-func getActualMinArgs(gen: var JSFuncGenerator): int =
-  var ma = gen.minArgs
-  if gen.thisname.isSome and not gen.isstatic:
-    dec ma
-  if gen.passCtx:
-    dec ma
-  assert ma >= 0
-  return ma
-
-func until(s: string; c: char; starti = 0): string =
-  result = ""
-  for i, cc in s:
-    if cc == c:
-      break
-    result &= cc
-
-proc initGenerator(fun: NimNode; t: BoundFunctionType; thisname = some("this");
-    jsname = ""; unforgeable = false; staticName = ""): JSFuncGenerator =
-  let jsFunCallList = newStmtList()
-  let funcParams = getParams(fun)
-  var gen = JSFuncGenerator(
-    t: t,
-    funcName: getFuncName(fun, jsname, staticName),
-    generics: getGenerics(fun),
-    funcParams: funcParams,
-    returnType: getReturn(fun),
-    minArgs: funcParams.getMinArgs(),
-    thisname: thisname,
-    errval: getErrVal(t),
-    dielabel: ident("ondie"),
-    jsFunCallList: jsFunCallList,
-    jsFunCallLists: @[jsFunCallList],
-    jsFunCall: newCall(fun[0]),
-    unforgeable: unforgeable,
-    isstatic: staticName != ""
-  )
-  gen.addJSContext()
-  gen.actualMinArgs = gen.getActualMinArgs() # must come after passctx is set
-  if staticName == "":
-    gen.addThisName(thisname)
-  else:
-    gen.thisType = staticName.until('.')
-    gen.newName = ident($gen.t & "_" & gen.funcName)
-  return gen
-
-proc makeJSCallAndRet(gen: var JSFuncGenerator; okstmt, errstmt: NimNode) =
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = if gen.returnType.isSome:
-    quote do:
-      block `dl`:
-        return ctx.toJS(`jfcl`)
-      `errstmt`
-  else:
-    quote do:
-      block `dl`:
-        `jfcl`
-        `okstmt`
-      `errstmt`
-
-proc makeCtorJSCallAndRet(gen: var JSFuncGenerator; errstmt: NimNode) =
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = quote do:
-    block `dl`:
-      return ctx.toJSNew(`jfcl`, this)
-    `errstmt`
-
-macro jsctor*(fun: typed) =
-  var gen = initGenerator(fun, bfConstructor, thisname = none(string))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO implement function overloading
-    error("Function overloading hasn't been implemented yet...")
-  gen.addRequiredParams()
-  gen.addOptionalParams()
-  gen.finishFunCallList()
-  let errstmt = quote do:
-    return JS_ThrowTypeError(ctx, "Invalid parameters passed to constructor")
-  gen.makeCtorJSCallAndRet(errstmt)
-  let jsProc = gen.newJSProc(getJSParams())
-  gen.registerConstructor(jsProc)
-  return newStmtList(fun)
-
-macro jshasprop*(fun: typed) =
-  var gen = initGenerator(fun, bfPropertyHas, thisname = some("obj"))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("obj")
-  gen.addFixParam("atom")
-  gen.finishFunCallList()
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = quote do:
-    block `dl`:
-      let retv = `jfcl`
-      return cint(retv)
-    doAssert false # TODO?
-  let jsProc = gen.newJSProc(getJSHasPropParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-macro jsgetprop*(fun: typed) =
-  var gen = initGenerator(fun, bfPropertyGet, thisname = some("obj"))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("obj")
-  gen.addFixParam("prop")
-  gen.finishFunCallList()
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = quote do:
-    block `dl`:
-      let retv = ctx.toJS(`jfcl`)
-      if retv != JS_NULL:
-        if desc != nil:
-          # From quickjs.h:
-          # > If 1 is returned, the property descriptor 'desc' is filled
-          # > if != NULL.
-          # So desc may be nil.
-          desc[].setter = JS_UNDEFINED
-          desc[].getter = JS_UNDEFINED
-          desc[].value = retv
-          desc[].flags = 0
-        return cint(1)
-    return cint(0)
-  let jsProc = gen.newJSProc(getJSGetPropParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-macro jssetprop*(fun: typed) =
-  var gen = initGenerator(fun, bfPropertySet, thisname = some("obj"))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("receiver")
-  gen.addFixParam("atom")
-  gen.addFixParam("value")
-  gen.finishFunCallList()
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = if gen.returnType.isSome:
-    quote do:
-      block `dl`:
-        let v = toJS(ctx, `jfcl`)
-        if not JS_IsException(v):
-          return cint(1)
-      return cint(-1)
-  else:
-    quote do:
-      block `dl`:
-        `jfcl`
-        return cint(1)
-      return cint(-1)
-  let jsProc = gen.newJSProc(getJSSetPropParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-macro jsdelprop*(fun: typed) =
-  var gen = initGenerator(fun, bfPropertyDel, thisname = some("obj"))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("obj")
-  gen.addFixParam("prop")
-  gen.finishFunCallList()
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = quote do:
-    block `dl`:
-      let retv = `jfcl`
-      return cint(retv)
-    return cint(-1)
-  let jsProc = gen.newJSProc(getJSDelPropParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-macro jspropnames*(fun: typed) =
-  var gen = initGenerator(fun, bfPropertyNames, thisname = some("obj"))
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("obj")
-  gen.finishFunCallList()
-  let jfcl = gen.jsFunCallList
-  let dl = gen.dielabel
-  gen.jsCallAndRet = quote do:
-    block `dl`:
-      let retv = `jfcl`
-      ptab[] = retv.buffer
-      plen[] = retv.len
-      return cint(0)
-    return cint(-1)
-  let jsProc = gen.newJSProc(getJSPropNamesParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-macro jsfgetn(jsname: static string; uf: static bool; fun: typed) =
-  var gen = initGenerator(fun, bfGetter, jsname = jsname, unforgeable = uf)
-  if gen.actualMinArgs != 0 or gen.funcParams.len != gen.minArgs:
-    error("jsfget functions must only accept one parameter.")
-  if gen.returnType.isNone:
-    error("jsfget functions must have a return type.")
-  if gen.newName.strVal in existingFuncs:
-    #TODO TODO TODO ditto
-    error("Function overloading hasn't been implemented yet...")
-  gen.addFixParam("this")
-  gen.finishFunCallList()
-  gen.makeJSCallAndRet(nil, quote do: discard)
-  let jsProc = gen.newJSProc(getJSGetterParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-# "Why?" So the compiler doesn't cry.
-template jsfget*(fun: typed) =
-  jsfgetn("", false, fun)
-
-template jsuffget*(fun: typed) =
-  jsfgetn("", true, fun)
-
-template jsfget*(jsname: static string; fun: typed) =
-  jsfgetn(jsname, false, fun)
-
-template jsuffget*(jsname: static string; fun: typed) =
-  jsfgetn(jsname, true, fun)
-
-# Ideally we could simulate JS setters using nim setters, but nim setters
-# won't accept types that don't match their reflected field's type.
-macro jsfsetn(jsname: static string; fun: typed) =
-  var gen = initGenerator(fun, bfSetter, jsname = jsname)
-  if gen.actualMinArgs != 1 or gen.funcParams.len != gen.minArgs:
-    error("jsfset functions must accept two parameters")
-  if gen.returnType.isSome:
-    let rt = gen.returnType.get
-    #TODO ??
-    let rtType = rt[0]
-    let errType = getTypeInst(Err)
-    if not errType.sameType(rtType) and not rtType.sameType(errType):
-      error("jsfset functions must not have a return type")
-  gen.addFixParam("this")
-  gen.addFixParam("val")
-  gen.finishFunCallList()
-  # return param anyway
-  let okstmt = quote do: discard
-  let errstmt = quote do: return JS_DupValue(ctx, val)
-  gen.makeJSCallAndRet(okstmt, errstmt)
-  let jsProc = gen.newJSProc(getJSSetterParams(), false)
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-template jsfset*(fun: typed) =
-  jsfsetn("", fun)
-
-template jsfset*(jsname: static string; fun: typed) =
-  jsfsetn(jsname, fun)
-
-macro jsfuncn*(jsname: static string; uf: static bool;
-    staticName: static string; fun: typed) =
-  var gen = initGenerator(fun, bfFunction, jsname = jsname, unforgeable = uf,
-    staticName = staticName)
-  if gen.minArgs == 0 and not gen.isstatic:
-    error("Zero-parameter functions are not supported. " &
-      "(Maybe pass Window or Client?)")
-  if not gen.isstatic:
-    gen.addFixParam("this")
-  gen.addRequiredParams()
-  gen.addOptionalParams()
-  gen.finishFunCallList()
-  let okstmt = quote do:
-    return JS_UNDEFINED
-  let errstmt = quote do:
-    return JS_ThrowTypeError(ctx, "Invalid parameters passed to function")
-  gen.makeJSCallAndRet(okstmt, errstmt)
-  let jsProc = gen.newJSProc(getJSParams())
-  gen.registerFunction()
-  return newStmtList(fun, jsProc)
-
-template jsfunc*(fun: typed) =
-  jsfuncn("", false, "", fun)
-
-template jsuffunc*(fun: typed) =
-  jsfuncn("", true, "", fun)
-
-template jsfunc*(jsname: static string; fun: typed) =
-  jsfuncn(jsname, false, "", fun)
-
-template jsuffunc*(jsname: static string; fun: typed) =
-  jsfuncn(jsname, true, "", fun)
-
-template jsstfunc*(name: static string; fun: typed) =
-  jsfuncn("", false, name, fun)
-
-macro jsfin*(fun: typed) =
-  var gen = initGenerator(fun, bfFinalizer, thisname = some("fin"))
-  let finName = gen.newName
-  let finFun = ident(gen.funcName)
-  let t = gen.thisTypeNode
-  if gen.minArgs == 1:
-    let jsProc = quote do:
-      proc `finName`(rt: JSRuntime; val: JSValue) =
-        let opaque = JS_GetOpaque(val, JS_GetClassID(val))
-        if opaque != nil:
-          `finFun`(cast[`t`](opaque))
-    gen.registerFunction()
-    result = newStmtList(fun, jsProc)
-  elif gen.minArgs == 2:
-    let jsProc = quote do:
-      proc `finName`(rt: JSRuntime; val: JSValue) =
-        let opaque = JS_GetOpaque(val, JS_GetClassID(val))
-        if opaque != nil:
-          `finFun`(rt, cast[`t`](opaque))
-    gen.registerFunction()
-    result = newStmtList(fun, jsProc)
-  else:
-    error("Expected one or two parameters")
-
-# Having the same names for these and the macros leads to weird bugs, so the
-# macros get an additional f.
-template jsget*() {.pragma.}
-template jsget*(name: string) {.pragma.}
-template jsset*() {.pragma.}
-template jsset*(name: string) {.pragma.}
-template jsgetset*() {.pragma.}
-template jsgetset*(name: string) {.pragma.}
-template jsufget*() {.pragma.}
-template jsufget*(name: string) {.pragma.}
-
-proc js_illegal_ctor*(ctx: JSContext; this: JSValue; argc: cint;
-    argv: ptr UncheckedArray[JSValue]): JSValue {.cdecl.} =
-  return JS_ThrowTypeError(ctx, "Illegal constructor")
-
-type
-  JSObjectPragma = object
-    name: string
-    varsym: NimNode
-    unforgeable: bool
-
-  JSObjectPragmas = object
-    jsget: seq[JSObjectPragma]
-    jsset: seq[JSObjectPragma]
-    jsinclude: seq[JSObjectPragma]
-
-func getPragmaName(varPragma: NimNode): string =
-  if varPragma.kind == nnkExprColonExpr:
-    return $varPragma[0]
-  return $varPragma
-
-func getStringFromPragma(varPragma: NimNode): Option[string] =
-  if varPragma.kind == nnkExprColonExpr:
-    if not varPragma.len == 1 and varPragma[1].kind == nnkStrLit:
-      error("Expected string as pragma argument")
-    return some($varPragma[1])
-
-proc findPragmas(t: NimNode): JSObjectPragmas =
-  let typ = t.getTypeInst()[1] # The type, as declared.
-  var impl = typ.getTypeImpl() # ref t
-  if impl.kind in {nnkRefTy, nnkPtrTy}:
-    impl = impl[0].getImpl()
-  else:
-    impl = typ.getImpl()
-  # stolen from std's macros.customPragmaNode
-  var identDefsStack = newSeq[NimNode](impl[2].len)
-  for i in 0 ..< identDefsStack.len:
-    identDefsStack[i] = impl[2][i]
-  var pragmas: JSObjectPragmas
-  while identDefsStack.len > 0:
-    var identDefs = identDefsStack.pop()
-    case identDefs.kind
-    of nnkRecList:
-      for child in identDefs.children:
-        identDefsStack.add(child)
-    of nnkRecCase:
-      # Add condition definition
-      identDefsStack.add(identDefs[0])
-      # Add branches
-      for i in 1 ..< identDefs.len:
-        identDefsStack.add(identDefs[i].last)
-    else:
-      for i in 0 .. identDefs.len - 3:
-        let varNode = identDefs[i]
-        if varNode.kind == nnkPragmaExpr:
-          var varName = varNode[0]
-          if varName.kind == nnkPostfix:
-            # This is a public field. We are skipping the postfix *
-            varName = varName[1]
-          var varPragmas = varNode[1]
-          for varPragma in varPragmas:
-            let pragmaName = getPragmaName(varPragma)
-            var op = JSObjectPragma(
-              name: getStringFromPragma(varPragma).get($varName),
-              varsym: varName
-            )
-            case pragmaName
-            of "jsget": pragmas.jsget.add(op)
-            of "jsset": pragmas.jsset.add(op)
-            of "jsufget": # LegacyUnforgeable
-              op.unforgeable = true
-              pragmas.jsget.add(op)
-            of "jsgetset":
-              pragmas.jsget.add(op)
-              pragmas.jsset.add(op)
-            of "jsinclude": pragmas.jsinclude.add(op)
-  return pragmas
-
-proc nim_finalize_for_js*(obj: pointer) =
-  for rt in runtimes:
-    let rtOpaque = rt.getOpaque()
-    rtOpaque.plist.withValue(obj, v):
-      let p = v[]
-      let val = JS_MKPTR(JS_TAG_OBJECT, p)
-      let classid = JS_GetClassID(val)
-      rtOpaque.fins.withValue(classid, fin):
-        fin[](rt, val)
-      JS_SetOpaque(val, nil)
-      rtOpaque.plist.del(obj)
-      if rtOpaque.destroying == obj:
-        # Allow QJS to collect the JSValue through checkDestroy.
-        rtOpaque.destroying = nil
-      else:
-        JS_FreeValueRT(rt, val)
-
-type
-  TabGetSet* = object
-    name*: string
-    get*: JSGetterMagicFunction
-    set*: JSSetterMagicFunction
-    magic*: int16
-
-  TabFunc* = object
-    name*: string
-    fun*: JSCFunction
-
-template jsDestructor*[U](T: typedesc[ref U]) =
-  static:
-    jsDtors.incl($T)
-  {.warning[Deprecated]:off.}:
-    proc `=destroy`(obj: var U) =
-      nim_finalize_for_js(addr obj)
-
-template jsDestructor*(T: typedesc[object]) =
-  static:
-    jsDtors.incl($T)
-  {.warning[Deprecated]:off.}:
-    proc `=destroy`(obj: var T) =
-      nim_finalize_for_js(addr obj)
-
-type RegistryInfo = object
-  t: NimNode # NimNode of type
-  name: string # JS name, if this is the empty string then it equals tname
-  tabList: NimNode # array of function table
-  ctorImpl: NimNode # definition & body of constructor
-  ctorFun: NimNode # constructor ident
-  getset: Table[string, (NimNode, NimNode, bool)] # name -> get, set, uf
-  propGetFun: NimNode # custom get function ident
-  propSetFun: NimNode # custom set function ident
-  propDelFun: NimNode # custom del function ident
-  propHasFun: NimNode # custom has function ident
-  propNamesFun: NimNode # custom property names function ident
-  finFun: NimNode # finalizer ident
-  finName: NimNode # finalizer wrapper ident
-  dfin: NimNode # CheckDestroy finalizer ident
-  classDef: NimNode # ClassDef ident
-  tabUnforgeable: NimNode # array of unforgeable function table
-  tabStatic: NimNode # array of static function table
-
-func tname(info: RegistryInfo): string =
-  return info.t.strVal
-
-# Differs from tname if the Nim object's name differs from the JS object's
-# name.
-func jsname(info: RegistryInfo): string =
-  if info.name != "":
-    return info.name
-  return info.tname
-
-proc newRegistryInfo(t: NimNode; name: string): RegistryInfo =
-  return RegistryInfo(
-    t: t,
-    name: name,
-    classDef: ident("classDef"),
-    tabList: newNimNode(nnkBracket),
-    tabUnforgeable: newNimNode(nnkBracket),
-    tabStatic: newNimNode(nnkBracket),
-    finName: newNilLit(),
-    finFun: newNilLit(),
-    propGetFun: newNilLit(),
-    propSetFun: newNilLit(),
-    propDelFun: newNilLit(),
-    propHasFun: newNilLit(),
-    propNamesFun: newNilLit()
-  )
-
-proc bindConstructor(stmts: NimNode; info: var RegistryInfo): NimNode =
-  if info.ctorFun != nil:
-    stmts.add(info.ctorImpl)
-    return info.ctorFun
-  return ident("js_illegal_ctor")
-
-proc registerGetters(stmts: NimNode; info: RegistryInfo;
-    jsget: seq[JSObjectPragma]) =
-  let t = info.t
-  let tname = info.tname
-  let jsname = info.jsname
-  for op in jsget:
-    let node = op.varsym
-    let fn = op.name
-    let id = ident($bfGetter & "_" & tname & "_" & fn)
-    stmts.add(quote do:
-      proc `id`(ctx: JSContext; this: JSValue): JSValue {.cdecl.} =
-        if not ctx.isInstanceOf(this, `tname`):
-          return JS_ThrowTypeError(ctx,
-            "'%s' called on an object that is not an instance of %s", `fn`,
-            `jsname`)
-        let arg_0 = fromJSP_or_return(`t`, ctx, this)
-        when typeof(arg_0.`node`) is object:
-          return toJSP(ctx, arg_0, arg_0.`node`)
-        else:
-          return toJS(ctx, arg_0.`node`)
-    )
-    registerFunction(tname, BoundFunction(
-      t: bfGetter,
-      name: fn,
-      id: id,
-      unforgeable: op.unforgeable
-    ))
-
-proc registerSetters(stmts: NimNode; info: RegistryInfo;
-    jsset: seq[JSObjectPragma]) =
-  let t = info.t
-  let tname = info.tname
-  let jsname = info.jsname
-  for op in jsset:
-    let node = op.varsym
-    let fn = op.name
-    let id = ident($bfSetter & "_" & tname & "_" & fn)
-    stmts.add(quote do:
-      proc `id`(ctx: JSContext; this: JSValue; val: JSValue): JSValue
-          {.cdecl.} =
-        if not ctx.isInstanceOf(this, `tname`):
-          return JS_ThrowTypeError(ctx,
-            "'%s' called on an object that is not an instance of %s", `fn`,
-            `jsname`)
-        let arg_0 = fromJSP_or_return(`t`, ctx, this)
-        let arg_1 = val
-        # Note: if you get a compiler error that leads back to here, that
-        # might be because you added jsset to a non-ref object type.
-        arg_0.`node` = fromJS_or_return(typeof(arg_0.`node`), ctx, arg_1)
-        return JS_DupValue(ctx, arg_1)
-    )
-    registerFunction(tname, bfSetter, fn, id)
-
-proc bindFunctions(stmts: NimNode; info: var RegistryInfo) =
-  BoundFunctions.withValue(info.tname, funs):
-    for fun in funs[].mitems:
-      var f0 = fun.name
-      let f1 = fun.id
-      if fun.name.endsWith("_exceptions"):
-        fun.name = fun.name.substr(0, fun.name.high - "_exceptions".len)
-      case fun.t
-      of bfFunction:
-        f0 = fun.name
-        if fun.unforgeable:
-          info.tabUnforgeable.add(quote do:
-            JS_CFUNC_DEF_NOCONF(`f0`, 0, cast[JSCFunction](`f1`)))
-        elif fun.isstatic:
-          info.tabStatic.add(quote do:
-            JS_CFUNC_DEF(`f0`, 0, cast[JSCFunction](`f1`)))
-        else:
-          info.tabList.add(quote do:
-            JS_CFUNC_DEF(`f0`, 0, cast[JSCFunction](`f1`)))
-      of bfConstructor:
-        info.ctorImpl = fun.ctorBody
-        if info.ctorFun != nil:
-          error("Class " & info.tname & " has 2+ constructors.")
-        info.ctorFun = f1
-      of bfGetter:
-        info.getset.withValue(f0, exv):
-          exv[0] = f1
-          exv[2] = fun.unforgeable
-        do:
-          info.getset[f0] = (f1, newNilLit(), fun.unforgeable)
-      of bfSetter:
-        info.getset.withValue(f0, exv):
-          exv[1] = f1
-        do:
-          info.getset[f0] = (newNilLit(), f1, false)
-      of bfPropertyGet:
-        if info.propGetFun.kind != nnkNilLit:
-          error("Class " & info.tname & " has 2+ property getters.")
-        info.propGetFun = f1
-      of bfPropertySet:
-        if info.propSetFun.kind != nnkNilLit:
-          error("Class " & info.tname & " has 2+ property setters.")
-        info.propSetFun = f1
-      of bfPropertyDel:
-        if info.propDelFun.kind != nnkNilLit:
-          error("Class " & info.tname & " has 2+ property setters.")
-        info.propDelFun = f1
-      of bfPropertyHas:
-        if info.propHasFun.kind != nnkNilLit:
-          error("Class " & info.tname & " has 2+ hasprop getters.")
-        info.propHasFun = f1
-      of bfPropertyNames:
-        if info.propNamesFun.kind != nnkNilLit:
-          error("Class " & info.tname & " has 2+ propnames getters.")
-        info.propNamesFun = f1
-      of bfFinalizer:
-        f0 = fun.name
-        info.finFun = ident(f0)
-        info.finName = f1
-
-proc bindGetSet(stmts: NimNode; info: RegistryInfo) =
-  for k, (get, set, unforgeable) in info.getset:
-    if not unforgeable:
-      info.tabList.add(quote do: JS_CGETSET_DEF(`k`, `get`, `set`))
-    else:
-      info.tabUnforgeable.add(quote do:
-        JS_CGETSET_DEF_NOCONF(`k`, `get`, `set`))
-
-proc bindExtraGetSet(stmts: NimNode; info: var RegistryInfo;
-    extraGetSet: openArray[TabGetSet]) =
-  for x in extraGetSet:
-    let k = x.name
-    let g = x.get
-    let s = x.set
-    let m = x.magic
-    info.tabList.add(quote do: JS_CGETSET_MAGIC_DEF(`k`, `g`, `s`, `m`))
-
-proc bindCheckDestroy(stmts: NimNode; info: RegistryInfo) =
-  let t = info.t
-  let dfin = info.dfin
-  stmts.add(quote do:
-    proc `dfin`(rt: JSRuntime; val: JSValue): JS_BOOL {.cdecl.} =
-      let opaque = JS_GetOpaque(val, JS_GetClassID(val))
-      if opaque != nil:
-        when `t` is ref object:
-          # Before this function is called, the ownership model is
-          # JSObject -> Nim object.
-          # Here we change it to Nim object -> JSObject.
-          # As a result, Nim object's reference count can now reach zero (it is
-          # no longer "referenced" by the JS object).
-          # nim_finalize_for_js will be invoked by the Nim GC when the Nim
-          # refcount reaches zero. Then, the JS object's opaque will be set
-          # to nil, and its refcount decreased again, so next time this
-          # function will return true.
-          #
-          # Actually, we need another hack to ensure correct
-          # operation. GC_unref may call the destructor of this object, and
-          # in this case we cannot ask QJS to keep the JSValue alive. So we set
-          # the "destroying" pointer to the current opaque, and return true if
-          # the opaque was collected.
-          rt.getOpaque().destroying = opaque
-          GC_unref(cast[`t`](opaque))
-          if rt.getOpaque().destroying == nil:
-            # Looks like GC_unref called nim_finalize_for_js for this pointer.
-            # This means we can allow QJS to collect this JSValue.
-            return true
-          else:
-            rt.getOpaque().destroying = nil
-            # Returning false from this function signals to the QJS GC that it
-            # should not be collected yet. Accordingly, the JSObject's refcount
-            # will be set to one again.
-            return false
-        else:
-          # This is not a reference, just a pointer with a reference to the
-          # root ancestor object.
-          # Remove the reference, allowing destruction of the root object once
-          # again.
-          let rtOpaque = rt.getOpaque()
-          var crefunref: tuple[cref, cunref: (proc())]
-          discard rtOpaque.refmap.pop(opaque, crefunref)
-          crefunref.cunref()
-          # Of course, nim_finalize_for_js might only be called later for
-          # this object, because the parent can still have references to it.
-          # (And for the same reason, a reference to the same object might
-          # still be necessary.)
-          # Accordingly, we return false here as well.
-          return false
-      return true
-  )
-
-proc bindEndStmts(endstmts: NimNode; info: RegistryInfo) =
-  let jsname = info.jsname
-  let dfin = info.dfin
-  let classDef = info.classDef
-  if info.propGetFun.kind != nnkNilLit or
-      info.propSetFun.kind != nnkNilLit or
-      info.propDelFun.kind != nnkNilLit or
-      info.propHasFun.kind != nnkNilLit or
-      info.propNamesFun.kind != nnkNilLit:
-    let propGetFun = info.propGetFun
-    let propSetFun = info.propSetFun
-    let propDelFun = info.propDelFun
-    let propHasFun = info.propHasFun
-    let propNamesFun = info.propNamesFun
-    endstmts.add(quote do:
-      var exotic {.global.} = JSClassExoticMethods(
-        get_own_property: `propGetFun`,
-        get_own_property_names: `propNamesFun`,
-        has_property: `propHasFun`,
-        set_property: `propSetFun`,
-        delete_property: `propDelFun`
-      )
-      var cd {.global.} = JSClassDef(
-        class_name: `jsname`,
-        can_destroy: `dfin`,
-        exotic: addr exotic
-      )
-      let `classDef` = JSClassDefConst(addr cd)
-    )
-  else:
-    endstmts.add(quote do:
-      var cd {.global.} = JSClassDef(
-        class_name: `jsname`,
-        can_destroy: `dfin`
-      )
-      let `classDef` = JSClassDefConst(addr cd))
-
-macro registerType*(ctx: JSContext; t: typed; parent: JSClassID = 0;
-    asglobal: static bool = false; globalparent: static bool = false;
-    nointerface = false; name: static string = "";
-    hasExtraGetSet: static bool = false;
-    extraGetSet: static openArray[TabGetSet] = []; namespace = JS_NULL;
-    errid = opt(JSErrorEnum); ishtmldda = false): JSClassID =
-  var stmts = newStmtList()
-  var info = newRegistryInfo(t, name)
-  if not asglobal:
-    info.dfin = ident("js_" & t.strVal & "ClassCheckDestroy")
-    if info.tname notin jsDtors:
-      warning("No destructor has been defined for type " & info.tname)
-  else:
-    info.dfin = newNilLit()
-    if info.tname in jsDtors:
-      error("Global object " & info.tname & " must not have a destructor!")
-  let pragmas = findPragmas(t)
-  stmts.registerGetters(info, pragmas.jsget)
-  stmts.registerSetters(info, pragmas.jsset)
-  stmts.bindFunctions(info)
-  stmts.bindGetSet(info)
-  if hasExtraGetSet:
-    #HACK: for some reason, extraGetSet gets weird contents when nothing is
-    # passed to it. So we need an extra flag to signal if anything has
-    # been passed to it at all.
-    stmts.bindExtraGetSet(info, extraGetSet)
-  let sctr = stmts.bindConstructor(info)
-  if not asglobal:
-    stmts.bindCheckDestroy(info)
-  let endstmts = newStmtList()
-  endstmts.bindEndStmts(info)
-  let tabList = info.tabList
-  let finName = info.finName
-  let classDef = info.classDef
-  let tname = info.tname
-  let unforgeable = info.tabUnforgeable
-  let staticfuns = info.tabStatic
-  let global = asglobal and not globalparent
-  endstmts.add(quote do:
-    `ctx`.newJSClass(`classDef`, `tname`, getTypePtr(`t`), `sctr`, `tabList`,
-      `parent`, bool(`global`), `nointerface`, `finName`, `namespace`,
-      `errid`, `unforgeable`, `staticfuns`, `ishtmldda`)
-  )
-  stmts.add(newBlockStmt(endstmts))
-  return stmts
-
-proc getMemoryUsage*(rt: JSRuntime): string =
-  var m: JSMemoryUsage
-  JS_ComputeMemoryUsage(rt, addr m)
-  template row(title: string; count, size, sz2, cnt2: int64, name: string):
-      string =
-    let fv0 = $(float(sz2) / float(cnt2))
-    var fv = fv0.until('.')
-    if fv.len < fv0.len:
-      fv &= '.' & fv0[fv.len + 1]
-    else:
-      fv &= ".0"
-    title & ": " & $count & " " & $size & " (" & fv & ")" & name & "\n"
-  template row(title: string; count, size, sz2: int64, name: string):
-      string =
-    row(title, count, size, sz2, count, name)
-  template row(title: string; count, size: int64, name: string): string =
-    row(title, count, size, size, name)
-  var s = ""
-  if m.malloc_count != 0:
-    s &= row("memory allocated", m.malloc_count, m.malloc_size, "/block")
-    s &= row("memory used", m.memory_used_count, m.memory_used_size,
-      m.malloc_size - m.memory_used_size, " average slack")
-  if m.atom_count != 0:
-    s &= row("atoms", m.atom_count, m.atom_size, "/atom")
-  if m.str_count != 0:
-    s &= row("strings", m.str_count, m.str_size, "/string")
-  if m.obj_count != 0:
-    s &= row("objects", m.obj_count, m.obj_size, "/object") &
-      row("properties", m.prop_count, m.prop_size, m.prop_size, m.obj_count,
-        "/object") &
-      row("shapes", m.shape_count, m.shape_size, "/shape")
-  if m.js_func_count != 0:
-    s &= row("js functions", m.js_func_count, m.js_func_size, "/function")
-  if m.c_func_count != 0:
-    s &= "native functions: " & $m.c_func_count & "\n"
-  if m.array_count != 0:
-    s &= "arrays: " & $m.array_count & "\n" &
-      "fast arrays: " & $m.fast_array_count & "\n" &
-      row("fast array elements", m.fast_array_elements,
-        m.fast_array_elements * sizeof(JSValue), m.fast_array_elements,
-        m.fast_array_count, "")
-  if m.binary_object_count != 0:
-    s &= "binary objects: " & $m.binary_object_count & " " &
-      $m.binary_object_size
-  return s
-
-proc eval*(ctx: JSContext; s, file: string; evalFlags = JS_EVAL_TYPE_GLOBAL):
-    JSValue =
-  return JS_Eval(ctx, cstring(s), csize_t(s.len), cstring(file),
-    cint(evalFlags))
-
-proc compileScript*(ctx: JSContext; s, file: string): JSValue =
-  return ctx.eval(s, file, JS_EVAL_FLAG_COMPILE_ONLY)
-
-proc compileModule*(ctx: JSContext; s, file: string): JSValue =
-  return ctx.eval(s, file, JS_EVAL_TYPE_MODULE or JS_EVAL_FLAG_COMPILE_ONLY)
-
-proc evalFunction*(ctx: JSContext; val: JSValue): JSValue =
-  return JS_EvalFunction(ctx, val)
diff --git a/src/js/jscolor.nim b/src/js/jscolor.nim
index a73fb599..aa6fd8ed 100644
--- a/src/js/jscolor.nim
+++ b/src/js/jscolor.nim
@@ -1,11 +1,12 @@
 import std/strutils
 
-import bindings/quickjs
-import js/jserror
-import js/fromjs
-import js/javascript
-import js/tojs
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/quickjs
+import monoucha/tojs
 import types/color
+import types/opt
 import utils/charcategory
 import utils/twtstr
 
diff --git a/src/js/jserror.nim b/src/js/jserror.nim
deleted file mode 100644
index b4101830..00000000
--- a/src/js/jserror.nim
+++ /dev/null
@@ -1,59 +0,0 @@
-import types/opt
-
-type
-  JSError* = ref object of RootObj
-    e*: JSErrorEnum
-    message*: string
-
-  JSErrorEnum* = enum
-    # QuickJS internal errors
-    jeEvalError = "EvalError"
-    jeRangeError = "RangeError"
-    jeReferenceError = "ReferenceError"
-    jeSyntaxError = "SyntaxError"
-    jeTypeError = "TypeError"
-    jeURIError = "URIError"
-    jeInternalError = "InternalError"
-    jeAggregateError = "AggregateError"
-    # Chawan errors
-    jeDOMException = "DOMException"
-
-  JSResult*[T] = Result[T, JSError]
-
-const QuickJSErrors* = [
-  jeEvalError,
-  jeRangeError,
-  jeReferenceError,
-  jeSyntaxError,
-  jeTypeError,
-  jeURIError,
-  jeInternalError,
-  jeAggregateError
-]
-
-proc newEvalError*(message: string): JSError =
-  return JSError(e: jeEvalError, message: message)
-
-proc newRangeError*(message: string): JSError =
-  return JSError(e: jeRangeError, message: message)
-
-proc newReferenceError*(message: string): JSError =
-  return JSError(e: jeReferenceError, message: message)
-
-proc newSyntaxError*(message: string): JSError =
-  return JSError(e: jeSyntaxError, message: message)
-
-proc newTypeError*(message: string): JSError =
-  return JSError(e: jeTypeError, message: message)
-
-proc newURIError*(message: string): JSError =
-  return JSError(e: jeURIError, message: message)
-
-proc newInternalError*(message: string): JSError =
-  return JSError(e: jeInternalError, message: message)
-
-proc newAggregateError*(message: string): JSError =
-  return JSError(e: jeAggregateError, message: message)
-
-template errTypeError*(message: string): untyped =
-  err(newTypeError(message))
diff --git a/src/js/jsmodule.nim b/src/js/jsmodule.nim
index 90ad7130..cdb836e6 100644
--- a/src/js/jsmodule.nim
+++ b/src/js/jsmodule.nim
@@ -1,7 +1,7 @@
-import bindings/constcharp
-import bindings/quickjs
-import js/javascript
-import js/tojs
+import monoucha/constcharp
+import monoucha/javascript
+import monoucha/quickjs
+import monoucha/tojs
 
 proc setImportMeta(ctx: JSContext; funcVal: JSValue; isMain: bool) =
   let m = cast[JSModuleDef](JS_VALUE_GET_PTR(funcVal))
diff --git a/src/js/jsopaque.nim b/src/js/jsopaque.nim
deleted file mode 100644
index 2ce6bbc1..00000000
--- a/src/js/jsopaque.nim
+++ /dev/null
@@ -1,128 +0,0 @@
-import std/tables
-
-import bindings/quickjs
-import js/jserror
-import types/opt
-
-type
-  JSSymbolRef* = enum
-    jsyIterator = "iterator"
-    jsyAsyncIterator = "asyncIterator"
-    jsyToStringTag = "toStringTag"
-
-  JSStrRef* = enum
-    jstDone = "done"
-    jstValue = "value"
-    jstNext = "next"
-    jstPrototype = "prototype"
-    jstThen = "then"
-
-  JSValueRef* = enum
-    jsvArrayPrototypeValues = "Array.prototype.values"
-    jsvUint8Array = "Uint8Array"
-    jsvObjectPrototypeValueOf = "Object.prototype.valueOf"
-    jsvSet = "Set"
-    jsvFunction = "Function"
-
-  JSContextOpaque* = ref object
-    creg*: Table[string, JSClassID]
-    typemap*: Table[pointer, JSClassID]
-    ctors*: Table[JSClassID, JSValue]
-    parents*: Table[JSClassID, JSClassID]
-    # Parent unforgeables are merged on class creation.
-    # (i.e. to set all unforgeables on the prototype chain, it is enough to set)
-    # `unforgeable[classid]'.)
-    unforgeable*: Table[JSClassID, seq[JSCFunctionListEntry]]
-    gclaz*: string
-    gparent*: JSClassID
-    symRefs*: array[JSSymbolRef, JSAtom]
-    strRefs*: array[JSStrRef, JSAtom]
-    valRefs*: array[JSValueRef, JSValue]
-    errCtorRefs*: array[JSErrorEnum, JSValue]
-    htmldda*: JSClassID # only one of these exists: document.all.
-    globalUnref*: proc() {.closure.}
-
-  JSFinalizerFunction* = proc(rt: JSRuntime; val: JSValue) {.nimcall,
-    raises: [].}
-
-  JSRuntimeOpaque* = ref object
-    plist*: Table[pointer, pointer] # Nim, JS
-    flist*: seq[seq[JSCFunctionListEntry]]
-    fins*: Table[JSClassID, JSFinalizerFunction]
-    refmap*: Table[pointer, tuple[cref, cunref: (proc() {.closure.})]]
-    destroying*: pointer
-
-func newJSContextOpaque*(ctx: JSContext): JSContextOpaque =
-  let opaque = JSContextOpaque()
-  block: # get well-known symbols and other functions
-    let global = JS_GetGlobalObject(ctx)
-    let sym = JS_GetPropertyStr(ctx, global, "Symbol")
-    for s in JSSymbolRef:
-      let name = $s
-      let val = JS_GetPropertyStr(ctx, sym, cstring(name))
-      assert JS_IsSymbol(val)
-      opaque.symRefs[s] = JS_ValueToAtom(ctx, val)
-      JS_FreeValue(ctx, val)
-    JS_FreeValue(ctx, sym)
-    for s in JSStrRef:
-      let ss = $s
-      opaque.strRefs[s] = JS_NewAtomLen(ctx, cstring(ss), csize_t(ss.len))
-    for s in JSValueRef:
-      let ss = $s
-      let ret = JS_Eval(ctx, cstring(ss), csize_t(ss.len), "<init>", 0)
-      assert JS_IsFunction(ctx, ret)
-      opaque.valRefs[s] = ret
-    for e in JSErrorEnum:
-      let s = $e
-      let err = JS_GetPropertyStr(ctx, global, cstring(s))
-      opaque.errCtorRefs[e] = err
-    JS_FreeValue(ctx, global)
-  return opaque
-
-func getOpaque*(ctx: JSContext): JSContextOpaque =
-  return cast[JSContextOpaque](JS_GetContextOpaque(ctx))
-
-func getOpaque*(rt: JSRuntime): JSRuntimeOpaque =
-  return cast[JSRuntimeOpaque](JS_GetRuntimeOpaque(rt))
-
-func isGlobal*(ctx: JSContext; class: string): bool =
-  assert class != ""
-  return ctx.getOpaque().gclaz == class
-
-proc setOpaque*(ctx: JSContext; val: JSValue; opaque: pointer) =
-  let rt = JS_GetRuntime(ctx)
-  let rtOpaque = rt.getOpaque()
-  let p = JS_VALUE_GET_PTR(val)
-  rtOpaque.plist[opaque] = p
-  JS_SetOpaque(val, opaque)
-
-# getOpaque, but doesn't work for global objects.
-func getOpaque0*(val: JSValue): pointer =
-  if JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT:
-    return JS_GetOpaque(val, JS_GetClassID(val))
-
-func getGlobalOpaque0*(ctx: JSContext; val = JS_UNDEFINED): Opt[pointer] =
-  let global = JS_GetGlobalObject(ctx)
-  if JS_IsUndefined(val) or val == global:
-    let opaque = JS_GetOpaque(global, JS_CLASS_OBJECT)
-    JS_FreeValue(ctx, global)
-    return ok(opaque)
-  JS_FreeValue(ctx, global)
-  return err()
-
-func getGlobalOpaque*(ctx: JSContext; T: typedesc; val = JS_UNDEFINED): Opt[T] =
-  return ok(cast[T](?getGlobalOpaque0(ctx, val)))
-
-func getOpaque*(ctx: JSContext; val: JSValue; class: string): pointer =
-  # Unfortunately, we can't change the global object's class.
-  #TODO: or maybe we can, but I'm afraid of breaking something.
-  # This needs further investigation.
-  if ctx.isGlobal(class):
-    let global = JS_GetGlobalObject(ctx)
-    let opaque = JS_GetOpaque(global, JS_CLASS_OBJECT)
-    JS_FreeValue(ctx, global)
-    return opaque
-  return getOpaque0(val)
-
-func getOpaque*[T: ref object](ctx: JSContext; val: JSValue): T =
-  cast[T](getOpaque(ctx, val, $T))
diff --git a/src/js/jspropenumlist.nim b/src/js/jspropenumlist.nim
deleted file mode 100644
index 65c16c2f..00000000
--- a/src/js/jspropenumlist.nim
+++ /dev/null
@@ -1,42 +0,0 @@
-import bindings/quickjs
-
-type
-  JSPropertyEnumArray* = ptr UncheckedArray[JSPropertyEnum]
-
-  JSPropertyEnumList* = object
-    buffer*: JSPropertyEnumArray
-    size: uint32
-    len*: uint32
-    ctx: JSContext
-
-  JSPropertyEnumWrapper* = object
-    is_enumerable: bool
-    name: string
-
-func newJSPropertyEnumList*(ctx: JSContext; size: uint32): JSPropertyEnumList =
-  let p = js_malloc(ctx, csize_t(sizeof(JSPropertyEnum)) * csize_t(size))
-  let buffer = cast[JSPropertyEnumArray](p)
-  return JSPropertyEnumList(
-    ctx: ctx,
-    buffer: buffer,
-    size: size
-  )
-
-proc grow(this: var JSPropertyEnumList) =
-  this.size *= 2
-  let p = js_realloc(this.ctx, this.buffer, csize_t(this.size))
-  this.buffer = cast[JSPropertyEnumArray](p)
-
-proc add*(this: var JSPropertyEnumList; val: uint32) =
-  let i = this.len
-  inc this.len
-  if this.size < this.len:
-    this.grow()
-  this.buffer[i].atom = JS_NewAtomUInt32(this.ctx, val)
-
-proc add*(this: var JSPropertyEnumList; val: string) =
-  let i = this.len
-  inc this.len
-  if this.size < this.len:
-    this.grow()
-  this.buffer[i].atom = JS_NewAtomLen(this.ctx, cstring(val), csize_t(val.len))
diff --git a/src/js/jsregex.nim b/src/js/jsregex.nim
deleted file mode 100644
index 9fb09872..00000000
--- a/src/js/jsregex.nim
+++ /dev/null
@@ -1,150 +0,0 @@
-# Interface for QuickJS libregexp.
-import std/unicode
-
-import bindings/libregexp
-import types/opt
-import utils/twtstr
-
-export LREFlags
-
-type
-  Regex* = object
-    bytecode: seq[uint8]
-    when defined(debug):
-      buf: string
-
-  RegexCapture* = tuple # start, end
-    s, e: int
-
-  RegexResult* = object
-    success*: bool
-    captures*: seq[seq[RegexCapture]]
-
-when defined(debug):
-  func `$`*(regex: Regex): string =
-    regex.buf
-
-# this is hardcoded into quickjs, so we must override it here.
-proc lre_realloc(opaque, p: pointer; size: csize_t): pointer {.exportc.} =
-  return realloc(p, size)
-
-proc compileRegex*(buf: string; flags: LREFlags = {}): Result[Regex, string] =
-  var errorMsg = newString(64)
-  var plen: cint
-  let bytecode = lre_compile(addr plen, cstring(errorMsg), cint(errorMsg.len),
-    cstring(buf), csize_t(buf.len), flags.toCInt, nil)
-  if bytecode == nil:
-    return err(errorMsg.until('\0')) # Failed to compile.
-  assert plen > 0
-  var bcseq = newSeqUninitialized[uint8](plen)
-  copyMem(addr bcseq[0], bytecode, plen)
-  dealloc(bytecode)
-  var regex = Regex(bytecode: bcseq)
-  when defined(debug):
-    regex.buf = buf
-  return ok(regex)
-
-func countBackslashes(buf: string; i: int): int =
-  var j = 0
-  for i in countdown(i, 0):
-    if buf[i] != '\\':
-      break
-    inc j
-  return j
-
-# ^abcd -> ^abcd
-# efgh$ -> efgh$
-# ^ijkl$ -> ^ijkl$
-# mnop -> ^mnop$
-proc compileMatchRegex*(buf: string): Result[Regex, string] =
-  if buf.len == 0:
-    return compileRegex(buf)
-  if buf[0] == '^':
-    return compileRegex(buf)
-  if buf[^1] == '$':
-    # Check whether the final dollar sign is escaped.
-    if buf.len == 1 or buf[^2] != '\\':
-      return compileRegex(buf)
-    let j = buf.countBackslashes(buf.high - 2)
-    if j mod 2 == 1: # odd, because we do not count the last backslash
-      return compileRegex(buf)
-    # escaped. proceed as if no dollar sign was at the end
-  if buf[^1] == '\\':
-    # Check if the regex contains an invalid trailing backslash.
-    let j = buf.countBackslashes(buf.high - 1)
-    if j mod 2 != 1: # odd, because we do not count the last backslash
-      return err("unexpected end")
-  var buf2 = "^"
-  buf2 &= buf
-  buf2 &= "$"
-  return compileRegex(buf2)
-
-proc compileSearchRegex*(str: string; defaultFlags: LREFlags):
-    Result[Regex, string] =
-  # Emulate vim's \c/\C: override defaultFlags if one is found, then remove it
-  # from str.
-  # Also, replace \< and \> with \b as (a bit sloppy) vi emulation.
-  var flags = defaultFlags
-  var s = newStringOfCap(str.len)
-  var quot = false
-  for c in str:
-    if quot:
-      quot = false
-      case c
-      of 'c': flags.incl(LRE_FLAG_IGNORECASE)
-      of 'C': flags.excl(LRE_FLAG_IGNORECASE)
-      of '<', '>': s &= "\\b"
-      else: s &= '\\' & c
-    elif c == '\\':
-      quot = true
-    else:
-      s &= c
-  if quot:
-    s &= '\\'
-  flags.incl(LRE_FLAG_GLOBAL) # for easy backwards matching
-  return compileRegex(s, flags)
-
-proc exec*(regex: Regex; str: string; start = 0; length = -1; nocaps = false):
-    RegexResult =
-  let length = if length == -1:
-    str.len
-  else:
-    length
-  assert start in 0 .. length
-  let bytecode = unsafeAddr regex.bytecode[0]
-  let captureCount = lre_get_capture_count(bytecode)
-  var capture: ptr UncheckedArray[int] = nil
-  if captureCount > 0:
-    let size = sizeof(ptr uint8) * captureCount * 2
-    capture = cast[ptr UncheckedArray[int]](alloc0(size))
-  var cstr = cstring(str)
-  let flags = lre_get_flags(bytecode).toLREFlags
-  var start = start
-  while true:
-    let ret = lre_exec(cast[ptr ptr uint8](capture), bytecode,
-      cast[ptr uint8](cstr), cint(start), cint(length), cint(3), nil)
-    if ret != 1: #TODO error handling? (-1)
-      break
-    result.success = true
-    if captureCount == 0 or nocaps:
-      break
-    var caps: seq[RegexCapture] = @[]
-    let cstrAddress = cast[int](cstr)
-    let ps = start
-    start = capture[1] - cstrAddress
-    for i in 0 ..< captureCount:
-      let s = capture[i * 2] - cstrAddress
-      let e = capture[i * 2 + 1] - cstrAddress
-      caps.add((s, e))
-    result.captures.add(caps)
-    if LRE_FLAG_GLOBAL notin flags:
-      break
-    if start >= str.len:
-      break
-    if ps == start:
-      start += runeLenAt(str, start)
-  if captureCount > 0:
-    dealloc(capture)
-
-proc match*(regex: Regex; str: string; start = 0; length = str.len): bool =
-  return regex.exec(str, start, length, nocaps = true).success
diff --git a/src/js/jstypes.nim b/src/js/jstypes.nim
deleted file mode 100644
index 9e1d72ea..00000000
--- a/src/js/jstypes.nim
+++ /dev/null
@@ -1,43 +0,0 @@
-import bindings/quickjs
-
-# This is the WebIDL dictionary type.
-# We only use it for type inference in generics.
-#TODO required members
-type JSDict* = object of RootObj
-
-# Containers compatible with the internal representation of strings in QuickJS.
-# To convert these, a copy is still needed; however, they remove the UTF-8
-# transcoding step.
-type
-  NarrowString* = distinct string
-  WideString* = distinct seq[uint16]
-
-# Various containers for array buffer types.
-# Converting these only requires copying the metadata; buffers are never copied.
-type
-  JSArrayBuffer* = object
-    p*: ptr UncheckedArray[uint8]
-    len*: csize_t
-    dealloc*: JSFreeArrayBufferDataFunc
-
-  JSArrayBufferView* = object
-    abuf*: JSArrayBuffer
-    offset*: csize_t # offset into the buffer
-    nmemb*: csize_t # number of members
-    nsize*: csize_t # member size
-
-  JSUint8Array* = object
-    abuf*: JSArrayBuffer
-    offset*: csize_t # offset into the buffer
-    nmemb*: csize_t # number of members
-
-func high*(abuf: JSArrayBuffer): int =
-  return int(abuf.len) - 1
-
-# A specialization of JSValue to make writing generic code for functions
-# easier.
-type JSValueFunction* = ref object
-  fun*: JSValue
-
-converter toJSValue*(f: JSValueFunction): JSValue =
-  f.fun
diff --git a/src/js/jsutils.nim b/src/js/jsutils.nim
deleted file mode 100644
index b8a8398e..00000000
--- a/src/js/jsutils.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-import bindings/quickjs
-
-template toJSValueArray*(a: openArray[JSValue]): ptr UncheckedArray[JSValue] =
-  cast[ptr UncheckedArray[JSValue]](unsafeAddr a[0])
-
-# Warning: this must be a template, because we're taking the address of
-# the passed value, and Nim is pass-by-value.
-template toJSValueArray*(a: JSValue): ptr UncheckedArray[JSValue] =
-  cast[ptr UncheckedArray[JSValue]](unsafeAddr a)
diff --git a/src/js/timeout.nim b/src/js/timeout.nim
index 3c95dada..9c1b0dba 100644
--- a/src/js/timeout.nim
+++ b/src/js/timeout.nim
@@ -2,7 +2,7 @@ import std/selectors
 import std/tables
 
 import io/dynstream
-import js/javascript
+import monoucha/javascript
 
 type TimeoutState* = object
   timeoutid: int32
diff --git a/src/js/tojs.nim b/src/js/tojs.nim
deleted file mode 100644
index 59109884..00000000
--- a/src/js/tojs.nim
+++ /dev/null
@@ -1,399 +0,0 @@
-# Automatic conversion of Nim types to JavaScript types.
-#
-# Every conversion involves copying unless explicitly noted below.
-#
-# * Primitives are converted to their respective JavaScript counterparts.
-# * seq is converted to a JS array. Note: this always copies the seq's contents.
-# * enum is converted to its stringifier's output.
-# * JSValue is returned as-is, *without* a DupValue operation.
-# * JSError is converted to a new error object corresponding to the error
-#   it represents.
-# * JSArrayBuffer, JSUint8Array are converted to a JS object without copying
-#   their contents.
-# * NarrowString is converted to a JS narrow string (with copying). For more
-#   information on JS string handling, see js/jstypes.nim.
-# * Finally, ref object is converted to a JS object whose opaque is the ref
-#   object. (See below.)
-#
-# Note that ref objects can be seamlessly converted to JS objects, despite
-# the fact that they are managed by two separate garbage collectors. This
-# works thanks to a patch in QJS and machine oil. Basically:
-#
-# * Nim objects registered with registerType can be paired with one (1)
-#   JS object each.
-# * This happens on-demand, whenever the Nim object has to be converted into JS.
-# * Once the conversion happened, the JS object will be kept alive until the
-#   Nim object is destroyed, so that JS properties on the JS object are not
-#   lost during a re-conversion.
-# * Similarly, the Nim object is kept alive so long as the JS object is alive.
-# * The patched in can_destroy hook is used to synchronize reference counts
-#   of the two objects; this way, no memory leak occurs.
-#
-# There are also toJSP variants of object converters. These work identically
-# to ref object converters, except the reference count of the closest
-# `ref object' ancestor is incremented/decremented when synchronizing refcounts
-# with the JS object pair.
-
-import std/options
-import std/tables
-import std/unicode
-
-import bindings/quickjs
-import js/jserror
-import js/jstypes
-import js/jsutils
-import js/jsopaque
-import types/opt
-
-# Convert Nim types to the corresponding JavaScript type.
-# This does not work with var objects.
-proc toJS*(ctx: JSContext; s: string): JSValue
-proc toJS*(ctx: JSContext; r: Rune): JSValue
-proc toJS*(ctx: JSContext; n: int64): JSValue
-proc toJS*(ctx: JSContext; n: int32): JSValue
-proc toJS*(ctx: JSContext; n: int): JSValue
-proc toJS*(ctx: JSContext; n: uint16): JSValue
-proc toJS*(ctx: JSContext; n: uint32): JSValue
-proc toJS*(ctx: JSContext; n: uint64): JSValue
-proc toJS*(ctx: JSContext; n: float64): JSValue
-proc toJS*(ctx: JSContext; b: bool): JSValue
-proc toJS*[U, V](ctx: JSContext; t: Table[U, V]): JSValue
-proc toJS*(ctx: JSContext; opt: Option): JSValue
-proc toJS*[T, E](ctx: JSContext; opt: Result[T, E]): JSValue
-proc toJS*(ctx: JSContext; s: seq): JSValue
-proc toJS*[T](ctx: JSContext; s: set[T]): JSValue
-proc toJS*(ctx: JSContext; t: tuple): JSValue
-proc toJS*(ctx: JSContext; e: enum): JSValue
-proc toJS*(ctx: JSContext; j: JSValue): JSValue
-proc toJS*(ctx: JSContext; obj: ref object): JSValue
-proc toJS*(ctx: JSContext; err: JSError): JSValue
-proc toJS*(ctx: JSContext; abuf: JSArrayBuffer): JSValue
-proc toJS*(ctx: JSContext; u8a: JSUint8Array): JSValue
-proc toJS*(ctx: JSContext; ns: NarrowString): JSValue
-proc toJS*(ctx: JSContext; dict: JSDict): JSValue
-
-# Convert Nim types to the corresponding JavaScript type, with knowledge of
-# the parent object.
-# This supports conversion of var object types.
-#
-# The idea here is to allow conversion of var objects to quasi-reference types
-# by saving a pointer to their ancestor and incrementing/decrementing the
-# ancestor's reference count instead.
-proc toJSP*(ctx: JSContext; parent: ref object; child: var object): JSValue
-proc toJSP*(ctx: JSContext; parent: ptr object; child: var object): JSValue
-
-# Same as toJS, but used in constructors. ctor contains the target prototype,
-# used for subclassing from JS.
-proc toJSNew*(ctx: JSContext; obj: ref object; ctor: JSValue): JSValue
-proc toJSNew*[T, E](ctx: JSContext; opt: Result[T, E]; ctor: JSValue): JSValue
-
-# Avoid accidentally calling toJSP on objects that we have explicit toJS
-# converters for.
-template makeToJSP(typ: untyped) =
-  template toJSP*(ctx: JSContext; parent: ref object; child: var typ): JSValue =
-    toJS(ctx, child)
-  template toJSP*(ctx: JSContext; parent: ptr object; child: var typ): JSValue =
-    toJS(ctx, child)
-makeToJSP(Table)
-makeToJSP(Option)
-makeToJSP(Result)
-makeToJSP(JSValue)
-makeToJSP(JSDict)
-
-# Note: this consumes `prop'.
-proc defineProperty(ctx: JSContext; this: JSValue; name: JSAtom;
-    prop: JSValue; flags = cint(0)) =
-  if JS_DefinePropertyValue(ctx, this, name, prop, flags) <= 0:
-    raise newException(Defect, "Failed to define property string")
-
-proc defineProperty(ctx: JSContext; this, name, prop: JSValue;
-    flags = cint(0)) =
-  let atom = JS_ValueToAtom(ctx, prop);
-  JS_FreeValue(ctx, prop);
-  if unlikely(atom == JS_ATOM_NULL):
-    raise newException(Defect, "Failed to define property string")
-  ctx.defineProperty(this, atom, prop, flags)
-  JS_FreeAtom(ctx, atom);
-
-proc definePropertyC*(ctx: JSContext; this: JSValue; name: JSAtom;
-    prop: JSValue) =
-  ctx.defineProperty(this, name, prop, JS_PROP_CONFIGURABLE)
-
-proc defineProperty(ctx: JSContext; this: JSValue; name: string;
-    prop: JSValue; flags = cint(0)) =
-  if JS_DefinePropertyValueStr(ctx, this, cstring(name), prop, flags) <= 0:
-    raise newException(Defect, "Failed to define property string: " & name)
-
-proc definePropertyC*(ctx: JSContext; this: JSValue; name: string;
-    prop: JSValue) =
-  ctx.defineProperty(this, name, prop, JS_PROP_CONFIGURABLE)
-
-proc defineProperty*[T](ctx: JSContext; this: JSValue; name: string; prop: T;
-    flags = cint(0)) =
-  defineProperty(ctx, this, name, toJS(ctx, prop), flags)
-
-proc definePropertyE*[T](ctx: JSContext; this: JSValue; name: string;
-    prop: T) =
-  defineProperty(ctx, this, name, prop, JS_PROP_ENUMERABLE)
-
-proc definePropertyCW*[T](ctx: JSContext; this: JSValue; name: string;
-    prop: T) =
-  defineProperty(ctx, this, name, prop, JS_PROP_CONFIGURABLE or
-    JS_PROP_WRITABLE)
-
-proc definePropertyCWE*[T](ctx: JSContext; this: JSValue; name: string;
-    prop: T) =
-  defineProperty(ctx, this, name, prop, JS_PROP_C_W_E)
-
-proc newFunction*(ctx: JSContext; args: openArray[string]; body: string):
-    JSValue =
-  var paramList: seq[JSValue] = @[]
-  for arg in args:
-    paramList.add(toJS(ctx, arg))
-  paramList.add(toJS(ctx, body))
-  let fun = JS_CallConstructor(ctx, ctx.getOpaque().valRefs[jsvFunction],
-    cint(paramList.len), paramList.toJSValueArray())
-  for param in paramList:
-    JS_FreeValue(ctx, param)
-  return fun
-
-proc toJS*(ctx: JSContext; s: cstring): JSValue =
-  return JS_NewString(ctx, s)
-
-proc toJS*(ctx: JSContext; s: string): JSValue =
-  return toJS(ctx, cstring(s))
-
-proc toJS*(ctx: JSContext; r: Rune): JSValue =
-  return toJS(ctx, $r)
-
-proc toJS*(ctx: JSContext; n: int32): JSValue =
-  return JS_NewInt32(ctx, n)
-
-proc toJS*(ctx: JSContext; n: int64): JSValue =
-  return JS_NewInt64(ctx, n)
-
-# Always int32, so we don't risk 32-bit only breakage.
-proc toJS*(ctx: JSContext; n: int): JSValue =
-  return toJS(ctx, int32(n))
-
-proc toJS*(ctx: JSContext; n: uint16): JSValue =
-  return JS_NewUint32(ctx, uint32(n))
-
-proc toJS*(ctx: JSContext; n: uint32): JSValue =
-  return JS_NewUint32(ctx, n)
-
-proc toJS*(ctx: JSContext; n: uint64): JSValue =
-  #TODO this is incorrect
-  return JS_NewFloat64(ctx, float64(n))
-
-proc toJS*(ctx: JSContext; n: float64): JSValue =
-  return JS_NewFloat64(ctx, n)
-
-proc toJS*(ctx: JSContext; b: bool): JSValue =
-  return JS_NewBool(ctx, b)
-
-proc toJS*[U, V](ctx: JSContext; t: Table[U, V]): JSValue =
-  let obj = JS_NewObject(ctx)
-  if not JS_IsException(obj):
-    for k, v in t:
-      definePropertyCWE(ctx, obj, k, v)
-  return obj
-
-proc toJS*(ctx: JSContext; opt: Option): JSValue =
-  if opt.isSome:
-    return toJS(ctx, opt.get)
-  return JS_NULL
-
-proc toJS*[T, E](ctx: JSContext; opt: Result[T, E]): JSValue =
-  if opt.isSome:
-    when not (T is void):
-      return toJS(ctx, opt.get)
-    else:
-      return JS_UNDEFINED
-  else:
-    when not (E is void):
-      if opt.error != nil:
-        return JS_Throw(ctx, toJS(ctx, opt.error))
-    return JS_EXCEPTION
-
-proc toJS*(ctx: JSContext; s: seq): JSValue =
-  let a = JS_NewArray(ctx)
-  if not JS_IsException(a):
-    for i, x in s:
-      let val = toJS(ctx, x)
-      if JS_IsException(val):
-        return val
-      ctx.defineProperty(a, JS_NewInt64(ctx, int64(i)), val,
-        JS_PROP_C_W_E or JS_PROP_THROW)
-  return a
-
-proc toJS*[T](ctx: JSContext; s: set[T]): JSValue =
-  #TODO this is a bit lazy :p
-  var x = newSeq[T]()
-  for e in s:
-    x.add(e)
-  var a = toJS(ctx, x)
-  if JS_IsException(a):
-    return a
-  let ret = JS_CallConstructor(ctx, ctx.getOpaque().valRefs[jsvSet], 1,
-    a.toJSValueArray())
-  JS_FreeValue(ctx, a)
-  return ret
-
-proc toJS(ctx: JSContext; t: tuple): JSValue =
-  let a = JS_NewArray(ctx)
-  if not JS_IsException(a):
-    var i = 0
-    for f in t.fields:
-      let val = toJS(ctx, f)
-      if JS_IsException(val):
-        return val
-      ctx.defineProperty(a, JS_NewInt64(ctx, int64(i)), val,
-        JS_PROP_C_W_E or JS_PROP_THROW)
-      inc i
-  return a
-
-proc toJSP0(ctx: JSContext; p, tp: pointer; ctor: JSValue;
-    needsref: var bool): JSValue =
-  JS_GetRuntime(ctx).getOpaque().plist.withValue(p, obj):
-    # a JSValue already points to this object.
-    return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, obj[]))
-  let ctxOpaque = ctx.getOpaque()
-  let class = ctxOpaque.typemap[tp]
-  let jsObj = JS_NewObjectFromCtor(ctx, ctor, class)
-  if JS_IsException(jsObj):
-    return jsObj
-  setOpaque(ctx, jsObj, p)
-  # We are constructing a new JS object, so we must add unforgeable properties
-  # here.
-  ctxOpaque.unforgeable.withValue(class, uf):
-    JS_SetPropertyFunctionList(ctx, jsObj, addr uf[][0], cint(uf[].len))
-  needsref = true
-  if unlikely(ctxOpaque.htmldda == class):
-    JS_SetIsHTMLDDA(ctx, jsObj)
-  return jsObj
-
-# Get a unique pointer for each type.
-proc getTypePtr*[T](x: T): pointer =
-  when T is RootRef:
-    # I'm so sorry.
-    # (This dereferences the object's first member, m_type. Probably.)
-    return cast[ptr pointer](x)[]
-  elif T is RootObj:
-    return cast[pointer](x)
-  else:
-    return getTypeInfo(x)
-
-func getTypePtr*(t: typedesc[ref object]): pointer =
-  var x = t()
-  return getTypePtr(x)
-
-func getTypePtr*(t: type): pointer =
-  var x: t
-  return getTypePtr(x)
-
-proc toJSRefObj(ctx: JSContext; obj: ref object): JSValue =
-  if obj == nil:
-    return JS_NULL
-  let p = cast[pointer](obj)
-  let tp = getTypePtr(obj)
-  var needsref = false
-  let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref)
-  if needsref:
-    GC_ref(obj)
-  return val
-
-proc toJS*(ctx: JSContext; obj: ref object): JSValue =
-  return toJSRefObj(ctx, obj)
-
-proc toJSNew*(ctx: JSContext; obj: ref object; ctor: JSValue): JSValue =
-  if obj == nil:
-    return JS_NULL
-  let p = cast[pointer](obj)
-  let tp = getTypePtr(obj)
-  var needsref = false
-  let val = toJSP0(ctx, p, tp, ctor, needsref)
-  if needsref:
-    GC_ref(obj)
-  return val
-
-proc toJSNew[T, E](ctx: JSContext; opt: Result[T, E]; ctor: JSValue): JSValue =
-  if opt.isSome:
-    when not (T is void):
-      return toJSNew(ctx, opt.get, ctor)
-    else:
-      return JS_UNDEFINED
-  else:
-    when not (E is void):
-      let res = toJS(ctx, opt.error)
-      if not JS_IsNull(res):
-        return JS_Throw(ctx, res)
-    else:
-      return JS_NULL
-
-proc toJS(ctx: JSContext; e: enum): JSValue =
-  return toJS(ctx, $e)
-
-proc toJS(ctx: JSContext; j: JSValue): JSValue =
-  return j
-
-proc toJS*(ctx: JSContext; err: JSError): JSValue =
-  if err == nil:
-    return JS_EXCEPTION
-  if err.e notin QuickJSErrors:
-    return toJSRefObj(ctx, err)
-  var msg = toJS(ctx, err.message)
-  if JS_IsException(msg):
-    return msg
-  let ctor = ctx.getOpaque().errCtorRefs[err.e]
-  let ret = JS_CallConstructor(ctx, ctor, 1, msg.toJSValueArray())
-  JS_FreeValue(ctx, msg)
-  return ret
-
-proc toJS*(ctx: JSContext; abuf: JSArrayBuffer): JSValue =
-  return JS_NewArrayBuffer(ctx, abuf.p, abuf.len, abuf.dealloc, nil, false)
-
-proc toJS*(ctx: JSContext; u8a: JSUint8Array): JSValue =
-  let jsabuf = toJS(ctx, u8a.abuf)
-  let ctor = ctx.getOpaque().valRefs[jsvUint8Array]
-  let ret = JS_CallConstructor(ctx, ctor, 1, jsabuf.toJSValueArray())
-  JS_FreeValue(ctx, jsabuf)
-  return ret
-
-proc toJS*(ctx: JSContext; ns: NarrowString): JSValue =
-  return JS_NewNarrowStringLen(ctx, cstring(ns), csize_t(string(ns).len))
-
-proc toJS*(ctx: JSContext; dict: JSDict): JSValue =
-  let obj = JS_NewObject(ctx)
-  if JS_IsException(obj):
-    return obj
-  for k, v in dict.fieldPairs:
-    ctx.defineProperty(obj, k, v)
-  return obj
-
-proc toJSP(ctx: JSContext; parent: ref object; child: var object): JSValue =
-  let p = addr child
-  # Save parent as the original ancestor for this tree.
-  JS_GetRuntime(ctx).getOpaque().refmap[p] = (
-    (proc() =
-      GC_ref(parent)),
-    (proc() =
-      GC_unref(parent))
-  )
-  let tp = getTypePtr(child)
-  var needsref = false
-  let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref)
-  if needsref:
-    GC_ref(parent)
-  return val
-
-proc toJSP(ctx: JSContext; parent: ptr object; child: var object): JSValue =
-  let p = addr child
-  # Increment the reference count of parent's root ancestor, and save the
-  # increment/decrement callbacks for the child as well.
-  let rtOpaque = JS_GetRuntime(ctx).getOpaque()
-  let ru = rtOpaque.refmap[parent]
-  ru.cref()
-  rtOpaque.refmap[p] = ru
-  let tp = getTypePtr(child)
-  return toJSP0(ctx, p, tp)
diff --git a/src/loader/headers.nim b/src/loader/headers.nim
index 21900189..ff7f9ed3 100644
--- a/src/loader/headers.nim
+++ b/src/loader/headers.nim
@@ -1,9 +1,10 @@
 import std/tables
 
-import bindings/quickjs
-import js/jserror
-import js/fromjs
-import js/javascript
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/quickjs
+import types/opt
 import utils/twtstr
 
 type
diff --git a/src/loader/loader.nim b/src/loader/loader.nim
index 8bc23c16..c84f247a 100644
--- a/src/loader/loader.nim
+++ b/src/loader/loader.nim
@@ -33,15 +33,16 @@ import io/serversocket
 import io/socketstream
 import io/tempfile
 import io/urlfilter
-import js/jserror
-import js/javascript
 import loader/cgi
 import loader/connecterror
 import loader/headers
 import loader/loaderhandle
 import loader/request
 import loader/response
+import monoucha/javascript
+import monoucha/jserror
 import types/cookie
+import types/opt
 import types/referrer
 import types/urimethodmap
 import types/url
diff --git a/src/loader/request.nim b/src/loader/request.nim
index 1398d25f..fc6c43f5 100644
--- a/src/loader/request.nim
+++ b/src/loader/request.nim
@@ -2,15 +2,16 @@ import std/options
 import std/strutils
 import std/tables
 
-import bindings/quickjs
 import html/script
-import js/fromjs
-import js/javascript
-import js/jserror
-import js/jstypes
 import loader/headers
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jstypes
+import monoucha/quickjs
 import types/blob
 import types/formdata
+import types/opt
 import types/referrer
 import types/url
 
diff --git a/src/loader/response.nim b/src/loader/response.nim
index 4cf17c99..ca300957 100644
--- a/src/loader/response.nim
+++ b/src/loader/response.nim
@@ -1,22 +1,22 @@
 import std/strutils
 import std/tables
 
-import bindings/quickjs
+import chagashi/charset
+import chagashi/decoder
+import chagashi/validator
 import io/promise
 import io/socketstream
-import js/jserror
-import js/javascript
 import loader/headers
 import loader/request
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/quickjs
 import types/blob
+import types/opt
 import types/url
 import utils/mimeguess
 import utils/twtstr
 
-import chagashi/charset
-import chagashi/decoder
-import chagashi/validator
-
 type
   ResponseType* = enum
     TYPE_DEFAULT = "default"
diff --git a/src/local/client.nim b/src/local/client.nim
index a14ce63c..4b81f668 100644
--- a/src/local/client.nim
+++ b/src/local/client.nim
@@ -9,8 +9,6 @@ import std/strutils
 import std/tables
 import std/unicode
 
-import bindings/constcharp
-import bindings/quickjs
 import config/config
 import html/catom
 import html/chadombuilder
@@ -30,16 +28,9 @@ import js/base64
 import js/console
 import js/domexception
 import js/encoding
-import js/fromjs
 import js/intl
-import js/javascript
-import js/jserror
 import js/jsmodule
-import js/jsopaque
-import js/jstypes
-import js/jsutils
 import js/timeout
-import js/tojs
 import loader/headers
 import loader/loader
 import loader/request
@@ -47,6 +38,15 @@ import local/container
 import local/lineedit
 import local/pager
 import local/term
+import monoucha/constcharp
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jsopaque
+import monoucha/jstypes
+import monoucha/jsutils
+import monoucha/quickjs
+import monoucha/tojs
 import server/buffer
 import server/forkserver
 import types/blob
diff --git a/src/local/container.nim b/src/local/container.nim
index 00553190..8439b32a 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -3,6 +3,7 @@ import std/net
 import std/options
 import std/os
 import std/posix
+import std/tables
 import std/unicode
 
 import config/config
@@ -12,18 +13,19 @@ import io/dynstream
 import io/promise
 import io/serversocket
 import io/socketstream
-import js/javascript
-import js/jsregex
-import js/jstypes
 import layout/renderdocument
 import loader/headers
 import loader/loader
 import loader/request
 import local/select
+import monoucha/javascript
+import monoucha/jsregex
+import monoucha/jstypes
 import server/buffer
 import types/cell
 import types/color
 import types/cookie
+import types/opt
 import types/referrer
 import types/url
 import types/winattrs
diff --git a/src/local/lineedit.nim b/src/local/lineedit.nim
index 04507de7..22fd3988 100644
--- a/src/local/lineedit.nim
+++ b/src/local/lineedit.nim
@@ -1,8 +1,8 @@
 import std/strutils
 import std/unicode
 
-import bindings/quickjs
-import js/javascript
+import monoucha/quickjs
+import monoucha/javascript
 import types/cell
 import types/opt
 import types/winattrs
diff --git a/src/local/pager.nim b/src/local/pager.nim
index 367802d9..8eb42b65 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -8,8 +8,6 @@ import std/selectors
 import std/tables
 import std/unicode
 
-import bindings/libregexp
-import bindings/quickjs
 import config/chapath
 import config/config
 import config/mailcap
@@ -21,13 +19,6 @@ import io/socketstream
 import io/stdio
 import io/tempfile
 import io/urlfilter
-import js/fromjs
-import js/javascript
-import js/jserror
-import js/jsregex
-import js/jstypes
-import js/jsutils
-import js/tojs
 import loader/connecterror
 import loader/headers
 import loader/loader
@@ -36,6 +27,15 @@ import local/container
 import local/lineedit
 import local/select
 import local/term
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/jsregex
+import monoucha/jstypes
+import monoucha/jsutils
+import monoucha/libregexp
+import monoucha/quickjs
+import monoucha/tojs
 import server/buffer
 import server/forkserver
 import types/cell
@@ -46,6 +46,7 @@ import types/url
 import types/winattrs
 import utils/luwrap
 import utils/mimeguess
+import utils/regexutils
 import utils/strwidth
 import utils/twtstr
 
diff --git a/src/local/select.nim b/src/local/select.nim
index 997eff1c..9e2f2c22 100644
--- a/src/local/select.nim
+++ b/src/local/select.nim
@@ -1,6 +1,6 @@
 import std/unicode
 
-import js/jsregex
+import monoucha/jsregex
 import server/buffer
 import types/cell
 import utils/luwrap
diff --git a/src/main.nim b/src/main.nim
index 808eff00..e070597f 100644
--- a/src/main.nim
+++ b/src/main.nim
@@ -1,21 +1,20 @@
 import version
 
-# Note: we can't just import std/os or the compiler cries. (No idea why.)
-from std/os import getEnv, putEnv, commandLineParams, getCurrentDir
 import std/options
+import std/os
 import std/posix
 
-import server/forkserver
+import chagashi/charset
 import config/chapath
 import config/config
-import js/javascript
 import local/client
 import local/term
+import monoucha/javascript
+import server/forkserver
+import types/opt
 import utils/strwidth
 import utils/twtstr
 
-import chagashi/charset
-
 const ChaVersionStr = block:
   var s = "Chawan browser v0.1 "
   when defined(debug):
diff --git a/src/server/buffer.nim b/src/server/buffer.nim
index 9a141135..e471c1ae 100644
--- a/src/server/buffer.nim
+++ b/src/server/buffer.nim
@@ -10,8 +10,6 @@ import std/selectors
 import std/tables
 import std/unicode
 
-import bindings/libregexp
-import bindings/quickjs
 import config/config
 import css/cascade
 import css/cssparser
@@ -37,14 +35,16 @@ import io/promise
 import io/serversocket
 import io/socketstream
 import js/console
-import js/fromjs
-import js/javascript
-import js/jsregex
 import js/timeout
-import js/tojs
 import layout/renderdocument
 import loader/headers
 import loader/loader
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jsregex
+import monoucha/libregexp
+import monoucha/quickjs
+import monoucha/tojs
 import types/blob
 import types/cell
 import types/color
diff --git a/src/types/blob.nim b/src/types/blob.nim
index dd812c02..ce5311ea 100644
--- a/src/types/blob.nim
+++ b/src/types/blob.nim
@@ -2,9 +2,9 @@ import std/options
 import std/posix
 import std/strutils
 
-import js/fromjs
-import js/javascript
-import js/jstypes
+import monoucha/fromjs
+import monoucha/javascript
+import monoucha/jstypes
 import utils/mimeguess
 
 type
diff --git a/src/types/cookie.nim b/src/types/cookie.nim
index d1302710..2cc5928c 100644
--- a/src/types/cookie.nim
+++ b/src/types/cookie.nim
@@ -2,9 +2,9 @@ import std/strutils
 import std/times
 
 import io/urlfilter
-import js/jserror
-import js/javascript
-import js/jsregex
+import monoucha/jserror
+import monoucha/javascript
+import monoucha/jsregex
 import types/url
 import types/opt
 import utils/twtstr
diff --git a/src/types/formdata.nim b/src/types/formdata.nim
index ec3d8bbf..26fd7975 100644
--- a/src/types/formdata.nim
+++ b/src/types/formdata.nim
@@ -2,7 +2,7 @@ import std/strutils
 
 import io/dynstream
 import io/posixstream
-import js/javascript
+import monoucha/javascript
 import types/blob
 import utils/twtstr
 
diff --git a/src/types/url.nim b/src/types/url.nim
index 075e3b08..627af4aa 100644
--- a/src/types/url.nim
+++ b/src/types/url.nim
@@ -5,11 +5,12 @@ import std/strutils
 import std/tables
 import std/unicode
 
-import bindings/libunicode
-import js/jserror
-import js/javascript
 import lib/punycode
+import monoucha/javascript
+import monoucha/jserror
+import monoucha/libunicode
 import types/blob
+import types/opt
 import utils/luwrap
 import utils/map
 import utils/twtstr
diff --git a/src/utils/luwrap.nim b/src/utils/luwrap.nim
index 853d3015..7ccb2c51 100644
--- a/src/utils/luwrap.nim
+++ b/src/utils/luwrap.nim
@@ -2,7 +2,7 @@ import std/algorithm
 import std/strutils
 import std/unicode
 
-import bindings/libunicode
+import monoucha/libunicode
 import utils/charcategory
 
 proc passRealloc(opaque, p: pointer; size: csize_t): pointer {.cdecl.} =
@@ -50,7 +50,8 @@ proc capitalizeLU*(s: string): string =
   result = newStringOfCap(s.len)
   var wordStart = true
   for r in s.runes:
-    if lre_is_space(uint32(r)) == 1:
+    if uint32(r) < 256 and char(r) in AsciiWhitespace or
+        lre_is_space_non_ascii(uint32(r)) == 1:
       wordStart = true
       result &= $r
     elif wordStart:
diff --git a/src/utils/regexutils.nim b/src/utils/regexutils.nim
new file mode 100644
index 00000000..ab7308f6
--- /dev/null
+++ b/src/utils/regexutils.nim
@@ -0,0 +1,64 @@
+import types/opt
+
+import monoucha/jsregex
+import monoucha/libregexp
+
+func countBackslashes(buf: string; i: int): int =
+  var j = 0
+  for i in countdown(i, 0):
+    if buf[i] != '\\':
+      break
+    inc j
+  return j
+
+# ^abcd -> ^abcd
+# efgh$ -> efgh$
+# ^ijkl$ -> ^ijkl$
+# mnop -> ^mnop$
+proc compileMatchRegex*(buf: string): Result[Regex, string] =
+  if buf.len == 0:
+    return compileRegex(buf)
+  if buf[0] == '^':
+    return compileRegex(buf)
+  if buf[^1] == '$':
+    # Check whether the final dollar sign is escaped.
+    if buf.len == 1 or buf[^2] != '\\':
+      return compileRegex(buf)
+    let j = buf.countBackslashes(buf.high - 2)
+    if j mod 2 == 1: # odd, because we do not count the last backslash
+      return compileRegex(buf)
+    # escaped. proceed as if no dollar sign was at the end
+  if buf[^1] == '\\':
+    # Check if the regex contains an invalid trailing backslash.
+    let j = buf.countBackslashes(buf.high - 1)
+    if j mod 2 != 1: # odd, because we do not count the last backslash
+      return err("unexpected end")
+  var buf2 = "^"
+  buf2 &= buf
+  buf2 &= "$"
+  return compileRegex(buf2)
+
+proc compileSearchRegex*(str: string; defaultFlags: LREFlags):
+    Result[Regex, string] =
+  # Emulate vim's \c/\C: override defaultFlags if one is found, then remove it
+  # from str.
+  # Also, replace \< and \> with \b as (a bit sloppy) vi emulation.
+  var flags = defaultFlags
+  var s = newStringOfCap(str.len)
+  var quot = false
+  for c in str:
+    if quot:
+      quot = false
+      case c
+      of 'c': flags.incl(LRE_FLAG_IGNORECASE)
+      of 'C': flags.excl(LRE_FLAG_IGNORECASE)
+      of '<', '>': s &= "\\b"
+      else: s &= '\\' & c
+    elif c == '\\':
+      quot = true
+    else:
+      s &= c
+  if quot:
+    s &= '\\'
+  flags.incl(LRE_FLAG_GLOBAL) # for easy backwards matching
+  return compileRegex(s, flags)
diff --git a/src/version.nim b/src/version.nim
index 66e81a9f..c1c90c54 100644
--- a/src/version.nim
+++ b/src/version.nim
@@ -24,7 +24,9 @@ macro checkVersion(xs: static string; major, minor, patch: int) =
 
 tryImport chagashi/version, "chagashi"
 tryImport chame/version, "chame"
+tryImport monoucha/version, "monoucha"
 
 static:
   checkVersion("chagashi", 0, 4, 2)
   checkVersion("chame", 0, 14, 5)
+  checkVersion("monoucha", 0, 1, 1)