feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,161 @@
import * as WonkaJs from "./web/WonkaJs.bs.js";
import * as Wonka_sinks from "./Wonka_sinks.bs.js";
import * as Wonka_sources from "./Wonka_sources.bs.js";
import * as Wonka_operators from "./Wonka_operators.bs.js";
var Types;
var fromArray = Wonka_sources.fromArray;
var fromList = Wonka_sources.fromList;
var fromValue = Wonka_sources.fromValue;
var make = Wonka_sources.make;
var makeSubject = Wonka_sources.makeSubject;
var empty = Wonka_sources.empty;
var never = Wonka_sources.never;
var buffer = Wonka_operators.buffer;
var combine = Wonka_operators.combine;
var concatMap = Wonka_operators.concatMap;
var concatAll = Wonka_operators.concatAll;
var concat = Wonka_operators.concat;
var filter = Wonka_operators.filter;
var map = Wonka_operators.map;
var mergeMap = Wonka_operators.mergeMap;
var merge = Wonka_operators.merge;
var mergeAll = Wonka_operators.mergeAll;
var flatten = Wonka_operators.flatten;
var onEnd = Wonka_operators.onEnd;
var onPush = Wonka_operators.onPush;
var tap = Wonka_operators.tap;
var onStart = Wonka_operators.onStart;
var sample = Wonka_operators.sample;
var scan = Wonka_operators.scan;
var share = Wonka_operators.share;
var skip = Wonka_operators.skip;
var skipUntil = Wonka_operators.skipUntil;
var skipWhile = Wonka_operators.skipWhile;
var switchMap = Wonka_operators.switchMap;
var switchAll = Wonka_operators.switchAll;
var take = Wonka_operators.take;
var takeLast = Wonka_operators.takeLast;
var takeUntil = Wonka_operators.takeUntil;
var takeWhile = Wonka_operators.takeWhile;
var subscribe = Wonka_sinks.subscribe;
var forEach = Wonka_sinks.forEach;
var publish = Wonka_sinks.publish;
var toArray = Wonka_sinks.toArray;
var fromObservable = WonkaJs.fromObservable;
var toObservable = WonkaJs.toObservable;
var fromCallbag = WonkaJs.fromCallbag;
var toCallbag = WonkaJs.toCallbag;
var debounce = WonkaJs.debounce;
var delay = WonkaJs.delay;
var throttle = WonkaJs.throttle;
var toPromise = WonkaJs.toPromise;
var interval = WonkaJs.interval;
var fromDomEvent = WonkaJs.fromDomEvent;
var fromPromise = WonkaJs.fromPromise;
export {
Types ,
fromArray ,
fromList ,
fromValue ,
make ,
makeSubject ,
empty ,
never ,
buffer ,
combine ,
concatMap ,
concatAll ,
concat ,
filter ,
map ,
mergeMap ,
merge ,
mergeAll ,
flatten ,
onEnd ,
onPush ,
tap ,
onStart ,
sample ,
scan ,
share ,
skip ,
skipUntil ,
skipWhile ,
switchMap ,
switchAll ,
take ,
takeLast ,
takeUntil ,
takeWhile ,
subscribe ,
forEach ,
publish ,
toArray ,
fromObservable ,
toObservable ,
fromCallbag ,
toCallbag ,
debounce ,
delay ,
throttle ,
toPromise ,
interval ,
fromDomEvent ,
fromPromise ,
}
/* WonkaJs Not a pure module */

View File

@@ -0,0 +1,13 @@
module Types = Wonka_types
include Wonka_sources
include Wonka_operators
include Wonka_sinks
#if BS_NATIVE then
#if BSB_BACKEND = "js" then
include WonkaJs
#end
#else
include WonkaJs
#end

View File

@@ -0,0 +1,15 @@
export * from './helpers/pipe';
export * from './Wonka_sources.gen';
export * from './Wonka_operators.gen';
export * from './Wonka_sinks.gen';
export * from './web/WonkaJs.gen';
export {
sinkT as Sink,
sourceT as Source,
operatorT as Operator,
subscriptionT as Subscription,
observerT as Observer,
subjectT as Subject
} from './Wonka_types.gen';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
/* TypeScript file generated from Wonka_operators.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Curry = require('bs-platform/lib/es6/curry.js');
// tslint:disable-next-line:no-var-requires
const Wonka_operatorsBS = require('./Wonka_operators.bs');
import {operatorT as Wonka_types_operatorT} from './Wonka_types.gen';
import {sourceT as Wonka_types_sourceT} from './Wonka_types.gen';
export const buffer: <a,b>(notifier:Wonka_types_sourceT<a>) => Wonka_types_operatorT<b,b[]> = Wonka_operatorsBS.buffer;
export const combine: <a,b>(sourceA:Wonka_types_sourceT<a>, sourceB:Wonka_types_sourceT<b>) => Wonka_types_sourceT<[a, b]> = function <a,b>(Arg1: any, Arg2: any) {
const result = Curry._2(Wonka_operatorsBS.combine, Arg1, Arg2);
return result
};
export const concatMap: <a,b>(f:((_1:a) => Wonka_types_sourceT<b>)) => Wonka_types_operatorT<a,b> = Wonka_operatorsBS.concatMap;
export const concatAll: <a>(source:Wonka_types_sourceT<Wonka_types_sourceT<a>>) => Wonka_types_sourceT<a> = Wonka_operatorsBS.concatAll;
export const concat: <a>(sources:Wonka_types_sourceT<a>[]) => Wonka_types_sourceT<a> = Wonka_operatorsBS.concat;
export const filter: <a>(f:((_1:a) => boolean)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.filter;
export const map: <a,b>(f:((_1:a) => b)) => Wonka_types_operatorT<a,b> = Wonka_operatorsBS.map;
export const mergeMap: <a,b>(f:((_1:a) => Wonka_types_sourceT<b>)) => Wonka_types_operatorT<a,b> = Wonka_operatorsBS.mergeMap;
export const merge: <a>(sources:Wonka_types_sourceT<a>[]) => Wonka_types_sourceT<a> = Wonka_operatorsBS.merge;
export const mergeAll: <a>(source:Wonka_types_sourceT<Wonka_types_sourceT<a>>) => Wonka_types_sourceT<a> = Wonka_operatorsBS.mergeAll;
export const flatten: <T1>(_1:Wonka_types_sourceT<Wonka_types_sourceT<T1>>) => Wonka_types_sourceT<T1> = Wonka_operatorsBS.flatten;
export const onEnd: <a>(f:(() => void)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.onEnd;
export const onPush: <a>(f:((_1:a) => void)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.onPush;
export const tap: <T1>(_1:((_1:T1) => void)) => Wonka_types_operatorT<T1,T1> = Wonka_operatorsBS.tap;
export const onStart: <a>(f:(() => void)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.onStart;
export const sample: <a,b>(notifier:Wonka_types_sourceT<a>) => Wonka_types_operatorT<b,b> = Wonka_operatorsBS.sample;
export const scan: <a,acc>(f:((_1:acc, _2:a) => acc), seed:acc) => Wonka_types_operatorT<a,acc> = function <a,acc>(Arg1: any, Arg2: any) {
const result = Curry._2(Wonka_operatorsBS.scan, Arg1, Arg2);
return result
};
export const share: <a>(source:Wonka_types_sourceT<a>) => Wonka_types_sourceT<a> = Wonka_operatorsBS.share;
export const skip: <a>(wait:number) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.skip;
export const skipUntil: <a,b>(notifier:Wonka_types_sourceT<a>) => Wonka_types_operatorT<b,b> = Wonka_operatorsBS.skipUntil;
export const skipWhile: <a>(f:((_1:a) => boolean)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.skipWhile;
export const switchMap: <a,b>(f:((_1:a) => Wonka_types_sourceT<b>)) => Wonka_types_operatorT<a,b> = Wonka_operatorsBS.switchMap;
export const switchAll: <a>(source:Wonka_types_sourceT<Wonka_types_sourceT<a>>) => Wonka_types_sourceT<a> = Wonka_operatorsBS.switchAll;
export const take: <a>(max:number) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.take;
export const takeLast: <a>(max:number) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.takeLast;
export const takeUntil: <a,b>(notifier:Wonka_types_sourceT<a>) => Wonka_types_operatorT<b,b> = Wonka_operatorsBS.takeUntil;
export const takeWhile: <a>(f:((_1:a) => boolean)) => Wonka_types_operatorT<a,a> = Wonka_operatorsBS.takeWhile;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
import * as Curry from "bs-platform/lib/es6/curry.js";
import * as Wonka_helpers from "./helpers/Wonka_helpers.bs.js";
function subscribe(f) {
return (function (source) {
var state = {
talkback: Wonka_helpers.talkbackPlaceholder,
ended: false
};
Curry._1(source, (function (signal) {
if (typeof signal === "number") {
state.ended = true;
return ;
}
if (signal.tag) {
if (!state.ended) {
f(signal[0]);
return state.talkback(/* Pull */0);
} else {
return ;
}
}
var x = signal[0];
state.talkback = x;
return x(/* Pull */0);
}));
return {
unsubscribe: (function (param) {
if (!state.ended) {
state.ended = true;
return state.talkback(/* Close */1);
}
})
};
});
}
function forEach(f) {
return (function (source) {
subscribe(f)(source);
});
}
function publish(source) {
return subscribe((function (param) {
}))(source);
}
function toArray(source) {
var state = {
values: new Array(),
talkback: Wonka_helpers.talkbackPlaceholder,
value: undefined,
ended: false
};
Curry._1(source, (function (signal) {
if (typeof signal === "number") {
state.ended = true;
return ;
}
if (signal.tag) {
state.values.push(signal[0]);
return state.talkback(/* Pull */0);
}
var x = signal[0];
state.talkback = x;
return x(/* Pull */0);
}));
if (!state.ended) {
state.talkback(/* Close */1);
}
return state.values;
}
export {
subscribe ,
forEach ,
publish ,
toArray ,
}
/* No side effect */

View File

@@ -0,0 +1,24 @@
/* TypeScript file generated from Wonka_sinks.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Wonka_sinksBS = require('./Wonka_sinks.bs');
import {sourceT as Wonka_types_sourceT} from './Wonka_types.gen';
import {subscriptionT as Wonka_types_subscriptionT} from './Wonka_types.gen';
// tslint:disable-next-line:interface-over-type-literal
export type subscribeConsumerT<a> = (_1:Wonka_types_sourceT<a>) => Wonka_types_subscriptionT;
// tslint:disable-next-line:interface-over-type-literal
export type forEachConsumerT<a> = (_1:Wonka_types_sourceT<a>) => void;
export const subscribe: <a>(f:((_1:a) => void)) => subscribeConsumerT<a> = Wonka_sinksBS.subscribe;
export const forEach: <a>(f:((_1:a) => void)) => forEachConsumerT<a> = Wonka_sinksBS.forEach;
export const publish: <a>(source:Wonka_types_sourceT<a>) => Wonka_types_subscriptionT = Wonka_sinksBS.publish;
export const toArray: <a>(source:Wonka_types_sourceT<a>) => a[] = Wonka_sinksBS.toArray;

View File

@@ -0,0 +1,86 @@
open Wonka_types;
open Wonka_helpers;
type subscribeStateT = {
mutable talkback: (. talkbackT) => unit,
mutable ended: bool,
};
[@genType]
type subscribeConsumerT('a) = sourceT('a) => subscriptionT;
[@genType]
let subscribe = (f: (. 'a) => unit): subscribeConsumerT('a) =>
curry(source => {
let state: subscribeStateT = {
talkback: talkbackPlaceholder,
ended: false,
};
source((. signal) =>
switch (signal) {
| Start(x) =>
state.talkback = x;
x(. Pull);
| Push(x) when !state.ended =>
f(. x);
state.talkback(. Pull);
| Push(_) => ()
| End => state.ended = true
}
);
{
unsubscribe: () =>
if (!state.ended) {
state.ended = true;
state.talkback(. Close);
},
};
});
[@genType]
type forEachConsumerT('a) = sourceT('a) => unit;
[@genType]
let forEach = (f: (. 'a) => unit): forEachConsumerT('a) =>
curry(source => ignore(subscribe(f, source)));
[@genType]
let publish = (source: sourceT('a)): subscriptionT =>
subscribe((. _) => (), source);
type toArrayStateT('a) = {
values: Rebel.MutableQueue.t('a),
mutable talkback: (. talkbackT) => unit,
mutable value: option('a),
mutable ended: bool,
};
[@genType]
let toArray = (source: sourceT('a)): array('a) => {
let state: toArrayStateT('a) = {
values: Rebel.MutableQueue.make(),
talkback: talkbackPlaceholder,
value: None,
ended: false,
};
source((. signal) =>
switch (signal) {
| Start(x) =>
state.talkback = x;
x(. Pull);
| Push(value) =>
Rebel.MutableQueue.add(state.values, value);
state.talkback(. Pull);
| End => state.ended = true
}
);
if (!state.ended) {
state.talkback(. Close);
};
Rebel.MutableQueue.toArray(state.values);
};

View File

@@ -0,0 +1,297 @@
import * as deriving from './helpers/Wonka_deriving';
import * as sinks from './Wonka_sinks.gen';
import * as sources from './Wonka_sources.gen';
import * as web from './web/WonkaJs.gen';
import * as types from './Wonka_types.gen';
import Observable from 'zen-observable';
import callbagIterate from 'callbag-iterate';
import callbagTake from 'callbag-take';
describe('subscribe', () => {
it('sends Pull talkback signals every Push signal', () => {
let pulls = 0;
const fn = jest.fn();
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
if (pulls < 3) {
pulls++;
sink(deriving.push(0));
} else {
sink(deriving.end());
expect(pulls).toBe(3);
}
}
}));
};
sinks.subscribe(fn)(source);
expect(fn).toHaveBeenCalledTimes(3);
expect(pulls).toBe(3);
});
it('cancels when unsubscribe is called', () => {
let pulls = 0;
let closing = 0;
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
if (!pulls) {
pulls++;
sink(deriving.push(0));
}
} else if (tb === deriving.close) {
closing++;
}
}));
};
const sub = sinks.subscribe(() => {})(source);
expect(pulls).toBe(1);
sub.unsubscribe();
expect(closing).toBe(1);
});
it('ignores cancellation when the source has already ended', () => {
let pulls = 0;
let closing = 0;
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
pulls++;
sink(deriving.end());
} else if (tb === deriving.close) {
closing++;
}
}));
};
const sub = sinks.subscribe(() => {})(source);
expect(pulls).toBe(1);
sub.unsubscribe();
expect(closing).toBe(0);
});
it('ignores Push signals after the source has ended', () => {
const fn = jest.fn();
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
sink(deriving.end());
sink(deriving.push(0));
}
}));
};
sinks.subscribe(fn)(source);
expect(fn).not.toHaveBeenCalled();
});
it('ignores Push signals after cancellation', () => {
const fn = jest.fn();
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.close) {
sink(deriving.push(0));
}
}));
};
sinks.subscribe(fn)(source).unsubscribe();
expect(fn).not.toHaveBeenCalled();
});
});
describe('publish', () => {
it('sends Pull talkback signals every Push signal', () => {
let pulls = 0;
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
if (pulls < 3) {
pulls++;
sink(deriving.push(0));
} else {
sink(deriving.end());
expect(pulls).toBe(3);
}
}
}));
};
sinks.publish(source);
expect(pulls).toBe(3);
});
});
describe('toArray', () => {
it('sends Pull talkback signals every Push signal', () => {
let pulls = 0;
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
if (pulls < 3) {
pulls++;
sink(deriving.push(0));
} else {
sink(deriving.end());
expect(pulls).toBe(3);
}
}
}));
};
const array = sinks.toArray(source);
expect(array).toEqual([0, 0, 0]);
expect(pulls).toBe(3);
});
it('sends a Close talkback signal after all synchronous values have been pulled', () => {
let pulls = 0;
let ending = 0;
const source: types.sourceT<any> = sink => {
sink(deriving.start(tb => {
if (tb === deriving.pull) {
if (!pulls) {
pulls++;
sink(deriving.push(0));
}
} else if (tb === deriving.close) {
ending++;
}
}));
};
const array = sinks.toArray(source);
expect(array).toEqual([0]);
expect(ending).toBe(1);
});
});
describe('toPromise', () => {
it('creates a Promise that resolves on the last value', async () => {
let pulls = 0;
let sink = null;
const source: types.sourceT<any> = _sink => {
sink = _sink;
sink(deriving.start(tb => {
if (tb === deriving.pull)
pulls++;
}));
};
const fn = jest.fn();
const promise = web.toPromise(source).then(fn);
expect(pulls).toBe(1);
sink(deriving.push(0));
expect(pulls).toBe(2);
sink(deriving.push(1));
sink(deriving.end());
expect(fn).not.toHaveBeenCalled();
await promise;
expect(fn).toHaveBeenCalledWith(1);
});
it('creates a Promise for synchronous sources', async () => {
const fn = jest.fn();
await web.toPromise(sources.fromArray([1, 2, 3])).then(fn);
expect(fn).toHaveBeenCalledWith(3);
});
});
describe('toObservable', () => {
it('creates an Observable mirroring the Wonka source', () => {
const next = jest.fn();
const complete = jest.fn();
let pulls = 0;
let sink = null;
const source: types.sourceT<any> = _sink => {
sink = _sink;
sink(deriving.start(tb => {
if (tb === deriving.pull)
pulls++;
}));
};
Observable.from(web.toObservable(source) as any).subscribe({
next,
complete,
});
expect(pulls).toBe(1);
sink(deriving.push(0));
expect(next).toHaveBeenCalledWith(0);
sink(deriving.push(1));
expect(next).toHaveBeenCalledWith(1);
sink(deriving.end());
expect(complete).toHaveBeenCalled();
});
it('forwards cancellations from the Observable as a talkback', () => {
let ending = 0;
const source: types.sourceT<any> = sink =>
sink(deriving.start(tb => {
if (tb === deriving.close)
ending++;
}));
const sub = Observable.from(web.toObservable(source) as any).subscribe({});
expect(ending).toBe(0);
sub.unsubscribe();
expect(ending).toBe(1);
});
});
describe('toCallbag', () => {
it('creates a Callbag mirroring the Wonka source', () => {
const fn = jest.fn();
let pulls = 0;
let sink = null;
const source: types.sourceT<any> = _sink => {
sink = _sink;
sink(deriving.start(tb => {
if (tb === deriving.pull)
pulls++;
}));
};
callbagIterate(fn)(web.toCallbag(source));
expect(pulls).toBe(1);
sink(deriving.push(0));
expect(fn).toHaveBeenCalledWith(0);
sink(deriving.push(1));
expect(fn).toHaveBeenCalledWith(1);
sink(deriving.end());
});
it('forwards cancellations from the Callbag as a talkback', () => {
let ending = 0;
const fn = jest.fn();
const source: types.sourceT<any> = sink =>
sink(deriving.start(tb => {
if (tb === deriving.pull)
sink(deriving.push(0));
if (tb === deriving.close)
ending++;
}));
callbagIterate(fn)(callbagTake(1)(web.toCallbag(source) as any));
expect(fn.mock.calls).toEqual([[0]]);
expect(ending).toBe(1);
});
});

View File

@@ -0,0 +1,207 @@
import * as Block from "bs-platform/lib/es6/block.js";
import * as Wonka_helpers from "./helpers/Wonka_helpers.bs.js";
function fromArray(arr) {
return (function (sink) {
var size = arr.length;
var state = {
ended: false,
looping: false,
pulled: false,
current: 0
};
return sink(/* Start */Block.__(0, [(function (signal) {
var match = state.looping;
if (signal) {
state.ended = true;
return ;
}
if (match) {
state.pulled = true;
return ;
}
state.pulled = true;
state.looping = true;
while(state.pulled && !state.ended) {
if (state.current < size) {
var x = arr[state.current];
state.current = state.current + 1 | 0;
state.pulled = false;
sink(/* Push */Block.__(1, [x]));
} else {
state.ended = true;
sink(/* End */0);
}
};
state.looping = false;
})]));
});
}
function fromList(ls) {
return (function (sink) {
var state = {
ended: false,
looping: false,
pulled: false,
current: ls
};
return sink(/* Start */Block.__(0, [(function (signal) {
var match = state.looping;
if (signal) {
state.ended = true;
return ;
}
if (match) {
state.pulled = true;
return ;
}
state.pulled = true;
state.looping = true;
while(state.pulled && !state.ended) {
var match$1 = state.current;
if (match$1) {
state.current = match$1[1];
state.pulled = false;
sink(/* Push */Block.__(1, [match$1[0]]));
} else {
state.ended = true;
sink(/* End */0);
}
};
state.looping = false;
})]));
});
}
function fromValue(x) {
return (function (sink) {
var ended = {
contents: false
};
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
ended.contents = true;
return ;
} else if (!ended.contents) {
ended.contents = true;
sink(/* Push */Block.__(1, [x]));
return sink(/* End */0);
} else {
return ;
}
})]));
});
}
function make(f) {
return (function (sink) {
var state = {
teardown: (function () {
}),
ended: false
};
state.teardown = f({
next: (function (value) {
if (!state.ended) {
return sink(/* Push */Block.__(1, [value]));
}
}),
complete: (function (param) {
if (!state.ended) {
state.ended = true;
return sink(/* End */0);
}
})
});
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal && !state.ended) {
state.ended = true;
return state.teardown();
}
})]));
});
}
function makeSubject(param) {
var state = {
sinks: new Array(),
ended: false
};
var source = function (sink) {
state.sinks = state.sinks.concat(sink);
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
state.sinks = state.sinks.filter((function (x) {
return x !== sink;
}));
return ;
}
})]));
};
var next = function (value) {
if (!state.ended) {
state.sinks.forEach((function (sink) {
return sink(/* Push */Block.__(1, [value]));
}));
return ;
}
};
var complete = function (param) {
if (!state.ended) {
state.ended = true;
state.sinks.forEach((function (sink) {
return sink(/* End */0);
}));
return ;
}
};
return {
source: source,
next: next,
complete: complete
};
}
function empty(sink) {
var ended = {
contents: false
};
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
ended.contents = true;
return ;
} else if (!ended.contents) {
return sink(/* End */0);
} else {
return ;
}
})]));
}
function never(sink) {
return sink(/* Start */Block.__(0, [Wonka_helpers.talkbackPlaceholder]));
}
export {
fromArray ,
fromList ,
fromValue ,
make ,
makeSubject ,
empty ,
never ,
}
/* No side effect */

View File

@@ -0,0 +1,32 @@
/* TypeScript file generated from Wonka_sources.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Wonka_sourcesBS = require('./Wonka_sources.bs');
import {list} from '../src/shims/ReasonPervasives.shim';
import {observerT as Wonka_types_observerT} from './Wonka_types.gen';
import {sinkT as Wonka_types_sinkT} from './Wonka_types.gen';
import {sourceT as Wonka_types_sourceT} from './Wonka_types.gen';
import {subjectT as Wonka_types_subjectT} from './Wonka_types.gen';
import {teardownT as Wonka_types_teardownT} from './Wonka_types.gen';
export const fromArray: <a>(arr:a[]) => Wonka_types_sourceT<a> = Wonka_sourcesBS.fromArray;
export const fromList: <a>(ls:list<a>) => Wonka_types_sourceT<a> = Wonka_sourcesBS.fromList;
export const fromValue: <a>(x:a) => Wonka_types_sourceT<a> = Wonka_sourcesBS.fromValue;
export const make: <a>(f:((_1:Wonka_types_observerT<a>) => Wonka_types_teardownT)) => Wonka_types_sourceT<a> = Wonka_sourcesBS.make;
export const makeSubject: <a>() => Wonka_types_subjectT<a> = Wonka_sourcesBS.makeSubject;
export const empty: <a>(sink:Wonka_types_sinkT<a>) => void = Wonka_sourcesBS.empty;
export const never: <a>(sink:Wonka_types_sinkT<a>) => void = Wonka_sourcesBS.never;

View File

@@ -0,0 +1,193 @@
open Wonka_types;
open Wonka_helpers;
type trampolineT('a) = {
mutable ended: bool,
mutable looping: bool,
mutable pulled: bool,
mutable current: 'a,
};
[@genType]
let fromArray = (arr: array('a)): sourceT('a) =>
curry(sink => {
let size = Rebel.Array.size(arr);
let state = {ended: false, looping: false, pulled: false, current: 0};
sink(.
Start(
(. signal) =>
switch (signal, state.looping) {
| (Pull, false) =>
state.pulled = true;
state.looping = true;
while (state.pulled && !state.ended) {
if (state.current < size) {
let x = Rebel.Array.getUnsafe(arr, state.current);
state.current = state.current + 1;
state.pulled = false;
sink(. Push(x));
} else {
state.ended = true;
sink(. End);
};
};
state.looping = false;
| (Pull, true) => state.pulled = true
| (Close, _) => state.ended = true
},
),
);
});
[@genType]
let fromList = (ls: list('a)): sourceT('a) =>
curry(sink => {
let state = {ended: false, looping: false, pulled: false, current: ls};
sink(.
Start(
(. signal) =>
switch (signal, state.looping) {
| (Pull, false) =>
state.pulled = true;
state.looping = true;
while (state.pulled && !state.ended) {
switch (state.current) {
| [x, ...rest] =>
state.current = rest;
state.pulled = false;
sink(. Push(x));
| [] =>
state.ended = true;
sink(. End);
};
};
state.looping = false;
| (Pull, true) => state.pulled = true
| (Close, _) => state.ended = true
},
),
);
});
[@genType]
let fromValue = (x: 'a): sourceT('a) =>
curry(sink => {
let ended = ref(false);
sink(.
Start(
(. signal) =>
switch (signal) {
| Pull when ! ended^ =>
ended := true;
sink(. Push(x));
sink(. End);
| Pull => ()
| Close => ended := true
},
),
);
});
type makeStateT = {
mutable teardown: (. unit) => unit,
mutable ended: bool,
};
[@genType]
let make = (f: (. observerT('a)) => teardownT): sourceT('a) =>
curry(sink => {
let state: makeStateT = {teardown: (.) => (), ended: false};
state.teardown =
f(. {
next: value =>
if (!state.ended) {
sink(. Push(value));
},
complete: () =>
if (!state.ended) {
state.ended = true;
sink(. End);
},
});
sink(.
Start(
(. signal) =>
switch (signal) {
| Close when !state.ended =>
state.ended = true;
state.teardown(.);
| _ => ()
},
),
);
});
type subjectState('a) = {
mutable sinks: Rebel.Array.t(sinkT('a)),
mutable ended: bool,
};
[@genType]
let makeSubject = (): subjectT('a) => {
let state: subjectState('a) = {
sinks: Rebel.Array.makeEmpty(),
ended: false,
};
let source = sink => {
state.sinks = Rebel.Array.append(state.sinks, sink);
sink(.
Start(
(. signal) =>
switch (signal) {
| Close =>
state.sinks = Rebel.Array.filter(state.sinks, x => x !== sink)
| _ => ()
},
),
);
};
let next = value =>
if (!state.ended) {
Rebel.Array.forEach(state.sinks, sink => sink(. Push(value)));
};
let complete = () =>
if (!state.ended) {
state.ended = true;
Rebel.Array.forEach(state.sinks, sink => sink(. End));
};
{source, next, complete};
};
[@genType]
let empty = (sink: sinkT('a)): unit => {
let ended = ref(false);
sink(.
Start(
(. signal) => {
switch (signal) {
| Close => ended := true
| Pull when ! ended^ => sink(. End)
| _ => ()
}
},
),
);
};
[@genType]
let never = (sink: sinkT('a)): unit => {
sink(. Start(talkbackPlaceholder));
};

View File

@@ -0,0 +1,457 @@
import * as deriving from './helpers/Wonka_deriving';
import * as sources from './Wonka_sources.gen';
import * as operators from './Wonka_operators.gen';
import * as types from './Wonka_types.gen';
import * as web from './web/WonkaJs.gen';
import callbagFromArray from 'callbag-from-iter';
import Observable from 'zen-observable';
const collectSignals = (
source: types.sourceT<any>,
onStart?: (talkbackCb: (tb: types.talkbackT) => void) => void
) => {
let talkback = null;
const signals = [];
source(signal => {
signals.push(signal);
if (deriving.isStart(signal)) {
talkback = deriving.unboxStart(signal);
if (onStart) onStart(talkback);
talkback(deriving.pull);
} else if (deriving.isPush(signal)) {
talkback(deriving.pull);
}
})
return signals;
};
/* When a Close talkback signal is sent the source should immediately end */
const passesActiveClose = (source: types.sourceT<any>) =>
it('stops emitting when a Close talkback signal is received (spec)', () => {
let talkback = null;
const sink: types.sinkT<any> = signal => {
expect(deriving.isPush(signal)).toBeFalsy();
expect(deriving.isEnd(signal)).toBeFalsy();
if (deriving.isStart(signal)) {
talkback = deriving.unboxStart(signal);
talkback(deriving.close);
}
};
source(sink);
expect(talkback).not.toBe(null);
});
/* All synchronous, cold sources won't send anything unless a Pull signal
has been received. */
const passesColdPull = (source: types.sourceT<any>) =>
it('sends nothing when no Pull talkback signal has been sent (spec)', () => {
let pushes = 0;
let talkback = null;
const sink: types.sinkT<any> = signal => {
if (deriving.isPush(signal)) {
pushes++;
} else if (deriving.isStart(signal)) {
talkback = deriving.unboxStart(signal);
}
};
source(sink);
expect(talkback).not.toBe(null);
expect(pushes).toBe(0);
setTimeout(() => {
expect(pushes).toBe(0);
talkback(deriving.pull);
}, 10);
jest.runAllTimers();
expect(pushes).toBe(1);
});
/* All synchronous, cold sources need to use trampoline scheduling to avoid
recursively sending more and more Push signals which would eventually lead
to a call stack overflow when too many values are emitted. */
const passesTrampoline = (source: types.sourceT<any>) =>
it('uses trampoline scheduling instead of recursive push signals (spec)', () => {
let talkback = null;
let pushes = 0;
const signals = [];
const sink: types.sinkT<any> = signal => {
if (deriving.isPush(signal)) {
const lastPushes = ++pushes;
signals.push(signal);
talkback(deriving.pull);
expect(lastPushes).toBe(pushes);
} else if (deriving.isStart(signal)) {
talkback = deriving.unboxStart(signal);
talkback(deriving.pull);
expect(pushes).toBe(2);
} else if (deriving.isEnd(signal)) {
signals.push(signal);
expect(pushes).toBe(2);
}
};
source(sink);
expect(signals).toEqual([
deriving.push(1),
deriving.push(2),
deriving.end(),
]);
});
beforeEach(() => {
jest.useFakeTimers();
});
describe('fromArray', () => {
passesTrampoline(sources.fromArray([1, 2]));
passesColdPull(sources.fromArray([0]));
passesActiveClose(sources.fromArray([0]));
});
describe('fromList', () => {
passesTrampoline(sources.fromList([1, [2]] as any));
passesColdPull(sources.fromList([0] as any));
passesActiveClose(sources.fromList([0] as any));
});
describe('fromValue', () => {
passesColdPull(sources.fromValue(0));
passesActiveClose(sources.fromValue(0));
it('sends a single value and ends', () => {
expect(collectSignals(sources.fromValue(1))).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.end()
]);
});
});
describe('merge', () => {
const source = operators.merge<any>([
sources.fromValue(0),
sources.empty
]);
passesColdPull(source);
passesActiveClose(source);
it('correctly merges two sources where the second is empty', () => {
const source = operators.merge<any>([
sources.fromValue(0),
sources.empty
]);
expect(collectSignals(source)).toEqual([
deriving.start(expect.any(Function)),
deriving.push(0),
deriving.end(),
]);
});
it('correctly merges hot sources', () => {
const onStart = jest.fn();
const source = operators.merge<any>([
operators.onStart(onStart)(sources.never),
operators.onStart(onStart)(sources.fromArray([1, 2])),
]);
const signals = collectSignals(source);
expect(onStart).toHaveBeenCalledTimes(2);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.push(2),
]);
});
it('correctly merges asynchronous sources', () => {
jest.useFakeTimers();
const onStart = jest.fn();
const source = operators.merge<any>([
operators.onStart(onStart)(sources.fromValue(-1)),
operators.onStart(onStart)(
operators.take(2)(web.interval(50))
),
]);
const signals = collectSignals(source);
jest.advanceTimersByTime(100);
expect(onStart).toHaveBeenCalledTimes(2);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(-1),
deriving.push(0),
deriving.push(1),
deriving.end(),
]);
});
});
describe('concat', () => {
const source = operators.concat<any>([
sources.fromValue(0),
sources.empty
]);
passesColdPull(source);
passesActiveClose(source);
it('correctly concats two sources where the second is empty', () => {
const source = operators.concat<any>([
sources.fromValue(0),
sources.empty
]);
expect(collectSignals(source)).toEqual([
deriving.start(expect.any(Function)),
deriving.push(0),
deriving.end(),
]);
});
});
describe('make', () => {
it('may be used to create async sources', () => {
const teardown = jest.fn();
const source = sources.make(observer => {
setTimeout(() => observer.next(1), 10);
setTimeout(() => observer.complete(), 20);
return teardown;
});
const signals = collectSignals(source);
expect(signals).toEqual([deriving.start(expect.any(Function))]);
jest.runAllTimers();
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.end(),
]);
});
it('supports active cancellation', () => {
const teardown = jest.fn();
const source = sources.make(() => teardown);
const sink: types.sinkT<any> = signal => {
expect(deriving.isPush(signal)).toBeFalsy();
expect(deriving.isEnd(signal)).toBeFalsy();
if (deriving.isStart(signal))
setTimeout(() => deriving.unboxStart(signal)(deriving.close));
};
source(sink);
expect(teardown).not.toHaveBeenCalled();
jest.runAllTimers();
expect(teardown).toHaveBeenCalled();
});
});
describe('makeSubject', () => {
it('may be used to emit signals programmatically', () => {
const { source, next, complete } = sources.makeSubject();
const signals = collectSignals(source);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
]);
next(1);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
]);
complete();
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.end(),
]);
});
it('ignores signals after complete has been called', () => {
const { source, next, complete } = sources.makeSubject();
const signals = collectSignals(source);
complete();
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.end(),
]);
next(1);
complete();
expect(signals.length).toBe(2);
});
});
describe('never', () => {
it('emits nothing and ends immediately', () => {
const signals = collectSignals(sources.never);
expect(signals).toEqual([deriving.start(expect.any(Function)) ]);
});
});
describe('empty', () => {
it('emits nothing and ends immediately', () => {
const signals = collectSignals(sources.empty);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.end(),
]);
});
});
describe('fromPromise', () => {
passesActiveClose(web.fromPromise(Promise.resolve(null)));
it('emits a value when the promise resolves', async () => {
const promise = Promise.resolve(1);
const signals = collectSignals(web.fromPromise(promise));
expect(signals).toEqual([
deriving.start(expect.any(Function)),
]);
await promise;
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.end(),
]);
});
});
describe('fromObservable', () => {
beforeEach(() => {
jest.useRealTimers();
});
it('converts an Observable to a Wonka source', async () => {
const source = web.fromObservable(Observable.from([1, 2]));
const signals = collectSignals(source);
await new Promise(resolve => setTimeout(resolve));
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.push(2),
deriving.end(),
]);
});
it('supports cancellation on converted Observables', async () => {
const source = web.fromObservable(Observable.from([1, 2]));
const signals = collectSignals(source, talkback => {
talkback(deriving.close);
});
await new Promise(resolve => setTimeout(resolve));
expect(signals).toEqual([
deriving.start(expect.any(Function)),
]);
});
});
describe('fromCallbag', () => {
it('converts a Callbag to a Wonka source', () => {
const source = web.fromCallbag(callbagFromArray([1, 2]));
const signals = collectSignals(source);
expect(signals).toEqual([
deriving.start(expect.any(Function)),
deriving.push(1),
deriving.push(2),
deriving.end(),
]);
});
it('supports cancellation on converted Observables', () => {
const source = web.fromCallbag(callbagFromArray([1, 2]));
const signals = collectSignals(source, talkback => {
talkback(deriving.close);
});
expect(signals).toEqual([
deriving.start(expect.any(Function)),
]);
});
});
describe('interval', () => {
it('emits Push signals until Cancel is sent', () => {
let pushes = 0;
let talkback = null;
const sink: types.sinkT<any> = signal => {
if (deriving.isPush(signal)) {
pushes++;
} else if (deriving.isStart(signal)) {
talkback = deriving.unboxStart(signal);
}
};
web.interval(100)(sink);
expect(talkback).not.toBe(null);
expect(pushes).toBe(0);
jest.advanceTimersByTime(100);
expect(pushes).toBe(1);
jest.advanceTimersByTime(100);
expect(pushes).toBe(2);
talkback(deriving.close);
jest.advanceTimersByTime(100);
expect(pushes).toBe(2);
});
});
describe('fromDomEvent', () => {
it('emits Push signals for events on a DOM element', () => {
let talkback = null;
const element = {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
};
const sink: types.sinkT<any> = signal => {
expect(deriving.isEnd(signal)).toBeFalsy();
if (deriving.isStart(signal))
talkback = deriving.unboxStart(signal);
};
web.fromDomEvent(element as any, 'click')(sink);
expect(element.addEventListener).toHaveBeenCalledWith('click', expect.any(Function));
expect(element.removeEventListener).not.toHaveBeenCalled();
const listener = element.addEventListener.mock.calls[0][1];
listener(1);
listener(2);
talkback(deriving.close);
expect(element.removeEventListener).toHaveBeenCalledWith('click', listener);
});
});

View File

@@ -0,0 +1 @@
/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */

View File

@@ -0,0 +1,38 @@
/* TypeScript file generated from Wonka_types.re by genType. */
/* eslint-disable import/first */
import {signalT as $$signalT} from './shims/Js.shim';
import {talkbackT as $$talkbackT} from './shims/Js.shim';
// tslint:disable-next-line:interface-over-type-literal
export type talkbackT = $$talkbackT;
// tslint:disable-next-line:interface-over-type-literal
export type signalT<a> = $$signalT<a>;
// tslint:disable-next-line:interface-over-type-literal
export type sinkT<a> = (_1:signalT<a>) => void;
// tslint:disable-next-line:interface-over-type-literal
export type sourceT<a> = (_1:sinkT<a>) => void;
// tslint:disable-next-line:interface-over-type-literal
export type operatorT<a,b> = (_1:sourceT<a>) => sourceT<b>;
// tslint:disable-next-line:interface-over-type-literal
export type teardownT = () => void;
// tslint:disable-next-line:interface-over-type-literal
export type subscriptionT = { readonly unsubscribe: () => void };
// tslint:disable-next-line:interface-over-type-literal
export type observerT<a> = { readonly next: (_1:a) => void; readonly complete: () => void };
// tslint:disable-next-line:interface-over-type-literal
export type subjectT<a> = {
readonly source: sourceT<a>;
readonly next: (_1:a) => void;
readonly complete: () => void
};

View File

@@ -0,0 +1,72 @@
/* A sink has the signature: `signalT('a) => unit`
* A source thus has the signature: `sink => unit`, or `(signalT('a) => unit) => unit`
*
* Effectively a sink is a callback receiving signals as its first argument.
* - Start(talkback) will be carrying a talkback using which the sink can attempt
* to pull values (Pull) or request the source to end its stream (End)
* - Push(payload) carries a value that the source sends to the sink.
* This can happen at any time, since a source can be both pullable or
* merely listenable.
* - End signifies the end of the source stream, be it because of a talkback (End)
* or because the source is exhausted.
*
* In detail, a talkback is simply a callback that receives a talkback signal as
* its first argument. It's thus typically anonymously created by the source.
*
* A source is a factory that accepts a sink. Calling a source with a sink will
* instantiate and initiate the source's stream, after which the source sends the sink
* a talkback (Start(talkback)). This is called the "handshake".
*
* Typically an operator factory won't call the source with a sink it receives
* immediately—because this would cause the operator to simply be a noop—but instead
* it will create an intermediate sink with the same signature to perform its own
* logic.
*
* At that point the operator can for instance intercept the talkback for its own
* purposes, or call the actual sink as it sees fit.
*/
[@genType.import "./shims/Js.shim"]
type talkbackT =
| Pull
| Close;
[@genType.import "./shims/Js.shim"]
type signalT('a) =
| Start((. talkbackT) => unit)
| Push('a)
| End;
[@genType]
type sinkT('a) = (. signalT('a)) => unit;
[@genType]
type sourceT('a) = sinkT('a) => unit;
[@genType]
type operatorT('a, 'b) = sourceT('a) => sourceT('b);
[@genType]
type teardownT = (. unit) => unit;
[@genType]
type subscriptionT = {unsubscribe: unit => unit};
[@genType]
type observerT('a) = {
next: 'a => unit,
complete: unit => unit,
};
[@genType]
type subjectT('a) = {
source: sourceT('a),
next: 'a => unit,
complete: unit => unit,
};
/* Sinks and sources need to explicitly be their own callbacks;
* This means that currying needs to be forced for Bucklescript
* not to optimise them away
*/
external curry: 'a => 'a = "%identity";

View File

@@ -0,0 +1,10 @@
EXCLUDE_QUERY_DIR
B /Users/phil/.esy/3____________________________________________________________________/i/belt-d295eda5/lib/belt
B /Users/phil/Development/wonka/_esy/default/store/b/wonka-3b561974/default/src/dune/.wonka.objs/byte
S /Users/phil/.esy/3____________________________________________________________________/i/belt-d295eda5/lib/belt
S ..
S .
S ../helpers
S ../include/rebel_native
FLG -ppx '/Users/phil/Development/wonka/_esy/default/store/b/wonka-3b561974/default/.ppx/1532bed008d8875f00cab31e45a595e2/ppx.exe --as-ppx --cookie '\''library-name="wonka"'\'''
FLG -open Wonka__ -w -40

View File

@@ -0,0 +1 @@
include Rebel_native;

View File

@@ -0,0 +1,4 @@
module Types = Wonka_types;
include Wonka_sources;
include Wonka_operators;
include Wonka_sinks;

View File

@@ -0,0 +1,9 @@
(library
(name wonka)
(public_name wonka)
(libraries belt)
(preprocess (pps belt.ppx)))
(copy_files ../include/rebel_native/*.{re,rei})
(copy_files ../Wonka_*.{re,rei})
(copy_files ../helpers/Wonka_*.{re,rei})

View File

@@ -0,0 +1,28 @@
import { __ as block } from 'bs-platform/lib/es6/block';
import { talkbackPlaceholder } from './Wonka_helpers.bs';
import {
talkbackT,
signalT
} from '../Wonka_types.gen';
type talkbackCb = (tb: talkbackT) => void;
export const pull = (0 as any as talkbackT);
export const close = (1 as any as talkbackT);
export const start = <a>(tb: talkbackCb): signalT<a> => block(0, [tb]) as any;
export const push = <a>(x: a): signalT<a> => block(1, [x]) as any;
export const end = <a>(): signalT<a> => 0 as any;
export const isStart = <a>(s: signalT<a>) =>
typeof s !== 'number' && (s as any).tag === 0;
export const isPush = <a>(s: signalT<a>) =>
typeof s !== 'number' && (s as any).tag === 1;
export const isEnd = <a>(s: signalT<a>) =>
typeof s === 'number' && (s as any) === 0;
export const unboxPush = <a>(s: signalT<a>): a | null =>
isPush(s) ? (s as any)[0] : null;
export const unboxStart = <a>(s: signalT<a>): talkbackCb =>
isStart(s) ? (s as any)[0] : (talkbackPlaceholder as any);

View File

@@ -0,0 +1,9 @@
// @flow
import * as Wonka from '../../';
Wonka.pipe(
Wonka.fromArray([1, 2, 3]),
Wonka.map(x => x * 2),
Wonka.publish
);

View File

@@ -0,0 +1,12 @@
function talkbackPlaceholder(param) {
}
export {
talkbackPlaceholder ,
}
/* No side effect */

View File

@@ -0,0 +1,3 @@
open Wonka_types;
let talkbackPlaceholder = (. _: talkbackT) => ();

View File

@@ -0,0 +1,161 @@
import { sourceT as Source } from '../Wonka_types.gen';
interface UnaryFn<T, R> {
(source: T): R;
}
/* pipe definitions for source + operators composition */
function pipe<T, A>(source: Source<T>, op1: UnaryFn<Source<T>, Source<A>>): Source<A>;
function pipe<T, A, B>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>
): Source<B>;
function pipe<T, A, B, C>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>
): Source<C>;
function pipe<T, A, B, C, D>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>
): Source<D>;
function pipe<T, A, B, C, D, E>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>
): Source<E>;
function pipe<T, A, B, C, D, E, F>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>
): Source<F>;
function pipe<T, A, B, C, D, E, F, G>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>,
op7: UnaryFn<Source<F>, Source<G>>
): Source<G>;
function pipe<T, A, B, C, D, E, F, G, H>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>,
op7: UnaryFn<Source<F>, Source<G>>,
op8: UnaryFn<Source<G>, Source<H>>
): Source<H>;
/* pipe definitions for source + operators + consumer composition */
function pipe<T, R>(source: Source<T>, consumer: UnaryFn<Source<T>, R>): R;
function pipe<T, A, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
consumer: UnaryFn<Source<A>, R>
): R;
function pipe<T, A, B, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
consumer: UnaryFn<Source<B>, R>
): R;
function pipe<T, A, B, C, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
consumer: UnaryFn<Source<C>, R>
): R;
function pipe<T, A, B, C, D, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
consumer: UnaryFn<Source<D>, R>
): R;
function pipe<T, A, B, C, D, E, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
consumer: UnaryFn<Source<E>, R>
): R;
function pipe<T, A, B, C, D, E, F, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>,
consumer: UnaryFn<Source<F>, R>
): R;
function pipe<T, A, B, C, D, E, F, G, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>,
op7: UnaryFn<Source<F>, Source<G>>,
consumer: UnaryFn<Source<G>, R>
): R;
function pipe<T, A, B, C, D, E, F, G, H, R>(
source: Source<T>,
op1: UnaryFn<Source<T>, Source<A>>,
op2: UnaryFn<Source<A>, Source<B>>,
op3: UnaryFn<Source<B>, Source<C>>,
op4: UnaryFn<Source<C>, Source<D>>,
op5: UnaryFn<Source<D>, Source<E>>,
op6: UnaryFn<Source<E>, Source<F>>,
op7: UnaryFn<Source<F>, Source<G>>,
op8: UnaryFn<Source<G>, Source<H>>,
consumer: UnaryFn<Source<H>, R>
): R;
function pipe() {
let x = arguments[0];
for (let i = 1, l = arguments.length; i < l; i++)
x = arguments[i](x);
return x;
}
export { pipe };

View File

@@ -0,0 +1,17 @@
import * as Rebel_js from "./rebel_js/Rebel_js.bs.js";
var $$Array = Rebel_js.$$Array;
var MutableStack = Rebel_js.MutableStack;
var MutableQueue = Rebel_js.MutableQueue;
export {
$$Array ,
MutableStack ,
MutableQueue ,
}
/* No side effect */

View File

@@ -0,0 +1,9 @@
#if BS_NATIVE then
#if BSB_BACKEND = "js" then
include Rebel_js
#else
include Rebel_native
#end
#else
include Rebel_js
#end

View File

@@ -0,0 +1,62 @@
import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
function make(len, vals) {
var res = new Array(len);
res.fill(vals);
return res;
}
function set(arr, index, x) {
if (index < arr.length && index >= 0) {
arr[index] = x;
return true;
} else {
return false;
}
}
function reverse(arr) {
var res = arr.slice();
arr.reverse();
return res;
}
function includes(arr, x) {
return arr.indexOf(x) > -1;
}
function remove(arr, pos) {
return arr.slice().splice(pos);
}
function removeCount(arr, pos, count) {
return arr.slice().splice(pos, count);
}
function splice(arr, pos, remove, add) {
return arr.slice().splice(pos, remove, add);
}
var Js = {
splice: splice
};
var shuffle = Belt_Array.shuffle;
var shuffleInPlace = Belt_Array.shuffleInPlace;
export {
shuffle ,
shuffleInPlace ,
make ,
set ,
reverse ,
includes ,
remove ,
removeCount ,
Js ,
}
/* No side effect */

View File

@@ -0,0 +1,94 @@
type t('a) = array('a);
[@bs.new] external makeEmpty: unit => t('a) = "Array";
[@bs.new] external makeUninitialized: int => t('a) = "Array";
[@bs.get] external size: t('a) => int = "length";
[@bs.get_index] external get: (t('a), int) => option('a) = "";
[@bs.get_index] external getUnsafe: (t('a), int) => 'a = "";
[@bs.set_index] external setUnsafe: (t('a), int, 'a) => unit = "";
[@bs.send] external fill: (t('a), 'a) => unit = "fill";
[@bs.send] external reverseInPlace: t('a) => unit = "reverse";
[@bs.send] external copy: t('a) => t('a) = "slice";
[@bs.send]
external slice: (t('a), ~start: int, ~end_: int) => t('a) = "slice";
[@bs.send] external append: (t('a), 'a) => t('a) = "concat";
[@bs.send] external concat: (t('a), t('a)) => t('a) = "concat";
[@bs.send] external map: (t('a), 'a => 'b) => t('b) = "map";
[@bs.send] external mapi: (t('a), ('a, int) => 'b) => t('b) = "map";
[@bs.send] external some: (t('a), 'a => bool) => bool = "some";
[@bs.send] external somei: (t('a), ('a, int) => bool) => bool = "some";
[@bs.send] external every: (t('a), 'a => bool) => bool = "every";
[@bs.send] external everyi: (t('a), ('a, int) => bool) => bool = "every";
[@bs.send] external filter: (t('a), 'a => bool) => t('a) = "filter";
[@bs.send] external filteri: (t('a), ('a, int) => bool) => t('a) = "filter";
[@bs.send] external find: (t('a), 'a => bool) => option('a) = "find";
[@bs.send] external findi: (t('a), ('a, int) => bool) => option('a) = "find";
[@bs.send] external findIndex: (t('a), 'a => bool) => int = "findIndex";
[@bs.send] external forEach: (t('a), 'a => unit) => unit = "forEach";
[@bs.send] external forEachi: (t('a), ('a, int) => unit) => unit = "forEach";
[@bs.send] external reduce: (t('a), ('b, 'a) => 'b, 'b) => 'b = "reduce";
[@bs.send]
external reduceRight: (t('a), ('b, 'a) => 'b, 'b) => 'b = "reduceRight";
[@bs.send] external indexOf: (t('a), 'a) => int = "indexOf";
[@bs.send] external lastIndexOf: (t('a), 'a) => int = "lastIndexOf";
/* No need to replicate what Belt already has */
let shuffle = Belt.Array.shuffle;
let shuffleInPlace = Belt.Array.shuffleInPlace;
let make = (len: int, vals: 'a): t('a) => {
let res = makeUninitialized(len);
fill(res, vals);
res;
};
let set = (arr: t('a), index: int, x: 'a) =>
if (index < size(arr) && index >= 0) {
setUnsafe(arr, index, x);
true;
} else {
false;
};
let reverse = (arr: t('a)): t('a) => {
let res = copy(arr);
reverseInPlace(arr);
res;
};
let includes = (arr: t('a), x: 'a): bool => indexOf(arr, x) > (-1);
[@bs.send] external removeInPlace: (t('a), int) => t('a) = "splice";
[@bs.send]
external removeCountInPlace: (t('a), ~pos: int, ~count: int) => t('a) =
"splice";
let remove = (arr: t('a), pos: int) => removeInPlace(copy(arr), pos);
let removeCount = (arr: t('a), ~pos: int, ~count: int) =>
removeCountInPlace(copy(arr), ~pos, ~count);
module Js = {
[@bs.send] external push: (t('a), 'a) => unit = "push";
[@bs.send] external pop: t('a) => option('a) = "pop";
[@bs.send] external unshift: (t('a), 'a) => unit = "unshift";
[@bs.send] external shift: t('a) => option('a) = "shift";
[@bs.scope ("Array", "prototype", "push")] [@bs.val]
external pushMany: (t('a), t('a)) => unit = "apply";
[@bs.scope ("Array", "prototype", "unshift")] [@bs.val]
external unshiftMany: (t('a), t('a)) => unit = "apply";
[@bs.send]
external spliceInPlace:
(t('a), ~pos: int, ~remove: int, ~add: t('a)) => t('a) =
"splice";
let splice = (arr: t('a), ~pos: int, ~remove: int, ~add: t('a)) =>
spliceInPlace(copy(arr), ~pos, ~remove, ~add);
};

View File

@@ -0,0 +1,32 @@
import * as Curry from "bs-platform/lib/es6/curry.js";
function isEmpty(q) {
return q.length === 0;
}
function reduceU(q, accu, f) {
return q.reduce((function (acc, x) {
return f(acc, x);
}), accu);
}
function reduce(q, accu, f) {
return q.reduce(Curry.__2(f), accu);
}
function transfer(q1, q2) {
Array.prototype.push.apply(q1, q2);
q1.length = 0;
}
export {
isEmpty ,
reduceU ,
reduce ,
transfer ,
}
/* No side effect */

View File

@@ -0,0 +1,31 @@
type t('a) = array('a);
external fromArray: array('a) => t('a) = "%identity";
external toArray: t('a) => array('a) = "%identity";
[@bs.new] external make: unit => t('a) = "Array";
[@bs.set] external clear: (t('a), [@bs.as 0] _) => unit = "length";
[@bs.send] external add: (t('a), 'a) => unit = "push";
[@bs.get] external peek: t('a) => option('a) = "0";
[@bs.send] external pop: t('a) => option('a) = "shift";
[@bs.send] external copy: t('a) => t('a) = "slice";
[@bs.get] external size: t('a) => int = "length";
[@bs.send] external mapU: (t('a), (. 'a) => 'b) => t('b) = "map";
[@bs.send] external map: (t('a), 'a => 'b) => t('b) = "map";
[@bs.send] external forEachU: (t('a), (. 'a) => unit) => unit = "forEach";
[@bs.send] external forEach: (t('a), 'a => unit) => unit = "forEach";
let isEmpty = (q: t('a)): bool => size(q) === 0;
let reduceU = (q: t('a), accu: 'b, f: (. 'b, 'a) => 'b): 'b =>
Js.Array.reduce((acc, x) => f(. acc, x), accu, q);
let reduce = (q: t('a), accu: 'b, f: ('b, 'a) => 'b): 'b =>
Js.Array.reduce(f, accu, q);
[@bs.scope ("Array", "prototype", "push")] [@bs.val]
external addMany: (t('a), t('a)) => unit = "apply";
let transfer = (q1: t('a), q2: t('a)) => {
addMany(q1, q2);
clear(q1);
};

View File

@@ -0,0 +1,20 @@
var Helpers = { };
function isEmpty(stack) {
return stack.length === 0;
}
function top(stack) {
return stack[stack.length - 1 | 0];
}
export {
Helpers ,
isEmpty ,
top ,
}
/* No side effect */

View File

@@ -0,0 +1,19 @@
type t('a) = array('a);
module Helpers = {
[@bs.get_index] external get: (t('a), int) => option('a) = "";
};
[@bs.new] external make: unit => t('a) = "Array";
[@bs.set] external clear: (t('a), [@bs.as 0] _) => unit = "length";
[@bs.send] external push: (t('a), 'a) => unit = "push";
[@bs.send] external pop: t('a) => option('a) = "pop";
[@bs.send] external copy: t('a) => t('a) = "slice";
[@bs.get] external size: t('a) => int = "length";
[@bs.send] external forEachU: (t('a), (. 'a) => unit) => unit = "forEach";
[@bs.send] external forEach: (t('a), 'a => unit) => unit = "forEach";
let isEmpty = (stack: t('a)): bool => size(stack) === 0;
let top = (stack: t('a)): option('a) =>
Helpers.get(stack, size(stack) - 1);

View File

@@ -0,0 +1,16 @@
var $$Array;
var MutableStack;
var MutableQueue;
export {
$$Array ,
MutableStack ,
MutableQueue ,
}
/* No side effect */

View File

@@ -0,0 +1,3 @@
module Array = Array_js;
module MutableStack = MutableStack_js;
module MutableQueue = MutableQueue_js;

View File

@@ -0,0 +1,282 @@
import * as $$Array from "bs-platform/lib/es6/array.js";
import * as Curry from "bs-platform/lib/es6/curry.js";
import * as Belt_Array from "bs-platform/lib/es6/belt_Array.js";
import * as Caml_option from "bs-platform/lib/es6/caml_option.js";
function makeEmpty(param) {
return [];
}
function makeUninitialized(prim) {
return new Array(prim);
}
function size(prim) {
return prim.length;
}
function getUnsafe(prim, prim$1) {
return prim[prim$1];
}
function setUnsafe(prim, prim$1, prim$2) {
prim[prim$1] = prim$2;
}
function fill(arr, x) {
return Belt_Array.fill(arr, 0, arr.length, x);
}
function copy(prim) {
return prim.slice(0);
}
function slice(arr, start, end_) {
var len = end_ - start | 0;
return Belt_Array.slice(arr, start, len);
}
function append(arr, x) {
return Belt_Array.concat(arr, [x]);
}
function somei(arr, f) {
var len = arr.length;
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return false;
}
if (Curry._2(f, arr[i], i)) {
return true;
}
_i = i + 1 | 0;
continue ;
};
}
function everyi(arr, f) {
var len = arr.length;
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return true;
}
if (!Curry._2(f, arr[i], i)) {
return false;
}
_i = i + 1 | 0;
continue ;
};
}
function findi(arr, f) {
var len = arr.length;
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return ;
}
var x = arr[i];
if (Curry._2(f, x, i)) {
return Caml_option.some(x);
}
_i = i + 1 | 0;
continue ;
};
}
function findIndex(arr, f) {
var len = arr.length;
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return -1;
}
if (Curry._1(f, arr[i])) {
return i;
}
_i = i + 1 | 0;
continue ;
};
}
function lastIndexOf(arr, x) {
var len = arr.length;
var _i = len - 1 | 0;
while(true) {
var i = _i;
if (i < 0) {
return -1;
}
if (x === arr[i]) {
return i;
}
_i = i - 1 | 0;
continue ;
};
}
function filteri(arr, f) {
var len = arr.length;
var res = arr.slice(0);
var j = {
contents: -1
};
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return $$Array.sub(res, 0, j.contents + 1 | 0);
}
var x = arr[i];
if (Curry._2(f, x, i)) {
j.contents = j.contents + 1 | 0;
arr[j.contents] = x;
}
_i = i + 1 | 0;
continue ;
};
}
function removeCount(arr, pos, count) {
var len = arr.length;
var pos2 = (pos + count | 0) - 1 | 0;
var res = $$Array.sub(arr, 0, len - count | 0);
var _i = 0;
while(true) {
var i = _i;
if (i >= len) {
return res;
}
if (i >= pos && i <= pos2) {
_i = i + 1 | 0;
continue ;
}
var j = i > pos2 ? i - count | 0 : i;
arr[j] = arr[i];
_i = i + 1 | 0;
continue ;
};
}
function find(arr, f) {
return findi(arr, (function (x, _i) {
return Curry._1(f, x);
}));
}
function indexOf(arr, x) {
return findIndex(arr, (function (item) {
return item === x;
}));
}
function includes(arr, x) {
return findIndex(arr, (function (item) {
return item === x;
})) > -1;
}
function filter(arr, f) {
return filteri(arr, (function (x, _i) {
return Curry._1(f, x);
}));
}
function remove(arr, index) {
return removeCount(arr, index, 1);
}
function mapi(arr, f) {
return Belt_Array.mapWithIndexU(arr, (function (i, x) {
return Curry._2(f, x, i);
}));
}
function forEachi(arr, f) {
return Belt_Array.forEachWithIndexU(arr, (function (i, x) {
return Curry._2(f, x, i);
}));
}
function reduce(arr, reducer, acc) {
return Belt_Array.reduce(arr, acc, reducer);
}
function reduceRight(arr, reducer, acc) {
return Belt_Array.reduceReverse(arr, acc, reducer);
}
var make = Belt_Array.make;
var get = Belt_Array.get;
var set = Belt_Array.set;
var reverseInPlace = Belt_Array.reverseInPlace;
var reverse = Belt_Array.reverse;
var shuffle = Belt_Array.shuffle;
var shuffleInPlace = Belt_Array.shuffleInPlace;
var concat = Belt_Array.concat;
var some = Belt_Array.some;
var every = Belt_Array.every;
var map = Belt_Array.map;
var forEach = Belt_Array.forEach;
export {
makeEmpty ,
makeUninitialized ,
make ,
size ,
get ,
getUnsafe ,
set ,
setUnsafe ,
fill ,
reverseInPlace ,
reverse ,
shuffle ,
shuffleInPlace ,
copy ,
slice ,
concat ,
append ,
somei ,
everyi ,
findi ,
findIndex ,
lastIndexOf ,
filteri ,
removeCount ,
find ,
indexOf ,
includes ,
filter ,
remove ,
some ,
every ,
map ,
mapi ,
forEach ,
forEachi ,
reduce ,
reduceRight ,
}
/* No side effect */

View File

@@ -0,0 +1,166 @@
type t('a) = array('a);
let makeEmpty = (): t('a) => [||];
let makeUninitialized = Belt.Array.makeUninitializedUnsafe;
let make = Belt.Array.make;
let size = Belt.Array.size;
let get = Belt.Array.get;
let getUnsafe = Belt.Array.getUnsafe;
let set = Belt.Array.set;
let setUnsafe = Belt.Array.setUnsafe;
let fill = (arr: t('a), x: 'a) =>
Belt.Array.fill(arr, ~offset=0, ~len=size(arr), x);
let reverseInPlace = Belt.Array.reverseInPlace;
let reverse = Belt.Array.reverse;
let shuffle = Belt.Array.shuffle;
let shuffleInPlace = Belt.Array.shuffleInPlace;
let copy = Belt.Array.copy;
let slice = (arr: t('a), ~start: int, ~end_: int): t('a) => {
let len = end_ - start;
Belt.Array.slice(arr, ~offset=start, ~len);
};
let concat = Belt.Array.concat;
let append = (arr: t('a), x: 'a) => Belt.Array.concat(arr, [|x|]);
let somei = (arr: t('a), f: ('a, int) => bool): bool => {
let len = size(arr);
let rec search = (i: int) =>
if (i >= len) {
false;
} else if (f(getUnsafe(arr, i), i)) {
true;
} else {
search(i + 1);
};
search(0);
};
let everyi = (arr: t('a), f: ('a, int) => bool): bool => {
let len = size(arr);
let rec search = (i: int) =>
if (i >= len) {
true;
} else if (!f(getUnsafe(arr, i), i)) {
false;
} else {
search(i + 1);
};
search(0);
};
let findi = (arr: t('a), f: ('a, int) => bool): option('a) => {
let len = size(arr);
let rec search = (i: int) =>
if (i >= len) {
None;
} else {
let x = getUnsafe(arr, i);
if (f(x, i)) {
Some(x);
} else {
search(i + 1);
};
};
search(0);
};
let findIndex = (arr: t('a), f: 'a => bool): int => {
let len = size(arr);
let rec search = (i: int) =>
if (i >= len) {
(-1);
} else if (f(getUnsafe(arr, i))) {
i;
} else {
search(i + 1);
};
search(0);
};
let lastIndexOf = (arr: t('a), x: 'a): int => {
let len = size(arr);
let rec search = (i: int) =>
if (i < 0) {
(-1);
} else if (x === getUnsafe(arr, i)) {
i;
} else {
search(i - 1);
};
search(len - 1);
};
let filteri = (arr: t('a), f: ('a, int) => bool): t('a) => {
let len = size(arr);
let res: t('a) = copy(arr);
let j = ref(-1);
let rec filter = (i: int) =>
if (i >= len) {
Array.sub(res, 0, j^ + 1);
} else {
let x = getUnsafe(arr, i);
if (f(x, i)) {
j := j^ + 1;
Belt.Array.setUnsafe(arr, j^, x);
};
filter(i + 1);
};
filter(0);
};
let removeCount = (arr: t('a), ~pos: int, ~count: int): t('a) => {
let len = size(arr);
let pos2 = pos + count - 1;
let res = Array.sub(arr, 0, len - count);
let rec copy = (i: int) =>
if (i >= len) {
res;
} else if (i >= pos && i <= pos2) {
copy(i + 1);
} else {
let j = i > pos2 ? i - count : i;
Belt.Array.setUnsafe(arr, j, Belt.Array.getUnsafe(arr, i));
copy(i + 1);
};
copy(0);
};
let find = (arr: t('a), f: 'a => bool): option('a) =>
findi(arr, (x, _i) => f(x));
let indexOf = (arr: t('a), x: 'a): int => findIndex(arr, item => item === x);
let includes = (arr: t('a), x: 'a): bool =>
findIndex(arr, item => item === x) > (-1);
let filter = (arr: t('a), f: 'a => bool): t('a) =>
filteri(arr, (x, _i) => f(x));
let remove = (arr: t('a), index: int): t('a) =>
removeCount(arr, ~pos=index, ~count=1);
let some = Belt.Array.some;
let every = Belt.Array.every;
let map = Belt.Array.map;
let mapi = (arr: t('a), f: ('a, int) => 'b): t('b) =>
Belt.Array.mapWithIndexU(arr, (. i, x) => f(x, i));
let forEach = Belt.Array.forEach;
let forEachi = (arr: t('a), f: ('a, int) => unit): unit =>
Belt.Array.forEachWithIndexU(arr, (. i, x) => f(x, i));
let reduce = (arr: t('a), reducer: ('b, 'a) => 'b, acc: 'b): 'b =>
Belt.Array.reduce(arr, acc, reducer);
let reduceRight = (arr: t('a), reducer: ('b, 'a) => 'b, acc: 'b): 'b =>
Belt.Array.reduceReverse(arr, acc, reducer);

View File

@@ -0,0 +1,64 @@
import * as Belt_MutableQueue from "bs-platform/lib/es6/belt_MutableQueue.js";
function addMany(q1, q2) {
return Belt_MutableQueue.transfer(Belt_MutableQueue.copy(q1), q2);
}
var fromArray = Belt_MutableQueue.fromArray;
var toArray = Belt_MutableQueue.toArray;
var make = Belt_MutableQueue.make;
var clear = Belt_MutableQueue.clear;
var add = Belt_MutableQueue.add;
var peek = Belt_MutableQueue.peek;
var pop = Belt_MutableQueue.pop;
var copy = Belt_MutableQueue.copy;
var size = Belt_MutableQueue.size;
var mapU = Belt_MutableQueue.mapU;
var map = Belt_MutableQueue.map;
var forEachU = Belt_MutableQueue.forEachU;
var forEach = Belt_MutableQueue.forEach;
var isEmpty = Belt_MutableQueue.isEmpty;
var reduceU = Belt_MutableQueue.reduceU;
var reduce = Belt_MutableQueue.reduce;
var transfer = Belt_MutableQueue.transfer;
export {
fromArray ,
toArray ,
make ,
clear ,
add ,
peek ,
pop ,
copy ,
size ,
mapU ,
map ,
forEachU ,
forEach ,
isEmpty ,
reduceU ,
reduce ,
addMany ,
transfer ,
}
/* No side effect */

View File

@@ -0,0 +1,24 @@
type t('a) = Belt.MutableQueue.t('a);
let fromArray = Belt.MutableQueue.fromArray;
let toArray = Belt.MutableQueue.toArray;
let make = Belt.MutableQueue.make;
let clear = Belt.MutableQueue.clear;
let add = Belt.MutableQueue.add;
let peek = Belt.MutableQueue.peek;
let pop = Belt.MutableQueue.pop;
let copy = Belt.MutableQueue.copy;
let size = Belt.MutableQueue.size;
let mapU = Belt.MutableQueue.mapU;
let map = Belt.MutableQueue.map;
let forEachU = Belt.MutableQueue.forEachU;
let forEach = Belt.MutableQueue.forEach;
let isEmpty = Belt.MutableQueue.isEmpty;
let reduceU = Belt.MutableQueue.reduceU;
let reduce = Belt.MutableQueue.reduce;
let addMany = (q1: t('a), q2: t('a)) =>
Belt.MutableQueue.transfer(copy(q1), q2);
let transfer = Belt.MutableQueue.transfer;

View File

@@ -0,0 +1,38 @@
import * as Belt_MutableStack from "bs-platform/lib/es6/belt_MutableStack.js";
var make = Belt_MutableStack.make;
var clear = Belt_MutableStack.clear;
var push = Belt_MutableStack.push;
var pop = Belt_MutableStack.pop;
var copy = Belt_MutableStack.copy;
var size = Belt_MutableStack.size;
var forEachU = Belt_MutableStack.forEachU;
var forEach = Belt_MutableStack.forEach;
var isEmpty = Belt_MutableStack.isEmpty;
var top = Belt_MutableStack.top;
export {
make ,
clear ,
push ,
pop ,
copy ,
size ,
forEachU ,
forEach ,
isEmpty ,
top ,
}
/* No side effect */

View File

@@ -0,0 +1,13 @@
type t('a) = Belt.MutableStack.t('a);
let make = Belt.MutableStack.make;
let clear = Belt.MutableStack.clear;
let push = Belt.MutableStack.push;
let pop = Belt.MutableStack.pop;
let copy = Belt.MutableStack.copy;
let size = Belt.MutableStack.size;
let forEachU = Belt.MutableStack.forEachU;
let forEach = Belt.MutableStack.forEach;
let isEmpty = Belt.MutableStack.isEmpty;
let top = Belt.MutableStack.top;

View File

@@ -0,0 +1,16 @@
var $$Array;
var MutableStack;
var MutableQueue;
export {
$$Array ,
MutableStack ,
MutableQueue ,
}
/* No side effect */

View File

@@ -0,0 +1,3 @@
module Array = Array_native;
module MutableStack = MutableStack_native;
module MutableQueue = MutableQueue_native;

View File

@@ -0,0 +1,2 @@
export type element = HTMLElement;
export type event = Event;

View File

@@ -0,0 +1,31 @@
export type Exn_t = Error;
export type Internal_meth<_T, R> = () => R;
export type talkbackT = 0 | 1;
export type signalT<a> =
| ({ tag: 0 } & [(talkback: talkbackT) => void])
| ({ tag: 1 } & [a])
| 0;
export interface observableSubscriptionT {
unsubscribe(): void;
}
export interface observableObserverT<a> {
next(value: a): void;
error(error: any): void;
complete(): void;
}
export interface observableT<a> {
subscribe(observer: observableObserverT<a>): observableSubscriptionT;
}
interface Callbag<I, O> {
(t: 0, d: Callbag<O, I>): void;
(t: 1, d: I): void;
(t: 2, d?: any): void;
}
export type callbagT<a> = Callbag<void, a>;

View File

@@ -0,0 +1,11 @@
// tslint:disable-next-line:max-classes-per-file
export abstract class EmptyList {
protected opaque: any;
}
// tslint:disable-next-line:max-classes-per-file
export abstract class Cons<T> {
protected opaque!: T;
}
export type list<T> = Cons<T> | EmptyList;

View File

@@ -0,0 +1,250 @@
import * as Block from "bs-platform/lib/es6/block.js";
import * as Curry from "bs-platform/lib/es6/curry.js";
import * as Caml_option from "bs-platform/lib/es6/caml_option.js";
import * as Wonka_callbag from "./Wonka_callbag.bs.js";
import * as Wonka_operators from "../Wonka_operators.bs.js";
import * as Wonka_observable from "./Wonka_observable.bs.js";
function debounce(f) {
return (function (source) {
return (function (sink) {
var state = {
id: undefined,
deferredEnded: false,
ended: false
};
var $$clearTimeout$1 = function (param) {
var timeoutId = state.id;
if (timeoutId !== undefined) {
state.id = undefined;
clearTimeout(Caml_option.valFromOption(timeoutId));
return ;
}
};
return Curry._1(source, (function (signal) {
if (typeof signal === "number") {
if (state.ended) {
return ;
}
state.ended = true;
var match = state.id;
if (match !== undefined) {
state.deferredEnded = true;
return ;
} else {
return sink(/* End */0);
}
}
if (signal.tag) {
if (!state.ended) {
$$clearTimeout$1(undefined);
state.id = Caml_option.some(setTimeout((function (param) {
state.id = undefined;
sink(signal);
if (state.deferredEnded) {
return sink(/* End */0);
}
}), f(signal[0])));
return ;
} else {
return ;
}
}
var tb = signal[0];
return sink(/* Start */Block.__(0, [(function (signal) {
if (!state.ended) {
if (signal) {
state.ended = true;
state.deferredEnded = false;
$$clearTimeout$1(undefined);
return tb(/* Close */1);
} else {
return tb(/* Pull */0);
}
}
})]));
}));
});
});
}
function delay(wait) {
return (function (source) {
return (function (sink) {
var active = {
contents: 0
};
return Curry._1(source, (function (signal) {
if (typeof signal !== "number" && !signal.tag) {
return sink(signal);
}
active.contents = active.contents + 1 | 0;
setTimeout((function (param) {
if (active.contents !== 0) {
active.contents = active.contents - 1 | 0;
return sink(signal);
}
}), wait);
}));
});
});
}
function throttle(f) {
return (function (source) {
return (function (sink) {
var skip = {
contents: false
};
var id = {
contents: undefined
};
var $$clearTimeout$1 = function (param) {
var timeoutId = id.contents;
if (timeoutId !== undefined) {
clearTimeout(Caml_option.valFromOption(timeoutId));
return ;
}
};
return Curry._1(source, (function (signal) {
if (typeof signal === "number") {
$$clearTimeout$1(undefined);
return sink(/* End */0);
}
if (signal.tag) {
if (!skip.contents) {
skip.contents = true;
$$clearTimeout$1(undefined);
id.contents = Caml_option.some(setTimeout((function (param) {
id.contents = undefined;
skip.contents = false;
}), f(signal[0])));
return sink(signal);
} else {
return ;
}
}
var tb = signal[0];
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
$$clearTimeout$1(undefined);
return tb(/* Close */1);
} else {
return tb(signal);
}
})]));
}));
});
});
}
function toPromise(source) {
return new Promise((function (resolve, param) {
Curry._1(Wonka_operators.takeLast(1)(source), (function (signal) {
if (typeof signal === "number") {
return ;
} else if (signal.tag) {
return resolve(signal[0]);
} else {
return signal[0](/* Pull */0);
}
}));
}));
}
function interval(p) {
return (function (sink) {
var i = {
contents: 0
};
var id = setInterval((function (param) {
var num = i.contents;
i.contents = i.contents + 1 | 0;
return sink(/* Push */Block.__(1, [num]));
}), p);
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
clearInterval(id);
return ;
}
})]));
});
}
function fromDomEvent(element, $$event) {
return (function (sink) {
var addEventListener = (function (element, event, handler) {
element.addEventListener(event, handler);
});
var removeEventListener = (function (element, event, handler) {
element.removeEventListener(event, handler);
});
var handler = function ($$event) {
return sink(/* Push */Block.__(1, [$$event]));
};
sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
return removeEventListener(element, $$event, handler);
}
})]));
return addEventListener(element, $$event, handler);
});
}
function fromPromise(promise) {
return (function (sink) {
var ended = {
contents: false
};
promise.then((function (value) {
if (!ended.contents) {
sink(/* Push */Block.__(1, [value]));
sink(/* End */0);
}
return Promise.resolve(undefined);
}));
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
ended.contents = true;
return ;
}
})]));
});
}
var fromObservable = Wonka_observable.fromObservable;
var toObservable = Wonka_observable.toObservable;
var fromCallbag = Wonka_callbag.fromCallbag;
var toCallbag = Wonka_callbag.toCallbag;
export {
fromObservable ,
toObservable ,
fromCallbag ,
toCallbag ,
debounce ,
delay ,
throttle ,
toPromise ,
interval ,
fromDomEvent ,
fromPromise ,
}
/* Wonka_observable Not a pure module */

View File

@@ -0,0 +1,46 @@
/* TypeScript file generated from WonkaJs.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Curry = require('bs-platform/lib/es6/curry.js');
// tslint:disable-next-line:no-var-requires
const WonkaJsBS = require('./WonkaJs.bs');
import {callbagT as Wonka_callbag_callbagT} from './Wonka_callbag.gen';
import {element as Dom_element} from '../../src/shims/Dom.shim';
import {event as Dom_event} from '../../src/shims/Dom.shim';
import {observableT as Wonka_observable_observableT} from './Wonka_observable.gen';
import {operatorT as Wonka_types_operatorT} from '../../src/Wonka_types.gen';
import {sourceT as Wonka_types_sourceT} from '../../src/Wonka_types.gen';
export const fromObservable: <T1>(_1:Wonka_observable_observableT<T1>) => Wonka_types_sourceT<T1> = WonkaJsBS.fromObservable;
export const toObservable: <T1>(_1:Wonka_types_sourceT<T1>) => Wonka_observable_observableT<T1> = WonkaJsBS.toObservable;
export const fromCallbag: <T1>(_1:Wonka_callbag_callbagT<T1>) => Wonka_types_sourceT<T1> = WonkaJsBS.fromCallbag;
export const toCallbag: <T1>(_1:Wonka_types_sourceT<T1>) => Wonka_callbag_callbagT<T1> = WonkaJsBS.toCallbag;
export const debounce: <a>(f:((_1:a) => number)) => Wonka_types_operatorT<a,a> = WonkaJsBS.debounce;
export const delay: <a>(wait:number) => Wonka_types_operatorT<a,a> = WonkaJsBS.delay;
export const throttle: <a>(f:((_1:a) => number)) => Wonka_types_operatorT<a,a> = WonkaJsBS.throttle;
export const toPromise: <a>(source:Wonka_types_sourceT<a>) => Promise<a> = WonkaJsBS.toPromise;
export const interval: (p:number) => Wonka_types_sourceT<number> = WonkaJsBS.interval;
export const fromDomEvent: (element:Dom_element, event:string) => Wonka_types_sourceT<Dom_event> = function (Arg1: any, Arg2: any) {
const result = Curry._2(WonkaJsBS.fromDomEvent, Arg1, Arg2);
return result
};
export const fromPromise: <a>(promise:Promise<a>) => Wonka_types_sourceT<a> = WonkaJsBS.fromPromise;

View File

@@ -0,0 +1,263 @@
open Wonka_types;
[@genType]
let fromObservable = Wonka_observable.fromObservable;
[@genType]
let toObservable = Wonka_observable.toObservable;
[@genType]
let fromCallbag = Wonka_callbag.fromCallbag;
[@genType]
let toCallbag = Wonka_callbag.toCallbag;
/* operators */
type debounceStateT = {
mutable id: option(Js.Global.timeoutId),
mutable deferredEnded: bool,
mutable ended: bool,
};
[@genType]
let debounce = (f: (. 'a) => int): operatorT('a, 'a) =>
curry(source =>
curry(sink => {
let state: debounceStateT = {
id: None,
deferredEnded: false,
ended: false,
};
let clearTimeout = () =>
switch (state.id) {
| Some(timeoutId) =>
state.id = None;
Js.Global.clearTimeout(timeoutId);
| None => ()
};
source((. signal) =>
switch (signal) {
| Start(tb) =>
sink(.
Start(
(. signal) =>
if (!state.ended) {
switch (signal) {
| Close =>
state.ended = true;
state.deferredEnded = false;
clearTimeout();
tb(. Close);
| Pull => tb(. Pull)
};
},
),
)
| Push(x) when !state.ended =>
clearTimeout();
state.id =
Some(
Js.Global.setTimeout(
() => {
state.id = None;
sink(. signal);
if (state.deferredEnded) {
sink(. End);
};
},
f(. x),
),
);
| Push(_) => ()
| End when !state.ended =>
state.ended = true;
switch (state.id) {
| Some(_) => state.deferredEnded = true
| None => sink(. End)
};
| End => ()
}
);
})
);
[@genType]
let delay = (wait: int): operatorT('a, 'a) =>
curry(source =>
curry(sink => {
let active = ref(0);
source((. signal) =>
switch (signal) {
| Start(_) => sink(. signal)
| _ =>
active := active^ + 1;
ignore(
Js.Global.setTimeout(
() =>
if (active^ !== 0) {
active := active^ - 1;
sink(. signal);
},
wait,
),
);
}
);
})
);
[@genType]
let throttle = (f: (. 'a) => int): operatorT('a, 'a) =>
curry(source =>
curry(sink => {
let skip = ref(false);
let id: ref(option(Js.Global.timeoutId)) = ref(None);
let clearTimeout = () =>
switch (id^) {
| Some(timeoutId) => Js.Global.clearTimeout(timeoutId)
| None => ()
};
source((. signal) =>
switch (signal) {
| Start(tb) =>
sink(.
Start(
(. signal) =>
switch (signal) {
| Close =>
clearTimeout();
tb(. Close);
| _ => tb(. signal)
},
),
)
| End =>
clearTimeout();
sink(. End);
| Push(x) when ! skip^ =>
skip := true;
clearTimeout();
id :=
Some(
Js.Global.setTimeout(
() => {
id := None;
skip := false;
},
f(. x),
),
);
sink(. signal);
| Push(_) => ()
}
);
})
);
/* sinks */
[@genType]
let toPromise = (source: sourceT('a)): Js.Promise.t('a) => {
Js.Promise.make((~resolve, ~reject as _) => {
Wonka_operators.takeLast(1, source, (. signal) =>
switch (signal) {
| Start(x) => x(. Pull)
| Push(x) => resolve(. x)
| End => ()
}
);
();
});
};
/* sources */
[@genType]
let interval = (p: int): sourceT(int) =>
curry(sink => {
let i = ref(0);
let id =
Js.Global.setInterval(
() => {
let num = i^;
i := i^ + 1;
sink(. Push(num));
},
p,
);
sink(.
Start(
(. signal) =>
switch (signal) {
| Close => Js.Global.clearInterval(id)
| _ => ()
},
),
);
});
[@genType]
let fromDomEvent = (element: Dom.element, event: string): sourceT(Dom.event) =>
curry(sink => {
let addEventListener: (Dom.element, string, Dom.event => unit) => unit = [%raw
{|
function (element, event, handler) {
element.addEventListener(event, handler);
}
|}
];
let removeEventListener: (Dom.element, string, Dom.event => unit) => unit = [%raw
{|
function (element, event, handler) {
element.removeEventListener(event, handler);
}
|}
];
let handler = event => sink(. Push(event));
sink(.
Start(
(. signal) =>
switch (signal) {
| Close => removeEventListener(element, event, handler)
| _ => ()
},
),
);
addEventListener(element, event, handler);
});
[@genType]
let fromPromise = (promise: Js.Promise.t('a)): sourceT('a) =>
curry(sink => {
let ended = ref(false);
ignore(
Js.Promise.then_(
value => {
if (! ended^) {
sink(. Push(value));
sink(. End);
};
Js.Promise.resolve();
},
promise,
),
);
sink(.
Start(
(. signal) =>
switch (signal) {
| Close => ended := true
| _ => ()
},
),
);
});

View File

@@ -0,0 +1,64 @@
import * as Block from "bs-platform/lib/es6/block.js";
import * as Curry from "bs-platform/lib/es6/curry.js";
function fromCallbag(callbag) {
return (function (sink) {
var wrappedSink = function (signal, data) {
switch (signal) {
case /* CALLBAG_START */0 :
var wrappedTalkback = function (talkbackSignal) {
if (talkbackSignal) {
return data(/* CALLBAG_END */2);
} else {
return data(/* CALLBAG_DATA */1);
}
};
return sink(/* Start */Block.__(0, [wrappedTalkback]));
case /* CALLBAG_DATA */1 :
return sink(/* Push */Block.__(1, [data]));
case /* CALLBAG_END */2 :
return sink(/* End */0);
}
};
return Curry._2(callbag, /* CALLBAG_START */0, wrappedSink);
});
}
function toCallbag(source) {
return (function (signal, data) {
if (signal === /* CALLBAG_START */0) {
return Curry._1(source, (function (signal) {
if (typeof signal === "number") {
return Curry._2(data, /* CALLBAG_END */2, undefined);
}
if (signal.tag) {
return Curry._2(data, /* CALLBAG_DATA */1, signal[0]);
}
var talkbackFn = signal[0];
var wrappedTalkbackFn = function (talkback) {
switch (talkback) {
case /* CALLBAG_START */0 :
return ;
case /* CALLBAG_DATA */1 :
return talkbackFn(/* Pull */0);
case /* CALLBAG_END */2 :
return talkbackFn(/* Close */1);
}
};
return Curry._2(data, /* CALLBAG_START */0, wrappedTalkbackFn);
}));
}
});
}
export {
fromCallbag ,
toCallbag ,
}
/* No side effect */

View File

@@ -0,0 +1,27 @@
/* TypeScript file generated from Wonka_callbag.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Wonka_callbagBS = require('./Wonka_callbag.bs');
import {callbagT as $$callbagT} from '../shims/Js.shim';
import {sourceT as Wonka_types_sourceT} from '../../src/Wonka_types.gen';
// tslint:disable-next-line:interface-over-type-literal
export type callbagSignal = 0 | 1 | 2;
// tslint:disable-next-line:max-classes-per-file
// tslint:disable-next-line:class-name
export abstract class callbagData<a> { protected opaque!: a }; /* simulate opaque types */
// tslint:disable-next-line:interface-over-type-literal
export type callbagTalkback = (_1:callbagSignal) => void;
// tslint:disable-next-line:interface-over-type-literal
export type callbagT<a> = $$callbagT<a>;
export const fromCallbag: <a>(callbag:callbagT<a>) => Wonka_types_sourceT<a> = Wonka_callbagBS.fromCallbag;
export const toCallbag: <a>(source:Wonka_types_sourceT<a>) => callbagT<a> = Wonka_callbagBS.toCallbag;

View File

@@ -0,0 +1,65 @@
open Wonka_types;
[@genType]
type callbagSignal =
| [@genType.as 0] CALLBAG_START /* 0 */
| [@genType.as 1] CALLBAG_DATA /* 1 */
| [@genType.as 2] CALLBAG_END /* 2 */;
[@genType]
type callbagData('a);
[@genType]
type callbagTalkback = (. callbagSignal) => unit;
[@genType.import "../shims/Js.shim"]
type callbagT('a) = (callbagSignal, callbagData('a)) => unit;
external unsafe_getCallbag: callbagData('a) => callbagT('a) = "%identity";
external unsafe_getTalkback: callbagData('a) => callbagTalkback = "%identity";
external unsafe_getValue: callbagData('a) => 'a = "%identity";
external unsafe_wrap: 'any => callbagData('a) = "%identity";
[@genType]
let fromCallbag = (callbag: callbagT('a)): sourceT('a) =>
curry(sink => {
let wrappedSink =
(. signal, data) =>
switch (signal) {
| CALLBAG_START =>
let talkback = unsafe_getTalkback(data);
let wrappedTalkback = (
(. talkbackSignal: talkbackT) =>
switch (talkbackSignal) {
| Pull => talkback(. CALLBAG_DATA)
| Close => talkback(. CALLBAG_END)
}
);
sink(. Start(wrappedTalkback));
| CALLBAG_DATA => sink(. Push(unsafe_getValue(data)))
| CALLBAG_END => sink(. End)
};
callbag(CALLBAG_START, unsafe_wrap(wrappedSink));
});
[@genType]
let toCallbag = (source: sourceT('a)): callbagT('a) =>
curry((signal, data) =>
if (signal === CALLBAG_START) {
let callbag = unsafe_getCallbag(data);
source((. signal) =>
switch (signal) {
| Start(talkbackFn) =>
let wrappedTalkbackFn = (talkback: callbagSignal) =>
switch (talkback) {
| CALLBAG_START => ()
| CALLBAG_DATA => talkbackFn(. Pull)
| CALLBAG_END => talkbackFn(. Close)
};
callbag(CALLBAG_START, unsafe_wrap(wrappedTalkbackFn));
| Push(data) => callbag(CALLBAG_DATA, unsafe_wrap(data))
| End => callbag(CALLBAG_END, unsafe_wrap())
}
);
}
);

View File

@@ -0,0 +1,89 @@
import * as Block from "bs-platform/lib/es6/block.js";
import * as Curry from "bs-platform/lib/es6/curry.js";
import * as Wonka_helpers from "../helpers/Wonka_helpers.bs.js";
var observableSymbol = (typeof Symbol === 'function'
? Symbol.observable || (Symbol.observable = Symbol('observable'))
: '@@observable');
function fromObservable(input) {
var match = input[observableSymbol];
var observable = match !== undefined ? input[observableSymbol]() : input;
return (function (sink) {
var observer = {
next: (function (value) {
return sink(/* Push */Block.__(1, [value]));
}),
complete: (function () {
return sink(/* End */0);
}),
error: (function (param) {
})
};
var subscription = observable.subscribe(observer);
return sink(/* Start */Block.__(0, [(function (signal) {
if (signal) {
return subscription.unsubscribe();
}
})]));
});
}
function toObservable(source) {
var observable = {
subscribe: (function (_observer) {
var next = ((typeof _observer === 'object' ? _observer.next.bind(_observer) : _observer) || function () {});
var complete = ((typeof _observer === 'object' ? _observer.complete.bind(_observer) : arguments[2]) || function () {});
var state = {
talkback: Wonka_helpers.talkbackPlaceholder,
ended: false
};
Curry._1(source, (function (signal) {
if (typeof signal === "number") {
state.ended = true;
return complete();
}
if (signal.tag) {
if (!state.ended) {
next(signal[0]);
return state.talkback(/* Pull */0);
} else {
return ;
}
}
var x = signal[0];
state.talkback = x;
return x(/* Pull */0);
}));
var subscription = {
unsubscribe: (function () {
var self = this ;
if (!state.ended) {
self["closed"] = false;
state.ended = true;
return state.talkback(/* Close */1);
}
})
};
subscription["closed"] = false;
return subscription;
})
};
observable[observableSymbol] = (function (param) {
return observable;
});
return observable;
}
export {
observableSymbol ,
fromObservable ,
toObservable ,
}
/* observableSymbol Not a pure module */

View File

@@ -0,0 +1,27 @@
/* TypeScript file generated from Wonka_observable.re by genType. */
/* eslint-disable import/first */
// tslint:disable-next-line:no-var-requires
const Wonka_observableBS = require('./Wonka_observable.bs');
import {observableObserverT as $$observableObserverT} from '../shims/Js.shim';
import {observableSubscriptionT as $$observableSubscriptionT} from '../shims/Js.shim';
import {observableT as $$observableT} from '../shims/Js.shim';
import {sourceT as Wonka_types_sourceT} from '../../src/Wonka_types.gen';
// tslint:disable-next-line:interface-over-type-literal
export type observableSubscriptionT = $$observableSubscriptionT;
// tslint:disable-next-line:interface-over-type-literal
export type observableObserverT<a> = $$observableObserverT<a>;
// tslint:disable-next-line:interface-over-type-literal
export type observableT<a> = $$observableT<a>;
export const fromObservable: <a>(input:observableT<a>) => Wonka_types_sourceT<a> = Wonka_observableBS.fromObservable;
export const toObservable: <a>(source:Wonka_types_sourceT<a>) => observableT<a> = Wonka_observableBS.toObservable;

View File

@@ -0,0 +1,140 @@
open Wonka_types;
open Wonka_helpers;
let observableSymbol: string = [%raw
{|
typeof Symbol === 'function'
? Symbol.observable || (Symbol.observable = Symbol('observable'))
: '@@observable'
|}
];
[@genType.import "../shims/Js.shim"]
type observableSubscriptionT = {. [@bs.meth] "unsubscribe": unit => unit};
[@bs.set_index]
external subscription_set: (observableSubscriptionT, string, bool) => unit;
[@genType.import "../shims/Js.shim"]
type observableObserverT('a) = {
.
[@bs.meth] "next": 'a => unit,
[@bs.meth] "error": Js.Exn.t => unit,
[@bs.meth] "complete": unit => unit,
};
[@genType.import "../shims/Js.shim"]
type observableT('a) = {
.
[@bs.meth] "subscribe": observableObserverT('a) => observableSubscriptionT,
};
type observableFactoryT('a) = (. unit) => observableT('a);
[@bs.get_index]
external observable_get:
(observableT('a), string) => option(observableFactoryT('a));
[@bs.get_index]
external observable_unsafe_get:
(observableT('a), string) => observableFactoryT('a);
[@bs.set_index]
external observable_set:
(observableT('a), string, unit => observableT('a)) => unit;
[@genType]
let fromObservable = (input: observableT('a)): sourceT('a) => {
let observable =
switch (input->observable_get(observableSymbol)) {
| Some(_) => (input->observable_unsafe_get(observableSymbol))(.)
| None => input
};
curry(sink => {
let observer: observableObserverT('a) =
[@bs]
{
as _;
pub next = value => sink(. Push(value));
pub complete = () => sink(. End);
pub error = _ => ()
};
let subscription = observable##subscribe(observer);
sink(.
Start(
(. signal) =>
switch (signal) {
| Close => subscription##unsubscribe()
| _ => ()
},
),
);
});
};
type observableStateT = {
mutable talkback: (. talkbackT) => unit,
mutable ended: bool,
};
[@genType]
let toObservable = (source: sourceT('a)): observableT('a) => {
let observable: observableT('a) =
[@bs]
{
as _;
pub subscribe =
(_observer: observableObserverT('a)): observableSubscriptionT => {
let next: (. 'a) => unit = [%raw
{|
(typeof _observer === 'object' ? _observer.next.bind(_observer) : _observer) || function () {}
|}
];
let complete: (. unit) => unit = [%raw
{|
(typeof _observer === 'object' ? _observer.complete.bind(_observer) : arguments[2]) || function () {}
|}
];
let state: observableStateT = {
talkback: talkbackPlaceholder,
ended: false,
};
source((. signal) =>
switch (signal) {
| Start(x) =>
state.talkback = x;
x(. Pull);
| Push(x) when !state.ended =>
next(. x);
state.talkback(. Pull);
| Push(_) => ()
| End =>
state.ended = true;
complete(.);
}
);
let subscription =
[@bs]
{
as self;
pub unsubscribe = () =>
if (!state.ended) {
self->subscription_set("closed", false);
state.ended = true;
state.talkback(. Close);
}
};
subscription->subscription_set("closed", false);
subscription;
}
};
observable->observable_set(observableSymbol, () => observable);
observable;
};