• Stars
    star
    2,349
  • Rank 18,824 (Top 0.4 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created about 8 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An extremely intuitive, small, and fast functional reactive stream library for JavaScript
          _
__  _____| |_ _ __ ___  __ _ _ __ ___
\ \/ / __| __| '__/ _ \/ _` | '_ ` _ \
 >  <\__ \ |_| | |  __/ (_| | | | | | |
/_/\_\___/\__|_|  \___|\__,_|_| |_| |_|

An extremely intuitive, small, and fast
functional reactive stream library for JavaScript

  • Only 26 core operators and factories
  • Only "hot" streams
  • Written in TypeScript
  • Approximately 30 kB in size, when minified
  • On average, faster than RxJS 4, Kefir, Bacon.js, as fast as RxJS 5, and slower than most.js
  • Tailored for Cycle.js, or applications with limited use of subscribe

ComVer Gitter

Example

import xs from 'xstream'

// Tick every second incremental numbers,
// only pass even numbers, then map them to their square,
// and stop after 5 seconds has passed

var stream = xs.periodic(1000)
  .filter(i => i % 2 === 0)
  .map(i => i * i)
  .endWhen(xs.periodic(5000).take(1))

// So far, the stream is idle.
// As soon as it gets its first listener, it starts executing.

stream.addListener({
  next: i => console.log(i),
  error: err => console.error(err),
  complete: () => console.log('completed'),
})

Installation

npm install xstream

Usage

ES2015 or TypeScript

import xs from 'xstream'

CommonJS

var xs = require('xstream').default

API

Factories

Methods and Operators

Extra factories and operators

To keep the core of xstream small and simple, less frequently-used methods are available under the xstream/extra directory, and must be imported separately. See EXTRA_DOCS for documentation.

Overview

XStream has four fundamental types: Stream, Listener, Producer, and MemoryStream.

Stream

A Stream is an event emitter with multiple Listeners. When an event happens on the Stream, it is broadcast to all its Listeners at the same time.

Streams have methods attached to them called operators, such as map, filter, fold, take, etc. When called, an operator creates and returns another Stream. Once the first Stream broadcasts an event, the event will pass through the operator logic and the output Stream may perhaps broadcast its own event based on the source one.

You can also trigger an event to happen on a Stream with the shamefullySend* methods. But you don't want to do that. Really, avoid doing that because it's not the reactive way and you'll be missing the point of this library. Ok?

Listener

A Listener is an object with one to three functions attached to it: next, error, and complete. There is usually one function for each type of event a Stream may emit but only next is always required.

  • next events are the typical type, they deliver a value.
  • error events abort (stop) the execution of the Stream, and happen when something goes wrong in the Stream (or upstream somewhere in the chain of operators)
  • complete events signal the peaceful stop of the execution of the Stream.

This is an example of a typical listener:

var listener = {
  next: (value) => {
    console.log('The Stream gave me a value: ', value);
  },
  error: (err) => {
    console.error('The Stream gave me an error: ', err);
  },
  complete: () => {
    console.log('The Stream told me it is done.');
  },
}

And this is how you would attach that Listener to a Stream:

stream.addListener(listener)

And when you think the Listener is done, you can remove it from the Stream:

stream.removeListener(listener)

Producer

A Producer is like a machine that produces events to be broadcast on a Stream.

Events from a Stream must come from somewhere, right? That's why we need Producers. They are objects with two functions attached: start(listener) and stop(). Once you call start with a listener, the Producer will start generating events and it will send those to the listener. When you call stop(), the Producer should quit doing its own thing.

Streams are also Listeners (actually they are "InternalListeners", not Listeners, but that's a detail you can ignore), so you can theoretically give a Stream as the listener in producer.start(streamAsListener). Then, essentially the Producer is now generating events that will be broadcast on the Stream. Nice, huh? Now a bunch of listeners can be attached to the Stream and they can all get those events originally coming from the Producer. That's why xs.create(producer) receives a Producer to be the heart of a new Stream. Check this out:

var producer = {
  start: function (listener) {
    this.id = setInterval(() => listener.next('yo'), 1000)
  },

  stop: function () {
    clearInterval(this.id)
  },

  id: 0,
}

// This fellow delivers a 'yo' next event every 1 second
var stream = xs.create(producer)

But remember, a Producer has only one listener, but a Stream may have many listeners.

You may wonder "when is start and stop called", and that's actually a fairly tricky topic, so let's get back to that soon. First let me tell you about MemoryStreams.

MemoryStream

A MemoryStream is just like a Stream: it has operators, it can have listeners attached, you can shamefully send events to it, blabla. But it has one special property: it has memory. It remembers the most recent (but just one) next event that it sent to its listeners.

Why is that useful? If a new Listener is added after that next event was sent, the MemoryStream will get its value stored in memory and will send it to the newly attached Listener.

This is important so MemoryStreams can represent values or pieces of state which are relevant even after some time has passed. You don't want to lose those, you want to keep them and send them to Listeners that arrive late, after the event was originally created.

How a Stream starts and stops

A Stream controls its Producer according to its number of Listeners, using reference counting with a synchronous start and a cancelable asynchronous stop. That's how a Stream starts and stops, basically. Usually this part of XStream is not so relevant to remember when building applications, but if you want to understand it for debugging or curiosity, it's explained in plain English below.

When you create a Stream with xs.create(producer), the start() function of the Producer is not yet called. The Stream is still "idle". It has the Producer, but the Producer was not turned on.

Once the first Listener is added to the Stream, the number of Listeners attached suddenly changed from 0 to 1. That's when the Stream calls start, because after all there is at least one Listener interested in this Stream.

More Listeners may be added in the future, but they don't affect whether the Producer will continue working or stop. Just the first Listener dictates when the Stream starts.

What matters for stopping the Producer is stream.removeListener. When the last Listener leaves (or in other words, when the number of Listeners suddenly changes from 1 to 0), the Stream schedules producer.stop() to happen on the next event loop. That is, asynchronously. If, however, a new Listener is added (number goes from 0 to 1) before that scheduled moment, the producer.stop() will be cancelled, and the Producer will continue generating events for its Stream normally.

The reason the Producer is not suddenly (synchronously) stopped, is that it is often necessary to swap the single listener of a Stream, but still keep its ongoing execution. For instance:

var listenerA = {/* ... */}
var listenerB = {/* ... */}

// number goes from 0 to 1, so the Stream's Producer starts
stream.addListener(listenerA)

// ...

// number goes from 1 to 0, but then immediately goes back
// to 1, because listenerB was added
stream.removeListener(listenerA)
stream.addListener(listenerB)

// Stream's Producer does not stop, everything continues as before

It's still useful to eventually (asynchronously) stop a Stream's internal Producer, because you don't want useless computation lying around producing gibberish. At least I don't.

Factories

Factories are functions that create Streams, such as xs.create(), xs.periodic(), etc.

create(producer)

Creates a new Stream given a Producer.

Arguments:

  • producer: Producer An optional Producer that dictates how to start, generate events, and stop the Stream.

Returns: Stream


createWithMemory(producer)

Creates a new MemoryStream given a Producer.

Arguments:

  • producer: Producer An optional Producer that dictates how to start, generate events, and stop the Stream.

Returns: MemoryStream


never()

Creates a Stream that does nothing when started. It never emits any event.

Marble diagram:

         never
-----------------------

Returns: Stream


empty()

Creates a Stream that immediately emits the "complete" notification when started, and that's it.

Marble diagram:

empty
-|

Returns: Stream


throw(error)

Creates a Stream that immediately emits an "error" notification with the value you passed as the error argument when the stream starts, and that's it.

Marble diagram:

throw(X)
-X

Arguments:

  • error The error event to emit on the created stream.

Returns: Stream


from(input)

Creates a stream from an Array, Promise, or an Observable.

Arguments:

  • input: Array|PromiseLike|Observable The input to make a stream from.

Returns: Stream


of(a, b)

Creates a Stream that immediately emits the arguments that you give to of, then completes.

Marble diagram:

of(1,2,3)
123|

Arguments:

  • a The first value you want to emit as an event on the stream.
  • b The second value you want to emit as an event on the stream. One or more of these values may be given as arguments.

Returns: Stream


fromArray(array)

Converts an array to a stream. The returned stream will emit synchronously all the items in the array, and then complete.

Marble diagram:

fromArray([1,2,3])
123|

Arguments:

  • array: Array The array to be converted as a stream.

Returns: Stream


fromPromise(promise)

Converts a promise to a stream. The returned stream will emit the resolved value of the promise, and then complete. However, if the promise is rejected, the stream will emit the corresponding error.

Marble diagram:

fromPromise( ----42 )
-----------------42|

Arguments:

  • promise: PromiseLike The promise to be converted as a stream.

Returns: Stream


fromObservable(observable)

Converts an Observable into a Stream.

Arguments:

  • observable: any The observable to be converted as a stream.

Returns: Stream


periodic(period)

Creates a stream that periodically emits incremental numbers, every period milliseconds.

Marble diagram:

    periodic(1000)
---0---1---2---3---4---...

Arguments:

  • period: number The interval in milliseconds to use as a rate of emission.

Returns: Stream


merge(stream1, stream2)

Blends multiple streams together, emitting events from all of them concurrently.

merge takes multiple streams as arguments, and creates a stream that behaves like each of the argument streams, in parallel.

Marble diagram:

--1----2-----3--------4---
----a-----b----c---d------
           merge
--1-a--2--b--3-c---d--4---

Arguments:

  • stream1: Stream A stream to merge together with other streams.
  • stream2: Stream A stream to merge together with other streams. Two or more streams may be given as arguments.

Returns: Stream


combine(stream1, stream2)

Combines multiple input streams together to return a stream whose events are arrays that collect the latest events from each input stream.

combine internally remembers the most recent event from each of the input streams. When any of the input streams emits an event, that event together with all the other saved events are combined into an array. That array will be emitted on the output stream. It's essentially a way of joining together the events from multiple streams.

Marble diagram:

--1----2-----3--------4---
----a-----b-----c--d------
         combine
----1a-2a-2b-3b-3c-3d-4d--

Arguments:

  • stream1: Stream A stream to combine together with other streams.
  • stream2: Stream A stream to combine together with other streams. Multiple streams, not just two, may be given as arguments.

Returns: Stream


Methods and Operators

Methods are functions attached to a Stream instance, like stream.addListener(). Operators are also methods, but return a new Stream, leaving the existing Stream unmodified, except for the fact that it has a child Stream attached as Listener.

addListener(listener)

Adds a Listener to the Stream.

Arguments:

  • listener: Listener

removeListener(listener)

Removes a Listener from the Stream, assuming the Listener was added to it.

Arguments:

  • listener: Listener\<T>

subscribe(listener)

Adds a Listener to the Stream returning a Subscription to remove that listener.

Arguments:

  • listener: Listener

Returns: Subscription


map(project)

Transforms each event from the input Stream through a project function, to get a Stream that emits those transformed events.

Marble diagram:

--1---3--5-----7------
   map(i => i * 10)
--10--30-50----70-----

Arguments:

  • project: Function A function of type (t: T) => U that takes event t of type T from the input Stream and produces an event of type U, to be emitted on the output Stream.

Returns: Stream


mapTo(projectedValue)

It's like map, but transforms each input event to always the same constant value on the output Stream.

Marble diagram:

--1---3--5-----7-----
      mapTo(10)
--10--10-10----10----

Arguments:

  • projectedValue A value to emit on the output Stream whenever the input Stream emits any value.

Returns: Stream


filter(passes)

Only allows events that pass the test given by the passes argument.

Each event from the input stream is given to the passes function. If the function returns true, the event is forwarded to the output stream, otherwise it is ignored and not forwarded.

Marble diagram:

--1---2--3-----4-----5---6--7-8--
    filter(i => i % 2 === 0)
------2--------4---------6----8--

Arguments:

  • passes: Function A function of type (t: T) => boolean that takes an event from the input stream and checks if it passes, by returning a boolean.

Returns: Stream


take(amount)

Lets the first amount many events from the input stream pass to the output stream, then makes the output stream complete.

Marble diagram:

--a---b--c----d---e--
   take(3)
--a---b--c|

Arguments:

  • amount: number How many events to allow from the input stream before completing the output stream.

Returns: Stream


drop(amount)

Ignores the first amount many events from the input stream, and then after that starts forwarding events from the input stream to the output stream.

Marble diagram:

--a---b--c----d---e--
      drop(3)
--------------d---e--

Arguments:

  • amount: number How many events to ignore from the input stream before forwarding all events from the input stream to the output stream.

Returns: Stream


last()

When the input stream completes, the output stream will emit the last event emitted by the input stream, and then will also complete.

Marble diagram:

--a---b--c--d----|
      last()
-----------------d|

Returns: Stream


startWith(initial)

Prepends the given initial value to the sequence of events emitted by the input stream. The returned stream is a MemoryStream, which means it is already remember()'d.

Marble diagram:

---1---2-----3---
  startWith(0)
0--1---2-----3---

Arguments:

  • initial The value or event to prepend.

Returns: MemoryStream


endWhen(other)

Uses another stream to determine when to complete the current stream.

When the given other stream emits an event or completes, the output stream will complete. Before that happens, the output stream will behaves like the input stream.

Marble diagram:

---1---2-----3--4----5----6---
  endWhen( --------a--b--| )
---1---2-----3--4--|

Arguments:

  • other Some other stream that is used to know when should the output stream of this operator complete.

Returns: Stream


fold(accumulate, seed)

"Folds" the stream onto itself.

Combines events from the past throughout the entire execution of the input stream, allowing you to accumulate them together. It's essentially like Array.prototype.reduce. The returned stream is a MemoryStream, which means it is already remember()'d.

The output stream starts by emitting the seed which you give as argument. Then, when an event happens on the input stream, it is combined with that seed value through the accumulate function, and the output value is emitted on the output stream. fold remembers that output value as acc ("accumulator"), and then when a new input event t happens, acc will be combined with that to produce the new acc and so forth.

Marble diagram:

------1-----1--2----1----1------
  fold((acc, x) => acc + x, 3)
3-----4-----5--7----8----9------

Arguments:

  • accumulate: Function A function of type (acc: R, t: T) => R that takes the previous accumulated value acc and the incoming event from the input stream and produces the new accumulated value.
  • seed The initial accumulated value, of type R.

Returns: MemoryStream


replaceError(replace)

Replaces an error with another stream.

When (and if) an error happens on the input stream, instead of forwarding that error to the output stream, replaceError will call the replace function which returns the stream that the output stream will replicate. And, in case that new stream also emits an error, replace will be called again to get another stream to start replicating.

Marble diagram:

--1---2-----3--4-----X
  replaceError( () => --10--| )
--1---2-----3--4--------10--|

Arguments:

  • replace: Function A function of type (err) => Stream that takes the error that occurred on the input stream or on the previous replacement stream and returns a new stream. The output stream will behave like the stream that this function returns.

Returns: Stream


flatten()

Flattens a "stream of streams", handling only one nested stream at a time (no concurrency).

If the input stream is a stream that emits streams, then this operator will return an output stream which is a flat stream: emits regular events. The flattening happens without concurrency. It works like this: when the input stream emits a nested stream, flatten will start imitating that nested one. However, as soon as the next nested stream is emitted on the input stream, flatten will forget the previous nested one it was imitating, and will start imitating the new nested one.

Marble diagram:

--+--------+---------------
  \        \
   \       ----1----2---3--
   --a--b----c----d--------
          flatten
-----a--b------1----2---3--

Returns: Stream


compose(operator)

Passes the input stream to a custom operator, to produce an output stream.

compose is a handy way of using an existing function in a chained style. Instead of writing outStream = f(inStream) you can write outStream = inStream.compose(f).

Arguments:

  • operator: function A function that takes a stream as input and returns a stream as well.

Returns: Stream


remember()

Returns an output stream that behaves like the input stream, but also remembers the most recent event that happens on the input stream, so that a newly added listener will immediately receive that memorised event.

Returns: MemoryStream


debug(labelOrSpy)

Returns an output stream that identically behaves like the input stream, but also runs a spy function for each event, to help you debug your app.

debug takes a spy function as argument, and runs that for each event happening on the input stream. If you don't provide the spy argument, then debug will just console.log each event. This helps you to understand the flow of events through some operator chain.

Please note that if the output stream has no listeners, then it will not start, which means spy will never run because no actual event happens in that case.

Marble diagram:

--1----2-----3-----4--
        debug
--1----2-----3-----4--

Arguments:

  • labelOrSpy: function A string to use as the label when printing debug information on the console, or a 'spy' function that takes an event as argument, and does not need to return anything.

Returns: Stream


imitate(target)

imitate changes this current Stream to emit the same events that the other given Stream does. This method returns nothing.

This method exists to allow one thing: circular dependency of streams. For instance, let's imagine that for some reason you need to create a circular dependency where stream first$ depends on stream second$ which in turn depends on first$:

import delay from 'xstream/extra/delay'

var first$ = second$.map(x => x * 10).take(3);
var second$ = first$.map(x => x + 1).startWith(1).compose(delay(100));

However, that is invalid JavaScript, because second$ is undefined on the first line. This is how imitate can help solve it:

import delay from 'xstream/extra/delay'

var secondProxy$ = xs.create();
var first$ = secondProxy$.map(x => x * 10).take(3);
var second$ = first$.map(x => x + 1).startWith(1).compose(delay(100));
secondProxy$.imitate(second$);

We create secondProxy$ before the others, so it can be used in the declaration of first$. Then, after both first$ and second$ are defined, we hook secondProxy$ with second$ with imitate() to tell that they are "the same". imitate will not trigger the start of any stream, it just binds secondProxy$ and second$ together.

The following is an example where imitate() is important in Cycle.js applications. A parent component contains some child components. A child has an action stream which is given to the parent to define its state:

const childActionProxy$ = xs.create();
const parent = Parent({...sources, childAction$: childActionProxy$});
const childAction$ = parent.state$.map(s => s.child.action$).flatten();
childActionProxy$.imitate(childAction$);

Note, though, that imitate() does not support MemoryStreams. If we would attempt to imitate a MemoryStream in a circular dependency, we would either get a race condition (where the symptom would be "nothing happens") or an infinite cyclic emission of values. It's useful to think about MemoryStreams as cells in a spreadsheet. It doesn't make any sense to define a spreadsheet cell A1 with a formula that depends on B1 and cell B1 defined with a formula that depends on A1.

If you find yourself wanting to use imitate() with a MemoryStream, you should rework your code around imitate() to use a Stream instead. Look for the stream in the circular dependency that represents an event stream, and that would be a candidate for creating a proxy Stream which then imitates the target Stream.

Arguments:

  • target: Stream The other stream to imitate on the current one. Must not be a MemoryStream.

shamefullySendNext(value)

Forces the Stream to emit the given value to its listeners.

As the name indicates, if you use this, you are most likely doing something The Wrong Way. Please try to understand the reactive way before using this method. Use it only when you know what you are doing.

Arguments:

  • value The "next" value you want to broadcast to all listeners of this Stream.

shamefullySendError(error)

Forces the Stream to emit the given error to its listeners.

As the name indicates, if you use this, you are most likely doing something The Wrong Way. Please try to understand the reactive way before using this method. Use it only when you know what you are doing.

Arguments:

  • error: any The error you want to broadcast to all the listeners of this Stream.

shamefullySendComplete()

Forces the Stream to emit the "completed" event to its listeners.

As the name indicates, if you use this, you are most likely doing something The Wrong Way. Please try to understand the reactive way before using this method. Use it only when you know what you are doing.


setDebugListener(listener)

Adds a "debug" listener to the stream. There can only be one debug listener, that's why this is 'setDebugListener'. To remove the debug listener, just call setDebugListener(null).

A debug listener is like any other listener. The only difference is that a debug listener is "stealthy": its presence/absence does not trigger the start/stop of the stream (or the producer inside the stream). This is useful so you can inspect what is going on without changing the behavior of the program. If you have an idle stream and you add a normal listener to it, the stream will start executing. But if you set a debug listener on an idle stream, it won't start executing (not until the first normal listener is added).

As the name indicates, we don't recommend using this method to build app logic. In fact, in most cases the debug operator works just fine. Only use this one if you know what you're doing.

Arguments:

  • listener: Listener\<T>

FAQ

Q: Why does imitate() support a Stream but not a MemoryStream?

A: MemoryStreams are meant for representing "values over time" (your age), while Streams represent simply events (your birthdays). MemoryStreams are usually initialized with a value, and imitate() is meant for creating circular dependencies of streams. If we would attempt to imitate a MemoryStream in a circular dependency, we would either get a race condition (where the symptom would be "nothing happens") or an infinite cyclic emission of values.

If you find yourself wanting to use imitate() with a MemoryStream, you should rework your code around imitate() to use a Stream instead. Look for the stream in the circular dependency that represents an event stream, and that would be a candidate for creating a MimicStream which then imitates the real event stream.

Q: What's the difference between xstream and RxJS?

A: Read this blog post on the topic. In short:

  • xstream Streams are multicast always.
  • RxJS Observables are unicast by default, and opt-in multicast.
  • xstream has few operators as a feature (helps against decision paralysis).
  • RxJS has many operators as a feature (helps for flexibility and power).

Q: What is the equivalent of withLatestFrom in xstream?

A: withLatestFrom is implemented as an extra named sampleCombine.


Misc.

Acknowledgements

xstream is built by staltz and TylorS.

CHANGELOG

Read the CHANGELOG for release notes of all versions of xstream.

License

MIT

More Repositories

1

rxmarbles

Interactive diagrams of Rx Observables
JavaScript
4,181
star
2

flux-challenge

A frontend challenge to test UI architectures and solutions
JavaScript
1,653
star
3

callbag-basics

๐Ÿ‘œ Tiny and fast reactive/iterable programming library
JavaScript
1,649
star
4

react-native-node

Run a separate Node.js process behind a React Native app
Java
1,109
star
5

matrixmultiplication.xyz

TypeScript
1,049
star
6

manyverse

A social network off the grid (real repo at https://gitlab.com/staltz/manyverse)
TypeScript
922
star
7

prevent-smoosh

Don't let TC39 use smoosh or smooshMap
JavaScript
736
star
8

use-profunctor-state

React Hook for state management with profunctor lenses
JavaScript
334
star
9

comver

MIGRATED TO https://gitlab.com/staltz/comver
333
star
10

rxjs-training

(From 2015) RxJS Workshop exercises for MLOC.js
JavaScript
319
star
11

toy-rx

A tiny implementation of RxJS that actually works, for learning
JavaScript
293
star
12

cycle-onionify

MIGRATED! This was transfered to https://cycle.js.org/api/state.html
JavaScript
284
star
13

dat-installer

Download, install, and update Android apps through Dat
TypeScript
279
star
14

zii

Chain function calls using a prototype function z
JavaScript
230
star
15

easy-ssb-pub

An easy-to-host server that runs an SSB "Pub"
JavaScript
211
star
16

html-looks-like

Assert that an HTML string looks approximately the same as another HTML
TypeScript
178
star
17

mvi-example

A demo of the Model-View-Intent architecture with Virtual DOM renderer, for single-page apps.
JavaScript
153
star
18

hyperpunk

A cyberpunk theme for HyperTerm
JavaScript
126
star
19

zig-nodejs-example

Node.js Native Module written in Zig
Zig
114
star
20

ssb-room

A server to find and connect to other SSB peers โ€“ a meeting place. AGPL-3.0
JavaScript
109
star
21

prevent-global-this

Don't let TC39 use globalThis
JavaScript
88
star
22

switch-path

switch case for URLs, a small tool for routing in JavaScript
TypeScript
77
star
23

react-native-emoji-modal

Feature-complete Emoji picker for React Native
TypeScript
77
star
24

react-propify-methods

A React utility to convert instance methods to props with Observables
JavaScript
75
star
25

ttosa

CSV data for Time Till Open Source Alternative
73
star
26

staltz.com

JavaScript
70
star
27

feedpunk

A scuttlebutt client in the terminal
JavaScript
58
star
28

promisify-4loc

Promisify a callback-style function in just 4 lines of code
JavaScript
58
star
29

rxtween

A library for creating RxJS Observables related to animation
JavaScript
57
star
30

git-done

The simplest TODO-based task tracker integrated into git.
Python
51
star
31

gun-asyncstorage

Use Gun.js in React Native through AsyncStorage
TypeScript
51
star
32

with-profunctor-state

React HOC for state management with profunctor lenses
JavaScript
46
star
33

fantasy-observable

A specification for interoperability of push-based data sources in JavaScript
42
star
34

ts-multipick

TypeScript Pick utility, but deeper: Pick2, Pick3, Pick4, ...
TypeScript
40
star
35

noderify

JavaScript
35
star
36

react-human-time

React component that periodically calls human-time
JavaScript
33
star
37

promisify-tuple

Promisify a callback-style function, resolving with [err,val]
JavaScript
31
star
38

intact

A simpler musical notation that works well with LinnStrument
GDScript
29
star
39

combineLatestObj

A convenient flavor of Rx.Observable.combineLatest to return an object
JavaScript
29
star
40

css2obj

Tagged template literal that takes CSS and returns a JavaScript object suitable for free-style
JavaScript
28
star
41

callbag-subject

๐Ÿ‘œ A callbag listener sink which is also a listenable source
JavaScript
27
star
42

cycle-custom-elementify

Converts a Cycle.js app to a custom element (Web Component)
TypeScript
26
star
43

ziii

Right-compose functions with a z() utility function
JavaScript
25
star
44

something-something

JavaScript
24
star
45

react-native-ssb-client

ssb-client for React Native apps that use nodejs-mobile-react-native
TypeScript
24
star
46

chai-virtual-dom

Chai assertion helpers for virtual-dom elements
JavaScript
24
star
47

callbag-share

๐Ÿ‘œ Callbag operator that broadcasts a single source to multiple sinks
JavaScript
22
star
48

uphill-rxjs-workshop

HTML
22
star
49

clarify-error

Add some additional context to a JavaScript error
JavaScript
22
star
50

callbag-pseudo-rxjs

๐Ÿ‘œ Proof of concept that implements a subset of RxJS using callbags
JavaScript
21
star
51

fp-js-workshop

Pragmatic functional programming in JavaScript - Workshop material
JavaScript
21
star
52

mvi-wc-poc

Proof-of-Concept of Web Components functioning in a Virtual DOM architecture (Model-View-Intent)
JavaScript
20
star
53

react-native-android-packagemanager

Call some PackageManager APIs from React Native
Java
19
star
54

pull-awaitable

Convert a pull-stream into an AsyncIterable, and use for-await-of
JavaScript
18
star
55

multiserver-worker

A multiserver plugin for Web Workers
JavaScript
18
star
56

callbag-to-awaitable

๐Ÿ‘œ Use async-await to consume a pullable callbag source
JavaScript
16
star
57

pull-flat-list

FlatList React Native component capable of scrolling through pull-streams
TypeScript
16
star
58

callbag-pipe

๐Ÿ‘œ Utility function for plugging callbags together in chain
JavaScript
16
star
59

ssb-mirror

JavaScript
16
star
60

ssb-keys-mnemonic

Module that converts from/to SSB keys and BIP39 mnemonic codes
JavaScript
15
star
61

callbag-interval

๐Ÿ‘œ A callbag listenable source that sends incremental numbers every x milliseconds.
JavaScript
15
star
62

poc-ssb-mobile

JavaScript
15
star
63

quicktask

Tiny microtask queue scheduler for all environments
TypeScript
15
star
64

react-mutant-hoc

A utility to make React components easily consume Mutant observables
JavaScript
14
star
65

jace

just another compiler experiment
Haskell
14
star
66

callbag-observe

๐Ÿ‘œ Callbag listener sink that receives data from any listenable source
JavaScript
14
star
67

frontmen-workshop

JavaScript
14
star
68

ppppp-sync

JavaScript
13
star
69

mutant-attachable

Utility for React components to easily subscribe to Mutant streams
TypeScript
13
star
70

callbag-worker

๐Ÿ‘œ Callbag utilities for communicating with a Web Worker
JavaScript
13
star
71

multiserver-dht

A multiserver plugin that uses a Distributed Hash Table
JavaScript
13
star
72

ssb-keys-neon

A drop-in replacement of ssb-keys, implemented in Rust and delivered as a native module in Node.js
Rust
13
star
73

react-xstream-hoc

A utility to make React components easily consume xstream streams
JavaScript
13
star
74

ssb-roadmap

MIGRATED to https://gitlab.com/staltz/ssb-roadmap
12
star
75

electron-ssb-client

Secure Scuttlebutt API for (Renderer process) Electron apps
TypeScript
11
star
76

amicispace

JavaScript
11
star
77

pull-worker

Convert a Web Worker API to a duplex pull-stream
JavaScript
11
star
78

callbag-for-each

๐Ÿ‘œ Callbag sink that consume both pullable and listenable sources
JavaScript
11
star
79

ppppp-db

JavaScript
10
star
80

react-native-process-shim

Shim the Node.js process for React Native's JS runtime
JavaScript
10
star
81

cycle-native-navigation

Cycle.js drivers for react-native-navigation
TypeScript
10
star
82

callbag-from-iter

๐Ÿ‘œ Convert an iterable or iterator to a callbag pullable source
JavaScript
9
star
83

cleanjs-example

Example of Cycle.js app passing the eslint-config-cleanjs lint
JavaScript
9
star
84

pull-stream-from-promise

MIGRATED TO https://gitlab.com/staltz/pull-stream-from-promise
9
star
85

pull-rn-channel

Convert a Node.js Mobile 'channel' to a duplex pull-stream
JavaScript
9
star
86

ssb-minibutt

Experimental
JavaScript
9
star
87

callbag-to-async-iterable

๐Ÿ‘œ Convert any pullable callbag source to an AsyncIterable
JavaScript
8
star
88

ssb-discovery-swarm

Scuttlebot plugin that exchanges invites with other sbots in a DHT
JavaScript
8
star
89

pull-thenable

Convert a pull-stream into a thenable
JavaScript
8
star
90

ppppp-dict

JavaScript
8
star
91

callbag-merge

๐Ÿ‘œ Callbag factory that merges data from multiple callbag sources
JavaScript
8
star
92

dagsync

Work in progress
JavaScript
8
star
93

callbag-concat

๐Ÿ‘œ Callbag factory that concatenates data from multiple callbag sources
JavaScript
8
star
94

trustnet-org

work in progress
JavaScript
8
star
95

callbag-map

๐Ÿ‘œ Callbag operator that applies a transformation on data passing through it
JavaScript
8
star
96

software-below-the-poverty-line

Dataset for my blog post
8
star
97

frontmania-rxjs-workshop

HTML
7
star
98

callbag-take

๐Ÿ‘œ Callbag operator that limits the total amount of data sent through
JavaScript
7
star
99

tallbag-for-each-poc-static-graph

Tallbag sink that consume both pullable and listenable sources
JavaScript
7
star
100

callbag-flatten

๐Ÿ‘œ Callbag operator that flattens a higher-order callbag source
JavaScript
7
star