cycle-gear
Advanced tools
Comparing version 4.1.0 to 5.0.0
@@ -13,2 +13,3 @@ import { Observable } from 'xstream'; | ||
export interface Gear<TActions, TModel> { | ||
name?: string; | ||
catch?: (error: any, actions: TActions) => Observable<any>; | ||
@@ -19,3 +20,9 @@ intent?: (sources: any) => TActions; | ||
} | ||
export declare type ToothReduce<TActions, TModel, TAccumulator> = (accumulator: TAccumulator, current: [TModel, Gear<TActions, TModel>]) => TAccumulator; | ||
export interface ToothConnector<TActions, TModel, TAccumulator> { | ||
reduce: ToothReduce<TActions, TModel, TAccumulator>; | ||
init: () => TAccumulator; | ||
} | ||
export declare type Transmission = ((sources: any) => Observable<Gear<any, any>>) | Observable<Gear<any, any>>; | ||
export declare type Gearbox = ((sources: any) => Observable<Iterable<Gear<any, any>>>) | Observable<Iterable<Gear<any, any>>>; | ||
export interface PedalOptions { | ||
@@ -26,2 +33,8 @@ defaultGear?: Gear<any, any>; | ||
} | ||
export interface MotorOptions extends PedalOptions { | ||
sourcesWrapper?: (sources: any, gear: Gear<any, any>) => any; | ||
defaultConnector?: ToothConnector<any, any, any>; | ||
connectors?: Map<string, ToothReduce<any, any, any>>; | ||
} | ||
export declare function pedal(transmission: Transmission, {defaultGear, defaultFilter, sinkMap}?: PedalOptions): (sources: any) => {}; | ||
export declare function motor(gearbox: Gearbox, {defaultGear, defaultFilter, defaultConnector, sourcesWrapper, connectors, sinkMap}?: MotorOptions): (sources: any) => {}; |
import { adapt } from '@cycle/run/lib/adapt'; | ||
import xs from 'xstream'; | ||
export function pedal(transmission, { defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} }, defaultFilter = () => true, sinkMap = new Map() } = {}) { | ||
function defaultsAndHelpers(defaultGear, defaultFilter) { | ||
const defaultCatch = defaultGear.catch || ((error) => xs.throw(error)); | ||
@@ -43,2 +43,25 @@ const defaultIntent = defaultGear.intent || (() => ({})); | ||
}; | ||
return { defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, emptyTeeth }; | ||
} | ||
function spinGear(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView) { | ||
const modelCache = new WeakMap(); | ||
return gear => { | ||
let state; | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear); | ||
} | ||
else { | ||
const actions = gear.intent ? gear.intent(sources) : defaultIntent(sources); | ||
state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError((err) => xs.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions))) | ||
.remember(); | ||
} | ||
const views = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[tooth]: state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])) | ||
}), {}); | ||
return views; | ||
}; | ||
} | ||
export function pedal(transmission, { defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} }, defaultFilter = () => true, sinkMap = new Map() } = {}) { | ||
const { defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, emptyTeeth } = defaultsAndHelpers(defaultGear, defaultFilter); | ||
return (sources) => { | ||
@@ -53,12 +76,3 @@ let gear; | ||
const spin = xs.fromObservable(gear) | ||
.map(gear => { | ||
const actions = gear.intent ? gear.intent(sources) : defaultIntent(sources); | ||
const state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError((err) => xs.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions))) | ||
.remember(); | ||
const views = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[tooth]: state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])) | ||
}), {}); | ||
return views; | ||
}) | ||
.map(spinGear(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView)) | ||
.startWith(emptyTeeth) | ||
@@ -72,1 +86,51 @@ .remember(); | ||
} | ||
function spinGears(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, sourcesWrapper, defaultConnector, connectors) { | ||
const modelCache = new WeakMap(); | ||
return gears => { | ||
const views = teeth.reduce((acc, cur) => (Object.assign({}, acc, { [cur]: [] })), {}); | ||
for (let gear of gears) { | ||
let state; | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear); | ||
} | ||
else { | ||
const wrappedSources = sourcesWrapper(sources, gear); | ||
const actions = gear.intent ? gear.intent(wrappedSources) : defaultIntent(wrappedSources); | ||
state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError((err) => xs.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions))) | ||
.remember(); | ||
} | ||
for (let tooth of teeth) { | ||
views[tooth].push(state | ||
.filter(toothFilter(tooth, (gear.teeth || {})[tooth])) | ||
.map(state => [toothView(tooth, (gear.teeth || {})[tooth])(state), gear])); | ||
} | ||
} | ||
return teeth.reduce((accum, tooth) => (Object.assign({}, accum, { [tooth]: xs.merge(...views[tooth]) | ||
.fold(connectors.has(tooth) ? connectors.get(tooth).reduce : defaultConnector.reduce, connectors.has(tooth) ? connectors.get(tooth).init() : defaultConnector.init()) })), {}); | ||
}; | ||
} | ||
const defaultDefaultConnector = { | ||
reduce: (acc, [cur, gear]) => (Object.assign({}, acc, { [gear.name || '?']: cur })), | ||
init: () => ({}) | ||
}; | ||
export function motor(gearbox, { defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} }, defaultFilter = () => true, defaultConnector = defaultDefaultConnector, sourcesWrapper = (sources) => sources, connectors = new Map(), sinkMap = new Map() } = {}) { | ||
const { defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, emptyTeeth } = defaultsAndHelpers(defaultGear, defaultFilter); | ||
return (sources) => { | ||
let gears; | ||
if (gearbox instanceof Function) { | ||
gears = gearbox(sources); | ||
} | ||
else { | ||
gears = gearbox; | ||
} | ||
const spin = xs.fromObservable(gears) | ||
.map(spinGears(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, sourcesWrapper, defaultConnector, connectors)) | ||
.startWith(emptyTeeth) | ||
.remember(); | ||
const sinks = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[sinkMap.has(tooth) ? sinkMap.get(tooth) : tooth]: adapt(spin.map((views) => views[tooth]).flatten()) | ||
}), {}); | ||
return sinks; | ||
}; | ||
} |
@@ -13,2 +13,3 @@ import { Observable } from 'xstream'; | ||
export interface Gear<TActions, TModel> { | ||
name?: string; | ||
catch?: (error: any, actions: TActions) => Observable<any>; | ||
@@ -19,3 +20,9 @@ intent?: (sources: any) => TActions; | ||
} | ||
export declare type ToothReduce<TActions, TModel, TAccumulator> = (accumulator: TAccumulator, current: [TModel, Gear<TActions, TModel>]) => TAccumulator; | ||
export interface ToothConnector<TActions, TModel, TAccumulator> { | ||
reduce: ToothReduce<TActions, TModel, TAccumulator>; | ||
init: () => TAccumulator; | ||
} | ||
export declare type Transmission = ((sources: any) => Observable<Gear<any, any>>) | Observable<Gear<any, any>>; | ||
export declare type Gearbox = ((sources: any) => Observable<Iterable<Gear<any, any>>>) | Observable<Iterable<Gear<any, any>>>; | ||
export interface PedalOptions { | ||
@@ -26,2 +33,8 @@ defaultGear?: Gear<any, any>; | ||
} | ||
export interface MotorOptions extends PedalOptions { | ||
sourcesWrapper?: (sources: any, gear: Gear<any, any>) => any; | ||
defaultConnector?: ToothConnector<any, any, any>; | ||
connectors?: Map<string, ToothReduce<any, any, any>>; | ||
} | ||
export declare function pedal(transmission: Transmission, {defaultGear, defaultFilter, sinkMap}?: PedalOptions): (sources: any) => {}; | ||
export declare function motor(gearbox: Gearbox, {defaultGear, defaultFilter, defaultConnector, sourcesWrapper, connectors, sinkMap}?: MotorOptions): (sources: any) => {}; |
"use strict"; | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spread = (this && this.__spread) || function () { | ||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var adapt_1 = require("@cycle/run/lib/adapt"); | ||
var xstream_1 = require("xstream"); | ||
function pedal(transmission, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.defaultGear, defaultGear = _c === void 0 ? { intent: function () { return ({}); }, model: function () { return xstream_1.default.of({}); }, teeth: {} } : _c, _d = _b.defaultFilter, defaultFilter = _d === void 0 ? function () { return true; } : _d, _e = _b.sinkMap, sinkMap = _e === void 0 ? new Map() : _e; | ||
function defaultsAndHelpers(defaultGear, defaultFilter) { | ||
var defaultCatch = defaultGear.catch || (function (error) { return xstream_1.default.throw(error); }); | ||
@@ -18,11 +55,20 @@ var defaultIntent = defaultGear.intent || (function () { return ({}); }); | ||
if (defaultGear.teeth) { | ||
for (var _i = 0, teeth_1 = teeth; _i < teeth_1.length; _i++) { | ||
var tooth = teeth_1[_i]; | ||
var defGearTooth = defaultGear.teeth[tooth]; | ||
if (defGearTooth instanceof Function) { | ||
toothDefaults[tooth] = { filter: defaultFilter, view: defGearTooth }; | ||
try { | ||
for (var teeth_1 = __values(teeth), teeth_1_1 = teeth_1.next(); !teeth_1_1.done; teeth_1_1 = teeth_1.next()) { | ||
var tooth = teeth_1_1.value; | ||
var defGearTooth = defaultGear.teeth[tooth]; | ||
if (defGearTooth instanceof Function) { | ||
toothDefaults[tooth] = { filter: defaultFilter, view: defGearTooth }; | ||
} | ||
else { | ||
toothDefaults[tooth] = { filter: defGearTooth.filter || defaultFilter, view: defGearTooth.view }; | ||
} | ||
} | ||
else { | ||
toothDefaults[tooth] = { filter: defGearTooth.filter || defaultFilter, view: defGearTooth.view }; | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (teeth_1_1 && !teeth_1_1.done && (_a = teeth_1.return)) _a.call(teeth_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
@@ -51,2 +97,30 @@ } | ||
}; | ||
return { defaultIntent: defaultIntent, defaultModel: defaultModel, defaultCatch: defaultCatch, teeth: teeth, toothFilter: toothFilter, toothView: toothView, emptyTeeth: emptyTeeth }; | ||
var e_1, _a; | ||
} | ||
function spinGear(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView) { | ||
var modelCache = new WeakMap(); | ||
return function (gear) { | ||
var state; | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear); | ||
} | ||
else { | ||
var actions_1 = gear.intent ? gear.intent(sources) : defaultIntent(sources); | ||
state = xstream_1.default.fromObservable(gear.model ? gear.model(actions_1) : defaultModel(actions_1)) | ||
.replaceError(function (err) { return xstream_1.default.fromObservable(gear.catch ? gear.catch(err, actions_1) : defaultCatch(err, actions_1)); }) | ||
.remember(); | ||
} | ||
var views = teeth.reduce(function (accum, tooth) { | ||
return Object.assign(accum, (_a = {}, | ||
_a[tooth] = state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])), | ||
_a)); | ||
var _a; | ||
}, {}); | ||
return views; | ||
}; | ||
} | ||
function pedal(transmission, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.defaultGear, defaultGear = _c === void 0 ? { intent: function () { return ({}); }, model: function () { return xstream_1.default.of({}); }, teeth: {} } : _c, _d = _b.defaultFilter, defaultFilter = _d === void 0 ? function () { return true; } : _d, _e = _b.sinkMap, sinkMap = _e === void 0 ? new Map() : _e; | ||
var _f = defaultsAndHelpers(defaultGear, defaultFilter), defaultIntent = _f.defaultIntent, defaultModel = _f.defaultModel, defaultCatch = _f.defaultCatch, teeth = _f.teeth, toothFilter = _f.toothFilter, toothView = _f.toothView, emptyTeeth = _f.emptyTeeth; | ||
return function (sources) { | ||
@@ -61,15 +135,3 @@ var gear; | ||
var spin = xstream_1.default.fromObservable(gear) | ||
.map(function (gear) { | ||
var actions = gear.intent ? gear.intent(sources) : defaultIntent(sources); | ||
var state = xstream_1.default.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError(function (err) { return xstream_1.default.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions)); }) | ||
.remember(); | ||
var views = teeth.reduce(function (accum, tooth) { | ||
return Object.assign(accum, (_a = {}, | ||
_a[tooth] = state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])), | ||
_a)); | ||
var _a; | ||
}, {}); | ||
return views; | ||
}) | ||
.map(spinGear(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView)) | ||
.startWith(emptyTeeth) | ||
@@ -87,1 +149,93 @@ .remember(); | ||
exports.pedal = pedal; | ||
function spinGears(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, sourcesWrapper, defaultConnector, connectors) { | ||
var modelCache = new WeakMap(); | ||
return function (gears) { | ||
var views = teeth.reduce(function (acc, cur) { | ||
return (__assign({}, acc, (_a = {}, _a[cur] = [], _a))); | ||
var _a; | ||
}, {}); | ||
var _loop_1 = function (gear) { | ||
var state = void 0; | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear); | ||
} | ||
else { | ||
var wrappedSources = sourcesWrapper(sources, gear); | ||
var actions_2 = gear.intent ? gear.intent(wrappedSources) : defaultIntent(wrappedSources); | ||
state = xstream_1.default.fromObservable(gear.model ? gear.model(actions_2) : defaultModel(actions_2)) | ||
.replaceError(function (err) { return xstream_1.default.fromObservable(gear.catch ? gear.catch(err, actions_2) : defaultCatch(err, actions_2)); }) | ||
.remember(); | ||
} | ||
var _loop_2 = function (tooth) { | ||
views[tooth].push(state | ||
.filter(toothFilter(tooth, (gear.teeth || {})[tooth])) | ||
.map(function (state) { return [toothView(tooth, (gear.teeth || {})[tooth])(state), gear]; })); | ||
}; | ||
try { | ||
for (var teeth_2 = __values(teeth), teeth_2_1 = teeth_2.next(); !teeth_2_1.done; teeth_2_1 = teeth_2.next()) { | ||
var tooth = teeth_2_1.value; | ||
_loop_2(tooth); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (teeth_2_1 && !teeth_2_1.done && (_a = teeth_2.return)) _a.call(teeth_2); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
var e_2, _a; | ||
}; | ||
try { | ||
for (var gears_1 = __values(gears), gears_1_1 = gears_1.next(); !gears_1_1.done; gears_1_1 = gears_1.next()) { | ||
var gear = gears_1_1.value; | ||
_loop_1(gear); | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (gears_1_1 && !gears_1_1.done && (_a = gears_1.return)) _a.call(gears_1); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
return teeth.reduce(function (accum, tooth) { | ||
return (__assign({}, accum, (_a = {}, _a[tooth] = xstream_1.default.merge.apply(xstream_1.default, __spread(views[tooth])).fold(connectors.has(tooth) ? connectors.get(tooth).reduce : defaultConnector.reduce, connectors.has(tooth) ? connectors.get(tooth).init() : defaultConnector.init()), _a))); | ||
var _a; | ||
}, {}); | ||
var e_3, _a; | ||
}; | ||
} | ||
var defaultDefaultConnector = { | ||
reduce: function (acc, _a) { | ||
var _b = __read(_a, 2), cur = _b[0], gear = _b[1]; | ||
return (__assign({}, acc, (_c = {}, _c[gear.name || '?'] = cur, _c))); | ||
var _c; | ||
}, | ||
init: function () { return ({}); } | ||
}; | ||
function motor(gearbox, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.defaultGear, defaultGear = _c === void 0 ? { intent: function () { return ({}); }, model: function () { return xstream_1.default.of({}); }, teeth: {} } : _c, _d = _b.defaultFilter, defaultFilter = _d === void 0 ? function () { return true; } : _d, _e = _b.defaultConnector, defaultConnector = _e === void 0 ? defaultDefaultConnector : _e, _f = _b.sourcesWrapper, sourcesWrapper = _f === void 0 ? function (sources) { return sources; } : _f, _g = _b.connectors, connectors = _g === void 0 ? new Map() : _g, _h = _b.sinkMap, sinkMap = _h === void 0 ? new Map() : _h; | ||
var _j = defaultsAndHelpers(defaultGear, defaultFilter), defaultIntent = _j.defaultIntent, defaultModel = _j.defaultModel, defaultCatch = _j.defaultCatch, teeth = _j.teeth, toothFilter = _j.toothFilter, toothView = _j.toothView, emptyTeeth = _j.emptyTeeth; | ||
return function (sources) { | ||
var gears; | ||
if (gearbox instanceof Function) { | ||
gears = gearbox(sources); | ||
} | ||
else { | ||
gears = gearbox; | ||
} | ||
var spin = xstream_1.default.fromObservable(gears) | ||
.map(spinGears(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, sourcesWrapper, defaultConnector, connectors)) | ||
.startWith(emptyTeeth) | ||
.remember(); | ||
var sinks = teeth.reduce(function (accum, tooth) { | ||
return Object.assign(accum, (_a = {}, | ||
_a[sinkMap.has(tooth) ? sinkMap.get(tooth) : tooth] = adapt_1.adapt(spin.map(function (views) { return views[tooth]; }).flatten()), | ||
_a)); | ||
var _a; | ||
}, {}); | ||
return sinks; | ||
}; | ||
} | ||
exports.motor = motor; |
174
index.ts
@@ -18,2 +18,3 @@ import { adapt } from '@cycle/run/lib/adapt' | ||
export interface Gear<TActions, TModel> { | ||
name?: string | ||
catch?: (error: any, actions: TActions) => Observable<any> | ||
@@ -25,4 +26,13 @@ intent?: (sources: any) => TActions | ||
export type ToothReduce<TActions, TModel, TAccumulator> = (accumulator: TAccumulator, current: [TModel, Gear<TActions, TModel>]) => TAccumulator | ||
export interface ToothConnector<TActions, TModel, TAccumulator> { | ||
reduce: ToothReduce<TActions, TModel, TAccumulator> | ||
init: () => TAccumulator | ||
} | ||
export type Transmission = ((sources: any) => Observable<Gear<any, any>>) | Observable<Gear<any, any>> | ||
export type Gearbox = ((sources: any) => Observable<Iterable<Gear<any, any>>>) | Observable<Iterable<Gear<any, any>>> | ||
export interface PedalOptions { | ||
@@ -34,16 +44,18 @@ defaultGear?: Gear<any, any> | ||
export function pedal(transmission: Transmission, { | ||
defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} as GearTeeth<any> }, | ||
defaultFilter = () => true, | ||
sinkMap = new Map() | ||
}: PedalOptions = {}) { | ||
export interface MotorOptions extends PedalOptions { | ||
sourcesWrapper?: (sources: any, gear: Gear<any, any>) => any | ||
defaultConnector?: ToothConnector<any, any, any> | ||
connectors?: Map<string, ToothReduce<any, any, any>> | ||
} | ||
function defaultsAndHelpers(defaultGear: Gear<any, any>, defaultFilter: (model: any) => boolean) { | ||
const defaultCatch = defaultGear.catch || ((error: any) => xs.throw(error)) | ||
const defaultIntent = defaultGear.intent || (() => ({})) | ||
const defaultModel = defaultGear.model || (() => xs.of({})) | ||
// Fully expand tooth defaults to avoid doing all the tests below every time | ||
const toothDefaults: { [name: string]: GearTooth<any> } = {} | ||
const toothDefaults: { | ||
[name: string]: GearTooth<any> | ||
} = {} | ||
const teeth = Object.keys(defaultGear.teeth || {}) | ||
const emptyTeeth = teeth.reduce((accum, cur) => Object.assign(accum, { [cur]: xs.never() }), {}) | ||
if (defaultGear.teeth) { | ||
@@ -54,3 +66,4 @@ for (let tooth of teeth) { | ||
toothDefaults[tooth] = { filter: defaultFilter, view: defGearTooth } | ||
} else { | ||
} | ||
else { | ||
toothDefaults[tooth] = { filter: defGearTooth.filter || defaultFilter, view: defGearTooth.view } | ||
@@ -65,3 +78,4 @@ } | ||
return toothDefaults[name].filter || defaultFilter | ||
} else { | ||
} | ||
else { | ||
return tooth.filter || toothDefaults[name].filter || defaultFilter | ||
@@ -75,5 +89,7 @@ } | ||
return toothDefaults[name].view | ||
} else if (tooth instanceof Function) { | ||
} | ||
else if (tooth instanceof Function) { | ||
return tooth | ||
} else { | ||
} | ||
else { | ||
return tooth.view | ||
@@ -83,2 +99,45 @@ } | ||
return { defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, emptyTeeth } | ||
} | ||
function spinGear(sources: any, | ||
defaultIntent: (sources: any) => any, | ||
defaultModel: (actions: any) => Observable<any>, | ||
defaultCatch: (error: any, actions: any) => Observable<any>, | ||
teeth: string[], | ||
toothFilter: (name: string, tooth: GearTooth<any> | GearView<any>) => (model: any) => boolean, | ||
toothView: (name: string, tooth: GearTooth<any> | GearView<any>) => GearView<any>): (t: Gear<any, any>) => {} { | ||
const modelCache = new WeakMap<Gear<any, any>, xs<any>>() | ||
return gear => { | ||
let state: xs<any> | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear) as xs<any> | ||
} else { | ||
const actions = gear.intent ? gear.intent(sources) : defaultIntent(sources) | ||
state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError((err: any) => xs.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions))) | ||
.remember() | ||
} | ||
const views = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[tooth]: state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])) | ||
}), {}) | ||
return views | ||
} | ||
} | ||
export function pedal(transmission: Transmission, { | ||
defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} as GearTeeth<any> }, | ||
defaultFilter = () => true, | ||
sinkMap = new Map() | ||
}: PedalOptions = {}) { | ||
const { | ||
defaultIntent, | ||
defaultModel, | ||
defaultCatch, | ||
teeth, | ||
toothFilter, | ||
toothView, | ||
emptyTeeth | ||
} = defaultsAndHelpers(defaultGear, defaultFilter) | ||
return (sources: any) => { | ||
@@ -93,14 +152,87 @@ let gear: Observable<Gear<any, any>> | ||
const spin = xs.fromObservable<Gear<any, any>>(gear) | ||
.map(gear => { | ||
const actions = gear.intent ? gear.intent(sources) : defaultIntent(sources) | ||
const state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.map(spinGear(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView)) | ||
.startWith(emptyTeeth) | ||
.remember() | ||
const sinks = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[sinkMap.has(tooth) ? sinkMap.get(tooth) : tooth]: adapt(spin.map((views: any) => views[tooth]).flatten()) | ||
}), | ||
{}) | ||
return sinks | ||
} | ||
} | ||
function spinGears(sources: any, | ||
defaultIntent: (sources: any) => any, | ||
defaultModel: (actions: any) => Observable<any>, | ||
defaultCatch: (error: any, actions: any) => Observable<any>, | ||
teeth: string[], | ||
toothFilter: (name: string, tooth: GearTooth<any> | GearView<any>) => (model: any) => boolean, | ||
toothView: (name: string, tooth: GearTooth<any> | GearView<any>) => GearView<any>, | ||
sourcesWrapper: (sources: any, gear: Gear<any, any>) => any, | ||
defaultConnector: ToothConnector<any, any, any>, | ||
connectors: Map<string, ToothConnector<any, any, any>>): (t: Iterable<Gear<any, any>>) => {} { | ||
const modelCache = new WeakMap<Gear<any, any>, xs<any>>() | ||
return gears => { | ||
const views = teeth.reduce((acc, cur) => ({...acc, [cur]: [] }), {} as {[tooth: string]: Array<Observable<any>>}) | ||
for (let gear of gears) { | ||
let state: xs<any> | ||
if (modelCache.has(gear)) { | ||
state = modelCache.get(gear) as xs<any> | ||
} else { | ||
const wrappedSources = sourcesWrapper(sources, gear) | ||
const actions = gear.intent ? gear.intent(wrappedSources) : defaultIntent(wrappedSources) | ||
state = xs.fromObservable(gear.model ? gear.model(actions) : defaultModel(actions)) | ||
.replaceError((err: any) => xs.fromObservable(gear.catch ? gear.catch(err, actions) : defaultCatch(err, actions))) | ||
.remember() | ||
const views = teeth.reduce((accum, tooth) => Object.assign(accum, { | ||
[tooth]: state.filter(toothFilter(tooth, (gear.teeth || {})[tooth])).map(toothView(tooth, (gear.teeth || {})[tooth])) | ||
}), | ||
{}) | ||
} | ||
for (let tooth of teeth) { | ||
views[tooth].push(state | ||
.filter(toothFilter(tooth, (gear.teeth || {})[tooth])) | ||
.map(state => [toothView(tooth, (gear.teeth || {})[tooth])(state), gear])) | ||
} | ||
} | ||
return teeth.reduce((accum, tooth) => ({ | ||
...accum, | ||
[tooth]: xs.merge(...views[tooth]) | ||
.fold(connectors.has(tooth) ? connectors.get(tooth)!.reduce : defaultConnector.reduce, | ||
connectors.has(tooth) ? connectors.get(tooth)!.init() : defaultConnector.init()) | ||
}), | ||
{}) | ||
} | ||
} | ||
return views | ||
}) | ||
const defaultDefaultConnector = { | ||
reduce: (acc: any, [cur, gear]: [any, Gear<any, any>]) => ({...acc, [gear.name || '?']: cur}), | ||
init: () => ({}) | ||
} | ||
export function motor(gearbox: Gearbox, { | ||
defaultGear = { intent: () => ({}), model: () => xs.of({}), teeth: {} as GearTeeth<any> }, | ||
defaultFilter = () => true, | ||
defaultConnector = defaultDefaultConnector, | ||
sourcesWrapper = (sources: any) => sources, | ||
connectors = new Map(), | ||
sinkMap = new Map() | ||
}: MotorOptions = {}) { | ||
const { | ||
defaultIntent, | ||
defaultModel, | ||
defaultCatch, | ||
teeth, | ||
toothFilter, | ||
toothView, | ||
emptyTeeth | ||
} = defaultsAndHelpers(defaultGear, defaultFilter) | ||
return (sources: any) => { | ||
let gears: Observable<Iterable<Gear<any, any>>> | ||
if (gearbox instanceof Function) { | ||
gears = gearbox(sources) | ||
} else { | ||
gears = gearbox | ||
} | ||
const spin = xs.fromObservable<Iterable<Gear<any, any>>>(gears) | ||
.map(spinGears(sources, defaultIntent, defaultModel, defaultCatch, teeth, toothFilter, toothView, sourcesWrapper, defaultConnector, connectors)) | ||
.startWith(emptyTeeth) | ||
@@ -107,0 +239,0 @@ .remember() |
{ | ||
"name": "cycle-gear", | ||
"version": "4.1.0", | ||
"version": "5.0.0", | ||
"description": "Main function factory for CycleJS", | ||
@@ -37,2 +37,3 @@ "main": "dist/index.js", | ||
"tslint-config-standard": "^3.0.0", | ||
"tslint-language-service": "^0.9.6", | ||
"typescript": "^2.1.5" | ||
@@ -39,0 +40,0 @@ }, |
@@ -48,1 +48,12 @@ # cycle-gear | ||
`transmission` might be some other sort of user-action dependent state machine. | ||
## `motor` | ||
`motor` is a main factory function for the `Gear` pattern. It takes a `gearbox` of | ||
Gears, default states for gears, which teeth to bind to which sinks, connectors to | ||
merge the output of gears, and from that builds a Cycle main to wire the gears up to | ||
Cycle sources and sinks. | ||
A `gearbox` is an observable of iterable sets of gears or a factory from Cycle sources | ||
to an observable of iterable sets of gears. This can be useful for component systems | ||
that have many similar, interlocking parts. |
@@ -10,4 +10,8 @@ { | ||
"noUnusedParameters": true, | ||
"downlevelIteration": true, | ||
"declaration": true, | ||
"outDir": "./dist/" | ||
"outDir": "./dist/", | ||
"plugins": [ | ||
{ "name": "tslint-language-service"} | ||
] | ||
}, | ||
@@ -14,0 +18,0 @@ "files": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
67462
713
59
3