Full Code of Gozala/reflex for AI

main 1a2460253cad cached
18 files
86.9 KB
23.1k tokens
221 symbols
1 requests
Download .txt
Repository: Gozala/reflex
Branch: main
Commit: 1a2460253cad
Files: 18
Total size: 86.9 KB

Directory structure:
gitextract_p4d61kwm/

├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── Readme.md
├── package.json
├── src/
│   ├── Application.js
│   ├── Attribute.js
│   ├── Basics.js
│   ├── Document.js
│   ├── Effect.js
│   ├── Element.js
│   ├── Program.ts
│   ├── Task.ts
│   ├── VirtualDOM.d.ts
│   ├── VirtualDOM.js
│   ├── Widget.js
│   └── lib.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
      - main
    paths:
      - "src/**"
  pull_request:
    branches:
      - main
    paths:
      - "src**"
      - ".github/workflows/ci.yml"
  workflow_dispatch:

jobs:
  check:
    name: Typecheck
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version:
          - 16
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup node ${{ matrix.node-version }}
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install dependencies
        uses: bahmutov/npm-install@v1

      - name: Typecheck
        uses: gozala/typescript-error-reporter-action@v1.0.8
        with:
          project: ./tsconfig.json
  test-node:
    name: Test Node
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        node-version:
          - 16
        os:
          - ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}

      - name: Install dependencies
        uses: bahmutov/npm-install@v1

      - name: Test
        run: yarn test:node
  test-web:
    name: Test Web
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version:
          - 16

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: 16

      - name: Install dependencies
        uses: bahmutov/npm-install@v1

      - name: Test
        run: yarn test:web


================================================
FILE: .github/workflows/release.yml
================================================
on:
  push:
    branches:
      - main
  workflow_dispatch:

name: release
jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: google-github-actions/release-please-action@v3
        id: release
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          release-type: node
          bump-minor-pre-major: true
      # The logic below handles the npm publication:
      - uses: actions/checkout@v2
        # these if statements ensure that a publication only occurs when
        # a new release is created:
        if: ${{ steps.release.outputs.release_created }}
      - uses: actions/setup-node@v2
        with:
          node-version: "16"
          registry-url: https://registry.npmjs.org/
        if: ${{ steps.release.outputs.release_created }}
      - name: Install
        uses: bahmutov/npm-install@v1
        if: ${{ steps.release.outputs.release_created }}
      - name: Publish
        run: npm publish --access=public
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
        if: ${{ steps.release.outputs.release_created }}


================================================
FILE: .gitignore
================================================
dist
.nyc_output
tmp
node_modules


================================================
FILE: Readme.md
================================================
# reflex [![Gitter][gitter.icon]][gitter.url] [![styled with prettier][prettier.icon]][prettier.url]

Reflex is a reactive UI library that is heavily inspired by (pretty much is a port of) [elm][] and it's amazingly simple yet powerful [architecture][elm architecture] where "[flux][]" (in [react][] terms) is simply a byproduct of a pattern. In order to keep a major attraction of [elm][] — [algebraic data types][] & type safety — the library uses [flow][], a static type checker, that being said it's your call whether you want to use [flow][] yourself or just happer a pure JS either way this library has things to offer.

Library is authored as pure ES modules and can be used with [import syntax][].

```js
import * as Reflex from "//reflex.hashbase.io/lib.js"
```

[elm]: http://elm-lang.org
[elm architecture]: http://elm-lang.org/guide/architecture
[react]: http://facebook.github.io/react/
[immutable.js]: https://facebook.github.io/immutable-js/
[flux]: https://facebook.github.io/flux/
[algebraic data types]: https://en.wikipedia.org/wiki/Algebraic_data_type
[flow]: http://flowtype.org
[gitter.url]: https://gitter.im/mozilla/reflex?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[gitter.icon]: https://badges.gitter.im/Join%20Chat.svg
[prettier.url]: https://github.com/prettier/prettier
[prettier.icon]: https://img.shields.io/badge/styled_with-prettier-ff69b4.svg
[import syntax]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import


================================================
FILE: package.json
================================================
{
  "name": "reflex",
  "version": "2.0.0",
  "description": "Functional reactive UI library",
  "keywords": [
    "reflex",
    "reactive",
    "functional",
    "UI"
  ],
  "author": "Irakli Gozalishvili <dev@gozala.io>",
  "scripts": {
    "prepare": "tsc --build",
    "test:web": "playwright-test test/**/*.spec.js --cov && nyc report",
    "test:node": "c8 --check-coverage --branches 85 --functions 70 --lines 80 mocha test/**/*.spec.js",
    "test": "npm run test:node",
    "typecheck": "tsc --build",
    "precommit": "lint-staged"
  },
  "type": "module",
  "main": "src/lib.js",
  "types": "./dist/src/lib.d.ts",
  "devDependencies": {
    "typescript": "^4.6.3",
    "@types/mocha": "^9.1.0",
    "@types/chai": "^4.3.0",
    "mocha": "^9.2.2",
    "chai": "^4.3.6",
    "husky": "^7.0.4",
    "lint-staged": "^12.4.0",
    "prettier": "^2.6.2"
  },
  "typesVersions": {
    "*": {
      "*": [
        "dist/*"
      ],
      "dist/src/lib.d.ts": [
        "dist/src/lib.d.ts"
      ]
    }
  },
  "exports": {
    ".": {
      "types": "./dist/src/lib.d.ts",
      "import": "./src/lib.js"
    },
    "./src/lib.js": {
      "types": "./dist/src/lib.d.ts",
      "import": "./src/lib.js"
    }
  },
  "files": [
    "src",
    "dist/src"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/gozala/reflex.git"
  },
  "homepage": "https://github.com/gozala/reflex",
  "c8": {
    "exclude": [
      "test/**",
      "dist/**"
    ]
  },
  "lint-staged": {
    "*.js": [
      "prettier --parser flow --no-semi --write",
      "git add"
    ]
  },
  "license": "MIT"
}


================================================
FILE: src/Application.js
================================================
import { DocumentWidget } from "./Document.js"

/**
 * @template T
 * @typedef {import('./Program').DocumentView<T>} DocumentView
 */
/**
 * @template Message, State
 * @extends {DocumentWidget<Message, State>}
 */
class ApplicationWidget extends DocumentWidget {
  /**
   *
   * @param {import('./Program').Router<Message>} router
   */
  constructor(router) {
    super()
    this.onExternalURLRequest = router.onExternalURLRequest
    this.onInternalURLRequest = router.onInternalURLRequest
    this.onURLChange = router.onURLChange
  }
  getURL() {
    return new URL(this.root.location.href)
  }
  /**
   * @param {MouseEvent} event
   */
  handleEvent(event) {
    switch (event.type) {
      case "navigate": // manually notify when we do pushState replaceState
      case "popstate":
      case "hashchange":
        return this.thread.send(this.onURLChange(this.getURL()))
      case "click": {
        if (
          !event.ctrlKey &&
          !event.metaKey &&
          !event.shiftKey &&
          event.button < 1 &&
          // @ts-ignore
          !event.target.target &&
          // @ts-ignore
          !event.target.download
        ) {
          event.preventDefault()
          const current = this.getURL()
          // @ts-ignore
          const next = new URL(event.currentTarget.href, current.href)

          const isInternal =
            current.protocol === next.protocol &&
            current.host === next.host &&
            current.port === next.port

          const message = isInternal
            ? this.onInternalURLRequest(next)
            : this.onExternalURLRequest(next)

          return this.thread.send(message)
        }
      }
    }
    return undefined
  }

  /**
   * @param {Document} document
   */
  addListeners(document) {
    const top = document.defaultView
    if (top) {
      top.addEventListener("popstate", this)
      top.addEventListener("hashchange", this)
      // @ts-ignore
      top.onnavigate = this
    } else {
      throw new Error("document.defaultView is not defined")
    }
  }
}

/**
 * @template {{url?:URL}} Options
 * @template Message, State
 * @param {import('./Program').Application<Message, State, DocumentView<Message>, Options>} application
 * @param {Options} options
 * @param {Document} document
 * @returns {ApplicationWidget<Message, State>}
 */
export const spawn = (
  application,
  options,
  document
) /*: ApplicationWidget<a, model, config> */ => {
  const self = new ApplicationWidget(application)
  const root = DocumentWidget.root(document)
  self.update = application.update
  self.view = application.view
  self.root = root
  self.node = root.widget ? root.widget.node : self.mount(root)
  self.thread = self.fork(root)
  root.widget = self
  self.addListeners(document)
  options.url = self.getURL()
  self.transact(application.init(options))

  return self
}


================================================
FILE: src/Attribute.js
================================================
// @flow strict

import { attribute, property, style, on } from "./VirtualDOM.js"

export { style, attribute, property, on }
// // TODO: defaultValue, defaultChecked, innerHTML, suppressContentEditableWarning, suppressHydrationWarning, style

/**
 * @template {string|number|boolean} T
 * @param {T} value
 */
export const defaultValue = value => property("defaultValue", value)

/**
 * @template {string|number|boolean} T
 * @param {T} value
 */
export const value = value => property("value", value)

/**
 * @template {string} T
 * @param {T} value
 */
export const acceptCharset = value => property("accept-charset", value)

/**
 * @template {string} T
 * @param {T} value
 */
export const className = value => attribute("class", value)

/**
 * @param  {string[]} values
 */
export const classList = (...values) => attribute("class", values.join(" "))

/**
 *
 * @param {string} value
 */
export const textContent = value => property("textContent", value)

/**
 * @param {string} value
 */
export const For = value => attribute("for", value)

/**
 * @param {string} value
 */
export const Equiv = value => attribute("equiv", value)

/**
 * @param {string} name
 * @param {string} value
 */
export const data = (name, value) => attribute(`data-${name}`, value)

/**
 * @param {string} name
 */
const setHTMLAttribute = name =>
  /**
   * @param {string} [value]
   */
  (value = "") => attribute(name, value)

export const src = setHTMLAttribute("src")
export const srcset = setHTMLAttribute("srcset")
export const alt = setHTMLAttribute("alt")
export const href = setHTMLAttribute("href")
export const id = setHTMLAttribute("id")
export const accept = setHTMLAttribute("accept")
export const type = setHTMLAttribute("type")
export const placeholder = setHTMLAttribute("placeholder")
export const title = setHTMLAttribute("title")

/**
 * @param {string} name
 */
const setBooleanHTMLAttribute = name =>
  /**
   * @param {boolean} value
   */
  value => attribute(name, value ? "true" : "false")

export const contentEditable = setBooleanHTMLAttribute("contenteditable")
export const draggable = setBooleanHTMLAttribute("draggable")
export const spellCheck = setBooleanHTMLAttribute("spellcheck")

/**
 * @param {string} name
 */
const setBooleanSVGAttribute = name =>
  /**
   * @param {boolean} value
   */
  value => attribute(name, value ? "true" : "false")

export const autoReverse = setBooleanSVGAttribute("autoReverse")
export const externalResourcesRequired = setBooleanSVGAttribute(
  "externalResourcesRequired"
)
export const preserveAlpha = setBooleanSVGAttribute("preserveAlpha")

/**
 *
 * @param {string} name
 */
const setModalHTMLAttribute = name =>
  /**
   *
   * @param {boolean} [value]
   */
  (value = true) => attribute(name, value ? "" : null)

export const allowFullScreen = setModalHTMLAttribute("allowfullscreen")
export const async = setModalHTMLAttribute("async")
export const autoFocus = setModalHTMLAttribute("autofocus")
export const autoPlay = setModalHTMLAttribute("autoplay")
export const controls = setModalHTMLAttribute("controls")
export const htmlDefault = setModalHTMLAttribute("default")
export const defer = setModalHTMLAttribute("defer")
export const disabled = setModalHTMLAttribute("disabled")
export const formNoValidate = setModalHTMLAttribute("formnovalidate")
export const hidden = setModalHTMLAttribute("hidden")
export const loop = setModalHTMLAttribute("loop")
export const noValidate = setModalHTMLAttribute("novalidate")
export const open = setModalHTMLAttribute("open")
export const playsInline = setModalHTMLAttribute("playsinline")
export const readOnly = setModalHTMLAttribute("readonly")
export const required = setModalHTMLAttribute("required")
export const reversed = setModalHTMLAttribute("reversed")
export const scoped = setModalHTMLAttribute("scoped")
export const seamless = setModalHTMLAttribute("seamless")
export const itemScope = setModalHTMLAttribute("itemscope")

/**
 * @param {string} name
 */
const setBooleanProperty = name =>
  /**
   *
   * @param {boolean} value
   */
  value => property(name, value)

export const checked = setBooleanProperty("checked")
export const multiple = setBooleanProperty("multiple")
export const muted = setBooleanProperty("muted")
export const selected = setBooleanProperty("selected")

/**
 * @param {string} name
 */
const setOptionalStringAttribute = name =>
  /**
   *
   * @param {string} [value]
   */
  (value = "") => attribute(name, value)

export const capture = setOptionalStringAttribute("capture")
export const download = setOptionalStringAttribute("download")

/**
 * @param {string} name
 */
const setNumberAttribute = name =>
  /**
   * @param {number} value
   */
  value => attribute(name, `${value}`)

export const cols = setNumberAttribute("cols")
export const rows = setNumberAttribute("rows")
export const size = setNumberAttribute("size")
export const span = setNumberAttribute("span")
export const tabIndex = setNumberAttribute("tabindex")

export const rowSpan = setNumberAttribute("rowSpan")
export const start = setNumberAttribute("start")


================================================
FILE: src/Basics.js
================================================
/**
 * @template T
 * @param {T} value
 * @returns {T}
 */
export const identity = value => value

/**
 * @template T
 * @param {T} value
 * @returns {() => T}
 */
export const always = value => () => value

export const True = always(/** @type {true}*/ true)
export const False = always(/** @type {false}*/ false)
export const Null = always(null)
export const Void = always(/** @type {undefined} */ undefined)
export const EmptyString = always(/** @type {""} */ "")
export const EmptyObject = always(Object.freeze({}))
/** @type {readonly any[]} */
const anyArray = Object.freeze(/** @type {any[]}*/ [])
export const EmptyArray /*: <$, a>($) => a[] */ = always(anyArray)

/** @type {Record<any, any>} */
const table = Object.freeze(Object.create(null))
export const EmptyTable = always(table)

/**
 * @param {never} value
 * @returns {any}
 */
export const unreachable = value => {
  console.error(`value passed to never`, value)
  throw TypeError(
    `unreachable was supposed to be unreachable but it was called with ${value}`
  )
}

/**
 * @template T
 * @param {T} [value]
 */
export const nothing = value => void value

const defaultReason =
  "Typesystem established invariant was broken at runtime, likely due to incorrect call from untyped JS."

/**
 * @param {Error} reason
 * @returns {never}
 */
export const panic = /*:: <error> */(reason = new Error(defaultReason)) => {
  throw reason
}


================================================
FILE: src/Document.js
================================================
// @flow strict

import { virtualize, diff, patch, doc } from "./VirtualDOM.js"
import { Widget, MainThread } from "./Widget.js"

/**
 * @template Message
 * @typedef {import('./Program').DocumentView<Message>} DocumentView
 */

/**
 * @template Message
 * @typedef {{
 *  body: Element
 *  title: string
 *  location: Location
 *  widget?: {
 *    node: DocumentView<Message>
 *    thread: MainThread<Message>
 *  }
 * }} RenderedDocument
 */

/**
 * @template Message, State
 * @extends {Widget<Message, State, DocumentView<Message>, RenderedDocument<Message>>}
 */
export class DocumentWidget extends Widget {
  /**
   * @template Message
   * @param {Document} document
   * @returns {RenderedDocument<Message>}
   */
  static root(document) {
    const root = document
    if (!document.body) {
      document.appendChild(document.createElement("body"))
    }
    return root
  }
  /**
   * @param {RenderedDocument<Message>} root
   * @returns {DocumentView<Message>}
   */
  mount(root) {
    return root.widget
      ? root.widget.node
      : doc(root.title, virtualize(root.body))
  }
  /**
   *
   * @param {RenderedDocument<Message>} root
   * @returns {MainThread<Message>}
   */
  fork(root) {
    const thread = root.widget ? root.widget.thread : Widget.fork(this)
    thread.root = this
    return thread
  }
  /**
   *
   * @param {State} state
   */
  render(state) {
    const newDocument = this.view(state)
    const renderedDocument = this.node
    const delta = diff(renderedDocument.body, newDocument.body)
    patch(this.root.body, renderedDocument.body, delta, this.thread)
    this.node = newDocument
    if (renderedDocument.title !== newDocument.title) {
      this.root.title = newDocument.title
    }
  }
}

/**
 * @template Message, State, Options
 * @param {import('./Program').Program<Message, State, DocumentView<Message>, Options>} program
 * @param {Options} options
 * @param {Document} document
 * @returns {Widget<Message, State, DocumentView<Message>, RenderedDocument<Message>>}
 */
export const spawn = ({ init, update, view }, options, document) => {
  const self = new DocumentWidget()
  const root = DocumentWidget.root(document)
  self.update = update
  self.view = view
  self.root = root
  self.node = self.mount(root)
  self.thread = self.fork(root)
  root.widget = self
  self.transact(init(options))
  return self
}


================================================
FILE: src/Effect.js
================================================
import { nothing } from "./Basics.js"

/**
 * @template T
 * @typedef {import('./Task').Effect<T>} Effect<T>
 */

/**
 * @implements {Effect<any>}
 */
class None {
  perform() {}
  map() {
    return this
  }
}

const none = new None()

/**
 * @template T
 * @typedef {import('./Task').Main<T>} Main
 */
/**
 * @template T
 * @implements {Effect<T>}
 */
class Send {
  /**
   *
   * @param {T} message
   */
  constructor(message) {
    /**
     * @private
     */
    this.message = message
  }
  /**
   * @param {Main<T>} main
   */
  perform(main) {
    main.send(this.message)
  }
  /**
   * @template U
   * @param {(value:T) => U} tag
   * @returns {Effect<U>}
   */
  map(tag) {
    return new Tagged(this, tag)
  }
}

/**
 * @template X, I, O
 */
class FX {
  /**
   * @param {import('./Task').Task<X, I>} task
   * @param {(value:I) => void|O} success
   * @param {(error:X) => void|O} failure
   */
  constructor(task, success, failure) {
    /**
     * @private
     */
    this.task = task
    /**
     * @private
     */
    this.success = success
    /**
     * @private
     */
    this.failure = failure
  }

  /**
   * @private
   * @param {Main<O>} main
   */
  async execute(main) {
    try {
      const value = await this.task()
      const message = this.success(value)
      if (message != null) {
        main.send(message)
      }
    } catch (error) {
      const message = this.failure(/** @type {X} */(error))
      if (message != null) {
        main.send(message)
      }
    }
  }

  /**
   * @param {Main<O>} main
   */
  perform(main) {
    this.execute(main)
  }

  /**
   * @template U
   * @param {(value:O) => U} tag
   * @returns {Effect<U>}
   */
  map(tag) {
    return new Tagged(this, tag)
  }
}

/**
 * @template T
 * @implements {Effect<T>}
 */
class Batch {
  /**
   *
   * @param {Effect<T>[]} effects
   */
  constructor(effects) {
    /**
     * @private
     */
    this.effects = effects
  }

  /**
   * @param {Main<T>} main
   */
  perform(main) {
    for (const fx of this.effects) {
      fx.perform(main)
    }
  }

  /**
   * @template U
   * @param {(value:T) => U} tag
   * @returns {Effect<U>}
   */
  map(tag) {
    return new Tagged(this, tag)
  }
}

/**
 * @typedef {import('./Task').Thread} Thread
 * @typedef {import('./Task').ThreadID} ThreadID
 */

/**
 * @template T, U
 * @implements {Effect<U>}
 * @implements {Main<T>}
 */
export class Tagged {
  /**
   * @param {Effect<T>} fx
   * @param {(value:T) => U} tag
   */
  constructor(fx, tag) {
    /**
     * @private
     */
    this.fx = fx
    /**
     * @private
     */
    this.tag = tag
    /**
     * @private
     * @type {Main<U>}
     */
    this.port
  }
  /**
   *
   * @param {Main<U>} main
   */
  perform(main) {
    this.port = main
    this.fx.perform(this)
  }

  /**
   * @param {Thread} thread
   */
  link(thread) {
    return this.port.link(thread)
  }
  /**
   * @param {Thread} thread
   */
  unlink(thread) {
    return this.port.unlink(thread)
  }
  /**
   * @param {ThreadID} id
   */
  linked(id) {
    return this.port.linked(id)
  }
  /**
   * @param {T} message
   */
  send(message) {
    return this.port.send(this.tag(message))
  }
  /**
   * @template E
   * @param {(value:U) => E} tag
   * @returns {Effect<E>}
   */
  map(tag) {
    return new Tagged(this, tag)
  }
}

export const nofx = none

/**
 * Creates an effect that will execute a given task and send
 * a message back to program. Provided handlers are used to turn task
 * result into message
 *
 * @template X, I, O
 * @param {import('./Task').Task<X, I>} task
 * @param {(value:I) => O|void} [ok]
 * @param {(error:X) => O|void} [error]
 */
export const fx = (task, ok = nothing, error = warn) /*: Effect<message> */ =>
  new FX(task, ok, error)

/**
 * Sends a given message to the program
 *
 * @template T
 * @param {T} message
 */
export const send = message => new Send(message)

/**
 * @template T
 * @param  {Effect<T>[]} fx
 */
export const batch = (...fx) => new Batch(fx)

/**
 * @param {unknown} error
 */
const warn = error => {
  console.warn("Task failed but error was not handled", error)
}


================================================
FILE: src/Element.js
================================================
import { node, text, doc, keyedNode, customElement } from "./VirtualDOM.js"

/**
 * @param {string} tag
 */
const factory = tag =>
  /**
   * @template T
   * @param {import('./VirtualDOM').Attribute<T>[]} [settings]
   * @param {import('./VirtualDOM').Node<T>[]} [children]
   */
  (settings, children) => node(tag, settings, children)

export { text, node, doc, customElement, keyedNode }
export const html = factory("html")
export const link = factory("link")
export const meta = factory("meta")
export const style = factory("style")
export const body = factory("body")
export const address = factory("address")
export const article = factory("article")
export const aside = factory("aside")
export const footer = factory("footer")
export const header = factory("header")
export const h1 = factory("h1")
export const h2 = factory("h2")
export const h3 = factory("h3")
export const h4 = factory("h4")
export const h5 = factory("h5")
export const h6 = factory("h6")
export const hgroup = factory("hgroup")
export const nav = factory("nav")
export const section = factory("section")
export const blockquote = factory("blockquote")
export const dd = factory("dd")
export const dir = factory("dir")
export const div = factory("div")
export const dl = factory("dl")
export const dt = factory("dt")
export const figcaption = factory("figcaption")
export const figure = factory("figure")
export const hr = factory("hr")
export const li = factory("li")
export const main = factory("main")
export const ol = factory("ol")
export const p = factory("p")
export const pre = factory("pre")
export const ul = factory("ul")
export const a = factory("a")
export const abbr = factory("abbr")
export const b = factory("b")
export const bdi = factory("bdi")
export const bdo = factory("bdo")
export const br = factory("br")
export const cite = factory("cite")
export const code = factory("code")
export const data = factory("data")
export const dfn = factory("dfn")
export const em = factory("em")
export const i = factory("i")
export const kbd = factory("kbd")
export const mark = factory("mark")
export const nobr = factory("nobr")
export const q = factory("q")
export const rp = factory("rp")
export const rt = factory("rt")
export const rtc = factory("rtc")
export const ruby = factory("ruby")
export const s = factory("s")
export const samp = factory("samp")
export const small = factory("small")
export const span = factory("span")
export const strong = factory("strong")
export const sub = factory("sub")
export const sup = factory("sup")
export const time = factory("time")
export const u = factory("u")
export const Var = factory("var")
export const wbr = factory("wbr")
export const area = factory("area")
export const audio = factory("audio")
export const img = factory("img")
export const map = factory("map")
export const track = factory("track")
export const video = factory("video")
export const applet = factory("applet")
export const embed = factory("embed")
export const noembed = factory("noembed")
export const object = factory("object")
export const param = factory("param")
export const picture = factory("picture")
export const source = factory("source")
export const canvas = factory("canvas")
export const noscript = factory("noscript")
export const script = factory("script")
export const del = factory("del")
export const ins = factory("ins")
export const caption = factory("caption")
export const col = factory("col")
export const colgroup = factory("colgroup")
export const table = factory("table")
export const tbody = factory("tbody")
export const td = factory("td")
export const tfoot = factory("tfoot")
export const th = factory("th")
export const thead = factory("thead")
export const tr = factory("tr")
export const button = factory("button")
export const datalist = factory("datalist")
export const fieldset = factory("fieldset")
export const form = factory("form")
export const input = factory("input")
export const label = factory("label")
export const legend = factory("legend")
export const meter = factory("meter")
export const optgroup = factory("optgroup")
export const option = factory("option")
export const output = factory("output")
export const progress = factory("progress")
export const select = factory("select")
export const textarea = factory("textarea")
export const details = factory("details")
export const dialog = factory("dialog")
export const menu = factory("menu")
export const menuitem = factory("menuitem")
export const summary = factory("summary")
export const content = factory("content")
export const element = factory("element")
export const shadow = factory("shadow")
export const slot = factory("slot")
export const template = factory("template")
export const acronym = factory("acronym")
export const basefont = factory("basefont")
export const bgsound = factory("bgsound")
export const big = factory("big")
export const blink = factory("blink")
export const center = factory("center")
export const command = factory("command")
export const font = factory("font")
export const frame = factory("frame")
export const frameset = factory("frameset")
export const image = factory("image")
export const isindex = factory("isindex")
export const keygen = factory("keygen")
export const listing = factory("listing")
export const marquee = factory("marquee")
export const multicol = factory("multicol")
export const nextid = factory("nextid")
export const noframes = factory("noframes")
export const plaintext = factory("plaintext")
export const spacer = factory("spacer")
export const strike = factory("strike")
export const tt = factory("tt")
export const xmp = factory("xmp")


================================================
FILE: src/Program.ts
================================================
import { Transaction, Main } from "./Task"
import { Node } from "./VirtualDOM"
export interface Program<Message, State, View, Options> {
  init(options: Options): Transaction<Message, State>
  update(message: Message, state: State): Transaction<Message, State>
  view(state: State): View
}

export interface Router<Message> {
  onExternalURLRequest(url: URL): Message
  onInternalURLRequest(url: URL): Message
  onURLChange(url: URL): Message
}
export interface Application<Message, State, View, Options>
  extends Router<Message>,
    Program<Message, State, View, Options> {}

export interface DocumentView<T> {
  title: string
  body: Node<T>
}

export interface RenderedDocument<T> {
  body: Element
  title: string
  location: Location
  widget?: {
    node: DocumentView<T>
    thread: Main<T>
  }
}


================================================
FILE: src/Task.ts
================================================
export type Phantom<T> = T & { readonly kind: unique symbol }

export type ThreadID = Phantom<string>

export interface Thread {
  exit(reason?: Error): void
}

export interface Port<T> {
  send(message: T): unknown
}

export interface Main<message> extends Port<message> {
  link(thread: Thread): ThreadID
  unlink(thread: Thread): void
  linked(thread: ThreadID): Thread | undefined | null
}

export interface IO<T> {
  perform(thread: Main<T>): unknown
}
export type Transaction<message, state> = [state, IO<message>]

export interface Sync<T> {
  sync(value: T): void
}

export interface Effect<T> extends IO<T> {
  map<U>(tag: (value: T) => U): Effect<U>
}

export interface AsyncResult<X, T> {
  then(succeed: (value: T) => void, fail: (error: X) => void): unknown
}

export interface Task<X, T> {
  (): AsyncResult<X, T>
}


================================================
FILE: src/VirtualDOM.d.ts
================================================
import { Port } from "./Task"
export interface Doc<T> {
  title: string
  body: Node<T>

  map<U>(f: (inn: T) => U): Doc<U>
}

export declare class Node<T> {
  settings(): Attribute<T>[]
  children(): Node<T>[]
  map<U>(f: (inn: T) => U): Node<U>
}
export declare class Attribute<T> {
  map<U>(f: (inn: T) => U): Attribute<U>
}
export type Keyed<T> = [string, T]

export declare function node<
  A extends Attribute<unknown>,
  C extends Node<unknown>
>(localName: string, attributes?: A[], children?: C[]): EventNode<A | C>

type EventSource<T> = Attribute<T> | Node<T>

type EventNode<E extends EventSource<unknown>> = E extends EventSource<infer T>
  ? Node<T>
  : never
export declare function customElement<A extends Attribute<unknown>>(
  localName: string,
  constructor: { new (): HTMLElement },
  attributes?: A[]
): EventNode<A>

export declare function keyedNode<
  A extends Attribute<unknown>,
  C extends Node<unknown>
>(localName: string, attributes?: A[], children?: Keyed<C>[]): EventNode<A | C>

export declare function keyedNodeNS<
  A extends Attribute<unknown>,
  C extends Node<unknown>
>(
  namespace: string,
  localName: string,
  attributes?: A[],
  children?: Keyed<C>[]
): EventNode<A | C>

export declare function nodeNS<
  A extends Attribute<unknown>,
  C extends Node<unknown>
>(
  namespace: string,
  localName: string,
  attributes?: A[],
  children?: C[]
): EventNode<A | C>

export declare function text(value: string): Node<never>
export declare function doc<T>(title: string, body: Node<T>): Doc<T>

export declare function property<T>(name: string, value: T): Attribute<never>
export declare function attribute(
  name: string,
  value: null | string
): Attribute<never>

export declare function attributeNS(
  namespace: string,
  name: string,
  value: string | boolean | number | null | void
): Attribute<never>
export declare function style(string, string): Attribute<never>

export interface Decoder<In, Out> {
  decode(inn): Out | Error
}

export type EncodedEvent =
  | Event
  | DragEvent
  | MouseEvent
  | KeyboardEvent
  | UIEvent

export interface DecodedEvent<T> {
  message: T
  preventDefault?: boolean
  stopPropagation?: boolean
}

export interface EventDecoder<T>
  extends Decoder<EncodedEvent, DecodedEvent<T>> {}

export declare function on<T>(
  type: string,
  decoder: EventDecoder<T>
): Attribute<T>

declare class Delta {}

export type { Delta }

export declare function diff<T>(before: Node<T>, after: Node<T>): Delta
export declare function patch<T>(
  root: EventTarget,
  current: Node<T>,
  delta: Delta,
  port: Port<T>
): void
export declare function virtualize<T>(EventTarget): Node<T>


================================================
FILE: src/VirtualDOM.js
================================================
// @ts-nocheck

const TEXT = "VirtualDOM.Text"
const NODE = "VirtualDOM.Node"
const KEYED_NODE = "VirtualDOM.Keyed.Node"
const CUSTOM_NODE = "VirtualDOM.Custom"
const CUSTOM_ELEMENT = "VirtualDOM.CustomElement"
const TAGGED_NODE = "VirtualDOM.Tagger"
const THUNK_NODE = "VirtualDOM.Thunk"

const OP_REDRAW = "VirtualDOM.OP.Redraw"
const OP_THUNK = "VirtualDOM.OP.Thunk"
const OP_TAGGER = "VirtualDOM.OP.Tagger"
const OP_TEXT = "VirtualDOM.OP.Text"
const OP_FACTS = "VirtualDOM.OP.Facts"
const OP_CUSTOM = "VirtualDOM.OP.Custom"
const OP_REMOVE_LAST = "VirtualDOM.OP.RemoveLast"
const OP_APPEND = "VirtualDOM.OP.Append"
const OP_REORDER = "VirtualDOM.OP.Reorder"
const OP_REMOVE = "VirtualDOM.OP.Remove"
const OP_DELETE = "VirtualDOM.OP.Delete"
const OP_INSERT = "VirtualDOM.OP.Insert"
const OP_MOVE = "VirtualDOM.OP.Move"

const SETTING_EVENT = "VirtualDOM.Setting.Event"
const SETTING_STYLE = "VirtualDOM.Setting.Style"
const SETTING_PROPERTY = "VirtualDOM.Setting.Property"
const SETTING_ATTRIBUTE = "VirtualDOM.Setting.Attribute"
const SETTING_ATTRIBUTE_NS = "VirtualDOM.Setting.AttributeNS"

// HELPERS

function appendChild(parent, child) {
  parent.appendChild(child)
}

var init = function(virtualNode, flagDecoder, debugMetadata, args) {
  const { node } = args

  node.parentNode.replaceChild(
    render(node.ownerDocument, virtualNode, function() {}),
    node
  )

  return {}
}

// TEXT

class Text {
  constructor(content) {
    this.nodeType = TEXT
    this.text = content
  }
  map(tagger) {
    return this
  }
  toInnerHTML() {
    return ""
  }
  toOuterHTML(indent) {
    return `${indent}${this.text}`
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
}

export const text = content => new Text(content)

// NODE

const noKids = Object.freeze([])
const noFacts = Object.freeze([])

class CustomElement {
  constructor(localName, elementConstructor, options, settings) {
    this.nodeType = CUSTOM_ELEMENT
    this.localName = localName
    this.elementConstructor = elementConstructor
    this.options = options
    this.settings = settings
    this.children = noKids
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  serializeSettings(key = "") {
    return Node.prototype.serializeSettings.call(this, key)
  }
  toOuterHTML(indent = "", key = "") {
    return Node.prototype.toOuterHTML.call(this, indent, key)
  }
  toInnerHTML(indent = "") {
    return Node.prototype.toInnerHTML.call(this, indent)
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

export const customElement = (localName, constructor, settings) =>
  new CustomElement(localName, constructor, undefined, organizeFacts(settings))

class Node {
  constructor(localName, settings, children, namespace, descendantsCount) {
    this.nodeType = NODE
    this.localName = localName
    this.settings = settings
    this.children = children
    this.namespace = namespace
    this.descendantsCount = descendantsCount
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  toInnerHTML(indent = "") {
    const { children } = this
    let html = ""
    if (children.length > 0) {
      const childIndent = `${indent}  `
      for (const child of children) {
        html += `\n${child.toOuterHTML(indent, "")}`
      }
    }
    return html
  }
  serializeSettings(key) {
    let buffer = ""

    if (key !== "") {
      buffer += ` key=${key}`
    }

    const { settings } = this
    for (const type in settings) {
      const group = settings[type]
      for (const name in group) {
        const value = group[name]
        switch (type) {
          case SETTING_ATTRIBUTE: {
            buffer += ` ${name}="${value}"`
            break
          }
          case SETTING_EVENT: {
            buffer += ` on${name}`
            break
          }
          case SETTING_PROPERTY: {
            buffer += ` ${name}=${value}`
          }
        }
      }
    }

    if (this.namespace) {
      buffer += ` xmlns="${namespace}"`
    }

    return buffer
  }
  toOuterHTML(indent = "", key = "") {
    const { localName } = this
    const open = `<${localName}${this.serializeSettings(key)}>`
    const close = `</${localName}>`
    const innerHTML = this.toInnerHTML(`${indent}  `)

    if (innerHTML === "") {
      return `${indent}${open}${close}`
    } else {
      return `${indent}${open}${innerHTML}\n${indent}${close}`
    }
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
}

export const nodeNS = (
  namespace,
  localName,
  factList = noFacts,
  kidList = noKids
) => {
  for (
    var kids = [], descendantsCount = 0, index = 0;
    kidList.length > index;
    index++
  ) {
    var kid = kidList[index]
    descendantsCount += kid.descendantsCount || 0
    kids.push(kid)
  }
  descendantsCount += kids.length

  return new Node(
    localName,
    organizeFacts(factList),
    kids,
    namespace,
    descendantsCount
  )
}

export var node = nodeNS.bind(null, null)

// KEYED NODE

class KeyedNode {
  constructor(localName, facts, kids, namespace, descendantsCount) {
    this.nodeType = KEYED_NODE
    this.localName = localName
    this.settings = facts
    this.children = kids
    this.namespace = namespace
    this.descendantsCount = descendantsCount
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  serializeSettings(key = "") {
    return Node.prototype.serializeSettings.call(this, key)
  }
  toOuterHTML(indent = "", key = "") {
    return Node.prototype.toOuterHTML.call(this, indent, key)
  }
  toInnerHTML(indent = "") {
    let html = ""
    const { children } = this
    if (children.length > 0) {
      for (const [key, child] of children) {
        html += `\n${child.toOuterHTML(indent, key)}`
      }
    }
    return html
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

export const keyedNodeNS = (namespace, localName, factList, kidList) => {
  for (
    var kids = [], descendantsCount = 0, index = 0;
    kidList.length > index;
    index++
  ) {
    var kid = kidList[index]
    descendantsCount += kid[1].descendantsCount || 0
    kids.push(kid)
  }
  descendantsCount += kids.length

  return new KeyedNode(
    localName,
    organizeFacts(factList),
    kids,
    namespace,
    descendantsCount
  )
}

export var keyedNode = keyedNodeNS.bind(null, null)

// CUSTOM

class CustomNode {
  constructor(facts, model, render, diff) {
    this.nodeType = CUSTOM_NODE
    this.settings = facts
    this.model = model
    this.render = render
    this.diff = diff
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  serializeSettings(key = "") {
    return Node.prototype.serializeSettings.call(this, key)
  }
  toOuterHTML(indent = "", key = "") {
    const settings = key === "" ? ` key=${key}` : ""
    return `${indent}<custom-element${settings}><custom-element>`
  }
  toInnerHTML(indent = "") {
    return ""
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

export const custom = (factList, model, render, diff) =>
  new CustomNode(organizeFacts(factList), model, render, diff)

class Doc {
  constructor(title, body) {
    this.title = title
    this.body = body
  }
  map(tagger) {
    return new Doc(this.title, this.body.map(tagger))
  }
  toOuterHTML(indent = "", key = "") {
    const innerHTML = this.toInnerHTML(`${indent}  `)
    return `${indent}<html>\n${innerHTML}\n${indent}<\html>`
  }
  toInnerHTML(indent = "") {
    const head = `<head><title>${this.title}</title></head>`
    const body = this.body.toOuterHTML(indent)
    if (body === "") {
      return `${indent}${head}`
    } else {
      return `${indent}${head}\n${body}`
    }
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

export const doc = (title, body) => new Doc(title, body)

// MAP

class TaggerNode {
  constructor(tagger, node) {
    this.nodeType = TAGGED_NODE
    this.tagger = tagger
    this.node = node
    this.descendantsCount = 1 + (node.descendantsCount || 0)
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  toOuterHTML(indent = "", key = "") {
    return this.node.toOuterHTML(indent, key)
  }
  toInnerHTML(indent = "") {
    return this.node.toInnerHTML(indent)
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

export const map = (tagger, node) => new TaggerNode(tagger, node)

// LAZY

class Thunk {
  constructor(refs, thunk) {
    this.nodeType = THUNK_NODE
    this.refs = refs
    this.force = thunk
    this.node = undefined
  }
  map(tagger) {
    return new TaggerNode(tagger, this)
  }
  toNode() {
    const { node } = this
    if (node) {
      return node
    } else {
      const node = this.force()
      this.node = node
      return node
    }
  }
  toOuterHTML(indent = "", key = "") {
    return this.toNode().toOuterHTML(indent, key)
  }
  toInnerHTML(indent = "") {
    const node = (vNode.node = vNode.force())
    return this.toNode().toInnerHTML(indent)
  }
  get outerHTML() {
    return this.toOuterHTML()
  }
  get innerHTML() {
    return this.toInnerHTML()
  }
}

const thunk = (refs, thunk) => new Thunk(refs, thunk)

export var lazy = function(func, a) {
  return thunk([func, a], function() {
    return func(a)
  })
}

export var lazy2 = function(func, a, b) {
  return thunk([func, a, b], function() {
    return func(a, b)
  })
}

export var lazy3 = function(func, a, b, c) {
  return thunk([func, a, b, c], function() {
    return func(a, b, c)
  })
}

export var lazy4 = function(func, a, b, c, d) {
  return thunk([func, a, b, c, d], function() {
    return func(a, b, c, d)
  })
}

export var lazy5 = function(func, a, b, c, d, e) {
  return thunk([func, a, b, c, d, e], function() {
    return func(a, b, c, d, e)
  })
}

export var lazy6 = function(func, a, b, c, d, e, f) {
  return thunk([func, a, b, c, d, e, f], function() {
    return func(a, b, c, d, e, f)
  })
}

export var lazy7 = function(func, a, b, c, d, e, f, g) {
  return thunk([func, a, b, c, d, e, f, g], function() {
    return func(a, b, c, d, e, f, g)
  })
}

export var lazy8 = function(func, a, b, c, d, e, f, g, h) {
  return thunk([func, a, b, c, d, e, f, g, h], function() {
    return func(a, b, c, d, e, f, g, h)
  })
}

// FACTS

const notaggers = Object.freeze([])
export const on = function(key, handler) {
  return new EventHandler(key, handler, Normal, notaggers)
}

class VirtualDOMStyle {
  constructor(key, value) {
    this.nodeType = SETTING_STYLE
    this.key = key
    this.value = value
  }
  map(tag) {
    return this
  }
}

export var style = function(key, value) {
  return new VirtualDOMStyle(key, value)
}

class VirtualDOMProperty {
  constructor(key, value) {
    this.nodeType = SETTING_PROPERTY
    this.key = key
    this.value = value
  }
  map(tag) {
    return this
  }
}

export var property = function(key, value) {
  return new VirtualDOMProperty(key, value)
}

class VirtualDOMAttribute {
  constructor(key, value) {
    this.nodeType = SETTING_ATTRIBUTE
    this.key = key
    this.value = value
  }
  map(tag) {
    return this
  }
}

export const attribute = (key, value) => new VirtualDOMAttribute(key, value)

class VirtualDOMAttributeNS {
  constructor(namespace, key, value) {
    this.nodeType = SETTING_ATTRIBUTE_NS
    this.key = key
    this.value = { namespace: namespace, value: value }
  }
  map(tag) {
    return this
  }
}

export const attributeNS = (namespace, key, value) =>
  new VirtualDOMAttributeNS(namespace, key, value)

// XSS ATTACK VECTOR CHECKS

export function noScript(localName) {
  return localName == "script" ? "p" : localName
}

export function noOnOrFormAction(key) {
  return /^(on|formAction$)/i.test(key) ? "data-" + key : key
}

export function noInnerHtmlOrFormAction(key) {
  return key == "innerHTML" || key == "formAction" ? "data-" + key : key
}

export function noJavaScriptUri__PROD(value) {
  return /^javascript:/i.test(value.replace(/\s/g, "")) ? "" : value
}

export function noJavaScriptUri__DEBUG(value) {
  return /^javascript:/i.test(value.replace(/\s/g, ""))
    ? 'javascript:alert("This is an XSS vector. Please use ports or web components instead.")'
    : value
}

export function noJavaScriptOrHtmlUri__PROD(value) {
  return /^\s*(javascript:|data:text\/html)/i.test(value) ? "" : value
}

export function noJavaScriptOrHtmlUri__DEBUG(value) {
  return /^\s*(javascript:|data:text\/html)/i.test(value)
    ? 'javascript:alert("This is an XSS vector. Please use ports or web components instead.")'
    : value
}

// MAP FACTS

class EventHandler {
  constructor(type, decoder, eventPhase, taggers) {
    this.nodeType = SETTING_EVENT
    this.type = type
    this.decoder = decoder
    this.eventPhase = eventPhase
    this.taggers = taggers
  }
  map(tag) {
    const { type, decoder, eventPhase, taggers } = this
    return new EventHandler(type, decoder, eventPhase, [tag, ...taggers])
  }
  forwardEvent(event, eventTarget) {
    const { eventPhase, taggers } = this
    const result = this.decoder.decode(event)

    if (result instanceof Error) {
      return
    }

    const tag = this.eventPhase

    // 0 = Normal
    // 1 = MayStopPropagation
    // 2 = MayPreventDefault
    // 3 = Custom

    const { stopPropagation, preventDefault } = result
    if (stopPropagation) {
      event.stopPropagation()
    }

    if (preventDefault) {
      event.preventDefault()
    }

    let message = result.message
    for (let tag of this.taggers) {
      message = tag(message)
    }

    let currentTarget = eventTarget
    while (currentTarget.tagger) {
      const { tagger } = currentTarget
      if (typeof tagger == "function") {
        message = tagger(message)
      } else {
        for (let i = tagger.length; i--; ) {
          message = tagger[i](message)
        }
      }

      currentTarget = currentTarget.parent
    }

    if (stopPropagation) {
      currentTarget.sync(message) // stopPropagation implies isSync
    } else {
      currentTarget.send(message)
    }
  }
  equal(other) {
    return (
      this.type === other.type &&
      this.eventPhase === other.eventPhase &&
      this.decoder === other.decoder &&
      pairwiseRefEqual(this.taggers, other.taggers)
    )
  }
}

// ORGANIZE FACTS

function organizeFacts(factList) {
  for (
    var facts = {}, index = 0;
    factList.length > index;
    index++ // WHILE_CONS
  ) {
    var fact = factList[index]

    var nodeType = fact.nodeType

    switch (nodeType) {
      case SETTING_PROPERTY: {
        const { key, value } = fact
        if (key === "className") {
          addClass(facts, key, value)
        } else {
          facts[key] = value
        }
        break
      }
      case SETTING_STYLE: {
        const { key, value } = fact
        var subFacts = facts[nodeType] || (facts[nodeType] = {})
        subFacts[key] = value
        break
      }
      case SETTING_EVENT: {
        const { type, handler } = fact
        var subFacts = facts[nodeType] || (facts[nodeType] = {})
        subFacts[type] = fact
        break
      }
      case SETTING_ATTRIBUTE: {
        const { key, value } = fact
        var subFacts = facts[nodeType] || (facts[nodeType] = {})
        if (key === "class") {
          addClass(subFacts, key, value)
        } else {
          subFacts[key] = value
        }
        break
      }
      case SETTING_ATTRIBUTE_NS: {
        const { key, value } = fact
        var subFacts = facts[nodeType] || (facts[nodeType] = {})
        if (key === "class") {
          addClass(subFacts, key, value)
        } else {
          subFacts[key] = value
        }
        break
      }
    }
  }

  return facts
}

function addClass(object, key, newClass) {
  var classes = object[key]
  object[key] = classes ? classes + " " + newClass : newClass
}

// RENDER

function render(doc, vNode, eventNode) {
  var nodeType = vNode.nodeType

  if (nodeType === THUNK_NODE) {
    return render(doc, vNode.node || (vNode.node = vNode.force()), eventNode)
  }

  if (nodeType === TEXT) {
    return doc.createTextNode(vNode.text)
  }

  if (nodeType === TAGGED_NODE) {
    var subNode = vNode.node
    var tagger = vNode.tagger

    while (subNode.nodeType === TAGGED_NODE) {
      typeof tagger !== "object"
        ? (tagger = [tagger, subNode.tagger])
        : tagger.push(subNode.tagger)

      subNode = subNode.node
    }

    var subEventRoot = { tagger: tagger, parent: eventNode }
    var domNode = render(doc, subNode, subEventRoot)
    domNode.elm_event_node_ref = subEventRoot
    return domNode
  }

  if (nodeType === CUSTOM_NODE) {
    var domNode = vNode.render(doc, vNode.model)
    applyFacts(domNode, eventNode, vNode.settings)
    return domNode
  }

  if (nodeType === CUSTOM_ELEMENT) {
    const { elementConstructor, localName, options } = vNode
    const { customElements } = document.defaultView
    const registration = customElements.get(localName)
    if (registration) {
      if (registration !== elementConstructor.registration) {
        Object.setPrototypeOf(
          registration.prototype,
          elementConstructor.prototype
        )
        elementConstructor.registration = registration
        registration.proto = elementConstructor.prototype
      }
    } else {
      const registration = class extends vNode.elementConstructor {
        constructor(...args) {
          super(...args)
        }
        connectedCallback(...args) {
          if (registration.proto.connectedCallback) {
            super.connectedCallback(...args)
          }
        }
        disconnectedCallback(...args) {
          if (registration.proto.disconnectedCallback) {
            super.disconnectedCallback(...args)
          }
        }
        adoptedCallback(...args) {
          if (registration.proto.adoptedCallback) {
            super.adoptedCallback(...args)
          }
        }
        attributeChangedCallback(...args) {
          if (registration.proto.attributeChangedCallback) {
            super.attributeChangedCallback(...args)
          }
        }
      }
      registration.proto = vNode.elementConstructor.prototype
      elementConstructor.registration = registration
      customElements.define(localName, registration, options)
    }
  }

  // at this point `nodeType` must be NODE or KEYED_NODE

  var domNode = vNode.namespace
    ? doc.createElementNS(vNode.namespace, vNode.localName)
    : doc.createElement(vNode.localName)

  const { onnavigate } = doc.defaultView
  if (onnavigate && vNode.localName == "a") {
    domNode.addEventListener("click", onnavigate)
  }

  applyFacts(domNode, eventNode, vNode.settings)

  for (var kids = vNode.children, i = 0; i < kids.length; i++) {
    appendChild(
      domNode,
      render(doc, nodeType === NODE ? kids[i] : kids[i][1], eventNode)
    )
  }

  return domNode
}

// APPLY FACTS

function applyFacts(domNode, eventNode, facts) {
  for (var key in facts) {
    var value = facts[key]

    switch (key) {
      case SETTING_STYLE: {
        applyStyles(domNode, value)
        break
      }
      case SETTING_EVENT: {
        applyEvents(domNode, eventNode, value)
        break
      }
      case SETTING_ATTRIBUTE: {
        applyAttrs(domNode, value)
        break
      }
      case SETTING_ATTRIBUTE_NS: {
        applyAttrsNS(domNode, value)
        break
      }
      default: {
        switch (key) {
          case "value": {
            if (domNode.value !== value) {
              domNode.value = value
            }
            break
          }
          case "checked":
            break
          case domNode[key]:
            break
          default: {
            domNode[key] = value
            break
          }
        }
      }
    }
  }
}

// APPLY STYLES

function applyStyles(domNode, styles) {
  var domNodeStyle = domNode.style

  for (var key in styles) {
    domNodeStyle[key] = styles[key]
  }
}

// APPLY ATTRS

function applyAttrs(domNode, attrs) {
  for (var key in attrs) {
    var value = attrs[key]
    value != null
      ? domNode.setAttribute(key, value)
      : domNode.removeAttribute(key)
  }
}

// APPLY NAMESPACED ATTRS

function applyAttrsNS(domNode, nsAttrs) {
  for (var key in nsAttrs) {
    var pair = nsAttrs[key]
    var namespace = pair.namespace
    var value = pair.value

    value
      ? domNode.setAttributeNS(namespace, key, value)
      : domNode.removeAttributeNS(namespace, key)
  }
}

// APPLY EVENTS

class EventRouter {
  constructor() {
    this.handlers = {}
    this.handleEvent = this.handleEvent.bind(this)
    this.target = null
  }
  handleEvent(event) {
    this.handlers[event.type].forwardEvent(event, this.target)
  }
}

function applyEvents(domNode, eventNode, events) {
  const router =
    domNode.eventRouter || (domNode.eventRouter = new EventRouter())
  router.target = eventNode
  const { handlers, handleEvent } = router

  for (var key in events) {
    const oldHandler = handlers[key]
    const newHandler = events[key]

    if (!newHandler) {
      domNode.removeEventListener(key, handleEvent)
      handlers[key] = null
      continue
    }

    if (oldHandler) {
      if (oldHandler.type === newHandler.type) {
        handlers[key] = newHandler
        continue
      } else {
        domNode.removeEventListener(key, handleEvent)
      }
    }

    const { eventPhase } = newHandler
    handlers[key] = newHandler
    const passive = eventPhase == Normal || eventPhase == MayStopPropagation
    domNode.addEventListener(key, handleEvent, passiveSupported && { passive })
  }
}

// PASSIVE EVENTS

var passiveSupported

try {
  window.addEventListener(
    "t",
    null,
    Object.defineProperty({}, "passive", {
      get: function() {
        passiveSupported = true
      }
    })
  )
} catch (e) {}

// EVENT HANDLERS

const Normal = 0
const MayStopPropagation = 1
const MayPreventDefault = 2
const Custom = 3

// DIFF

// TODO: Should we do patches like in iOS?
//
// type Patch
//   = At Int Patch
//   | Batch (List Patch)
//   | Change ...
//
// How could it not be better?
//
export function diff(x, y) {
  var patches = []
  diffHelp(x, y, patches, 0)
  return patches
}

function pushPatch(patches, op, index, data) {
  var patch = {
    op: op,
    index: index,
    changes: data,
    domNode: undefined,
    eventNode: undefined
  }
  patches.push(patch)
  return patch
}

function diffHelp(x, y, patches, index) {
  if (x === y) {
    return
  }

  var xType = x.nodeType
  var yType = y.nodeType

  // Bail if you run into different types of nodes. Implies that the
  // structure has changed significantly and it's not worth a diff.
  if (xType !== yType) {
    if (xType === NODE && yType === KEYED_NODE) {
      y = dekey(y)
      yType = NODE
    } else {
      pushPatch(patches, OP_REDRAW, index, y)
      return
    }
  }

  // Now we know that both nodes are the same type.
  switch (yType) {
    case THUNK_NODE: {
      var xRefs = x.refs
      var yRefs = y.refs
      var i = xRefs.length
      var same = i === yRefs.length
      while (same && i--) {
        same = xRefs[i] === yRefs[i]
      }
      if (same) {
        y.node = x.node
        return
      }
      y.node = y.force()
      var subPatches = []
      diffHelp(x.node, y.node, subPatches, 0)
      subPatches.length > 0 && pushPatch(patches, OP_THUNK, index, subPatches)
      return
    }
    case TAGGED_NODE: {
      // gather nested taggers
      var xTaggers = x.tagger
      var yTaggers = y.tagger
      var nesting = false

      var xSubNode = x.node
      while (xSubNode.nodeType === TAGGED_NODE) {
        nesting = true

        typeof xTaggers !== "object"
          ? (xTaggers = [xTaggers, xSubNode.tagger])
          : xTaggers.push(xSubNode.tagger)

        xSubNode = xSubNode.node
      }

      var ySubNode = y.node
      while (ySubNode.nodeType === TAGGED_NODE) {
        nesting = true

        typeof yTaggers !== "object"
          ? (yTaggers = [yTaggers, ySubNode.tagger])
          : yTaggers.push(ySubNode.tagger)

        ySubNode = ySubNode.node
      }

      // Just bail if different numbers of taggers. This implies the
      // structure of the virtual DOM has changed.
      if (nesting && xTaggers.length !== yTaggers.length) {
        pushPatch(patches, OP_REDRAW, index, y)
        return
      }

      // check if taggers are "the same"
      if (
        nesting ? !pairwiseRefEqual(xTaggers, yTaggers) : xTaggers !== yTaggers
      ) {
        pushPatch(patches, OP_TAGGER, index, yTaggers)
      }

      // diff everything below the taggers
      diffHelp(xSubNode, ySubNode, patches, index + 1)
      return
    }
    case TEXT: {
      if (x.text !== y.text) {
        pushPatch(patches, OP_TEXT, index, y.text)
      }
      return
    }
    case NODE: {
      diffNodes(x, y, patches, index, diffKids)
      return
    }
    case KEYED_NODE: {
      diffNodes(x, y, patches, index, diffKeyedKids)
      return
    }
    case CUSTOM_ELEMENT: {
      if (x.elementConstructor !== y.elementConstructor) {
        pushPatch(patches, OP_REDRAW, index, y)
        return
      } else {
        diffNodes(x, y, patches, index, diffKids)
        return
      }
    }

    case CUSTOM_NODE:
      if (x.render !== y.render) {
        pushPatch(patches, OP_REDRAW, index, y)
        return
      }

      var factsDiff = diffFacts(x.settings, y.settings)
      factsDiff && pushPatch(patches, OP_FACTS, index, factsDiff)

      var patch = y.diff(x.model, y.model)
      patch && pushPatch(patches, OP_CUSTOM, index, patch)

      return
  }
}

// assumes the incoming arrays are the same length
function pairwiseRefEqual(as, bs) {
  for (var i = 0; i < as.length; i++) {
    if (as[i] !== bs[i]) {
      return false
    }
  }

  return true
}

function diffNodes(x, y, patches, index, diffKids) {
  // Bail if obvious indicators have changed. Implies more serious
  // structural changes such that it's not worth it to diff.
  if (x.localName !== y.localName || x.namespace !== y.namespace) {
    pushPatch(patches, OP_REDRAW, index, y)
    return
  }

  var factsDiff = diffFacts(x.settings, y.settings)
  factsDiff && pushPatch(patches, OP_FACTS, index, factsDiff)

  diffKids(x, y, patches, index)
}

// DIFF FACTS

// TODO Instead of creating a new diff object, it's possible to just test if
// there *is* a diff. During the actual patch, do the diff again and make the
// modifications directly. This way, there's no new allocations. Worth it?
function diffFacts(x, y, category) {
  var diff

  // look for changes and removals
  for (var xKey in x) {
    if (
      xKey === SETTING_STYLE ||
      xKey === SETTING_EVENT ||
      xKey === SETTING_ATTRIBUTE ||
      xKey === SETTING_ATTRIBUTE_NS
    ) {
      var subDiff = diffFacts(x[xKey], y[xKey] || {}, xKey)
      if (subDiff) {
        diff = diff || {}
        diff[xKey] = subDiff
      }
      continue
    }

    // remove if not in the new facts
    if (!(xKey in y)) {
      diff = diff || {}
      diff[xKey] = !category
        ? typeof x[xKey] === "string"
          ? ""
          : null
        : category === SETTING_STYLE
        ? ""
        : category === SETTING_EVENT || category === SETTING_ATTRIBUTE
        ? undefined
        : { namespace: x[xKey].namespace, value: undefined }

      continue
    }

    var xValue = x[xKey]
    var yValue = y[xKey]

    // reference equal, so don't worry about it
    if (
      (xValue === yValue && xKey !== "value" && xKey !== "checked") ||
      (category === SETTING_EVENT && xValue.equal(yValue))
    ) {
      continue
    }

    diff = diff || {}
    diff[xKey] = yValue
  }

  // add new stuff
  for (var yKey in y) {
    if (!(yKey in x)) {
      diff = diff || {}
      diff[yKey] = y[yKey]
    }
  }

  return diff
}

// DIFF KIDS

function diffKids(xParent, yParent, patches, index) {
  var xKids = xParent.children
  var yKids = yParent.children

  var xLen = xKids.length
  var yLen = yKids.length

  // FIGURE OUT IF THERE ARE INSERTS OR REMOVALS

  if (xLen > yLen) {
    pushPatch(patches, OP_REMOVE_LAST, index, {
      offset: yLen,
      diff: xLen - yLen
    })
  } else if (xLen < yLen) {
    pushPatch(patches, OP_APPEND, index, {
      offset: xLen,
      children: yKids
    })
  }

  // PAIRWISE DIFF EVERYTHING ELSE

  for (var minLen = xLen < yLen ? xLen : yLen, i = 0; i < minLen; i++) {
    var xKid = xKids[i]
    diffHelp(xKid, yKids[i], patches, ++index)
    index += xKid.descendantsCount || 0
  }
}

// KEYED DIFF

function diffKeyedKids(xParent, yParent, patches, rootIndex) {
  var localPatches = []

  var changes = {} // Dict String Entry
  var inserts = [] // Array { index : Int, entry : Entry }
  // type Entry = { tag : String, vnode : VNode, index : Int, data : _ }

  var xKids = xParent.children
  var yKids = yParent.children
  var xLen = xKids.length
  var yLen = yKids.length
  var xIndex = 0
  var yIndex = 0

  var index = rootIndex

  while (xIndex < xLen && yIndex < yLen) {
    var x = xKids[xIndex]
    var y = yKids[yIndex]

    var [xKey, xNode] = x
    var [yKey, yNode] = y

    // check if keys match

    if (xKey === yKey) {
      index++
      diffHelp(xNode, yNode, localPatches, index)
      index += xNode.descendantsCount || 0

      xIndex++
      yIndex++
      continue
    }

    // look ahead 1 to detect insertions and removals.

    var xNext = xKids[xIndex + 1]
    var yNext = yKids[yIndex + 1]

    if (xNext) {
      var xNextKey = xNext[0]
      var xNextNode = xNext[1]
      var oldMatch = yKey === xNextKey
    }

    if (yNext) {
      var yNextKey = yNext[0]
      var yNextNode = yNext[1]
      var newMatch = xKey === yNextKey
    }

    // swap x and y
    if (newMatch && oldMatch) {
      index++
      diffHelp(xNode, yNextNode, localPatches, index)
      insertNode(changes, localPatches, xKey, yNode, yIndex, inserts)
      index += xNode.descendantsCount || 0

      index++
      removeNode(changes, localPatches, xKey, xNextNode, index)
      index += xNextNode.descendantsCount || 0

      xIndex += 2
      yIndex += 2
      continue
    }

    // insert y
    if (newMatch) {
      index++
      insertNode(changes, localPatches, yKey, yNode, yIndex, inserts)
      diffHelp(xNode, yNextNode, localPatches, index)
      index += xNode.descendantsCount || 0

      xIndex += 1
      yIndex += 2
      continue
    }

    // remove x
    if (oldMatch) {
      index++
      removeNode(changes, localPatches, xKey, xNode, index)
      index += xNode.descendantsCount || 0

      index++
      diffHelp(xNextNode, yNode, localPatches, index)
      index += xNextNode.descendantsCount || 0

      xIndex += 2
      yIndex += 1
      continue
    }

    // remove x, insert y
    if (xNext && xNextKey === yNextKey) {
      index++
      removeNode(changes, localPatches, xKey, xNode, index)
      insertNode(changes, localPatches, yKey, yNode, yIndex, inserts)
      index += xNode.descendantsCount || 0

      index++
      diffHelp(xNextNode, yNextNode, localPatches, index)
      index += xNextNode.descendantsCount || 0

      xIndex += 2
      yIndex += 2
      continue
    }

    break
  }

  // eat up any remaining nodes with removeNode and insertNode

  while (xIndex < xLen) {
    index++
    var x = xKids[xIndex]
    var xNode = x[1]
    removeNode(changes, localPatches, x[0], xNode, index)
    index += xNode.descendantsCount || 0
    xIndex++
  }

  while (yIndex < yLen) {
    var endInserts = endInserts || []
    var y = yKids[yIndex]
    insertNode(changes, localPatches, y[0], y[1], undefined, endInserts)
    yIndex++
  }

  if (localPatches.length > 0 || inserts.length > 0 || endInserts) {
    pushPatch(patches, OP_REORDER, rootIndex, {
      subPatches: localPatches,
      inserts: inserts,
      endInserts: endInserts
    })
  }
}

// CHANGES FROM KEYED DIFF

var POSTFIX = "_elmW6BL"

function insertNode(changes, localPatches, key, vnode, yIndex, inserts) {
  var entry = changes[key]

  // never seen this key before
  if (!entry) {
    entry = {
      op: OP_INSERT,
      vnode: vnode,
      index: yIndex,
      changes: undefined
    }

    inserts.push({ index: yIndex, entry: entry })
    changes[key] = entry

    return
  }

  // this key was removed earlier, a match!
  if (entry.op === OP_DELETE) {
    inserts.push({ index: yIndex, entry: entry })

    entry.op = OP_MOVE
    var subPatches = []
    diffHelp(entry.vnode, vnode, subPatches, entry.index)
    entry.index = yIndex
    entry.changes.changes = {
      subPatches: subPatches,
      entry: entry
    }

    return
  }

  // this key has already been inserted or moved, a duplicate!
  insertNode(changes, localPatches, key + POSTFIX, vnode, yIndex, inserts)
}

function removeNode(changes, localPatches, key, vnode, index) {
  var entry = changes[key]

  // never seen this key before
  if (!entry) {
    var patch = pushPatch(localPatches, OP_REMOVE, index, undefined)

    changes[key] = {
      op: OP_DELETE,
      vnode: vnode,
      index: index,
      changes: patch
    }

    return
  }

  // this key was inserted earlier, a match!
  if (entry.op === OP_INSERT) {
    entry.op = OP_MOVE
    var subPatches = []
    diffHelp(vnode, entry.vnode, subPatches, index)

    pushPatch(localPatches, OP_REMOVE, index, {
      subPatches: subPatches,
      entry: entry
    })

    return
  }

  // this key has already been removed or moved, a duplicate!
  removeNode(changes, localPatches, key + POSTFIX, vnode, index)
}

// ADD DOM NODES
//
// Each DOM node has an "index" assigned in order of traversal. It is important
// to minimize our crawl over the actual DOM, so these indexes (along with the
// descendantsCount of virtual nodes) let us skip touching entire subtrees of
// the DOM if we know there are no patches there.

function addDomNodes(domNode, vNode, patches, eventNode) {
  addDomNodesHelp(
    domNode,
    vNode,
    patches,
    0,
    0,
    vNode.descendantsCount,
    eventNode
  )
}

// assumes `patches` is non-empty and indexes increase monotonically.
function addDomNodesHelp(domNode, vNode, patches, i, low, high, eventNode) {
  var patch = patches[i]
  var index = patch.index

  while (index === low) {
    var patchType = patch.op

    if (patchType === OP_THUNK) {
      addDomNodes(domNode, vNode.node, patch.changes, eventNode)
    } else if (patchType === OP_REORDER) {
      patch.domNode = domNode
      patch.eventNode = eventNode

      var subPatches = patch.changes.subPatches
      if (subPatches.length > 0) {
        addDomNodesHelp(domNode, vNode, subPatches, 0, low, high, eventNode)
      }
    } else if (patchType === OP_REMOVE) {
      patch.domNode = domNode
      patch.eventNode = eventNode

      var data = patch.changes
      if (data) {
        data.entry.changes = domNode
        var subPatches = data.subPatches
        if (subPatches.length > 0) {
          addDomNodesHelp(domNode, vNode, subPatches, 0, low, high, eventNode)
        }
      }
    } else {
      patch.domNode = domNode
      patch.eventNode = eventNode
    }

    i++

    if (!(patch = patches[i]) || (index = patch.index) > high) {
      return i
    }
  }

  var nodeType = vNode.nodeType

  if (nodeType === TAGGED_NODE) {
    var subNode = vNode.node

    while (subNode.nodeType === TAGGED_NODE) {
      subNode = subNode.node
    }

    return addDomNodesHelp(
      domNode,
      subNode,
      patches,
      i,
      low + 1,
      high,
      domNode.elm_event_node_ref
    )
  }

  // tag must be NODE or KEYED_NODE at this point

  var vKids = vNode.children
  var childNodes = domNode.childNodes
  for (var j = 0; j < vKids.length; j++) {
    low++
    var vKid = nodeType === NODE ? vKids[j] : vKids[j][1]
    var nextLow = low + (vKid.descendantsCount || 0)
    if (low <= index && index <= nextLow) {
      i = addDomNodesHelp(
        childNodes[j],
        vKid,
        patches,
        i,
        low,
        nextLow,
        eventNode
      )
      if (!(patch = patches[i]) || (index = patch.index) > high) {
        return i
      }
    }
    low = nextLow
  }
  return i
}

// APPLY PATCHES

export const patch = (rootDomNode, oldVirtualNode, patches, eventNode) => {
  if (patches.length === 0) {
    return rootDomNode
  }

  addDomNodes(rootDomNode, oldVirtualNode, patches, eventNode)
  return applyPatchesHelp(rootDomNode, patches)
}

function applyPatchesHelp(rootDomNode, patches) {
  for (var i = 0; i < patches.length; i++) {
    var patch = patches[i]
    var localDomNode = patch.domNode
    var newNode = applyPatch(localDomNode, patch)
    if (localDomNode === rootDomNode) {
      rootDomNode = newNode
    }
  }
  return rootDomNode
}

function applyPatch(domNode, patch) {
  const doc = domNode.ownerDocument
  switch (patch.op) {
    case OP_REDRAW: {
      return applyPatchRedraw(domNode, patch.changes, patch.eventNode)
    }
    case OP_FACTS: {
      applyFacts(domNode, patch.eventNode, patch.changes)
      return domNode
    }
    case OP_TEXT: {
      domNode.replaceData(0, domNode.length, patch.changes)
      return domNode
    }
    case OP_THUNK: {
      return applyPatchesHelp(domNode, patch.changes)
    }
    case OP_TAGGER: {
      if (domNode.elm_event_node_ref) {
        domNode.elm_event_node_ref.tagger = patch.changes
      } else {
        domNode.elm_event_node_ref = {
          tagger: patch.changes,
          parent: patch.eventNode
        }
      }
      return domNode
    }
    case OP_REMOVE_LAST: {
      var data = patch.changes
      for (var i = 0; i < data.diff; i++) {
        domNode.removeChild(domNode.childNodes[data.offset])
      }
      return domNode
    }
    case OP_APPEND: {
      var data = patch.changes
      var kids = data.children
      var i = data.offset
      var theEnd = domNode.childNodes[i]
      for (; i < kids.length; i++) {
        domNode.insertBefore(render(doc, kids[i], patch.eventNode), theEnd)
      }
      return domNode
    }
    case OP_REMOVE: {
      var data = patch.changes
      if (!data) {
        domNode.parentNode.removeChild(domNode)
        return domNode
      }
      var entry = data.entry
      if (typeof entry.index !== "undefined") {
        domNode.parentNode.removeChild(domNode)
      }
      entry.changes = applyPatchesHelp(domNode, data.subPatches)
      return domNode
    }
    case OP_REORDER: {
      return applyPatchReorder(domNode, patch)
    }
    case OP_CUSTOM: {
      return patch.changes(domNode)
    }
    default: {
      throw TypeError("Unknown operation")
    }
  }
}

function applyPatchRedraw(domNode, vNode, eventNode) {
  const doc = domNode.ownerDocument
  var parentNode = domNode.parentNode
  var newNode = render(doc, vNode, eventNode)

  if (!newNode.elm_event_node_ref) {
    newNode.elm_event_node_ref = domNode.elm_event_node_ref
  }

  if (parentNode && newNode !== domNode) {
    parentNode.replaceChild(newNode, domNode)
  }
  return newNode
}

function applyPatchReorder(domNode, patch) {
  const doc = domNode.ownerDocument
  var data = patch.changes

  // remove end inserts
  var frag = applyPatchReorderEndInsertsHelp(doc, data.endInserts, patch)

  // removals
  domNode = applyPatchesHelp(domNode, data.subPatches)

  // inserts
  var inserts = data.inserts
  for (var i = 0; i < inserts.length; i++) {
    var insert = inserts[i]
    var entry = insert.entry
    var node =
      entry.op === OP_MOVE
        ? entry.changes
        : render(doc, entry.vnode, patch.eventNode)
    domNode.insertBefore(node, domNode.childNodes[insert.index])
  }

  // add end inserts
  if (frag) {
    appendChild(domNode, frag)
  }

  return domNode
}

function applyPatchReorderEndInsertsHelp(doc, endInserts, patch) {
  if (!endInserts) {
    return
  }

  var frag = doc.createDocumentFragment()
  for (var i = 0; i < endInserts.length; i++) {
    var insert = endInserts[i]
    var entry = insert.entry
    appendChild(
      frag,
      entry.op === OP_MOVE
        ? entry.changes
        : render(doc, entry.vnode, patch.eventNode)
    )
  }
  return frag
}

export function virtualize(root) {
  // TEXT NODES

  if (root.nodeType === 3) {
    return text(root.textContent)
  }

  // WEIRD NODES

  if (root.nodeType !== 1) {
    return text("")
  }

  // ELEMENT NODES

  var factList = []
  var attrs = root.attributes
  for (var i = attrs.length; i--; ) {
    var attr = attrs[i]
    var name = attr.name
    var value = attr.value

    switch (name) {
      case "style":
        break
      default:
        factList.push(attribute(name, value))
    }
  }

  const rules = root.style
  for (var i = rules.length; i--; ) {
    var name = rules[i]
    var value = rules[name]
    factList.push(style(name, value))
  }

  var localName = root.localName
  var kidList = []
  var kids = root.childNodes

  for (var i = kids.length; i--; ) {
    kidList.unshift(virtualize(kids[i]))
  }
  return node(localName, factList, kidList)
}

function dekey(keyedNode) {
  var keyedKids = keyedNode.children
  var len = keyedKids.length
  var kids = new Array(len)
  for (var i = 0; i < len; i++) {
    kids[i] = keyedKids[i][1]
  }

  return {
    nodeType: NODE,
    localName: keyedNode.localName,
    settings: keyedNode.settings,
    children: kids,
    namespace: keyedNode.namespace,
    descendantsCount: keyedNode.descendantsCount
  }
}


================================================
FILE: src/Widget.js
================================================
import { diff, patch, virtualize } from "./VirtualDOM.js"

/**
 * @typedef {import('./Task').ThreadID} ThreadID
 * @typedef {import('./Task').Thread} Thread
 */

/**
 * @template T
 * @typedef {import('./Task').Main<T>} Main
 */

/**
 * @template T
 * @typedef {import('./Task').Sync<T>} Sync
 */

/**
 * @template T
 * @implements {Main<T>}
 */
export class MainThread {
  /**
   * @param {import('./Task').Sync<T>} root
   */
  constructor(root) {
    this.root = root
    /**
     * @private
     * @type {Record<string, Thread>}
     */
    this.threads
    /**
     * @private
     * @type {number}
     */
    this.threadID
  }

  /**
   * Sends a message to this program
   *
   * @param {T} message
   */
  async send(message) {
    await 0
    this.root.sync(message)
  }

  /**
   * @param {T} message
   */
  sync(message) {
    this.root.sync(message)
  }

  /**
   * @param {Thread} thread
   * @returns {ThreadID}
   */
  link(thread) {
    if (this.threadID == null) {
      this.threadID = 0
      this.threads = {}
    }

    const id = `@${++this.threadID}`
    this.threads[id] = thread
    // @ts-expect-error - string isn't ThreadID
    return id
  }

  /**
   * @param {Thread} thread
   */
  unlink(thread) {
    const { threads } = this
    if (threads) {
      for (const id in threads) {
        if (thread === threads[id]) {
          delete threads[id]
          break
        }
      }
    }
  }

  /**
   * @param {ThreadID} threadID
   */
  linked(threadID) {
    const { threads } = this
    if (threads) {
      return threads[threadID]
    }
    return undefined
  }
}

/**
 * @template Message, State
 * @typedef {import('./Task').Transaction<Message, State>} Transaction
 */

/**
 * @template T, State, View, Target
 */
export class Widget {
  constructor() {
    /** @type {MainThread<T>} */
    this.thread
    /** @type {State} */
    this.state
    /** @type {(message:T, state:State) => Transaction<T, State>} */
    this.update
    /** @type {(state:State) => View} */
    this.view
    /** @type {Target} */
    this.root
    /** @type {View} */
    this.node
  }
  get version() {
    const stack = new Error().stack || ""
    const start = stack.indexOf("+")
    const end = stack.indexOf("/", start)
    return stack.slice(start, end)
  }

  /**
   * @template T
   * @param {Sync<T>} self
   * @returns {MainThread<T>}
   */
  static fork(self /*: Sync<a> */) /*: MainThread<a> */ {
    return new MainThread(self)
  }

  /**
   *
   * @param {T} message
   */
  sync(message) {
    this.transact(this.update(message, this.state))
  }

  /**
   * @param {Transaction<T, State>} transaction
   */
  transact([state, fx] /*: Transaction<a, model> */) {
    this.state = state
    this.render(state)
    fx.perform(this.thread)
  }

  /**
   * @param {State} _state
   */
  render(_state) {}

  /**
   * @param {Target} _root
   * @returns {View}
   */
  mount(_root) {
    return this.node
  }
  toJSON() {
    return this.state
  }
}

/**
 * @template T
 * @typedef {import('./VirtualDOM').Node<T>} Node
 */

/**
 * @template T, State
 * @extends {Widget<T, State, Node<T>, Element>}
 */
class ElementWidget extends Widget {
  /**
   * @param {Element} root
   * @returns {Node<T>}
   */
  mount(root) {
    return virtualize(root)
  }

  /**
   * @param {State} state
   */
  render(state) {
    const newNode = this.view(state)
    const renderedNode = this.node
    const delta = diff(renderedNode, newNode)
    patch(this.root, renderedNode, delta, this.thread)
    this.node = newNode
  }
}

/**
 * @template Message, State, View, Options
 * @typedef {import('./Program').Program<Message, State, View, Options>} Program
 */
/**
 * @template Message, State, Options
 * @param {Program<Message, State, Node<Message>, Options>} program
 * @param {Options} options
 * @param {Element} root
 * @returns {Widget<Message, State, Node<Message>, Element>}
 */
export const spawn = ({ init, update, view }, options, root) => {
  const self = new ElementWidget()
  self.thread = Widget.fork(self)
  self.update = update
  self.view = view
  self.root = root
  self.node = self.mount(root)
  self.transact(init(options))
  return self
}


================================================
FILE: src/lib.js
================================================
import * as DOM from "./VirtualDOM.js"
import * as Element from "./Element.js"
import * as Attribute from "./Attribute.js"

import * as Widget from "./Widget.js"
import * as Document from "./Document.js"
import * as Application from "./Application.js"

import * as Effect from "./Effect.js"
import { identity, unreachable, always, nothing } from "./Basics.js"

export {
  // DOM
  DOM,
  Element,
  Attribute,
  // Spawnining VirtualDOM
  Widget,
  Document,
  Application,
  // Effect System
  Effect,
  // Utilities
  identity,
  unreachable,
  always,
  nothing
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */
    "incremental": true,                              /* Enable incremental compilation */
    "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./dist",                          /* Specify the folder for .tsbuildinfo incremental compilation files. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "ES2020",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
    // "reactNamespace": "",                             /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */

    /* Modules */
    "module": "ES2020",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like `./node_modules/@types`. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "resolveJsonModule": true,                        /* Enable importing .json files */
    // "noResolve": true,                                /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
    "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
    "outDir": "./dist/",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like `__extends` in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing `const enum` declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied `any` type.. */
    // "strictNullChecks": true,                         /* When type checking, take into account `null` and `undefined`. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when `this` is given the type `any`. */
    // "useUnknownInCatchVariables": true,               /* Type catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when a local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": [
    "src",
    "test"
  ]
}
Download .txt
gitextract_p4d61kwm/

├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── Readme.md
├── package.json
├── src/
│   ├── Application.js
│   ├── Attribute.js
│   ├── Basics.js
│   ├── Document.js
│   ├── Effect.js
│   ├── Element.js
│   ├── Program.ts
│   ├── Task.ts
│   ├── VirtualDOM.d.ts
│   ├── VirtualDOM.js
│   ├── Widget.js
│   └── lib.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (221 symbols across 8 files)

FILE: src/Application.js
  class ApplicationWidget (line 11) | class ApplicationWidget extends DocumentWidget {
    method constructor (line 16) | constructor(router) {
    method getURL (line 22) | getURL() {
    method handleEvent (line 28) | handleEvent(event) {
    method addListeners (line 69) | addListeners(document) {

FILE: src/Document.js
  class DocumentWidget (line 28) | class DocumentWidget extends Widget {
    method root (line 34) | static root(document) {
    method mount (line 45) | mount(root) {
    method fork (line 55) | fork(root) {
    method render (line 64) | render(state) {

FILE: src/Effect.js
  class None (line 11) | class None {
    method perform (line 12) | perform() {}
    method map (line 13) | map() {
  class Send (line 28) | class Send {
    method constructor (line 33) | constructor(message) {
    method perform (line 42) | perform(main) {
    method map (line 50) | map(tag) {
  class FX (line 58) | class FX {
    method constructor (line 64) | constructor(task, success, failure) {
    method execute (line 83) | async execute(main) {
    method perform (line 101) | perform(main) {
    method map (line 110) | map(tag) {
  class Batch (line 119) | class Batch {
    method constructor (line 124) | constructor(effects) {
    method perform (line 134) | perform(main) {
    method map (line 145) | map(tag) {
  class Tagged (line 160) | class Tagged {
    method constructor (line 165) | constructor(fx, tag) {
    method perform (line 184) | perform(main) {
    method link (line 192) | link(thread) {
    method unlink (line 198) | unlink(thread) {
    method linked (line 204) | linked(id) {
    method send (line 210) | send(message) {
    method map (line 218) | map(tag) {

FILE: src/Program.ts
  type Program (line 3) | interface Program<Message, State, View, Options> {
  type Router (line 9) | interface Router<Message> {
  type Application (line 14) | interface Application<Message, State, View, Options>
  type DocumentView (line 18) | interface DocumentView<T> {
  type RenderedDocument (line 23) | interface RenderedDocument<T> {

FILE: src/Task.ts
  type Phantom (line 1) | type Phantom<T> = T & { readonly kind: unique symbol }
  type ThreadID (line 3) | type ThreadID = Phantom<string>
  type Thread (line 5) | interface Thread {
  type Port (line 9) | interface Port<T> {
  type Main (line 13) | interface Main<message> extends Port<message> {
  type IO (line 19) | interface IO<T> {
  type Transaction (line 22) | type Transaction<message, state> = [state, IO<message>]
  type Sync (line 24) | interface Sync<T> {
  type Effect (line 28) | interface Effect<T> extends IO<T> {
  type AsyncResult (line 32) | interface AsyncResult<X, T> {
  type Task (line 36) | interface Task<X, T> {

FILE: src/VirtualDOM.d.ts
  type Doc (line 2) | interface Doc<T> {
  class Node (line 9) | class Node<T> {
  class Attribute (line 14) | class Attribute<T> {
  type Keyed (line 17) | type Keyed<T> = [string, T]
  type EventSource (line 24) | type EventSource<T> = Attribute<T> | Node<T>
  type EventNode (line 26) | type EventNode<E extends EventSource<unknown>> = E extends EventSource<i...
  type Decoder (line 76) | interface Decoder<In, Out> {
  type EncodedEvent (line 80) | type EncodedEvent =
  type DecodedEvent (line 87) | interface DecodedEvent<T> {
  type EventDecoder (line 93) | interface EventDecoder<T>
  class Delta (line 101) | class Delta {}

FILE: src/VirtualDOM.js
  constant TEXT (line 3) | const TEXT = "VirtualDOM.Text"
  constant NODE (line 4) | const NODE = "VirtualDOM.Node"
  constant KEYED_NODE (line 5) | const KEYED_NODE = "VirtualDOM.Keyed.Node"
  constant CUSTOM_NODE (line 6) | const CUSTOM_NODE = "VirtualDOM.Custom"
  constant CUSTOM_ELEMENT (line 7) | const CUSTOM_ELEMENT = "VirtualDOM.CustomElement"
  constant TAGGED_NODE (line 8) | const TAGGED_NODE = "VirtualDOM.Tagger"
  constant THUNK_NODE (line 9) | const THUNK_NODE = "VirtualDOM.Thunk"
  constant OP_REDRAW (line 11) | const OP_REDRAW = "VirtualDOM.OP.Redraw"
  constant OP_THUNK (line 12) | const OP_THUNK = "VirtualDOM.OP.Thunk"
  constant OP_TAGGER (line 13) | const OP_TAGGER = "VirtualDOM.OP.Tagger"
  constant OP_TEXT (line 14) | const OP_TEXT = "VirtualDOM.OP.Text"
  constant OP_FACTS (line 15) | const OP_FACTS = "VirtualDOM.OP.Facts"
  constant OP_CUSTOM (line 16) | const OP_CUSTOM = "VirtualDOM.OP.Custom"
  constant OP_REMOVE_LAST (line 17) | const OP_REMOVE_LAST = "VirtualDOM.OP.RemoveLast"
  constant OP_APPEND (line 18) | const OP_APPEND = "VirtualDOM.OP.Append"
  constant OP_REORDER (line 19) | const OP_REORDER = "VirtualDOM.OP.Reorder"
  constant OP_REMOVE (line 20) | const OP_REMOVE = "VirtualDOM.OP.Remove"
  constant OP_DELETE (line 21) | const OP_DELETE = "VirtualDOM.OP.Delete"
  constant OP_INSERT (line 22) | const OP_INSERT = "VirtualDOM.OP.Insert"
  constant OP_MOVE (line 23) | const OP_MOVE = "VirtualDOM.OP.Move"
  constant SETTING_EVENT (line 25) | const SETTING_EVENT = "VirtualDOM.Setting.Event"
  constant SETTING_STYLE (line 26) | const SETTING_STYLE = "VirtualDOM.Setting.Style"
  constant SETTING_PROPERTY (line 27) | const SETTING_PROPERTY = "VirtualDOM.Setting.Property"
  constant SETTING_ATTRIBUTE (line 28) | const SETTING_ATTRIBUTE = "VirtualDOM.Setting.Attribute"
  constant SETTING_ATTRIBUTE_NS (line 29) | const SETTING_ATTRIBUTE_NS = "VirtualDOM.Setting.AttributeNS"
  function appendChild (line 33) | function appendChild(parent, child) {
  class Text (line 50) | class Text {
    method constructor (line 51) | constructor(content) {
    method map (line 55) | map(tagger) {
    method toInnerHTML (line 58) | toInnerHTML() {
    method toOuterHTML (line 61) | toOuterHTML(indent) {
    method innerHTML (line 64) | get innerHTML() {
    method outerHTML (line 67) | get outerHTML() {
  class CustomElement (line 79) | class CustomElement {
    method constructor (line 80) | constructor(localName, elementConstructor, options, settings) {
    method map (line 88) | map(tagger) {
    method serializeSettings (line 91) | serializeSettings(key = "") {
    method toOuterHTML (line 94) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 97) | toInnerHTML(indent = "") {
    method outerHTML (line 100) | get outerHTML() {
    method innerHTML (line 103) | get innerHTML() {
  class Node (line 111) | class Node {
    method constructor (line 112) | constructor(localName, settings, children, namespace, descendantsCount) {
    method map (line 120) | map(tagger) {
    method toInnerHTML (line 123) | toInnerHTML(indent = "") {
    method serializeSettings (line 134) | serializeSettings(key) {
    method toOuterHTML (line 168) | toOuterHTML(indent = "", key = "") {
    method innerHTML (line 180) | get innerHTML() {
    method outerHTML (line 183) | get outerHTML() {
  class KeyedNode (line 218) | class KeyedNode {
    method constructor (line 219) | constructor(localName, facts, kids, namespace, descendantsCount) {
    method map (line 227) | map(tagger) {
    method serializeSettings (line 230) | serializeSettings(key = "") {
    method toOuterHTML (line 233) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 236) | toInnerHTML(indent = "") {
    method outerHTML (line 246) | get outerHTML() {
    method innerHTML (line 249) | get innerHTML() {
  class CustomNode (line 279) | class CustomNode {
    method constructor (line 280) | constructor(facts, model, render, diff) {
    method map (line 287) | map(tagger) {
    method serializeSettings (line 290) | serializeSettings(key = "") {
    method toOuterHTML (line 293) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 297) | toInnerHTML(indent = "") {
    method outerHTML (line 300) | get outerHTML() {
    method innerHTML (line 303) | get innerHTML() {
  class Doc (line 311) | class Doc {
    method constructor (line 312) | constructor(title, body) {
    method map (line 316) | map(tagger) {
    method toOuterHTML (line 319) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 323) | toInnerHTML(indent = "") {
    method outerHTML (line 332) | get outerHTML() {
    method innerHTML (line 335) | get innerHTML() {
  class TaggerNode (line 344) | class TaggerNode {
    method constructor (line 345) | constructor(tagger, node) {
    method map (line 351) | map(tagger) {
    method toOuterHTML (line 354) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 357) | toInnerHTML(indent = "") {
    method outerHTML (line 360) | get outerHTML() {
    method innerHTML (line 363) | get innerHTML() {
  class Thunk (line 372) | class Thunk {
    method constructor (line 373) | constructor(refs, thunk) {
    method map (line 379) | map(tagger) {
    method toNode (line 382) | toNode() {
    method toOuterHTML (line 392) | toOuterHTML(indent = "", key = "") {
    method toInnerHTML (line 395) | toInnerHTML(indent = "") {
    method outerHTML (line 399) | get outerHTML() {
    method innerHTML (line 402) | get innerHTML() {
  class VirtualDOMStyle (line 464) | class VirtualDOMStyle {
    method constructor (line 465) | constructor(key, value) {
    method map (line 470) | map(tag) {
  class VirtualDOMProperty (line 479) | class VirtualDOMProperty {
    method constructor (line 480) | constructor(key, value) {
    method map (line 485) | map(tag) {
  class VirtualDOMAttribute (line 494) | class VirtualDOMAttribute {
    method constructor (line 495) | constructor(key, value) {
    method map (line 500) | map(tag) {
  class VirtualDOMAttributeNS (line 507) | class VirtualDOMAttributeNS {
    method constructor (line 508) | constructor(namespace, key, value) {
    method map (line 513) | map(tag) {
  function noScript (line 523) | function noScript(localName) {
  function noOnOrFormAction (line 527) | function noOnOrFormAction(key) {
  function noInnerHtmlOrFormAction (line 531) | function noInnerHtmlOrFormAction(key) {
  function noJavaScriptUri__PROD (line 535) | function noJavaScriptUri__PROD(value) {
  function noJavaScriptUri__DEBUG (line 539) | function noJavaScriptUri__DEBUG(value) {
  function noJavaScriptOrHtmlUri__PROD (line 545) | function noJavaScriptOrHtmlUri__PROD(value) {
  function noJavaScriptOrHtmlUri__DEBUG (line 549) | function noJavaScriptOrHtmlUri__DEBUG(value) {
  class EventHandler (line 557) | class EventHandler {
    method constructor (line 558) | constructor(type, decoder, eventPhase, taggers) {
    method map (line 565) | map(tag) {
    method forwardEvent (line 569) | forwardEvent(event, eventTarget) {
    method equal (line 618) | equal(other) {
  function organizeFacts (line 630) | function organizeFacts(factList) {
  function addClass (line 688) | function addClass(object, key, newClass) {
  function render (line 695) | function render(doc, vNode, eventNode) {
  function applyFacts (line 800) | function applyFacts(domNode, eventNode, facts) {
  function applyStyles (line 845) | function applyStyles(domNode, styles) {
  function applyAttrs (line 855) | function applyAttrs(domNode, attrs) {
  function applyAttrsNS (line 866) | function applyAttrsNS(domNode, nsAttrs) {
  class EventRouter (line 880) | class EventRouter {
    method constructor (line 881) | constructor() {
    method handleEvent (line 886) | handleEvent(event) {
  function applyEvents (line 891) | function applyEvents(domNode, eventNode, events) {
  function diff (line 957) | function diff(x, y) {
  function pushPatch (line 963) | function pushPatch(patches, op, index, data) {
  function diffHelp (line 975) | function diffHelp(x, y, patches, index) {
  function pairwiseRefEqual (line 1102) | function pairwiseRefEqual(as, bs) {
  function diffNodes (line 1112) | function diffNodes(x, y, patches, index, diffKids) {
  function diffFacts (line 1131) | function diffFacts(x, y, category) {
  function diffKids (line 1194) | function diffKids(xParent, yParent, patches, index) {
  function diffKeyedKids (line 1226) | function diffKeyedKids(xParent, yParent, patches, rootIndex) {
  function insertNode (line 1371) | function insertNode(changes, localPatches, key, vnode, yIndex, inserts) {
  function removeNode (line 1409) | function removeNode(changes, localPatches, key, vnode, index) {
  function addDomNodes (line 1451) | function addDomNodes(domNode, vNode, patches, eventNode) {
  function addDomNodesHelp (line 1464) | function addDomNodesHelp(domNode, vNode, patches, i, low, high, eventNod...
  function applyPatchesHelp (line 1563) | function applyPatchesHelp(rootDomNode, patches) {
  function applyPatch (line 1575) | function applyPatch(domNode, patch) {
  function applyPatchRedraw (line 1645) | function applyPatchRedraw(domNode, vNode, eventNode) {
  function applyPatchReorder (line 1660) | function applyPatchReorder(domNode, patch) {
  function applyPatchReorderEndInsertsHelp (line 1690) | function applyPatchReorderEndInsertsHelp(doc, endInserts, patch) {
  function virtualize (line 1709) | function virtualize(root) {
  function dekey (line 1756) | function dekey(keyedNode) {

FILE: src/Widget.js
  class MainThread (line 22) | class MainThread {
    method constructor (line 26) | constructor(root) {
    method send (line 45) | async send(message) {
    method sync (line 53) | sync(message) {
    method link (line 61) | link(thread) {
    method unlink (line 76) | unlink(thread) {
    method linked (line 91) | linked(threadID) {
  class Widget (line 108) | class Widget {
    method constructor (line 109) | constructor() {
    method version (line 123) | get version() {
    method fork (line 135) | static fork(self /*: Sync<a> */) /*: MainThread<a> */ {
    method sync (line 143) | sync(message) {
    method transact (line 150) | transact([state, fx] /*: Transaction<a, model> */) {
    method render (line 159) | render(_state) {}
    method mount (line 165) | mount(_root) {
    method toJSON (line 168) | toJSON() {
  class ElementWidget (line 182) | class ElementWidget extends Widget {
    method mount (line 187) | mount(root) {
    method render (line 194) | render(state) {
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (94K chars).
[
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1664,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - \"src/**\"\n  pull_request:\n    branches:\n      - main\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1084,
    "preview": "on:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\nname: release\njobs:\n  release-please:\n    runs-on: ubuntu-l"
  },
  {
    "path": ".gitignore",
    "chars": 34,
    "preview": "dist\n.nyc_output\ntmp\nnode_modules\n"
  },
  {
    "path": "Readme.md",
    "chars": 1521,
    "preview": "# reflex [![Gitter][gitter.icon]][gitter.url] [![styled with prettier][prettier.icon]][prettier.url]\n\nReflex is a reacti"
  },
  {
    "path": "package.json",
    "chars": 1602,
    "preview": "{\n  \"name\": \"reflex\",\n  \"version\": \"2.0.0\",\n  \"description\": \"Functional reactive UI library\",\n  \"keywords\": [\n    \"refl"
  },
  {
    "path": "src/Application.js",
    "chars": 2869,
    "preview": "import { DocumentWidget } from \"./Document.js\"\n\n/**\n * @template T\n * @typedef {import('./Program').DocumentView<T>} Doc"
  },
  {
    "path": "src/Attribute.js",
    "chars": 5080,
    "preview": "// @flow strict\n\nimport { attribute, property, style, on } from \"./VirtualDOM.js\"\n\nexport { style, attribute, property, "
  },
  {
    "path": "src/Basics.js",
    "chars": 1403,
    "preview": "/**\n * @template T\n * @param {T} value\n * @returns {T}\n */\nexport const identity = value => value\n\n/**\n * @template T\n *"
  },
  {
    "path": "src/Document.js",
    "chars": 2367,
    "preview": "// @flow strict\n\nimport { virtualize, diff, patch, doc } from \"./VirtualDOM.js\"\nimport { Widget, MainThread } from \"./Wi"
  },
  {
    "path": "src/Effect.js",
    "chars": 4125,
    "preview": "import { nothing } from \"./Basics.js\"\n\n/**\n * @template T\n * @typedef {import('./Task').Effect<T>} Effect<T>\n */\n\n/**\n *"
  },
  {
    "path": "src/Element.js",
    "chars": 5623,
    "preview": "import { node, text, doc, keyedNode, customElement } from \"./VirtualDOM.js\"\n\n/**\n * @param {string} tag\n */\nconst factor"
  },
  {
    "path": "src/Program.ts",
    "chars": 806,
    "preview": "import { Transaction, Main } from \"./Task\"\nimport { Node } from \"./VirtualDOM\"\nexport interface Program<Message, State, "
  },
  {
    "path": "src/Task.ts",
    "chars": 830,
    "preview": "export type Phantom<T> = T & { readonly kind: unique symbol }\n\nexport type ThreadID = Phantom<string>\n\nexport interface "
  },
  {
    "path": "src/VirtualDOM.d.ts",
    "chars": 2660,
    "preview": "import { Port } from \"./Task\"\nexport interface Doc<T> {\n  title: string\n  body: Node<T>\n\n  map<U>(f: (inn: T) => U): Doc"
  },
  {
    "path": "src/VirtualDOM.js",
    "chars": 41506,
    "preview": "// @ts-nocheck\n\nconst TEXT = \"VirtualDOM.Text\"\nconst NODE = \"VirtualDOM.Node\"\nconst KEYED_NODE = \"VirtualDOM.Keyed.Node\""
  },
  {
    "path": "src/Widget.js",
    "chars": 4176,
    "preview": "import { diff, patch, virtualize } from \"./VirtualDOM.js\"\n\n/**\n * @typedef {import('./Task').ThreadID} ThreadID\n * @type"
  },
  {
    "path": "src/lib.js",
    "chars": 567,
    "preview": "import * as DOM from \"./VirtualDOM.js\"\nimport * as Element from \"./Element.js\"\nimport * as Attribute from \"./Attribute.j"
  },
  {
    "path": "tsconfig.json",
    "chars": 11022,
    "preview": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig.json to read more about this file */\n\n    /* Projects */\n "
  }
]

About this extraction

This page contains the full source code of the Gozala/reflex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (86.9 KB), approximately 23.1k tokens, and a symbol index with 221 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!