Repository: mozilla/reflex
Branch: master
Commit: c5e75e98bc60
Files: 20
Total size: 40.3 KB
Directory structure:
gitextract_sq4vfbil/
├── .babelrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── History.md
├── License.md
├── Readme.md
├── package.json
├── src/
│ ├── application.js
│ ├── dom.js
│ ├── effects.js
│ ├── html.js
│ ├── preemptive-animation-frame.js
│ ├── reflex.js
│ ├── signal.js
│ ├── subscription.js
│ └── task.js
└── test/
└── test-api.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"sourceMaps": "inline",
"comments": false,
"presets": [
"flow-node"
]
}
================================================
FILE: .flowconfig
================================================
[ignore]
.*/dist/.*
.*/node_modules/babel.*
.*/node_modules/tap/*
.*/node_modules/json5/test/*
.*/node_modules/kefir/*
.*/node_modules/documentation/*
[libs]
[include]
[options]
suppress_comment= \\(.\\|\n\\)*\\@FlowIssue
suppress_comment= \\(.\\|\n\\)*\\@FlowIgnore
experimental.strict_type_args=true
================================================
FILE: .gitignore
================================================
lib
dist
**/*.js
**/*.flow
!src/**/*.js
!src/**/*.flow
!test/**/*.js
!test/**/*.flow
================================================
FILE: .npmignore
================================================
*~
~*
!dist
!lib
!**/*.js
!**/*.flow
================================================
FILE: .travis.yml
================================================
language: node_js
sudo: false
node_js:
- stable
deploy:
provider: npm
email: rfobic@gmail.com
on:
tags: true
api_key:
secure: 0SAU8CJgSNo9jMZub3DRPPpqNnYPoNPUY6lnEunPHDLxwCLOwv7s6fBrSsCOvGifp7OgqMu+gBcZQcmNf5jsLqFnm3y0xEqxkabZs2x6M7FPaY3fEr7G5jDcfgO+cqJtBFsEFgRKGoZt6yhXB8Cy8/Tr/uroyAzHLifWVQURoumfy5GelosB9Tjy1pCThGwoz+20zHUk5amqdgcJiauwsSeDRS3fpOFUn7rLlpcfP44+IzF8szOdNY5wkaL2LDgCOOcK+J5k7X0iHD7E/T7mCzekbg7HCU9Cj+3nou7QsWn/NLxhfVPwa+OCHFdPL88V+kG5hzHj92NMloezafb/jBTG2UaL9QEBoqwA/mRqjcwywT6ekFlt/E2WqaYrQcKBcDRgrp5L7L99E+OczTao0PWKPnBz9xL0/q6mbf560voLArCQznduzmt0ELV4d5rYrtUGxH/Rkt9sO+Oj62mII9ODOcqcEuxH0/8MU3NWh84aajBzBDVjOobNPPv2KmUOfe1BxPUDPw6AR+aphDfEqItHQ3zVsW3Gsx+oetdfDEYIwQhCEPWDZ/hHkRps63UxqoA7r2iRqyUaPT1yvM8KVigvRq5dph8a4w47m8Fz9ckZp/j1l9e53QXAB/EYw/ziqxc9ihc1TJCBKGY61rMWeRzoKehFd4pgjsTJIQIIeU4=
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Community Participation Guidelines
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
<!--
## Project Specific Etiquette
In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
Please update for your project.
-->
================================================
FILE: History.md
================================================
# Changes
## 0.0.3 / 2012-12-02
- Implement [model](./model) abstraction that can be used to map data to
a reactors that handle IO for the specific components in the application.
- Implement [collection](./collection) abstraction that is similar to model
but handles collections of things.
- Added some examples.
- Tests.
## 0.0.2 / 2012-10-23
- Implement notion of [state](./state.js) that can be `diff`-ed & `patch`-ed.
- Implement [writer](./writer.js) high order function for making `write`-ers
that can reflect state changes on the output.
## 0.0.1 / 2012-10-20
- Initial draft
================================================
FILE: License.md
================================================
Copyright 2012 Irakli Gozalishvili. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
================================================
FILE: Readme.md
================================================
# reflex [![NPM version][version.icon]][version.url] [![Build Status][travis.icon]][travis.url] [![Gitter][gitter.icon]][gitter.url] [![styled with prettier][prettier.icon]][prettier.url]
Reflex is a functional 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 for JS. All types are separated from implementation though, so it's your call if you want to take advantage of it or just ignore it.
The library is designed such that view drivers ([react][react-driver], [virtual-dom][virtual-dom-driver] & possibly more in the future) can be swapped without any changes to the application code base. In fact there is not a built-in view driver, so it's up to the user to choose one. In fact it's pretty easy to write a driver that would directly manipulate DOM.
## Install
npm install reflex
## Examples
For examples check out examples directory of either [virtual-dom][virtual-dom-driver] or [react][react-driver] drivers, in fact examples are identical only diff is one line which is path of imported driver.
[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
[virtual-dom-driver]:https://github.com/mozilla/reflex-virtual-dom-driver
[react-driver]:https://github.com/mozilla/reflex-react-driver
[version.url]: https://npmjs.org/package/reflex
[version.icon]: https://img.shields.io/npm/v/reflex.svg?style=flat
[travis.url]: https://travis-ci.org/mozilla/reflex
[travis.icon]: https://img.shields.io/travis/mozilla/reflex.svg?style=flat
[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
================================================
FILE: package.json
================================================
{
"name": "reflex",
"version": "1.1.0",
"description": "Functional reactive UI library",
"keywords": ["reflex", "reactive", "functional", "UI"],
"author": "Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)",
"main": "reflex",
"devDependencies": {
"babel-cli": "6.24.1",
"babel-preset-flow-node": "^1.0.2",
"babel-register": "6.24.1",
"blue-tape": "^1.0.0",
"documentation": "^4.0.0-rc.1",
"flow-bin": "^0.49.1",
"flow-copy-source": "^1.2.0",
"husky": "^0.14.0",
"lint-staged": "^4.0.0",
"prettier": "^1.4.4"
},
"scripts": {
"test": "npm run test:flow && npm run test:tape",
"test:tape": "blue-tape -r babel-register test/**/*.js",
"test:flow": "flow check",
"build:clear": "rm -rf ./*.js && rm -rf ./*.js.flow && rm -rf reflex",
"build:types": "flow-copy-source -v src .",
"build:node": "babel --out-dir . src",
"build:api": "documentation readme --section=API src/reflex.js",
"build:docs": "documentation build --document-exported src/** -f html --o docs",
"build": "npm run build:node && npm run build:types",
"prepublish": "npm run build && npm test",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": ["prettier --parser flow --no-semi --write", "git add"]
},
"repository": "https://github.com/mozilla/reflex",
"license": "MIT",
"dependencies": {
"reflex-driver": "^1.0.2"
}
}
================================================
FILE: src/application.js
================================================
/* @flow */
import { Task } from "./task"
import { Effects } from "./effects"
import { LazyRoot } from "./dom"
import { Node } from "reflex-driver"
import type { Address } from "./signal"
import { Subscription, Feed, unsubscribe } from "./subscription"
import type { Service, Subscribe, Subscriber } from "./subscription"
export type Init<model, action, flags> = (
flags: flags
) => [model, Effects<action>]
export type Update<model, action> = (
state: model,
action: action
) => [model, Effects<action>]
export type View<model, action> = (
state: model,
address: Address<action>
) => Node
export type Subscriptions<model, action> = (
state: model
) => Subscription<action>
type Services<message> = {
nextAddress: number,
outbox: Address<message>,
active: { [key: string]: Service<message, *, *, *, *> }
}
class Application<state, message> {
constructor(
send: Address<message>,
model: state,
view: Node,
task: Task<empty, void>,
services: Services<message>
) {
this.send = send
this.model = model
this.view = view
this.task = task
this.services = services
}
send: Address<message>
model: state
view: Node
task: Task<empty, void>
services: Services<message>
}
export type { Application }
export type Driver<model, message> = (
state: Application<model, message>
) => void
export type BeginnerConfiguration<model, action> = {
model: model,
update: (model: model, action: action) => model,
view: View<model, action>
}
export type AdvancedConfiguration<model, action, flags> = {
flags: flags,
init: Init<model, action, flags>,
update: Update<model, action>,
view: View<model, action>,
subscriptions?: Subscriptions<model, action>
}
const first = <a, b>(xs: [a, b]): a => xs[0]
const second = <a, b>(xs: [a, b]): b => xs[1]
export const beginner = <model, action>(
configuration: BeginnerConfiguration<model, action>
): AdvancedConfiguration<model, action, void> => ({
flags: void 0,
init: _ => [configuration.model, Effects.none],
update: (model, action) => [
configuration.update(model, action),
Effects.none
],
view: configuration.view,
subscriptions: unsubscribe
})
export const start = <model, message, options>(
configuration: AdvancedConfiguration<model, message, options>,
drive: Driver<model, message>
): Application<model, message> => {
const { init, view, update, flags } = configuration
const subscriptions: Subscriptions<model, message> =
configuration.subscriptions == null
? unsubscribe
: configuration.subscriptions
const send = action => {
const [model, fx] = update(application.model, action)
application.model = model
application.view = new LazyRoot(view, model, send)
application.task = fx.execute(send)
application.services = subscriptions(model).reduce(
subscribe,
application.services
)
exectueServices(application.services, send)
drive(application)
}
const [state, fx] = init(flags)
const application = new Application(
send,
state,
new LazyRoot(view, state, send),
fx.execute(send),
subscriptions(state).reduce(subscribe, {
nextAddress: 0,
outbox: send,
active: Object.create(null)
})
)
exectueServices(application.services, send)
drive(application)
return application
}
const subscribe = <message, out, inn, model, info>(
services: Services<message>,
subscription: Subscribe<message, out, inn, model, info>
): Services<message> => {
const { active, outbox } = services
const { feed, detail, tagger } = subscription
const service = active[String(feed.address)]
if (service == null || service.feed !== feed) {
const address = `/${++services.nextAddress}` //`
active[address] = spawnService(address, subscription, feed, outbox)
} else {
service.subscribers.push(subscription)
}
return services
}
const spawnService = <message, out, inn, model, info>(
address: string,
subscription: Subscriber<info, out, message>,
feed: Feed<out, inn, model, info>,
outbox: Address<message>
): Service<message, out, inn, model, info> => {
const subscribers = [subscription]
const state = feed.init()
feed.address = address
const send = (input: inn) => {
const [state, fx] = feed.update(service.state, input, outbox)
service.state = state
fx.execute(send)
}
const service = {
feed,
subscribers,
state,
inbox: send
}
return service
}
const exectueServices = <a>(services: Services<a>, send: Address<a>) => {
for (let address in services.active) {
exectueService(services.active[address], send)
}
}
const exectueService = <out, inn, msg, model, info>(
service: Service<out, inn, msg, model, info>,
outbox: Address<out>
) => {
const { state, feed, subscribers, inbox } = service
const [next, fx] = feed.update(state, feed.subscribe(subscribers), outbox)
service.state = next
fx.execute(inbox)
}
================================================
FILE: src/dom.js
================================================
/* @flow */
import { Driver, Node } from "reflex-driver"
import type { Properties } from "reflex-driver"
import type { Address } from "./signal"
export class LazyRoot<model, message> implements Node {
state: model
view: (state: model, mailbox: Address<message>) => *
mailbox: Address<message>
constructor(
view: (state: model, mailbox: Address<message>) => *,
state: model,
mailbox: Address<message>
) {
this.state = state
this.view = view
this.mailbox = mailbox
}
renderWith<node: Node>(renderer: Driver<node>): node {
driver = renderer
return this.view(this.state, this.mailbox)
}
}
class ErrorDriver implements Driver<Node> {
createElement(..._): Node {
throw new Error(`You need to use a reflex driver to create element nodes`)
}
createElementNS(..._): Node {
throw new Error(`You need to use a reflex driver to create element nodes`)
}
createTextNode(..._): Node {
throw new Error(`You need to use a reflex driver to create text nodes`)
}
createThunk(..._): Node {
throw new Error(`You need to use a reflex driver to create thunk nodes`)
}
render(node: Node): void {
throw new Error(`You need to use a reflex driver to render nodes`)
}
}
let driver: Driver<any> = new ErrorDriver()
export const text = (content: string): Node => driver.createTextNode(content)
export const element = (
tagName: string,
properties: ?Properties,
children: ?Array<string | Node>
): Node => driver.createElement(tagName, properties, children)
export const elementNS = (
namespaceURI: string,
tagName: string,
properties: ?Properties,
children: ?Array<string | Node>
): Node => driver.createElementNS(namespaceURI, tagName, properties, children)
export const thunk: <a, b, c, d, e, f, g, h, i, j>(
key: string,
view: (
a0: a,
a1: b,
a2: c,
a3: d,
a4: e,
a5: f,
a6: g,
a7: h,
a8: i,
a9: j
) => Node,
a0: a,
a1: b,
a2: c,
a3: d,
a4: e,
a5: f,
a6: g,
a7: h,
a8: i,
a9: j
) => Node = (key, view, ...args) => driver.createThunk(key, view, (args: any))
================================================
FILE: src/effects.js
================================================
/* @flow */
import { Task } from "./task"
import type { Address } from "./signal"
export type Time = number
const raise = error => {
throw Error(
`Effects should be created from task that empty fail but it did fail with error ${error}`
)
}
const ignore = _ => void 0
const nil = Task.succeed(void 0)
const empty = new Task((succeed, fail) => void 0)
export class Effects<a> {
static task<a>(task: Task<empty, a>): Effects<a> {
console.warn(
"Effects.task is deprecated please use Effects.perform instead"
)
return new Perform(task)
}
static perform<a>(task: Task<empty, a>): Effects<a> {
return new Perform(task)
}
static tick<a>(tag: (time: number) => a): Effects<a> {
console.warn(
"Effects.tick is deprecated please use Effects.perform(Task.requestAnimationFrame().map(tag)) instead"
)
return new Perform(Task.requestAnimationFrame().map(tag))
}
static receive<a>(action: a): Effects<a> {
const fx = new Perform(
new Task(
(succeed, fail) => void Promise.resolve(action).then(succeed, fail)
)
)
return fx
}
static batch<a>(effects: Array<Effects<a>>): Effects<a> {
return new Batch(effects)
}
map<b>(f: (a: a) => b): Effects<b> {
throw Error("Subclass of abstract Effect must implement map")
}
execute(address: Address<a>): Task<empty, void> {
throw Error("Subclass of abstract Effect must implement execute")
}
static none: Effects<any>
task: Task<empty, a>
}
class Perform<a> extends Effects<a> {
constructor(task: Task<empty, a>) {
super()
this.task = task
}
map<b>(f: (a: a) => b): Effects<b> {
return new Perform(this.task.map(f))
}
execute(address: Address<a>): Task<empty, void> {
return this.task.chain(value => Task.send(address, value))
}
}
class None<a> extends Effects<any> {
map<b>(f: (a: a) => b): Effects<b> {
return Effects.none
}
execute(address: Address<a>): Task<empty, void> {
return nil
}
}
Effects.none = new None()
class Batch<a> extends Effects<a> {
constructor(effects: Array<Effects<a>>) {
super()
this.effects = effects
}
map<b>(f: (a: a) => b): Effects<b> {
return new Batch(this.effects.map(effect => effect.map(f)))
}
execute(address: Address<a>): Task<empty, void> {
return new Task((succeed, fail) => {
const { effects } = this
const count = effects.length
let index = 0
while (index < count) {
const effect = effects[index]
if (!(effect instanceof None)) {
Task.fork(effect.execute(address), ignore, raise)
}
index = index + 1
}
succeed(void 0)
})
}
effects: Array<Effects<a>>
}
================================================
FILE: src/html.js
================================================
/* @flow */
import { Node } from "reflex-driver"
import type { Properties } from "reflex-driver"
import { element } from "./dom"
type Element = (
properties: ?Properties,
children: ?Array<string | Node>
) => Node
export type Html = {
a: Element,
abbr: Element,
address: Element,
area: Element,
article: Element,
aside: Element,
audio: Element,
b: Element,
base: Element,
bdi: Element,
bdo: Element,
big: Element,
blockquote: Element,
body: Element,
br: Element,
button: Element,
canvas: Element,
caption: Element,
cite: Element,
code: Element,
col: Element,
colgroup: Element,
data: Element,
datalist: Element,
dd: Element,
del: Element,
details: Element,
dfn: Element,
dialog: Element,
div: Element,
dl: Element,
dt: Element,
em: Element,
embed: Element,
fieldset: Element,
figcaption: Element,
figure: Element,
footer: Element,
form: Element,
h1: Element,
h2: Element,
h3: Element,
h4: Element,
h5: Element,
h6: Element,
head: Element,
header: Element,
hr: Element,
html: Element,
i: Element,
iframe: Element,
img: Element,
input: Element,
ins: Element,
kbd: Element,
keygen: Element,
label: Element,
legend: Element,
li: Element,
link: Element,
main: Element,
map: Element,
mark: Element,
menu: Element,
menuitem: Element,
meta: Element,
meter: Element,
nav: Element,
noscript: Element,
object: Element,
ol: Element,
optgroup: Element,
option: Element,
output: Element,
p: Element,
param: Element,
picture: Element,
pre: Element,
progress: Element,
q: Element,
rp: Element,
rt: Element,
ruby: Element,
s: Element,
samp: Element,
script: Element,
section: Element,
select: Element,
small: Element,
source: Element,
span: Element,
strong: Element,
style: Element,
sub: Element,
summary: Element,
sup: Element,
table: Element,
tbody: Element,
td: Element,
textarea: Element,
tfoot: Element,
th: Element,
thead: Element,
time: Element,
title: Element,
tr: Element,
track: Element,
u: Element,
ul: Element,
var: Element,
video: Element,
wbr: Element,
circle: Element,
clipPath: Element,
defs: Element,
ellipse: Element,
g: Element,
line: Element,
linearGradient: Element,
mask: Element,
path: Element,
pattern: Element,
polygon: Element,
polyline: Element,
radialGradient: Element,
rect: Element,
stop: Element,
svg: Element,
text: Element,
tspan: Element
}
export const html: Html = [
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"big",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"menu",
"menuitem",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"table",
"tbody",
"td",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr",
"circle",
"clipPath",
"defs",
"ellipse",
"g",
"line",
"linearGradient",
"mask",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"stop",
"svg",
"text",
"tspan"
].reduce((html, tagName) => {
const create: Element = (properties, children) =>
element(tagName, properties, children)
html[tagName] = create
return html
}, ((Object.create(null): any): Html))
================================================
FILE: src/preemptive-animation-frame.js
================================================
/* @flow */
type Time = number
type State = 0 | 1 | 2
// Invariants:
// 1. In the NO_REQUEST state, there is never a scheduled animation frame.
// 2. In the PENDING_REQUEST and EXTRA_REQUEST states, there is always exactly
// one scheduled animation frame.
const NO_REQUEST = 0
const PENDING_REQUEST = 1
const EXTRA_REQUEST = 2
let nextID: number = 0
let state: State = NO_REQUEST
let requests: Array<(time: Time) => any> = []
let ids: Array<number> = []
const absent = new String("absent")
export const requestAnimationFrame = <a>(request: (time: Time) => a) => {
if (state === NO_REQUEST) {
window.requestAnimationFrame(performAnimationFrame)
}
const id = ++nextID
requests.push(request)
ids.push(id)
state = PENDING_REQUEST
return id
}
export const cancelAnimationFrame = (id: number): void => {
const index = ids.indexOf(id)
if (index >= 0) {
ids.splice(index, 1)
requests.splice(index, 1)
}
}
export const forceAnimationFrame = (time: Time = window.performance.now()) =>
performAnimationFrame(time)
const performAnimationFrame = (time: Time) => {
switch (state) {
case NO_REQUEST:
// This state should not be possible. How can there be no
// request, yet somehow we are actively fulfilling a
// request?
throw Error(`Unexpected frame request`)
case PENDING_REQUEST:
// At this point, we do not *know* that another frame is
// needed, but we make an extra frame request just in
// case. It's possible to drop a frame if frame is requested
// too late, so we just do it preemptively.
window.requestAnimationFrame(performAnimationFrame)
state = EXTRA_REQUEST
ids.splice(0)
dispatchAnimationFrame(requests.splice(0), 0, time)
break
case EXTRA_REQUEST:
// Turns out the extra request was not needed, so we will
// stop requesting. No reason to call it all the time if
// no one needs it.
state = NO_REQUEST
break
}
}
const dispatchAnimationFrame = <a>(
requests: Array<(time: Time) => a>,
index: number,
time: Time
) => {
let exception: String | Error = absent
const count = requests.length
try {
while (index < count) {
const request = requests[index]
index = index + 1
request(time)
}
} catch (error) {
exception = error
}
if (index < count) {
dispatchAnimationFrame(requests, index, time)
}
if (exception != absent) {
throw exception
}
}
================================================
FILE: src/reflex.js
================================================
/* @flow */
import type { Node, Properties, Style, Attributes } from "reflex-driver"
export type DOM = Node
export { Node, Driver } from "reflex-driver"
export type { Address } from "./signal"
export type { Properties, Attributes, Style } from "reflex-driver"
export { Subscription, subscribe, unsubscribe } from "./subscription"
export type {
Application,
AdvancedConfiguration,
BeginnerConfiguration
} from "./application"
export type { Init, Update, View } from "./application"
export { forward } from "./signal"
export { element, elementNS, text, thunk } from "./dom"
export { html } from "./html"
export { start, beginner } from "./application"
export { Task } from "./task"
export { Effects } from "./effects"
================================================
FILE: src/signal.js
================================================
/* @flow */
export type Address<a> = (input: a) => void
const Forward = <a, b>(address: Address<b>, tag: (a: a) => b): Address<a> => {
const forward = (message: a) => address(tag(message))
forward.to = address
forward.tag = tag
return forward
}
if (global["reflex/address"] == null) {
global["reflex/address"] = 0
}
// Create a new address. This address will tag each message it receives and then
// forward it along to the given address.
// Example:
//
// const Remove = target => {type: "Remove", target}
// removeAddress = forward(address, Remove)
//
// Above example created `removeAddress` tags each message with `Remove` tag
// before forwarding them to a general `address`.
export const forward = <a, b>(
address: Address<a>,
tag: (value: b) => a
): Address<b> => {
// Genrate ID for each address that has a forwarding addresses so that
// forwarding addresses could be cached by that id and a tag-ing function.
const id =
address.id != null ? address.id : (address.id = global["reflex/address"]++)
const key = `reflex/address/${id}`
return tag[key] || (tag[key] = Forward(address, tag))
}
================================================
FILE: src/subscription.js
================================================
/* @flow */
import { Effects } from "./effects"
import type { Address } from "./signal"
type Tagger<a, b> = (value: a) => b
export interface Subscriber<info, inn, out> {
detail: info,
tagger: Tagger<inn, out>
}
export type Service<message, out, inn, model, info> = {
inbox: Address<inn>,
subscribers: Array<Subscriber<info, out, message>>,
state: model,
feed: Feed<out, inn, model, info>
}
export interface Feed<out, inn, model, info> {
address?: string,
init(): model,
subscribe<message>(subscribers: Array<Subscriber<info, out, message>>): inn,
update<message>(
state: model,
input: inn,
outbox: Address<message>
): [model, Effects<inn>]
}
export class Subscription<a> {
static none: Subscription<*>
static batch(subscriptions: Array<Subscription<a>>): Subscription<a> {
return new Batch(subscriptions)
}
map<b>(tag: (value: a) => b): Subscription<b> {
return this.map(tag)
}
reduce<state>(
reducer: (result: state, input: Subscribe<a, *, *, *, *>) => state,
init: state
): state {
return init
}
}
export class Subscribe<out, inn, msg, model, info> extends Subscription<out>
implements Subscriber<info, inn, out> {
feed: Feed<inn, msg, model, info>
detail: info
tagger: Tagger<inn, out>
constructor(
feed: Feed<inn, msg, model, info>,
detail: info,
tagger: Tagger<inn, out>
) {
super()
this.feed = feed
this.detail = detail
this.tagger = tagger
}
map<b>(tag: (value: out) => b): Subscription<b> {
const decoder = (value: inn): b => tag(this.tagger(value))
return new Subscribe(this.feed, this.detail, decoder)
}
reduce<state>(
reducer: (
result: state,
input: Subscribe<out, inn, msg, model, info>
) => state,
init: state
): state {
return reducer(init, this)
}
}
class Batch<a> extends Subscription<a> {
constructor(subscriptions: Array<Subscription<a>>) {
super()
this.subscriptions = subscriptions
}
map<b>(tag: (value: a) => b): Subscription<b> {
const subscriptions = this.subscriptions.map($ => $.map(tag))
return new Batch(subscriptions)
}
reduce<state>(
reducer: (result: state, input: Subscribe<a, *, *, *, *>) => state,
init: state
): state {
return this.subscriptions.reduce(
(result: state, subscription: Subscription<a>): state =>
subscription.reduce(reducer, result),
init
)
}
subscriptions: Array<Subscription<a>>
}
export const subscribe = <outer, inner, message, model, info>(
feed: Feed<inner, message, model, info>,
detail: info,
tagger: Tagger<inner, outer>
): Subscription<outer> => new Subscribe(feed, detail, tagger)
const none: Subscription<any> = new Batch([])
export const unsubscribe = <a>(_: mixed): Subscription<a> => none
Subscription.none = none
================================================
FILE: src/task.js
================================================
/* @flow */
import type { Address } from "./signal"
import {
requestAnimationFrame,
cancelAnimationFrame
} from "./preemptive-animation-frame"
export type ThreadID = number
export type Time = number
export type ProcessID = number
const raise = error => {
throw Error(
`Task was not supposet to never fail but it did fail with error ${error}`
)
}
const ignore = _ => void 0
export interface Process<error, value, message, reason> {
id: ProcessID,
isActive: boolean,
kill(reson: reason): void
}
export class Task<x, a> {
static create<x, a>(
execute: (succeed: (a: a) => void, fail: (x: x) => void) => void
): Task<x, a> {
console.warn("Task.create is deprecated API use new Task instead")
return new Task(execute)
}
static future<x, a>(request: () => Promise<a>): Task<x, a> {
console.warn("Task.future is deprecated API use new Task instead")
return new Future(request)
}
static succeed<x, a>(value: a): Task<x, a> {
return new Succeed(value)
}
static fail<x, a>(error: x): Task<x, a> {
return new Fail(error)
}
static spawn<x, y, a>(task: Task<x, a>): Task<y, ThreadID> {
return new Spawn(task)
}
static sleep<x>(time: Time): Task<x, void> {
return new Sleep(time)
}
static requestAnimationFrame<x>(): Task<x, Time> {
return new AnimationFrame()
}
static send<x, a>(address: Address<a>, message: a): Task<x, void> {
return new Send(address, message)
}
static fork<x, a, message, reason>(
task: Task<x, a>,
onSucceed: (a: a) => void,
onFail: (x: x) => void
): Process<x, a, message, reason> {
return Thread.fork(task, onSucceed, onFail)
}
static perform(task: Task<empty, void>): void {
Thread.fork(task, ignore, raise)
}
constructor<handle>(
execute: ?(succeed: (a: a) => void, fail: (x: x) => void) => handle,
cancel: ?(handle: handle) => void
) {
this.type = "Task"
const task = (this: any)
if (execute != null) {
task.fork = execute
}
if (cancel != null) {
task.abort = cancel
}
}
chain<b>(next: (a: a) => Task<x, b>): Task<x, b> {
return new Chain(this, next)
}
map<b>(f: (input: a) => b): Task<x, b> {
return new Map(this, f)
}
capture<y>(handle: (error: x) => Task<y, a>): Task<y, a> {
return new Capture(this, handle)
}
format<y>(f: (input: x) => y): Task<y, a> {
return new Format(this, f)
}
recover<y>(regain: (error: x) => a): Task<y, a> {
return new Recover(this, regain)
}
fork(succeed: (a: a) => void, fail: (x: x) => void): * {
return this.execute(succeed, fail)
}
abort(token: *): void {
return this.cancel(token)
}
type: *
execute: (succeed: (a: a) => void, fail: (x: x) => void) => *
cancel: (handle: *) => void
}
class Succeed<x, a> extends Task<x, a> {
constructor(value: a) {
super()
this.type = "Succeed"
this.value = value
}
fork(succeed: (a: a) => void, fail: (x: x) => void): void {
succeed(this.value)
}
type: "Succeed"
value: a
}
class Fail<x, a> extends Task<x, a> {
constructor(error: x) {
super()
this.type = "Fail"
this.error = error
}
fork(succeed: (a: a) => void, fail: (x: x) => void): void {
fail(this.error)
}
type: "Fail"
error: x
}
class Sleep<x, a: void> extends Task<x, void> {
constructor(time: Time) {
super()
this.time = time
}
fork(succeed: (a: a) => void, fail: (x: x) => void): number {
return setTimeout(succeed, this.time, void 0)
}
abort(id: number): void {
clearTimeout(id)
}
time: Time
}
class AnimationFrame<x> extends Task<x, Time> {
constructor() {
super()
}
fork(succeed: (a: Time) => void, fail: (x: x) => void): number {
return requestAnimationFrame(succeed)
}
abort(id: number): void {
cancelAnimationFrame(id)
}
}
let threadID = 0
class Spawn<x, y, a> extends Task<y, ThreadID> {
constructor(task: Task<x, a>) {
super()
this.task = task
}
fork(succeed: (a: ThreadID) => void, fail: (x: y) => void): void {
Promise.resolve(null).then(_ => Task.fork(this.task, noop, noop))
succeed(++threadID)
}
task: Task<x, a>
}
class Send<x, a> extends Task<x, void> {
constructor(address: Address<a>, message: a) {
super()
this.message = message
this.address = address
}
fork(succeed: (a: void) => void, fail: (x: x) => void): void {
succeed(void this.address(this.message))
}
message: a
address: Address<a>
}
class Future<x, a> extends Task<x, a> {
constructor(request: () => Promise<a>) {
super()
this.request = request
}
fork(succeed: (a: a) => void, fail: (x: x) => void): void {
this.request().then(succeed, fail)
}
request: () => Promise<a>
}
class Then<x, a, b> extends Task<x, b> {
constructor(task: Task<x, a>) {
super()
this.type = "Then"
this.task = task
}
fork(succeed: (value: b) => void, fail: (error: x) => void): void {
this.task.fork(
(value: a): void => void this.next(value).fork(succeed, fail),
fail
)
}
next(input: a): Task<x, b> {
throw Error("Subclass of absract Then must implement next method")
}
type: "Then"
task: Task<x, a>
}
class Chain<x, a, b> extends Then<x, a, b> {
constructor(task: Task<x, a>, next: (input: a) => Task<x, b>) {
super(task)
this.chainer = next
}
next(input: a): Task<x, b> {
return this.chainer(input)
}
chainer: (input: a) => Task<x, b>
}
class Map<x, a, b> extends Then<x, a, b> {
constructor(task: Task<x, a>, mapper: (input: a) => b) {
// Note: Had to trick flow into thinking that `Format.prototype.handle` was
// passed, otherwise it fails to infer polymorphic nature.
super(task)
this.mapper = mapper
}
next(input: a): Task<x, b> {
return new Succeed(this.mapper(input))
}
mapper: (input: a) => b
}
class Catch<x, y, a> extends Task<y, a> {
constructor(task: Task<x, a>) {
super()
this.type = "Catch"
this.task = task
}
fork(succeed: (value: a) => void, fail: (error: y) => void): void {
this.task.fork(
succeed,
error => void this.handle(error).fork(succeed, fail)
)
}
handle(error: x): Task<y, a> {
throw Error("Subclass of absract Catch must implement handle method")
}
type: "Catch"
task: Task<x, a>
}
class Capture<x, y, a> extends Catch<x, y, a> {
constructor(task: Task<x, a>, handle: (error: x) => Task<y, a>) {
super(task)
this.capturer = handle
}
handle(error: x): Task<y, a> {
return this.capturer(error)
}
capturer: (error: x) => Task<y, a>
}
class Recover<x, y, a> extends Catch<x, y, a> {
constructor(task: Task<x, a>, regain: (error: x) => a) {
super(task)
this.regain = regain
}
handle(error: x): Task<y, a> {
return new Succeed(this.regain(error))
}
regain: (error: x) => a
}
class Format<x, y, a> extends Catch<x, y, a> {
constructor(task: Task<x, a>, formatter: (error: x) => y) {
super(task)
this.formatter = formatter
}
handle(error: x): Task<y, a> {
return new Fail(this.formatter(error))
}
formatter: (error: x) => y
}
const noop = () => void 0
let nextID = 0
type Root<x, a> =
| Succeed<x, a>
| Fail<x, a>
| Then<x, *, a>
| Catch<*, x, a>
| Task<x, a>
class Thread<error, value, message, reason> {
id: ProcessID
root: Root<*, *>
stack: Array<Catch<*, *, *> | Then<*, *, *>>
position: number
mailbox: Array<message>
abortHandle: *
isActive: boolean
succeed: (input: value) => void
fail: (error: error) => void
isPending: boolean
isPaused: boolean
success: ?Succeed<*, *>
failure: ?Fail<*, *>
onSucceed: <value>(input: value) => void
onFail: <error>(error: error) => void
static fork<error, value, message, reason>(
task: Task<error, value>,
onSucceed: (input: value) => void,
onFail: (error: error) => void
): Process<error, value, message, reason> {
const process = new Thread()
process.id = ++nextID
process.position = 0
process.root = task
process.stack = []
process.mailbox = []
process.abortHandle = null
process.isActive = true
process.isPending = false
process.isPaused = true
process.success = null
process.failure = null
process.succeed = onSucceed
process.fail = onFail
process.onSucceed = process.onSucceed.bind(process)
process.onFail = process.onFail.bind(process)
process.schedule()
return process
}
onSucceed(ok) {
if (this.isPending) {
this.isPending = false
this.abortHandle = null
if (this.success != null) {
this.success.value = ok
} else {
this.success = new Succeed(ok)
}
this.root = this.success
this.schedule()
}
}
onFail(failure) {
if (this.isPending) {
this.isPending = false
this.abortHandle = null
if (this.failure != null) {
this.failure.error = failure
} else {
this.failure = new Fail(failure)
}
this.root = this.failure
this.schedule()
}
}
kill(exit: reason) {
if (this.isActive) {
this.isActive = false
if (this.root.abort) {
this.root.abort(this.abortHandle)
}
}
}
schedule() {
if (this.isPaused) {
this.isPaused = false
this.step()
}
}
step() {
const process = this
while (process.isActive) {
const root = process.root
switch (root.type) {
case "Succeed": {
const task: Succeed<*, *> = (root: any)
// If task succeeded skip all the error handling.
while (
process.position < process.stack.length &&
process.stack[process.position] instanceof Catch
) {
process.position++
}
// If end of the stack is reached then break
if (process.position >= process.stack.length) {
if (process.succeed != null) {
process.succeed(task.value)
}
return
}
// Otherwise step into next task.
const then = process.stack[process.position++]
if (then instanceof Then) {
process.root = then.next(task.value)
}
break
}
case "Fail": {
const task: Fail<*, *> = (root: any)
// If task fails skip all the chaining.
while (
process.position < process.stack.length &&
process.stack[process.position] instanceof Then
) {
process.position++
}
// If end of the stack is reached then break.
if (this.position >= process.stack.length) {
if (process.fail != null) {
process.fail(task.error)
}
return
}
// Otherwise step into next task.
const _catch = process.stack[process.position++]
if (_catch instanceof Catch) {
process.root = _catch.handle(task.error)
}
break
}
case "Then": {
const task: Then<*, *, *> = (root: any)
if (process.position === 0) {
process.stack.unshift(task)
} else {
process.stack[--process.position] = task
}
process.root = task.task
break
}
case "Catch": {
const task: Catch<*, *, *> = (root: any)
if (process.position === 0) {
process.stack.unshift(task)
} else {
process.stack[--process.position] = task
}
process.root = task.task
break
}
default: {
const task = root
process.isPending = true
process.abortHandle = task.fork(process.onSucceed, process.onFail)
process.isPaused = process.isPending
if (this.isPending) {
return
}
break
}
}
}
}
}
================================================
FILE: test/test-api.js
================================================
import test from "blue-tape"
import * as Reflex from "../"
test("test exported api", async assert => {
assert.ok(typeof Reflex.node, "function")
assert.ok(typeof Reflex.html, "object")
assert.ok(typeof Reflex.html.div, "function")
assert.ok(typeof Reflex.thunk, "function")
assert.ok(typeof Reflex.send, "function")
assert.ok(typeof Reflex.forward, "function")
assert.ok(typeof Reflex.Application, "function")
assert.ok(typeof Reflex.Task.succeed, "function")
assert.ok(typeof Reflex.Task.fail, "function")
assert.ok(typeof Reflex.Task.io, "function")
assert.ok(typeof Reflex.Task.onSuccess, "function")
assert.ok(typeof Reflex.Task.onFailure, "function")
assert.ok(typeof Reflex.Task.perform, "function")
assert.ok(typeof Reflex.Task.run, "function")
})
gitextract_sq4vfbil/
├── .babelrc
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── History.md
├── License.md
├── Readme.md
├── package.json
├── src/
│ ├── application.js
│ ├── dom.js
│ ├── effects.js
│ ├── html.js
│ ├── preemptive-animation-frame.js
│ ├── reflex.js
│ ├── signal.js
│ ├── subscription.js
│ └── task.js
└── test/
└── test-api.js
SYMBOL INDEX (11 symbols across 3 files)
FILE: src/application.js
class Application (line 35) | class Application<state, message> {
method constructor (line 36) | constructor(
FILE: src/preemptive-animation-frame.js
constant NO_REQUEST (line 10) | const NO_REQUEST = 0
constant PENDING_REQUEST (line 11) | const PENDING_REQUEST = 1
constant EXTRA_REQUEST (line 12) | const EXTRA_REQUEST = 2
FILE: src/task.js
method if (line 365) | if (this.isPending) {
method if (line 380) | if (this.isPending) {
method if (line 395) | if (this.isActive) {
method if (line 403) | if (this.isPaused) {
method any (line 414) | *> = (root: any)
method if (line 424) | if (process.position >= process.stack.length) {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (44K chars).
[
{
"path": ".babelrc",
"chars": 85,
"preview": "{\n \"sourceMaps\": \"inline\",\n \"comments\": false,\n \"presets\": [\n \"flow-node\"\n ]\n}"
},
{
"path": ".flowconfig",
"chars": 306,
"preview": "[ignore]\n.*/dist/.*\n.*/node_modules/babel.*\n.*/node_modules/tap/*\n.*/node_modules/json5/test/*\n.*/node_modules/kefir/*\n."
},
{
"path": ".gitignore",
"chars": 84,
"preview": "lib\ndist\n**/*.js\n**/*.flow\n!src/**/*.js\n!src/**/*.flow\n!test/**/*.js\n!test/**/*.flow"
},
{
"path": ".npmignore",
"chars": 36,
"preview": "*~\n~*\n!dist\n!lib\n!**/*.js\n!**/*.flow"
},
{
"path": ".travis.yml",
"chars": 827,
"preview": "language: node_js\nsudo: false\nnode_js:\n- stable\ndeploy:\n provider: npm\n email: rfobic@gmail.com\n on:\n tags: true\n "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 691,
"preview": "# Community Participation Guidelines\n\nThis repository is governed by Mozilla's code of conduct and etiquette guidelines."
},
{
"path": "History.md",
"chars": 615,
"preview": "# Changes\n\n## 0.0.3 / 2012-12-02\n\n - Implement [model](./model) abstraction that can be used to map data to\n a react"
},
{
"path": "License.md",
"chars": 1080,
"preview": "Copyright 2012 Irakli Gozalishvili. All rights reserved.\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "Readme.md",
"chars": 2354,
"preview": "# reflex [![NPM version][version.icon]][version.url] [![Build Status][travis.icon]][travis.url] [![Gitter][gitter.icon]]"
},
{
"path": "package.json",
"chars": 1424,
"preview": "{\n \"name\": \"reflex\",\n \"version\": \"1.1.0\",\n \"description\": \"Functional reactive UI library\",\n \"keywords\": [\"reflex\", "
},
{
"path": "src/application.js",
"chars": 4963,
"preview": "/* @flow */\n\nimport { Task } from \"./task\"\nimport { Effects } from \"./effects\"\nimport { LazyRoot } from \"./dom\"\nimport {"
},
{
"path": "src/dom.js",
"chars": 2106,
"preview": "/* @flow */\n\nimport { Driver, Node } from \"reflex-driver\"\nimport type { Properties } from \"reflex-driver\"\nimport type { "
},
{
"path": "src/effects.js",
"chars": 2708,
"preview": "/* @flow */\n\nimport { Task } from \"./task\"\n\nimport type { Address } from \"./signal\"\n\nexport type Time = number\n\nconst ra"
},
{
"path": "src/html.js",
"chars": 4126,
"preview": "/* @flow */\n\nimport { Node } from \"reflex-driver\"\nimport type { Properties } from \"reflex-driver\"\nimport { element } fro"
},
{
"path": "src/preemptive-animation-frame.js",
"chars": 2473,
"preview": "/* @flow */\n\ntype Time = number\ntype State = 0 | 1 | 2\n\n// Invariants:\n// 1. In the NO_REQUEST state, there is never a s"
},
{
"path": "src/reflex.js",
"chars": 730,
"preview": "/* @flow */\n\nimport type { Node, Properties, Style, Attributes } from \"reflex-driver\"\n\nexport type DOM = Node\nexport { N"
},
{
"path": "src/signal.js",
"chars": 1130,
"preview": "/* @flow */\n\nexport type Address<a> = (input: a) => void\n\nconst Forward = <a, b>(address: Address<b>, tag: (a: a) => b):"
},
{
"path": "src/subscription.js",
"chars": 2822,
"preview": "/* @flow */\n\nimport { Effects } from \"./effects\"\nimport type { Address } from \"./signal\"\n\ntype Tagger<a, b> = (value: a)"
},
{
"path": "src/task.js",
"chars": 11953,
"preview": "/* @flow */\n\nimport type { Address } from \"./signal\"\nimport {\n requestAnimationFrame,\n cancelAnimationFrame\n} from \"./"
},
{
"path": "test/test-api.js",
"chars": 788,
"preview": "import test from \"blue-tape\"\nimport * as Reflex from \"../\"\n\ntest(\"test exported api\", async assert => {\n assert.ok(type"
}
]
About this extraction
This page contains the full source code of the mozilla/reflex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (40.3 KB), approximately 12.1k tokens, and a symbol index with 11 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.