From e258d774246a8328740634393ddb53551d157d59 Mon Sep 17 00:00:00 2001 From: EisenbergEffect Date: Tue, 11 Jun 2019 20:44:32 -0700 Subject: [PATCH] chore(all): prepare release 1.4.0 --- dist/amd/aurelia-store.js | 1301 +++++++++++++------------ dist/aurelia-store.d.ts | 14 + dist/commonjs/aurelia-store.js | 137 ++- dist/es2015/aurelia-store.js | 124 ++- dist/es2017/aurelia-store.js | 124 ++- dist/native-modules/aurelia-store.js | 138 ++- dist/umd-es2015/aurelia-store.js | 1023 ++++++++++---------- dist/umd/aurelia-store.js | 1307 ++++++++++++++------------ doc/CHANGELOG.md | 11 + package-lock.json | 41 +- package.json | 2 +- 11 files changed, 2346 insertions(+), 1876 deletions(-) diff --git a/dist/amd/aurelia-store.js b/dist/amd/aurelia-store.js index 23986c1..541c766 100644 --- a/dist/amd/aurelia-store.js +++ b/dist/amd/aurelia-store.js @@ -1,636 +1,705 @@ define('aurelia-store', ['exports', 'rxjs', 'aurelia-dependency-injection', 'aurelia-logging', 'aurelia-pal', 'rxjs/operators'], function (exports, rxjs, aureliaDependencyInjection, aureliaLogging, aureliaPal, operators) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 + /* istanbul ignore next */ + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill + if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; + } + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - /* global Reflect, Promise */ + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } - var __assign = function() { - __assign = Object.assign || function __assign(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; - }; - return __assign.apply(this, arguments); - }; + var __assign = function() { + __assign = Object.assign || function __assign(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; + }; + return __assign.apply(this, arguments); + }; - function __awaiter(thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } + function __awaiter(thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } - } + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } - function jump(state, n) { - if (!isStateHistory(state)) { - return state; - } - if (n > 0) - return jumpToFuture(state, n - 1); - if (n < 0) - return jumpToPast(state, state.past.length + n); - return state; - } - function jumpToFuture(state, index) { - if (index < 0 || index >= state.future.length) { - return state; - } - var past = state.past, future = state.future, present = state.present; - var newPast = past.concat([present], future.slice(0, index)); - var newPresent = future[index]; - var newFuture = future.slice(index + 1); - return { past: newPast, present: newPresent, future: newFuture }; - } - function jumpToPast(state, index) { - if (index < 0 || index >= state.past.length) { - return state; - } - var past = state.past, future = state.future, present = state.present; - var newPast = past.slice(0, index); - var newFuture = past.slice(index + 1).concat([present], future); - var newPresent = past[index]; - return { past: newPast, present: newPresent, future: newFuture }; - } - function nextStateHistory(presentStateHistory, nextPresent) { - return Object.assign({}, presentStateHistory, { - past: presentStateHistory.past.concat([presentStateHistory.present]), - present: nextPresent, - future: [] - }); - } - function applyLimits(state, limit) { - if (isStateHistory(state)) { - if (state.past.length > limit) { - state.past = state.past.slice(state.past.length - limit); - } - if (state.future.length > limit) { - state.future = state.future.slice(0, limit); - } - } - return state; - } - function isStateHistory(history) { - return typeof history.present !== "undefined" && - typeof history.future !== "undefined" && - typeof history.past !== "undefined" && - Array.isArray(history.future) && - Array.isArray(history.past); - } + function jump(state, n) { + if (!isStateHistory(state)) { + return state; + } + if (n > 0) + return jumpToFuture(state, n - 1); + if (n < 0) + return jumpToPast(state, state.past.length + n); + return state; + } + function jumpToFuture(state, index) { + if (index < 0 || index >= state.future.length) { + return state; + } + var past = state.past, future = state.future, present = state.present; + var newPast = past.concat([present], future.slice(0, index)); + var newPresent = future[index]; + var newFuture = future.slice(index + 1); + return { past: newPast, present: newPresent, future: newFuture }; + } + function jumpToPast(state, index) { + if (index < 0 || index >= state.past.length) { + return state; + } + var past = state.past, future = state.future, present = state.present; + var newPast = past.slice(0, index); + var newFuture = past.slice(index + 1).concat([present], future); + var newPresent = past[index]; + return { past: newPast, present: newPresent, future: newFuture }; + } + function nextStateHistory(presentStateHistory, nextPresent) { + return Object.assign({}, presentStateHistory, { + past: presentStateHistory.past.concat([presentStateHistory.present]), + present: nextPresent, + future: [] + }); + } + function applyLimits(state, limit) { + if (isStateHistory(state)) { + if (state.past.length > limit) { + state.past = state.past.slice(state.past.length - limit); + } + if (state.future.length > limit) { + state.future = state.future.slice(0, limit); + } + } + return state; + } + function isStateHistory(history) { + return typeof history.present !== "undefined" && + typeof history.future !== "undefined" && + typeof history.past !== "undefined" && + Array.isArray(history.future) && + Array.isArray(history.past); + } - var DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; - (function (MiddlewarePlacement) { - MiddlewarePlacement["Before"] = "before"; - MiddlewarePlacement["After"] = "after"; - })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); - function logMiddleware(state, _, settings) { - var logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; - console[logType]("New state: ", state); - } - function localStorageMiddleware(state, _, settings) { - if (aureliaPal.PLATFORM.global.localStorage) { - var key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; - aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); - } - } - function rehydrateFromLocalStorage(state, key) { - if (!aureliaPal.PLATFORM.global.localStorage) { - return state; - } - var storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); - if (!storedState) { - return state; - } - try { - return JSON.parse(storedState); - } - catch (e) { } - return state; - } + var DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; + (function (MiddlewarePlacement) { + MiddlewarePlacement["Before"] = "before"; + MiddlewarePlacement["After"] = "after"; + })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); + function logMiddleware(state, _, settings) { + var logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; + console[logType]("New state: ", state); + } + function localStorageMiddleware(state, _, settings) { + if (aureliaPal.PLATFORM.global.localStorage) { + var key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; + aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); + } + } + function rehydrateFromLocalStorage(state, key) { + if (!aureliaPal.PLATFORM.global.localStorage) { + return state; + } + var storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); + if (!storedState) { + return state; + } + try { + return JSON.parse(storedState); + } + catch (e) { } + return state; + } - (function (LogLevel) { - LogLevel["trace"] = "trace"; - LogLevel["debug"] = "debug"; - LogLevel["info"] = "info"; - LogLevel["log"] = "log"; - LogLevel["warn"] = "warn"; - LogLevel["error"] = "error"; - })(exports.LogLevel || (exports.LogLevel = {})); - var LoggerIndexed = /** @class */ (function (_super) { - __extends(LoggerIndexed, _super); - function LoggerIndexed() { - return _super !== null && _super.apply(this, arguments) || this; - } - return LoggerIndexed; - }(aureliaLogging.Logger)); - function getLogType(options, definition, defaultLevel) { - if (definition && - options.logDefinitions && - options.logDefinitions.hasOwnProperty(definition) && - options.logDefinitions[definition] && - Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { - return options.logDefinitions[definition]; - } - return defaultLevel; - } + (function (LogLevel) { + LogLevel["trace"] = "trace"; + LogLevel["debug"] = "debug"; + LogLevel["info"] = "info"; + LogLevel["log"] = "log"; + LogLevel["warn"] = "warn"; + LogLevel["error"] = "error"; + })(exports.LogLevel || (exports.LogLevel = {})); + var LoggerIndexed = /** @class */ (function (_super) { + __extends(LoggerIndexed, _super); + function LoggerIndexed() { + return _super !== null && _super.apply(this, arguments) || this; + } + return LoggerIndexed; + }(aureliaLogging.Logger)); + function getLogType(options, definition, defaultLevel) { + if (definition && + options.logDefinitions && + options.logDefinitions.hasOwnProperty(definition) && + options.logDefinitions[definition] && + Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { + return options.logDefinitions[definition]; + } + return defaultLevel; + } - (function (PerformanceMeasurement) { - PerformanceMeasurement["StartEnd"] = "startEnd"; - PerformanceMeasurement["All"] = "all"; - })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); - var Store = /** @class */ (function () { - function Store(initialState, options) { - this.initialState = initialState; - this.logger = aureliaLogging.getLogger("aurelia-store"); - this.devToolsAvailable = false; - this.actions = new Map(); - this.middlewares = new Map(); - this.dispatchQueue = []; - this.options = options || {}; - var isUndoable = this.options.history && this.options.history.undoable === true; - this._state = new rxjs.BehaviorSubject(initialState); - this.state = this._state.asObservable(); - if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { - this.setupDevTools(); - } - if (isUndoable) { - this.registerHistoryMethods(); - } - } - Store.prototype.registerMiddleware = function (reducer, placement, settings) { - this.middlewares.set(reducer, { placement: placement, settings: settings }); - }; - Store.prototype.unregisterMiddleware = function (reducer) { - if (this.middlewares.has(reducer)) { - this.middlewares.delete(reducer); - } - }; - Store.prototype.isMiddlewareRegistered = function (middleware) { - return this.middlewares.has(middleware); - }; - Store.prototype.registerAction = function (name, reducer) { - if (reducer.length === 0) { - throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); - } - this.actions.set(reducer, { type: name }); - }; - Store.prototype.unregisterAction = function (reducer) { - if (this.actions.has(reducer)) { - this.actions.delete(reducer); - } - }; - Store.prototype.isActionRegistered = function (reducer) { - if (typeof reducer === "string") { - return Array.from(this.actions).find(function (action) { return action[1].type === reducer; }) !== undefined; - } - return this.actions.has(reducer); - }; - Store.prototype.resetToState = function (state) { - this._state.next(state); - }; - Store.prototype.dispatch = function (reducer) { - var _this = this; - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - var action; - if (typeof reducer === "string") { - var result = Array.from(this.actions) - .find(function (val) { return val[1].type === reducer; }); - if (result) { - action = result[0]; - } - } - else { - action = reducer; - } - return new Promise(function (resolve, reject) { - _this.dispatchQueue.push({ reducer: action, params: params, resolve: resolve, reject: reject }); - if (_this.dispatchQueue.length === 1) { - _this.handleQueue(); - } - }); - }; - Store.prototype.handleQueue = function () { - return __awaiter(this, void 0, void 0, function () { - var queueItem, e_1; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!(this.dispatchQueue.length > 0)) return [3 /*break*/, 5]; - queueItem = this.dispatchQueue[0]; - _a.label = 1; - case 1: - _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.internalDispatch.apply(this, [queueItem.reducer].concat(queueItem.params))]; - case 2: - _a.sent(); - queueItem.resolve(); - return [3 /*break*/, 4]; - case 3: - e_1 = _a.sent(); - queueItem.reject(e_1); - return [3 /*break*/, 4]; - case 4: - this.dispatchQueue.shift(); - this.handleQueue(); - _a.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - }; - Store.prototype.internalDispatch = function (reducer) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - return __awaiter(this, void 0, void 0, function () { - var action, beforeMiddleswaresResult, result, resultingState, measures, marks, totalDuration; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!this.actions.has(reducer)) { - throw new Error("Tried to dispatch an unregistered action" + (reducer ? " " + reducer.name : "")); - } - aureliaPal.PLATFORM.performance.mark("dispatch-start"); - action = __assign({}, this.actions.get(reducer), { params: params }); - if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + action.type); - } - return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, { - name: action.type, - params: params - })]; - case 1: - beforeMiddleswaresResult = _a.sent(); - if (beforeMiddleswaresResult === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - return [4 /*yield*/, reducer.apply(void 0, [beforeMiddleswaresResult].concat(params))]; - case 2: - result = _a.sent(); - if (result === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); - if (!result && typeof result !== "object") { - throw new Error("The reducer has to return a new state"); - } - return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, { - name: action.type, - params: params - })]; - case 3: - resultingState = _a.sent(); - if (resultingState === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - if (isStateHistory(resultingState) && - this.options.history && - this.options.history.limit) { - resultingState = applyLimits(resultingState, this.options.history.limit); - } - this._state.next(resultingState); - aureliaPal.PLATFORM.performance.mark("dispatch-end"); - if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { - aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); - measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + action.type + ":", measures); - } - else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { - marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); - totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + action.type + ":", marks); - } - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); - return [2 /*return*/]; - } - }); - }); - }; - Store.prototype.executeMiddlewares = function (state, placement, action) { - var _this = this; - return Array.from(this.middlewares) - .filter(function (middleware) { return middleware[1].placement === placement; }) - .reduce(function (prev, curr, _, _arr) { return __awaiter(_this, void 0, void 0, function () { - var result, _a, _b, _c, e_2; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - _d.trys.push([0, 5, 7, 8]); - _b = (_a = curr)[0]; - return [4 /*yield*/, prev]; - case 1: return [4 /*yield*/, _b.apply(_a, [_d.sent(), this._state.getValue(), curr[1].settings, action])]; - case 2: - result = _d.sent(); - if (result === false) { - _arr = []; - return [2 /*return*/, false]; - } - _c = result; - if (_c) return [3 /*break*/, 4]; - return [4 /*yield*/, prev]; - case 3: - _c = (_d.sent()); - _d.label = 4; - case 4: return [2 /*return*/, _c]; - case 5: - e_2 = _d.sent(); - if (this.options.propagateError) { - _arr = []; - throw e_2; - } - return [4 /*yield*/, prev]; - case 6: return [2 /*return*/, _d.sent()]; - case 7: - aureliaPal.PLATFORM.performance.mark("dispatch-" + placement + "-" + curr[0].name); - return [7 /*endfinally*/]; - case 8: return [2 /*return*/]; - } - }); - }); }, state); - }; - Store.prototype.setupDevTools = function () { - var _this = this; - if (aureliaPal.PLATFORM.global.devToolsExtension) { - this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); - this.devToolsAvailable = true; - this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); - this.devTools.init(this.initialState); - this.devTools.subscribe(function (message) { - _this.logger[getLogType(_this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools sent change " + message.type); - if (message.type === "DISPATCH") { - _this._state.next(JSON.parse(message.state)); - } - }); - } - }; - Store.prototype.updateDevToolsState = function (action, state) { - if (this.devToolsAvailable) { - this.devTools.send(action, state); - } - }; - Store.prototype.registerHistoryMethods = function () { - this.registerAction("jump", jump); - }; - return Store; - }()); - function dispatchify(action) { - var store = aureliaDependencyInjection.Container.instance.get(Store); - return function () { - var params = []; - for (var _i = 0; _i < arguments.length; _i++) { - params[_i] = arguments[_i]; - } - return store.dispatch.apply(store, [action].concat(params)); - }; - } + (function (PerformanceMeasurement) { + PerformanceMeasurement["StartEnd"] = "startEnd"; + PerformanceMeasurement["All"] = "all"; + })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); + var UnregisteredActionError = /** @class */ (function (_super) { + __extends(UnregisteredActionError, _super); + function UnregisteredActionError(reducer) { + return _super.call(this, "Tried to dispatch an unregistered action " + (reducer && (typeof reducer === "string" ? reducer : reducer.name))) || this; + } + return UnregisteredActionError; + }(Error)); + var Store = /** @class */ (function () { + function Store(initialState, options) { + this.initialState = initialState; + this.logger = aureliaLogging.getLogger("aurelia-store"); + this.devToolsAvailable = false; + this.actions = new Map(); + this.middlewares = new Map(); + this.dispatchQueue = []; + this.options = options || {}; + var isUndoable = this.options.history && this.options.history.undoable === true; + this._state = new rxjs.BehaviorSubject(initialState); + this.state = this._state.asObservable(); + if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { + this.setupDevTools(); + } + if (isUndoable) { + this.registerHistoryMethods(); + } + } + Store.prototype.registerMiddleware = function (reducer, placement, settings) { + this.middlewares.set(reducer, { placement: placement, settings: settings }); + }; + Store.prototype.unregisterMiddleware = function (reducer) { + if (this.middlewares.has(reducer)) { + this.middlewares.delete(reducer); + } + }; + Store.prototype.isMiddlewareRegistered = function (middleware) { + return this.middlewares.has(middleware); + }; + Store.prototype.registerAction = function (name, reducer) { + if (reducer.length === 0) { + throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); + } + this.actions.set(reducer, { type: name }); + }; + Store.prototype.unregisterAction = function (reducer) { + if (this.actions.has(reducer)) { + this.actions.delete(reducer); + } + }; + Store.prototype.isActionRegistered = function (reducer) { + if (typeof reducer === "string") { + return Array.from(this.actions).find(function (action) { return action[1].type === reducer; }) !== undefined; + } + return this.actions.has(reducer); + }; + Store.prototype.resetToState = function (state) { + this._state.next(state); + }; + Store.prototype.dispatch = function (reducer) { + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params: params + }]); + }; + Store.prototype.pipe = function (reducer) { + var _this = this; + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var pipeline = []; + var dispatchPipe = { + dispatch: function () { return _this.queueDispatch(pipeline); }, + pipe: function (nextReducer) { + var nextParams = []; + for (var _i = 1; _i < arguments.length; _i++) { + nextParams[_i - 1] = arguments[_i]; + } + var action = _this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe.apply(dispatchPipe, [reducer].concat(params)); + }; + Store.prototype.lookupAction = function (reducer) { + if (typeof reducer === "string") { + var result = Array.from(this.actions).find(function (_a) { + var _ = _a[0], action = _a[1]; + return action.type === reducer; + }); + if (result) { + return result[0]; + } + } + else if (this.actions.has(reducer)) { + return reducer; + } + return undefined; + }; + Store.prototype.queueDispatch = function (actions) { + var _this = this; + return new Promise(function (resolve, reject) { + _this.dispatchQueue.push({ actions: actions, resolve: resolve, reject: reject }); + if (_this.dispatchQueue.length === 1) { + _this.handleQueue(); + } + }); + }; + Store.prototype.handleQueue = function () { + return __awaiter(this, void 0, void 0, function () { + var queueItem, e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(this.dispatchQueue.length > 0)) return [3 /*break*/, 5]; + queueItem = this.dispatchQueue[0]; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.internalDispatch(queueItem.actions)]; + case 2: + _a.sent(); + queueItem.resolve(); + return [3 /*break*/, 4]; + case 3: + e_1 = _a.sent(); + queueItem.reject(e_1); + return [3 /*break*/, 4]; + case 4: + this.dispatchQueue.shift(); + this.handleQueue(); + _a.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + }; + Store.prototype.internalDispatch = function (actions) { + return __awaiter(this, void 0, void 0, function () { + var unregisteredAction, pipedActions, callingAction, beforeMiddleswaresResult, result, _i, pipedActions_1, action, resultingState, measures, marks, totalDuration; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + unregisteredAction = actions.find(function (a) { return !_this.actions.has(a.reducer); }); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); + } + aureliaPal.PLATFORM.performance.mark("dispatch-start"); + pipedActions = actions.map(function (a) { return ({ + type: _this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + }); }); + callingAction = { + name: pipedActions.map(function (a) { return a.type; }).join("->"), + params: pipedActions.reduce(function (p, a) { return p.concat(a.params); }, []), + pipedActions: pipedActions.map(function (a) { return ({ + name: a.type, + params: a.params + }); }) + }; + if (this.options.logDispatchedActions) { + this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + callingAction.name); + } + return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, callingAction)]; + case 1: + beforeMiddleswaresResult = _a.sent(); + if (beforeMiddleswaresResult === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + result = beforeMiddleswaresResult; + _i = 0, pipedActions_1 = pipedActions; + _a.label = 2; + case 2: + if (!(_i < pipedActions_1.length)) return [3 /*break*/, 5]; + action = pipedActions_1[_i]; + return [4 /*yield*/, action.reducer.apply(action, [result].concat(action.params))]; + case 3: + result = _a.sent(); + if (result === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); + if (!result && typeof result !== "object") { + throw new Error("The reducer has to return a new state"); + } + _a.label = 4; + case 4: + _i++; + return [3 /*break*/, 2]; + case 5: return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, callingAction)]; + case 6: + resultingState = _a.sent(); + if (resultingState === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + if (isStateHistory(resultingState) && + this.options.history && + this.options.history.limit) { + resultingState = applyLimits(resultingState, this.options.history.limit); + } + this._state.next(resultingState); + aureliaPal.PLATFORM.performance.mark("dispatch-end"); + if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { + aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); + measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + callingAction.name + ":", measures); + } + else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { + marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); + totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + callingAction.name + ":", marks); + } + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); + return [2 /*return*/]; + } + }); + }); + }; + Store.prototype.executeMiddlewares = function (state, placement, action) { + var _this = this; + return Array.from(this.middlewares) + .filter(function (middleware) { return middleware[1].placement === placement; }) + .reduce(function (prev, curr, _, _arr) { return __awaiter(_this, void 0, void 0, function () { + var result, _a, _b, _c, e_2; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + _d.trys.push([0, 5, 7, 8]); + _b = (_a = curr)[0]; + return [4 /*yield*/, prev]; + case 1: return [4 /*yield*/, _b.apply(_a, [_d.sent(), this._state.getValue(), curr[1].settings, action])]; + case 2: + result = _d.sent(); + if (result === false) { + _arr = []; + return [2 /*return*/, false]; + } + _c = result; + if (_c) return [3 /*break*/, 4]; + return [4 /*yield*/, prev]; + case 3: + _c = (_d.sent()); + _d.label = 4; + case 4: return [2 /*return*/, _c]; + case 5: + e_2 = _d.sent(); + if (this.options.propagateError) { + _arr = []; + throw e_2; + } + return [4 /*yield*/, prev]; + case 6: return [2 /*return*/, _d.sent()]; + case 7: + aureliaPal.PLATFORM.performance.mark("dispatch-" + placement + "-" + curr[0].name); + return [7 /*endfinally*/]; + case 8: return [2 /*return*/]; + } + }); + }); }, state); + }; + Store.prototype.setupDevTools = function () { + var _this = this; + if (aureliaPal.PLATFORM.global.devToolsExtension) { + this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); + this.devToolsAvailable = true; + this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); + this.devTools.init(this.initialState); + this.devTools.subscribe(function (message) { + _this.logger[getLogType(_this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools sent change " + message.type); + if (message.type === "DISPATCH") { + _this._state.next(JSON.parse(message.state)); + } + }); + } + }; + Store.prototype.updateDevToolsState = function (action, state) { + if (this.devToolsAvailable) { + this.devTools.send(action, state); + } + }; + Store.prototype.registerHistoryMethods = function () { + this.registerAction("jump", jump); + }; + return Store; + }()); + function dispatchify(action) { + var store = aureliaDependencyInjection.Container.instance.get(Store); + return function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return store.dispatch.apply(store, [action].concat(params)); + }; + } - function executeSteps(store, shouldLogResults) { - var steps = []; - for (var _i = 2; _i < arguments.length; _i++) { - steps[_i - 2] = arguments[_i]; - } - return __awaiter(this, void 0, void 0, function () { - var logStep, tryStep, lastStep; - return __generator(this, function (_a) { - logStep = function (step, stepIdx) { return function (res) { - if (shouldLogResults) { - console.group("Step " + stepIdx); - console.log(res); - console.groupEnd(); - } - step(res); - }; }; - tryStep = function (step, reject) { - return function (res) { - try { - step(res); - } - catch (err) { - reject(err); - } - }; - }; - lastStep = function (step, resolve) { - return function (res) { - step(res); - resolve(); - }; - }; - return [2 /*return*/, new Promise(function (resolve, reject) { - var currentStep = 0; - steps.slice(0, -1).forEach(function (step) { - store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); - currentStep++; - }); - store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); - })]; - }); - }); - } + function executeSteps(store, shouldLogResults) { + var steps = []; + for (var _i = 2; _i < arguments.length; _i++) { + steps[_i - 2] = arguments[_i]; + } + return __awaiter(this, void 0, void 0, function () { + var logStep, tryStep, lastStep; + return __generator(this, function (_a) { + logStep = function (step, stepIdx) { return function (res) { + if (shouldLogResults) { + console.group("Step " + stepIdx); + console.log(res); + console.groupEnd(); + } + step(res); + }; }; + tryStep = function (step, reject) { + return function (res) { + try { + step(res); + } + catch (err) { + reject(err); + } + }; + }; + lastStep = function (step, resolve) { + return function (res) { + step(res); + resolve(); + }; + }; + return [2 /*return*/, new Promise(function (resolve, reject) { + var currentStep = 0; + steps.slice(0, -1).forEach(function (step) { + store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); + currentStep++; + }); + store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); + })]; + }); + }); + } - var defaultSelector = function (store) { return store.state; }; - function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } - var $store; - // const store = Container.instance.get(Store) as Store; - var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); - function getSource(selector) { - // if for some reason getSource is invoked before setup (bind lifecycle, typically) - // then we have no choice but to get the store instance from global container instance - // otherwise, assume that $store variable in the closure would be already assigned the right - // value from created callback - // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios - var store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); - var source = selector(store); - if (source instanceof rxjs.Observable) { - return source; - } - return store.state; - } - function createSelectors() { - var _a; - var isSelectorObj = typeof _settings.selector === "object"; - var fallbackSelector = (_a = {}, - _a[_settings.target || "state"] = _settings.selector || defaultSelector, - _a); - return Object.entries(__assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(function (_a) { - var target = _a[0], selector = _a[1]; - var _b; - return ({ - targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], - selector: selector, - // numbers are the starting index to slice all the change handling args, - // which are prop name, new state and old state - changeHandlers: (_b = {}, - _b[_settings.onChanged || ""] = 1, - _b[(_settings.target || target) + "Changed"] = _settings.target ? 0 : 1, - _b["propertyChanged"] = 0, - _b) - }); - }); - } - return function (target) { - var originalCreated = target.prototype.created; - var originalSetup = typeof settings === "object" && settings.setup - ? target.prototype[settings.setup] - : target.prototype.bind; - var originalTeardown = typeof settings === "object" && settings.teardown - ? target.prototype[settings.teardown] - : target.prototype.unbind; - // only override if prototype callback is a function - if (typeof originalCreated === "function" || originalCreated === undefined) { - target.prototype.created = function created(_, view) { - // here we relies on the fact that the class Store - // has not been registered somewhere in one of child containers, instead of root container - // if there is any issue with this approach, needs to walk all the way up to resolve from root - // typically like invoking from global Container.instance - $store = view.container.get(Store); - if (originalCreated !== undefined) { - return originalCreated.call(this, _, view); - } - }; - } - target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { - var _this = this; - if (typeof settings == "object" && - typeof settings.onChanged === "string" && - !(settings.onChanged in this)) { - throw new Error("Provided onChanged handler does not exist on target VM"); - } - this._stateSubscriptions = createSelectors().map(function (s) { return getSource(s.selector).subscribe(function (state) { - var lastTargetIdx = s.targets.length - 1; - var oldState = s.targets.reduce(function (accu, curr) { - if (accu === void 0) { accu = {}; } - return accu[curr]; - }, _this); - Object.entries(s.changeHandlers).forEach(function (_a) { - var handlerName = _a[0], args = _a[1]; - if (handlerName in _this) { - _this[handlerName].apply(_this, [s.targets[lastTargetIdx], state, oldState].slice(args, 3)); - } - }); - s.targets.reduce(function (accu, curr, idx) { - accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; - return accu[curr]; - }, _this); - }); }); - if (originalSetup) { - return originalSetup.apply(this, arguments); - } - }; - target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { - if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { - this._stateSubscriptions.forEach(function (sub) { - if (sub instanceof rxjs.Subscription && sub.closed === false) { - sub.unsubscribe(); - } - }); - } - if (originalTeardown) { - return originalTeardown.apply(this, arguments); - } - }; - }; - } + var defaultSelector = function (store) { return store.state; }; + function connectTo(settings) { + var $store; + // const store = Container.instance.get(Store) as Store; + var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); + function getSource(selector) { + // if for some reason getSource is invoked before setup (bind lifecycle, typically) + // then we have no choice but to get the store instance from global container instance + // otherwise, assume that $store variable in the closure would be already assigned the right + // value from created callback + // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios + var store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); + var source = selector(store); + if (source instanceof rxjs.Observable) { + return source; + } + return store.state; + } + function createSelectors() { + var _a; + var isSelectorObj = typeof _settings.selector === "object"; + var fallbackSelector = (_a = {}, + _a[_settings.target || "state"] = _settings.selector || defaultSelector, + _a); + return Object.entries(__assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(function (_a) { + var target = _a[0], selector = _a[1]; + var _b; + return ({ + targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], + selector: selector, + // numbers are the starting index to slice all the change handling args, + // which are prop name, new state and old state + changeHandlers: (_b = {}, + _b[_settings.onChanged || ""] = 1, + _b[(_settings.target || target) + "Changed"] = _settings.target ? 0 : 1, + _b["propertyChanged"] = 0, + _b) + }); + }); + } + return function (target) { + var originalCreated = target.prototype.created; + var originalSetup = typeof settings === "object" && settings.setup + ? target.prototype[settings.setup] + : target.prototype.bind; + var originalTeardown = typeof settings === "object" && settings.teardown + ? target.prototype[settings.teardown] + : target.prototype.unbind; + // only override if prototype callback is a function + if (typeof originalCreated === "function" || originalCreated === undefined) { + target.prototype.created = function created(_, view) { + // here we relies on the fact that the class Store + // has not been registered somewhere in one of child containers, instead of root container + // if there is any issue with this approach, needs to walk all the way up to resolve from root + // typically like invoking from global Container.instance + $store = view.container.get(Store); + if (originalCreated !== undefined) { + return originalCreated.call(this, _, view); + } + }; + } + target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { + var _this = this; + if (typeof settings == "object" && + typeof settings.onChanged === "string" && + !(settings.onChanged in this)) { + throw new Error("Provided onChanged handler does not exist on target VM"); + } + this._stateSubscriptions = createSelectors().map(function (s) { return getSource(s.selector).subscribe(function (state) { + var lastTargetIdx = s.targets.length - 1; + var oldState = s.targets.reduce(function (accu, curr) { + if (accu === void 0) { accu = {}; } + return accu[curr]; + }, _this); + Object.entries(s.changeHandlers).forEach(function (_a) { + var handlerName = _a[0], args = _a[1]; + if (handlerName in _this) { + _this[handlerName].apply(_this, [s.targets[lastTargetIdx], state, oldState].slice(args, 3)); + } + }); + s.targets.reduce(function (accu, curr, idx) { + accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; + return accu[curr]; + }, _this); + }); }); + if (originalSetup) { + return originalSetup.apply(this, arguments); + } + }; + target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { + if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { + this._stateSubscriptions.forEach(function (sub) { + if (sub instanceof rxjs.Subscription && sub.closed === false) { + sub.unsubscribe(); + } + }); + } + if (originalTeardown) { + return originalTeardown.apply(this, arguments); + } + }; + }; + } - function configure(aurelia, options) { - if (!options || !options.initialState) { - throw new Error("initialState must be provided via options"); - } - var initState = options.initialState; - if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { - initState = { past: [], present: options.initialState, future: [] }; - } - delete options.initialState; - aurelia.container - .registerInstance(Store, new Store(initState, options)); - } + function configure(aurelia, options) { + if (!options || !options.initialState) { + throw new Error("initialState must be provided via options"); + } + var initState = options.initialState; + if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { + initState = { past: [], present: options.initialState, future: [] }; + } + delete options.initialState; + aurelia.container + .registerInstance(Store, new Store(initState, options)); + } - exports.configure = configure; - exports.Store = Store; - exports.dispatchify = dispatchify; - exports.executeSteps = executeSteps; - exports.jump = jump; - exports.nextStateHistory = nextStateHistory; - exports.applyLimits = applyLimits; - exports.isStateHistory = isStateHistory; - exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; - exports.logMiddleware = logMiddleware; - exports.localStorageMiddleware = localStorageMiddleware; - exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; - exports.LoggerIndexed = LoggerIndexed; - exports.getLogType = getLogType; - exports.connectTo = connectTo; + exports.configure = configure; + exports.UnregisteredActionError = UnregisteredActionError; + exports.Store = Store; + exports.dispatchify = dispatchify; + exports.executeSteps = executeSteps; + exports.jump = jump; + exports.nextStateHistory = nextStateHistory; + exports.applyLimits = applyLimits; + exports.isStateHistory = isStateHistory; + exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; + exports.logMiddleware = logMiddleware; + exports.localStorageMiddleware = localStorageMiddleware; + exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; + exports.LoggerIndexed = LoggerIndexed; + exports.getLogType = getLogType; + exports.connectTo = connectTo; - Object.defineProperty(exports, '__esModule', { value: true }); + Object.defineProperty(exports, '__esModule', { value: true }); }); diff --git a/dist/aurelia-store.d.ts b/dist/aurelia-store.d.ts index f27fee8..435ccdf 100644 --- a/dist/aurelia-store.d.ts +++ b/dist/aurelia-store.d.ts @@ -19,6 +19,10 @@ export declare const DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; export interface CallingAction { name: string; params?: any[]; + pipedActions?: { + name: string; + params?: any[]; + }[]; } export declare type Middleware = (state: T, originalState: T | undefined, settings: S, action?: CallingAction) => T | Promise | void | false; export declare enum MiddlewarePlacement { @@ -231,6 +235,13 @@ export interface StoreOptions { logDefinitions?: LogDefinitions; devToolsOptions?: DevToolsOptions; } +export interface PipedDispatch { + pipe:

(reducer: Reducer | string, ...params: P) => PipedDispatch; + dispatch: () => Promise; +} +export declare class UnregisteredActionError extends Error { + constructor(reducer?: string | Reducer); +} export declare class Store { private initialState; readonly state: Observable; @@ -252,6 +263,9 @@ export declare class Store { isActionRegistered(reducer: Reducer | string): boolean; resetToState(state: T): void; dispatch

(reducer: Reducer | string, ...params: P): Promise; + pipe

(reducer: Reducer | string, ...params: P): PipedDispatch; + private lookupAction; + private queueDispatch; private handleQueue; private internalDispatch; private executeMiddlewares; diff --git a/dist/commonjs/aurelia-store.js b/dist/commonjs/aurelia-store.js index 8b7cc46..931529c 100644 --- a/dist/commonjs/aurelia-store.js +++ b/dist/commonjs/aurelia-store.js @@ -8,6 +8,18 @@ var aureliaLogging = require('aurelia-logging'); var aureliaPal = require('aurelia-pal'); var operators = require('rxjs/operators'); +/* istanbul ignore next */ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill +if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; +} + /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use @@ -201,6 +213,13 @@ function getLogType(options, definition, defaultLevel) { PerformanceMeasurement["StartEnd"] = "startEnd"; PerformanceMeasurement["All"] = "all"; })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); +var UnregisteredActionError = /** @class */ (function (_super) { + __extends(UnregisteredActionError, _super); + function UnregisteredActionError(reducer) { + return _super.call(this, "Tried to dispatch an unregistered action " + (reducer && (typeof reducer === "string" ? reducer : reducer.name))) || this; + } + return UnregisteredActionError; +}(Error)); var Store = /** @class */ (function () { function Store(initialState, options) { this.initialState = initialState; @@ -252,24 +271,62 @@ var Store = /** @class */ (function () { this._state.next(state); }; Store.prototype.dispatch = function (reducer) { + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params: params + }]); + }; + Store.prototype.pipe = function (reducer) { var _this = this; var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } - var action; + var pipeline = []; + var dispatchPipe = { + dispatch: function () { return _this.queueDispatch(pipeline); }, + pipe: function (nextReducer) { + var nextParams = []; + for (var _i = 1; _i < arguments.length; _i++) { + nextParams[_i - 1] = arguments[_i]; + } + var action = _this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe.apply(dispatchPipe, [reducer].concat(params)); + }; + Store.prototype.lookupAction = function (reducer) { if (typeof reducer === "string") { - var result = Array.from(this.actions) - .find(function (val) { return val[1].type === reducer; }); + var result = Array.from(this.actions).find(function (_a) { + var _ = _a[0], action = _a[1]; + return action.type === reducer; + }); if (result) { - action = result[0]; + return result[0]; } } - else { - action = reducer; + else if (this.actions.has(reducer)) { + return reducer; } + return undefined; + }; + Store.prototype.queueDispatch = function (actions) { + var _this = this; return new Promise(function (resolve, reject) { - _this.dispatchQueue.push({ reducer: action, params: params, resolve: resolve, reject: reject }); + _this.dispatchQueue.push({ actions: actions, resolve: resolve, reject: reject }); if (_this.dispatchQueue.length === 1) { _this.handleQueue(); } @@ -286,7 +343,7 @@ var Store = /** @class */ (function () { _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.internalDispatch.apply(this, [queueItem.reducer].concat(queueItem.params))]; + return [4 /*yield*/, this.internalDispatch(queueItem.actions)]; case 2: _a.sent(); queueItem.resolve(); @@ -304,28 +361,35 @@ var Store = /** @class */ (function () { }); }); }; - Store.prototype.internalDispatch = function (reducer) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } + Store.prototype.internalDispatch = function (actions) { return __awaiter(this, void 0, void 0, function () { - var action, beforeMiddleswaresResult, result, resultingState, measures, marks, totalDuration; + var unregisteredAction, pipedActions, callingAction, beforeMiddleswaresResult, result, _i, pipedActions_1, action, resultingState, measures, marks, totalDuration; + var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: - if (!this.actions.has(reducer)) { - throw new Error("Tried to dispatch an unregistered action" + (reducer ? " " + reducer.name : "")); + unregisteredAction = actions.find(function (a) { return !_this.actions.has(a.reducer); }); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); } aureliaPal.PLATFORM.performance.mark("dispatch-start"); - action = __assign({}, this.actions.get(reducer), { params: params }); + pipedActions = actions.map(function (a) { return ({ + type: _this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + }); }); + callingAction = { + name: pipedActions.map(function (a) { return a.type; }).join("->"), + params: pipedActions.reduce(function (p, a) { return p.concat(a.params); }, []), + pipedActions: pipedActions.map(function (a) { return ({ + name: a.type, + params: a.params + }); }) + }; if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + action.type); + this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + callingAction.name); } - return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, { - name: action.type, - params: params - })]; + return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, callingAction)]; case 1: beforeMiddleswaresResult = _a.sent(); if (beforeMiddleswaresResult === false) { @@ -333,8 +397,14 @@ var Store = /** @class */ (function () { aureliaPal.PLATFORM.performance.clearMeasures(); return [2 /*return*/]; } - return [4 /*yield*/, reducer.apply(void 0, [beforeMiddleswaresResult].concat(params))]; + result = beforeMiddleswaresResult; + _i = 0, pipedActions_1 = pipedActions; + _a.label = 2; case 2: + if (!(_i < pipedActions_1.length)) return [3 /*break*/, 5]; + action = pipedActions_1[_i]; + return [4 /*yield*/, action.reducer.apply(action, [result].concat(action.params))]; + case 3: result = _a.sent(); if (result === false) { aureliaPal.PLATFORM.performance.clearMarks(); @@ -345,11 +415,12 @@ var Store = /** @class */ (function () { if (!result && typeof result !== "object") { throw new Error("The reducer has to return a new state"); } - return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, { - name: action.type, - params: params - })]; - case 3: + _a.label = 4; + case 4: + _i++; + return [3 /*break*/, 2]; + case 5: return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, callingAction)]; + case 6: resultingState = _a.sent(); if (resultingState === false) { aureliaPal.PLATFORM.performance.clearMarks(); @@ -366,16 +437,16 @@ var Store = /** @class */ (function () { if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + action.type + ":", measures); + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + callingAction.name + ":", measures); } else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + action.type + ":", marks); + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + callingAction.name + ":", marks); } aureliaPal.PLATFORM.performance.clearMarks(); aureliaPal.PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); return [2 /*return*/]; } }); @@ -505,9 +576,6 @@ function executeSteps(store, shouldLogResults) { var defaultSelector = function (store) { return store.state; }; function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } var $store; // const store = Container.instance.get(Store) as Store; var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); @@ -624,6 +692,7 @@ function configure(aurelia, options) { } exports.configure = configure; +exports.UnregisteredActionError = UnregisteredActionError; exports.Store = Store; exports.dispatchify = dispatchify; exports.executeSteps = executeSteps; diff --git a/dist/es2015/aurelia-store.js b/dist/es2015/aurelia-store.js index f95f1a8..5f5cc68 100644 --- a/dist/es2015/aurelia-store.js +++ b/dist/es2015/aurelia-store.js @@ -4,6 +4,18 @@ import { Logger, getLogger } from 'aurelia-logging'; import { PLATFORM } from 'aurelia-pal'; import { skip, take, delay } from 'rxjs/operators'; +/* istanbul ignore next */ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill +if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; +} + /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use @@ -142,6 +154,11 @@ var PerformanceMeasurement; PerformanceMeasurement["StartEnd"] = "startEnd"; PerformanceMeasurement["All"] = "all"; })(PerformanceMeasurement || (PerformanceMeasurement = {})); +class UnregisteredActionError extends Error { + constructor(reducer) { + super(`Tried to dispatch an unregistered action ${reducer && (typeof reducer === "string" ? reducer : reducer.name)}`); + } +} class Store { constructor(initialState, options) { this.initialState = initialState; @@ -193,19 +210,45 @@ class Store { this._state.next(state); } dispatch(reducer, ...params) { - let action; + const action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params + }]); + } + pipe(reducer, ...params) { + const pipeline = []; + const dispatchPipe = { + dispatch: () => this.queueDispatch(pipeline), + pipe: (nextReducer, ...nextParams) => { + const action = this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe(reducer, ...params); + } + lookupAction(reducer) { if (typeof reducer === "string") { - const result = Array.from(this.actions) - .find((val) => val[1].type === reducer); + const result = Array.from(this.actions).find(([_, action]) => action.type === reducer); if (result) { - action = result[0]; + return result[0]; } } - else { - action = reducer; + else if (this.actions.has(reducer)) { + return reducer; } + return undefined; + } + queueDispatch(actions) { return new Promise((resolve, reject) => { - this.dispatchQueue.push({ reducer: action, params, resolve, reject }); + this.dispatchQueue.push({ actions, resolve, reject }); if (this.dispatchQueue.length === 1) { this.handleQueue(); } @@ -216,7 +259,7 @@ class Store { if (this.dispatchQueue.length > 0) { const queueItem = this.dispatchQueue[0]; try { - yield this.internalDispatch(queueItem.reducer, ...queueItem.params); + yield this.internalDispatch(queueItem.actions); queueItem.resolve(); } catch (e) { @@ -227,39 +270,49 @@ class Store { } }); } - internalDispatch(reducer, ...params) { + internalDispatch(actions) { return __awaiter(this, void 0, void 0, function* () { - if (!this.actions.has(reducer)) { - throw new Error(`Tried to dispatch an unregistered action${reducer ? " " + reducer.name : ""}`); + const unregisteredAction = actions.find((a) => !this.actions.has(a.reducer)); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); } PLATFORM.performance.mark("dispatch-start"); - const action = Object.assign({}, this.actions.get(reducer), { params }); + const pipedActions = actions.map((a) => ({ + type: this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + })); + const callingAction = { + name: pipedActions.map((a) => a.type).join("->"), + params: pipedActions.reduce((p, a) => p.concat(a.params), []), + pipedActions: pipedActions.map((a) => ({ + name: a.type, + params: a.params + })) + }; if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)](`Dispatching: ${action.type}`); + this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)](`Dispatching: ${callingAction.name}`); } - const beforeMiddleswaresResult = yield this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, { - name: action.type, - params - }); + const beforeMiddleswaresResult = yield this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, callingAction); if (beforeMiddleswaresResult === false) { PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); return; } - const result = yield reducer(beforeMiddleswaresResult, ...params); - if (result === false) { - PLATFORM.performance.clearMarks(); - PLATFORM.performance.clearMeasures(); - return; - } - PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); - if (!result && typeof result !== "object") { - throw new Error("The reducer has to return a new state"); + let result = beforeMiddleswaresResult; + for (const action of pipedActions) { + result = yield action.reducer(result, ...action.params); + if (result === false) { + PLATFORM.performance.clearMarks(); + PLATFORM.performance.clearMeasures(); + return; + } + PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); + if (!result && typeof result !== "object") { + throw new Error("The reducer has to return a new state"); + } } - let resultingState = yield this.executeMiddlewares(result, MiddlewarePlacement.After, { - name: action.type, - params - }); + let resultingState = yield this.executeMiddlewares(result, MiddlewarePlacement.After, callingAction); if (resultingState === false) { PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); @@ -275,16 +328,16 @@ class Store { if (this.options.measurePerformance === PerformanceMeasurement.StartEnd) { PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); const measures = PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${action.type}:`, measures); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${callingAction.name}:`, measures); } else if (this.options.measurePerformance === PerformanceMeasurement.All) { const marks = PLATFORM.performance.getEntriesByType("mark"); const totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${action.type}:`, marks); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${callingAction.name}:`, marks); } PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); }); } executeMiddlewares(state, placement, action) { @@ -377,9 +430,6 @@ function executeSteps(store, shouldLogResults, ...steps) { const defaultSelector = (store) => store.state; function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } let $store; // const store = Container.instance.get(Store) as Store; const _settings = Object.assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); @@ -485,4 +535,4 @@ function configure(aurelia, options) { .registerInstance(Store, new Store(initState, options)); } -export { configure, PerformanceMeasurement, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; +export { configure, PerformanceMeasurement, UnregisteredActionError, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; diff --git a/dist/es2017/aurelia-store.js b/dist/es2017/aurelia-store.js index b477113..f61e2a2 100644 --- a/dist/es2017/aurelia-store.js +++ b/dist/es2017/aurelia-store.js @@ -4,6 +4,18 @@ import { Logger, getLogger } from 'aurelia-logging'; import { PLATFORM } from 'aurelia-pal'; import { skip, take, delay } from 'rxjs/operators'; +/* istanbul ignore next */ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill +if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; +} + function jump(state, n) { if (!isStateHistory(state)) { return state; @@ -118,6 +130,11 @@ var PerformanceMeasurement; PerformanceMeasurement["StartEnd"] = "startEnd"; PerformanceMeasurement["All"] = "all"; })(PerformanceMeasurement || (PerformanceMeasurement = {})); +class UnregisteredActionError extends Error { + constructor(reducer) { + super(`Tried to dispatch an unregistered action ${reducer && (typeof reducer === "string" ? reducer : reducer.name)}`); + } +} class Store { constructor(initialState, options) { this.initialState = initialState; @@ -169,19 +186,45 @@ class Store { this._state.next(state); } dispatch(reducer, ...params) { - let action; + const action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params + }]); + } + pipe(reducer, ...params) { + const pipeline = []; + const dispatchPipe = { + dispatch: () => this.queueDispatch(pipeline), + pipe: (nextReducer, ...nextParams) => { + const action = this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe(reducer, ...params); + } + lookupAction(reducer) { if (typeof reducer === "string") { - const result = Array.from(this.actions) - .find((val) => val[1].type === reducer); + const result = Array.from(this.actions).find(([_, action]) => action.type === reducer); if (result) { - action = result[0]; + return result[0]; } } - else { - action = reducer; + else if (this.actions.has(reducer)) { + return reducer; } + return undefined; + } + queueDispatch(actions) { return new Promise((resolve, reject) => { - this.dispatchQueue.push({ reducer: action, params, resolve, reject }); + this.dispatchQueue.push({ actions, resolve, reject }); if (this.dispatchQueue.length === 1) { this.handleQueue(); } @@ -191,7 +234,7 @@ class Store { if (this.dispatchQueue.length > 0) { const queueItem = this.dispatchQueue[0]; try { - await this.internalDispatch(queueItem.reducer, ...queueItem.params); + await this.internalDispatch(queueItem.actions); queueItem.resolve(); } catch (e) { @@ -201,38 +244,48 @@ class Store { this.handleQueue(); } } - async internalDispatch(reducer, ...params) { - if (!this.actions.has(reducer)) { - throw new Error(`Tried to dispatch an unregistered action${reducer ? " " + reducer.name : ""}`); + async internalDispatch(actions) { + const unregisteredAction = actions.find((a) => !this.actions.has(a.reducer)); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); } PLATFORM.performance.mark("dispatch-start"); - const action = Object.assign({}, this.actions.get(reducer), { params }); + const pipedActions = actions.map((a) => ({ + type: this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + })); + const callingAction = { + name: pipedActions.map((a) => a.type).join("->"), + params: pipedActions.reduce((p, a) => p.concat(a.params), []), + pipedActions: pipedActions.map((a) => ({ + name: a.type, + params: a.params + })) + }; if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)](`Dispatching: ${action.type}`); + this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)](`Dispatching: ${callingAction.name}`); } - const beforeMiddleswaresResult = await this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, { - name: action.type, - params - }); + const beforeMiddleswaresResult = await this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, callingAction); if (beforeMiddleswaresResult === false) { PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); return; } - const result = await reducer(beforeMiddleswaresResult, ...params); - if (result === false) { - PLATFORM.performance.clearMarks(); - PLATFORM.performance.clearMeasures(); - return; - } - PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); - if (!result && typeof result !== "object") { - throw new Error("The reducer has to return a new state"); + let result = beforeMiddleswaresResult; + for (const action of pipedActions) { + result = await action.reducer(result, ...action.params); + if (result === false) { + PLATFORM.performance.clearMarks(); + PLATFORM.performance.clearMeasures(); + return; + } + PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); + if (!result && typeof result !== "object") { + throw new Error("The reducer has to return a new state"); + } } - let resultingState = await this.executeMiddlewares(result, MiddlewarePlacement.After, { - name: action.type, - params - }); + let resultingState = await this.executeMiddlewares(result, MiddlewarePlacement.After, callingAction); if (resultingState === false) { PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); @@ -248,16 +301,16 @@ class Store { if (this.options.measurePerformance === PerformanceMeasurement.StartEnd) { PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); const measures = PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${action.type}:`, measures); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${callingAction.name}:`, measures); } else if (this.options.measurePerformance === PerformanceMeasurement.All) { const marks = PLATFORM.performance.getEntriesByType("mark"); const totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${action.type}:`, marks); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${callingAction.name}:`, marks); } PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); } executeMiddlewares(state, placement, action) { return Array.from(this.middlewares) @@ -347,9 +400,6 @@ async function executeSteps(store, shouldLogResults, ...steps) { const defaultSelector = (store) => store.state; function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } let $store; // const store = Container.instance.get(Store) as Store; const _settings = Object.assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); @@ -455,4 +505,4 @@ function configure(aurelia, options) { .registerInstance(Store, new Store(initState, options)); } -export { configure, PerformanceMeasurement, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; +export { configure, PerformanceMeasurement, UnregisteredActionError, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; diff --git a/dist/native-modules/aurelia-store.js b/dist/native-modules/aurelia-store.js index e7302b7..fc7c942 100644 --- a/dist/native-modules/aurelia-store.js +++ b/dist/native-modules/aurelia-store.js @@ -4,6 +4,18 @@ import { Logger, getLogger } from 'aurelia-logging'; import { PLATFORM } from 'aurelia-pal'; import { skip, take, delay } from 'rxjs/operators'; +/* istanbul ignore next */ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill +if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; +} + /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use @@ -200,6 +212,13 @@ var PerformanceMeasurement; PerformanceMeasurement["StartEnd"] = "startEnd"; PerformanceMeasurement["All"] = "all"; })(PerformanceMeasurement || (PerformanceMeasurement = {})); +var UnregisteredActionError = /** @class */ (function (_super) { + __extends(UnregisteredActionError, _super); + function UnregisteredActionError(reducer) { + return _super.call(this, "Tried to dispatch an unregistered action " + (reducer && (typeof reducer === "string" ? reducer : reducer.name))) || this; + } + return UnregisteredActionError; +}(Error)); var Store = /** @class */ (function () { function Store(initialState, options) { this.initialState = initialState; @@ -251,24 +270,62 @@ var Store = /** @class */ (function () { this._state.next(state); }; Store.prototype.dispatch = function (reducer) { + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params: params + }]); + }; + Store.prototype.pipe = function (reducer) { var _this = this; var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } - var action; + var pipeline = []; + var dispatchPipe = { + dispatch: function () { return _this.queueDispatch(pipeline); }, + pipe: function (nextReducer) { + var nextParams = []; + for (var _i = 1; _i < arguments.length; _i++) { + nextParams[_i - 1] = arguments[_i]; + } + var action = _this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe.apply(dispatchPipe, [reducer].concat(params)); + }; + Store.prototype.lookupAction = function (reducer) { if (typeof reducer === "string") { - var result = Array.from(this.actions) - .find(function (val) { return val[1].type === reducer; }); + var result = Array.from(this.actions).find(function (_a) { + var _ = _a[0], action = _a[1]; + return action.type === reducer; + }); if (result) { - action = result[0]; + return result[0]; } } - else { - action = reducer; + else if (this.actions.has(reducer)) { + return reducer; } + return undefined; + }; + Store.prototype.queueDispatch = function (actions) { + var _this = this; return new Promise(function (resolve, reject) { - _this.dispatchQueue.push({ reducer: action, params: params, resolve: resolve, reject: reject }); + _this.dispatchQueue.push({ actions: actions, resolve: resolve, reject: reject }); if (_this.dispatchQueue.length === 1) { _this.handleQueue(); } @@ -285,7 +342,7 @@ var Store = /** @class */ (function () { _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.internalDispatch.apply(this, [queueItem.reducer].concat(queueItem.params))]; + return [4 /*yield*/, this.internalDispatch(queueItem.actions)]; case 2: _a.sent(); queueItem.resolve(); @@ -303,28 +360,35 @@ var Store = /** @class */ (function () { }); }); }; - Store.prototype.internalDispatch = function (reducer) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } + Store.prototype.internalDispatch = function (actions) { return __awaiter(this, void 0, void 0, function () { - var action, beforeMiddleswaresResult, result, resultingState, measures, marks, totalDuration; + var unregisteredAction, pipedActions, callingAction, beforeMiddleswaresResult, result, _i, pipedActions_1, action, resultingState, measures, marks, totalDuration; + var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: - if (!this.actions.has(reducer)) { - throw new Error("Tried to dispatch an unregistered action" + (reducer ? " " + reducer.name : "")); + unregisteredAction = actions.find(function (a) { return !_this.actions.has(a.reducer); }); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); } PLATFORM.performance.mark("dispatch-start"); - action = __assign({}, this.actions.get(reducer), { params: params }); + pipedActions = actions.map(function (a) { return ({ + type: _this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + }); }); + callingAction = { + name: pipedActions.map(function (a) { return a.type; }).join("->"), + params: pipedActions.reduce(function (p, a) { return p.concat(a.params); }, []), + pipedActions: pipedActions.map(function (a) { return ({ + name: a.type, + params: a.params + }); }) + }; if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)]("Dispatching: " + action.type); + this.logger[getLogType(this.options, "dispatchedActions", LogLevel.info)]("Dispatching: " + callingAction.name); } - return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, { - name: action.type, - params: params - })]; + return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), MiddlewarePlacement.Before, callingAction)]; case 1: beforeMiddleswaresResult = _a.sent(); if (beforeMiddleswaresResult === false) { @@ -332,8 +396,14 @@ var Store = /** @class */ (function () { PLATFORM.performance.clearMeasures(); return [2 /*return*/]; } - return [4 /*yield*/, reducer.apply(void 0, [beforeMiddleswaresResult].concat(params))]; + result = beforeMiddleswaresResult; + _i = 0, pipedActions_1 = pipedActions; + _a.label = 2; case 2: + if (!(_i < pipedActions_1.length)) return [3 /*break*/, 5]; + action = pipedActions_1[_i]; + return [4 /*yield*/, action.reducer.apply(action, [result].concat(action.params))]; + case 3: result = _a.sent(); if (result === false) { PLATFORM.performance.clearMarks(); @@ -344,11 +414,12 @@ var Store = /** @class */ (function () { if (!result && typeof result !== "object") { throw new Error("The reducer has to return a new state"); } - return [4 /*yield*/, this.executeMiddlewares(result, MiddlewarePlacement.After, { - name: action.type, - params: params - })]; - case 3: + _a.label = 4; + case 4: + _i++; + return [3 /*break*/, 2]; + case 5: return [4 /*yield*/, this.executeMiddlewares(result, MiddlewarePlacement.After, callingAction)]; + case 6: resultingState = _a.sent(); if (resultingState === false) { PLATFORM.performance.clearMarks(); @@ -365,16 +436,16 @@ var Store = /** @class */ (function () { if (this.options.measurePerformance === PerformanceMeasurement.StartEnd) { PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); measures = PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + action.type + ":", measures); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + callingAction.name + ":", measures); } else if (this.options.measurePerformance === PerformanceMeasurement.All) { marks = PLATFORM.performance.getEntriesByType("mark"); totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + action.type + ":", marks); + this.logger[getLogType(this.options, "performanceLog", LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + callingAction.name + ":", marks); } PLATFORM.performance.clearMarks(); PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); return [2 /*return*/]; } }); @@ -504,9 +575,6 @@ function executeSteps(store, shouldLogResults) { var defaultSelector = function (store) { return store.state; }; function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } var $store; // const store = Container.instance.get(Store) as Store; var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); @@ -622,4 +690,4 @@ function configure(aurelia, options) { .registerInstance(Store, new Store(initState, options)); } -export { configure, PerformanceMeasurement, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; +export { configure, PerformanceMeasurement, UnregisteredActionError, Store, dispatchify, executeSteps, jump, nextStateHistory, applyLimits, isStateHistory, DEFAULT_LOCAL_STORAGE_KEY, MiddlewarePlacement, logMiddleware, localStorageMiddleware, rehydrateFromLocalStorage, LogLevel, LoggerIndexed, getLogType, connectTo }; diff --git a/dist/umd-es2015/aurelia-store.js b/dist/umd-es2015/aurelia-store.js index eb80a2b..71e56ac 100644 --- a/dist/umd-es2015/aurelia-store.js +++ b/dist/umd-es2015/aurelia-store.js @@ -1,503 +1,554 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('aurelia-dependency-injection'), require('aurelia-logging'), require('aurelia-pal'), require('rxjs/operators')) : - typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'aurelia-dependency-injection', 'aurelia-logging', 'aurelia-pal', 'rxjs/operators'], factory) : - (global = global || self, factory((global.au = global.au || {}, global.au.store = {}), global.rxjs, global.au, global.au.LogManager, global.au, global.rxjs)); + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('aurelia-dependency-injection'), require('aurelia-logging'), require('aurelia-pal'), require('rxjs/operators')) : + typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'aurelia-dependency-injection', 'aurelia-logging', 'aurelia-pal', 'rxjs/operators'], factory) : + (global = global || self, factory((global.au = global.au || {}, global.au.store = {}), global.rxjs, global.au, global.au.LogManager, global.au, global.rxjs)); }(this, function (exports, rxjs, aureliaDependencyInjection, aureliaLogging, aureliaPal, operators) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 + /* istanbul ignore next */ + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill + if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; + } + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ - function __awaiter(thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } + function __awaiter(thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } - function jump(state, n) { - if (!isStateHistory(state)) { - return state; - } - if (n > 0) - return jumpToFuture(state, n - 1); - if (n < 0) - return jumpToPast(state, state.past.length + n); - return state; - } - function jumpToFuture(state, index) { - if (index < 0 || index >= state.future.length) { - return state; - } - const { past, future, present } = state; - const newPast = [...past, present, ...future.slice(0, index)]; - const newPresent = future[index]; - const newFuture = future.slice(index + 1); - return { past: newPast, present: newPresent, future: newFuture }; - } - function jumpToPast(state, index) { - if (index < 0 || index >= state.past.length) { - return state; - } - const { past, future, present } = state; - const newPast = past.slice(0, index); - const newFuture = [...past.slice(index + 1), present, ...future]; - const newPresent = past[index]; - return { past: newPast, present: newPresent, future: newFuture }; - } - function nextStateHistory(presentStateHistory, nextPresent) { - return Object.assign({}, presentStateHistory, { - past: [...presentStateHistory.past, presentStateHistory.present], - present: nextPresent, - future: [] - }); - } - function applyLimits(state, limit) { - if (isStateHistory(state)) { - if (state.past.length > limit) { - state.past = state.past.slice(state.past.length - limit); - } - if (state.future.length > limit) { - state.future = state.future.slice(0, limit); - } - } - return state; - } - function isStateHistory(history) { - return typeof history.present !== "undefined" && - typeof history.future !== "undefined" && - typeof history.past !== "undefined" && - Array.isArray(history.future) && - Array.isArray(history.past); - } + function jump(state, n) { + if (!isStateHistory(state)) { + return state; + } + if (n > 0) + return jumpToFuture(state, n - 1); + if (n < 0) + return jumpToPast(state, state.past.length + n); + return state; + } + function jumpToFuture(state, index) { + if (index < 0 || index >= state.future.length) { + return state; + } + const { past, future, present } = state; + const newPast = [...past, present, ...future.slice(0, index)]; + const newPresent = future[index]; + const newFuture = future.slice(index + 1); + return { past: newPast, present: newPresent, future: newFuture }; + } + function jumpToPast(state, index) { + if (index < 0 || index >= state.past.length) { + return state; + } + const { past, future, present } = state; + const newPast = past.slice(0, index); + const newFuture = [...past.slice(index + 1), present, ...future]; + const newPresent = past[index]; + return { past: newPast, present: newPresent, future: newFuture }; + } + function nextStateHistory(presentStateHistory, nextPresent) { + return Object.assign({}, presentStateHistory, { + past: [...presentStateHistory.past, presentStateHistory.present], + present: nextPresent, + future: [] + }); + } + function applyLimits(state, limit) { + if (isStateHistory(state)) { + if (state.past.length > limit) { + state.past = state.past.slice(state.past.length - limit); + } + if (state.future.length > limit) { + state.future = state.future.slice(0, limit); + } + } + return state; + } + function isStateHistory(history) { + return typeof history.present !== "undefined" && + typeof history.future !== "undefined" && + typeof history.past !== "undefined" && + Array.isArray(history.future) && + Array.isArray(history.past); + } - const DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; - (function (MiddlewarePlacement) { - MiddlewarePlacement["Before"] = "before"; - MiddlewarePlacement["After"] = "after"; - })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); - function logMiddleware(state, _, settings) { - const logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; - console[logType]("New state: ", state); - } - function localStorageMiddleware(state, _, settings) { - if (aureliaPal.PLATFORM.global.localStorage) { - const key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; - aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); - } - } - function rehydrateFromLocalStorage(state, key) { - if (!aureliaPal.PLATFORM.global.localStorage) { - return state; - } - const storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); - if (!storedState) { - return state; - } - try { - return JSON.parse(storedState); - } - catch (e) { } - return state; - } + const DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; + (function (MiddlewarePlacement) { + MiddlewarePlacement["Before"] = "before"; + MiddlewarePlacement["After"] = "after"; + })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); + function logMiddleware(state, _, settings) { + const logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; + console[logType]("New state: ", state); + } + function localStorageMiddleware(state, _, settings) { + if (aureliaPal.PLATFORM.global.localStorage) { + const key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; + aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); + } + } + function rehydrateFromLocalStorage(state, key) { + if (!aureliaPal.PLATFORM.global.localStorage) { + return state; + } + const storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); + if (!storedState) { + return state; + } + try { + return JSON.parse(storedState); + } + catch (e) { } + return state; + } - (function (LogLevel) { - LogLevel["trace"] = "trace"; - LogLevel["debug"] = "debug"; - LogLevel["info"] = "info"; - LogLevel["log"] = "log"; - LogLevel["warn"] = "warn"; - LogLevel["error"] = "error"; - })(exports.LogLevel || (exports.LogLevel = {})); - class LoggerIndexed extends aureliaLogging.Logger { - } - function getLogType(options, definition, defaultLevel) { - if (definition && - options.logDefinitions && - options.logDefinitions.hasOwnProperty(definition) && - options.logDefinitions[definition] && - Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { - return options.logDefinitions[definition]; - } - return defaultLevel; - } + (function (LogLevel) { + LogLevel["trace"] = "trace"; + LogLevel["debug"] = "debug"; + LogLevel["info"] = "info"; + LogLevel["log"] = "log"; + LogLevel["warn"] = "warn"; + LogLevel["error"] = "error"; + })(exports.LogLevel || (exports.LogLevel = {})); + class LoggerIndexed extends aureliaLogging.Logger { + } + function getLogType(options, definition, defaultLevel) { + if (definition && + options.logDefinitions && + options.logDefinitions.hasOwnProperty(definition) && + options.logDefinitions[definition] && + Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { + return options.logDefinitions[definition]; + } + return defaultLevel; + } - (function (PerformanceMeasurement) { - PerformanceMeasurement["StartEnd"] = "startEnd"; - PerformanceMeasurement["All"] = "all"; - })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); - class Store { - constructor(initialState, options) { - this.initialState = initialState; - this.logger = aureliaLogging.getLogger("aurelia-store"); - this.devToolsAvailable = false; - this.actions = new Map(); - this.middlewares = new Map(); - this.dispatchQueue = []; - this.options = options || {}; - const isUndoable = this.options.history && this.options.history.undoable === true; - this._state = new rxjs.BehaviorSubject(initialState); - this.state = this._state.asObservable(); - if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { - this.setupDevTools(); - } - if (isUndoable) { - this.registerHistoryMethods(); - } - } - registerMiddleware(reducer, placement, settings) { - this.middlewares.set(reducer, { placement, settings }); - } - unregisterMiddleware(reducer) { - if (this.middlewares.has(reducer)) { - this.middlewares.delete(reducer); - } - } - isMiddlewareRegistered(middleware) { - return this.middlewares.has(middleware); - } - registerAction(name, reducer) { - if (reducer.length === 0) { - throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); - } - this.actions.set(reducer, { type: name }); - } - unregisterAction(reducer) { - if (this.actions.has(reducer)) { - this.actions.delete(reducer); - } - } - isActionRegistered(reducer) { - if (typeof reducer === "string") { - return Array.from(this.actions).find((action) => action[1].type === reducer) !== undefined; - } - return this.actions.has(reducer); - } - resetToState(state) { - this._state.next(state); - } - dispatch(reducer, ...params) { - let action; - if (typeof reducer === "string") { - const result = Array.from(this.actions) - .find((val) => val[1].type === reducer); - if (result) { - action = result[0]; - } - } - else { - action = reducer; - } - return new Promise((resolve, reject) => { - this.dispatchQueue.push({ reducer: action, params, resolve, reject }); - if (this.dispatchQueue.length === 1) { - this.handleQueue(); - } - }); - } - handleQueue() { - return __awaiter(this, void 0, void 0, function* () { - if (this.dispatchQueue.length > 0) { - const queueItem = this.dispatchQueue[0]; - try { - yield this.internalDispatch(queueItem.reducer, ...queueItem.params); - queueItem.resolve(); - } - catch (e) { - queueItem.reject(e); - } - this.dispatchQueue.shift(); - this.handleQueue(); - } - }); - } - internalDispatch(reducer, ...params) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.actions.has(reducer)) { - throw new Error(`Tried to dispatch an unregistered action${reducer ? " " + reducer.name : ""}`); - } - aureliaPal.PLATFORM.performance.mark("dispatch-start"); - const action = Object.assign({}, this.actions.get(reducer), { params }); - if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)](`Dispatching: ${action.type}`); - } - const beforeMiddleswaresResult = yield this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, { - name: action.type, - params - }); - if (beforeMiddleswaresResult === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return; - } - const result = yield reducer(beforeMiddleswaresResult, ...params); - if (result === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return; - } - aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); - if (!result && typeof result !== "object") { - throw new Error("The reducer has to return a new state"); - } - let resultingState = yield this.executeMiddlewares(result, exports.MiddlewarePlacement.After, { - name: action.type, - params - }); - if (resultingState === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return; - } - if (isStateHistory(resultingState) && - this.options.history && - this.options.history.limit) { - resultingState = applyLimits(resultingState, this.options.history.limit); - } - this._state.next(resultingState); - aureliaPal.PLATFORM.performance.mark("dispatch-end"); - if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { - aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); - const measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${action.type}:`, measures); - } - else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { - const marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); - const totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${action.type}:`, marks); - } - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); - }); - } - executeMiddlewares(state, placement, action) { - return Array.from(this.middlewares) - .filter((middleware) => middleware[1].placement === placement) - .reduce((prev, curr, _, _arr) => __awaiter(this, void 0, void 0, function* () { - try { - const result = yield curr[0](yield prev, this._state.getValue(), curr[1].settings, action); - if (result === false) { - _arr = []; - return false; - } - return result || (yield prev); - } - catch (e) { - if (this.options.propagateError) { - _arr = []; - throw e; - } - return yield prev; - } - finally { - aureliaPal.PLATFORM.performance.mark(`dispatch-${placement}-${curr[0].name}`); - } - }), state); - } - setupDevTools() { - if (aureliaPal.PLATFORM.global.devToolsExtension) { - this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); - this.devToolsAvailable = true; - this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); - this.devTools.init(this.initialState); - this.devTools.subscribe((message) => { - this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)](`DevTools sent change ${message.type}`); - if (message.type === "DISPATCH") { - this._state.next(JSON.parse(message.state)); - } - }); - } - } - updateDevToolsState(action, state) { - if (this.devToolsAvailable) { - this.devTools.send(action, state); - } - } - registerHistoryMethods() { - this.registerAction("jump", jump); - } - } - function dispatchify(action) { - const store = aureliaDependencyInjection.Container.instance.get(Store); - return function (...params) { - return store.dispatch(action, ...params); - }; - } + (function (PerformanceMeasurement) { + PerformanceMeasurement["StartEnd"] = "startEnd"; + PerformanceMeasurement["All"] = "all"; + })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); + class UnregisteredActionError extends Error { + constructor(reducer) { + super(`Tried to dispatch an unregistered action ${reducer && (typeof reducer === "string" ? reducer : reducer.name)}`); + } + } + class Store { + constructor(initialState, options) { + this.initialState = initialState; + this.logger = aureliaLogging.getLogger("aurelia-store"); + this.devToolsAvailable = false; + this.actions = new Map(); + this.middlewares = new Map(); + this.dispatchQueue = []; + this.options = options || {}; + const isUndoable = this.options.history && this.options.history.undoable === true; + this._state = new rxjs.BehaviorSubject(initialState); + this.state = this._state.asObservable(); + if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { + this.setupDevTools(); + } + if (isUndoable) { + this.registerHistoryMethods(); + } + } + registerMiddleware(reducer, placement, settings) { + this.middlewares.set(reducer, { placement, settings }); + } + unregisterMiddleware(reducer) { + if (this.middlewares.has(reducer)) { + this.middlewares.delete(reducer); + } + } + isMiddlewareRegistered(middleware) { + return this.middlewares.has(middleware); + } + registerAction(name, reducer) { + if (reducer.length === 0) { + throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); + } + this.actions.set(reducer, { type: name }); + } + unregisterAction(reducer) { + if (this.actions.has(reducer)) { + this.actions.delete(reducer); + } + } + isActionRegistered(reducer) { + if (typeof reducer === "string") { + return Array.from(this.actions).find((action) => action[1].type === reducer) !== undefined; + } + return this.actions.has(reducer); + } + resetToState(state) { + this._state.next(state); + } + dispatch(reducer, ...params) { + const action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params + }]); + } + pipe(reducer, ...params) { + const pipeline = []; + const dispatchPipe = { + dispatch: () => this.queueDispatch(pipeline), + pipe: (nextReducer, ...nextParams) => { + const action = this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe(reducer, ...params); + } + lookupAction(reducer) { + if (typeof reducer === "string") { + const result = Array.from(this.actions).find(([_, action]) => action.type === reducer); + if (result) { + return result[0]; + } + } + else if (this.actions.has(reducer)) { + return reducer; + } + return undefined; + } + queueDispatch(actions) { + return new Promise((resolve, reject) => { + this.dispatchQueue.push({ actions, resolve, reject }); + if (this.dispatchQueue.length === 1) { + this.handleQueue(); + } + }); + } + handleQueue() { + return __awaiter(this, void 0, void 0, function* () { + if (this.dispatchQueue.length > 0) { + const queueItem = this.dispatchQueue[0]; + try { + yield this.internalDispatch(queueItem.actions); + queueItem.resolve(); + } + catch (e) { + queueItem.reject(e); + } + this.dispatchQueue.shift(); + this.handleQueue(); + } + }); + } + internalDispatch(actions) { + return __awaiter(this, void 0, void 0, function* () { + const unregisteredAction = actions.find((a) => !this.actions.has(a.reducer)); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); + } + aureliaPal.PLATFORM.performance.mark("dispatch-start"); + const pipedActions = actions.map((a) => ({ + type: this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + })); + const callingAction = { + name: pipedActions.map((a) => a.type).join("->"), + params: pipedActions.reduce((p, a) => p.concat(a.params), []), + pipedActions: pipedActions.map((a) => ({ + name: a.type, + params: a.params + })) + }; + if (this.options.logDispatchedActions) { + this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)](`Dispatching: ${callingAction.name}`); + } + const beforeMiddleswaresResult = yield this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, callingAction); + if (beforeMiddleswaresResult === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return; + } + let result = beforeMiddleswaresResult; + for (const action of pipedActions) { + result = yield action.reducer(result, ...action.params); + if (result === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return; + } + aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); + if (!result && typeof result !== "object") { + throw new Error("The reducer has to return a new state"); + } + } + let resultingState = yield this.executeMiddlewares(result, exports.MiddlewarePlacement.After, callingAction); + if (resultingState === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return; + } + if (isStateHistory(resultingState) && + this.options.history && + this.options.history.limit) { + resultingState = applyLimits(resultingState, this.options.history.limit); + } + this._state.next(resultingState); + aureliaPal.PLATFORM.performance.mark("dispatch-end"); + if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { + aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); + const measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)](`Total duration ${measures[0].duration} of dispatched action ${callingAction.name}:`, measures); + } + else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { + const marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); + const totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)](`Total duration ${totalDuration} of dispatched action ${callingAction.name}:`, marks); + } + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); + }); + } + executeMiddlewares(state, placement, action) { + return Array.from(this.middlewares) + .filter((middleware) => middleware[1].placement === placement) + .reduce((prev, curr, _, _arr) => __awaiter(this, void 0, void 0, function* () { + try { + const result = yield curr[0](yield prev, this._state.getValue(), curr[1].settings, action); + if (result === false) { + _arr = []; + return false; + } + return result || (yield prev); + } + catch (e) { + if (this.options.propagateError) { + _arr = []; + throw e; + } + return yield prev; + } + finally { + aureliaPal.PLATFORM.performance.mark(`dispatch-${placement}-${curr[0].name}`); + } + }), state); + } + setupDevTools() { + if (aureliaPal.PLATFORM.global.devToolsExtension) { + this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); + this.devToolsAvailable = true; + this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); + this.devTools.init(this.initialState); + this.devTools.subscribe((message) => { + this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)](`DevTools sent change ${message.type}`); + if (message.type === "DISPATCH") { + this._state.next(JSON.parse(message.state)); + } + }); + } + } + updateDevToolsState(action, state) { + if (this.devToolsAvailable) { + this.devTools.send(action, state); + } + } + registerHistoryMethods() { + this.registerAction("jump", jump); + } + } + function dispatchify(action) { + const store = aureliaDependencyInjection.Container.instance.get(Store); + return function (...params) { + return store.dispatch(action, ...params); + }; + } - function executeSteps(store, shouldLogResults, ...steps) { - return __awaiter(this, void 0, void 0, function* () { - const logStep = (step, stepIdx) => (res) => { - if (shouldLogResults) { - console.group(`Step ${stepIdx}`); - console.log(res); - console.groupEnd(); - } - step(res); - }; - // tslint:disable-next-line:no-any - const tryStep = (step, reject) => (res) => { - try { - step(res); - } - catch (err) { - reject(err); - } - }; - const lastStep = (step, resolve) => (res) => { - step(res); - resolve(); - }; - return new Promise((resolve, reject) => { - let currentStep = 0; - steps.slice(0, -1).forEach((step) => { - store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); - currentStep++; - }); - store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); - }); - }); - } + function executeSteps(store, shouldLogResults, ...steps) { + return __awaiter(this, void 0, void 0, function* () { + const logStep = (step, stepIdx) => (res) => { + if (shouldLogResults) { + console.group(`Step ${stepIdx}`); + console.log(res); + console.groupEnd(); + } + step(res); + }; + // tslint:disable-next-line:no-any + const tryStep = (step, reject) => (res) => { + try { + step(res); + } + catch (err) { + reject(err); + } + }; + const lastStep = (step, resolve) => (res) => { + step(res); + resolve(); + }; + return new Promise((resolve, reject) => { + let currentStep = 0; + steps.slice(0, -1).forEach((step) => { + store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); + currentStep++; + }); + store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); + }); + }); + } - const defaultSelector = (store) => store.state; - function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } - let $store; - // const store = Container.instance.get(Store) as Store; - const _settings = Object.assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); - function getSource(selector) { - // if for some reason getSource is invoked before setup (bind lifecycle, typically) - // then we have no choice but to get the store instance from global container instance - // otherwise, assume that $store variable in the closure would be already assigned the right - // value from created callback - // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios - const store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); - const source = selector(store); - if (source instanceof rxjs.Observable) { - return source; - } - return store.state; - } - function createSelectors() { - const isSelectorObj = typeof _settings.selector === "object"; - const fallbackSelector = { - [_settings.target || "state"]: _settings.selector || defaultSelector - }; - return Object.entries(Object.assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(([target, selector]) => ({ - targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], - selector, - // numbers are the starting index to slice all the change handling args, - // which are prop name, new state and old state - changeHandlers: { - [_settings.onChanged || ""]: 1, - [`${_settings.target || target}Changed`]: _settings.target ? 0 : 1, - ["propertyChanged"]: 0 - } - })); - } - return function (target) { - const originalCreated = target.prototype.created; - const originalSetup = typeof settings === "object" && settings.setup - ? target.prototype[settings.setup] - : target.prototype.bind; - const originalTeardown = typeof settings === "object" && settings.teardown - ? target.prototype[settings.teardown] - : target.prototype.unbind; - // only override if prototype callback is a function - if (typeof originalCreated === "function" || originalCreated === undefined) { - target.prototype.created = function created(_, view) { - // here we relies on the fact that the class Store - // has not been registered somewhere in one of child containers, instead of root container - // if there is any issue with this approach, needs to walk all the way up to resolve from root - // typically like invoking from global Container.instance - $store = view.container.get(Store); - if (originalCreated !== undefined) { - return originalCreated.call(this, _, view); - } - }; - } - target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { - if (typeof settings == "object" && - typeof settings.onChanged === "string" && - !(settings.onChanged in this)) { - throw new Error("Provided onChanged handler does not exist on target VM"); - } - this._stateSubscriptions = createSelectors().map(s => getSource(s.selector).subscribe((state) => { - const lastTargetIdx = s.targets.length - 1; - const oldState = s.targets.reduce((accu = {}, curr) => accu[curr], this); - Object.entries(s.changeHandlers).forEach(([handlerName, args]) => { - if (handlerName in this) { - this[handlerName](...[s.targets[lastTargetIdx], state, oldState].slice(args, 3)); - } - }); - s.targets.reduce((accu, curr, idx) => { - accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; - return accu[curr]; - }, this); - })); - if (originalSetup) { - return originalSetup.apply(this, arguments); - } - }; - target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { - if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { - this._stateSubscriptions.forEach((sub) => { - if (sub instanceof rxjs.Subscription && sub.closed === false) { - sub.unsubscribe(); - } - }); - } - if (originalTeardown) { - return originalTeardown.apply(this, arguments); - } - }; - }; - } + const defaultSelector = (store) => store.state; + function connectTo(settings) { + let $store; + // const store = Container.instance.get(Store) as Store; + const _settings = Object.assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); + function getSource(selector) { + // if for some reason getSource is invoked before setup (bind lifecycle, typically) + // then we have no choice but to get the store instance from global container instance + // otherwise, assume that $store variable in the closure would be already assigned the right + // value from created callback + // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios + const store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); + const source = selector(store); + if (source instanceof rxjs.Observable) { + return source; + } + return store.state; + } + function createSelectors() { + const isSelectorObj = typeof _settings.selector === "object"; + const fallbackSelector = { + [_settings.target || "state"]: _settings.selector || defaultSelector + }; + return Object.entries(Object.assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(([target, selector]) => ({ + targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], + selector, + // numbers are the starting index to slice all the change handling args, + // which are prop name, new state and old state + changeHandlers: { + [_settings.onChanged || ""]: 1, + [`${_settings.target || target}Changed`]: _settings.target ? 0 : 1, + ["propertyChanged"]: 0 + } + })); + } + return function (target) { + const originalCreated = target.prototype.created; + const originalSetup = typeof settings === "object" && settings.setup + ? target.prototype[settings.setup] + : target.prototype.bind; + const originalTeardown = typeof settings === "object" && settings.teardown + ? target.prototype[settings.teardown] + : target.prototype.unbind; + // only override if prototype callback is a function + if (typeof originalCreated === "function" || originalCreated === undefined) { + target.prototype.created = function created(_, view) { + // here we relies on the fact that the class Store + // has not been registered somewhere in one of child containers, instead of root container + // if there is any issue with this approach, needs to walk all the way up to resolve from root + // typically like invoking from global Container.instance + $store = view.container.get(Store); + if (originalCreated !== undefined) { + return originalCreated.call(this, _, view); + } + }; + } + target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { + if (typeof settings == "object" && + typeof settings.onChanged === "string" && + !(settings.onChanged in this)) { + throw new Error("Provided onChanged handler does not exist on target VM"); + } + this._stateSubscriptions = createSelectors().map(s => getSource(s.selector).subscribe((state) => { + const lastTargetIdx = s.targets.length - 1; + const oldState = s.targets.reduce((accu = {}, curr) => accu[curr], this); + Object.entries(s.changeHandlers).forEach(([handlerName, args]) => { + if (handlerName in this) { + this[handlerName](...[s.targets[lastTargetIdx], state, oldState].slice(args, 3)); + } + }); + s.targets.reduce((accu, curr, idx) => { + accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; + return accu[curr]; + }, this); + })); + if (originalSetup) { + return originalSetup.apply(this, arguments); + } + }; + target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { + if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { + this._stateSubscriptions.forEach((sub) => { + if (sub instanceof rxjs.Subscription && sub.closed === false) { + sub.unsubscribe(); + } + }); + } + if (originalTeardown) { + return originalTeardown.apply(this, arguments); + } + }; + }; + } - function configure(aurelia, options) { - if (!options || !options.initialState) { - throw new Error("initialState must be provided via options"); - } - let initState = options.initialState; - if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { - initState = { past: [], present: options.initialState, future: [] }; - } - delete options.initialState; - aurelia.container - .registerInstance(Store, new Store(initState, options)); - } + function configure(aurelia, options) { + if (!options || !options.initialState) { + throw new Error("initialState must be provided via options"); + } + let initState = options.initialState; + if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { + initState = { past: [], present: options.initialState, future: [] }; + } + delete options.initialState; + aurelia.container + .registerInstance(Store, new Store(initState, options)); + } - exports.configure = configure; - exports.Store = Store; - exports.dispatchify = dispatchify; - exports.executeSteps = executeSteps; - exports.jump = jump; - exports.nextStateHistory = nextStateHistory; - exports.applyLimits = applyLimits; - exports.isStateHistory = isStateHistory; - exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; - exports.logMiddleware = logMiddleware; - exports.localStorageMiddleware = localStorageMiddleware; - exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; - exports.LoggerIndexed = LoggerIndexed; - exports.getLogType = getLogType; - exports.connectTo = connectTo; + exports.configure = configure; + exports.UnregisteredActionError = UnregisteredActionError; + exports.Store = Store; + exports.dispatchify = dispatchify; + exports.executeSteps = executeSteps; + exports.jump = jump; + exports.nextStateHistory = nextStateHistory; + exports.applyLimits = applyLimits; + exports.isStateHistory = isStateHistory; + exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; + exports.logMiddleware = logMiddleware; + exports.localStorageMiddleware = localStorageMiddleware; + exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; + exports.LoggerIndexed = LoggerIndexed; + exports.getLogType = getLogType; + exports.connectTo = connectTo; - Object.defineProperty(exports, '__esModule', { value: true }); + Object.defineProperty(exports, '__esModule', { value: true }); })); diff --git a/dist/umd/aurelia-store.js b/dist/umd/aurelia-store.js index 8b7251a..8035116 100644 --- a/dist/umd/aurelia-store.js +++ b/dist/umd/aurelia-store.js @@ -1,640 +1,709 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('aurelia-dependency-injection'), require('aurelia-logging'), require('aurelia-pal'), require('rxjs/operators')) : - typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'aurelia-dependency-injection', 'aurelia-logging', 'aurelia-pal', 'rxjs/operators'], factory) : - (global = global || self, factory((global.au = global.au || {}, global.au.store = {}), global.rxjs, global.au, global.au.LogManager, global.au, global.rxjs)); + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('aurelia-dependency-injection'), require('aurelia-logging'), require('aurelia-pal'), require('rxjs/operators')) : + typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'aurelia-dependency-injection', 'aurelia-logging', 'aurelia-pal', 'rxjs/operators'], factory) : + (global = global || self, factory((global.au = global.au || {}, global.au.store = {}), global.rxjs, global.au, global.au.LogManager, global.au, global.rxjs)); }(this, function (exports, rxjs, aureliaDependencyInjection, aureliaLogging, aureliaPal, operators) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 + /* istanbul ignore next */ + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill + if (!Object.entries) { + Object.entries = function (obj) { + var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + return resArray; + }; + } + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - /* global Reflect, Promise */ + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } - var __assign = function() { - __assign = Object.assign || function __assign(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; - }; - return __assign.apply(this, arguments); - }; + var __assign = function() { + __assign = Object.assign || function __assign(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; + }; + return __assign.apply(this, arguments); + }; - function __awaiter(thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } + function __awaiter(thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } - } + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } - function jump(state, n) { - if (!isStateHistory(state)) { - return state; - } - if (n > 0) - return jumpToFuture(state, n - 1); - if (n < 0) - return jumpToPast(state, state.past.length + n); - return state; - } - function jumpToFuture(state, index) { - if (index < 0 || index >= state.future.length) { - return state; - } - var past = state.past, future = state.future, present = state.present; - var newPast = past.concat([present], future.slice(0, index)); - var newPresent = future[index]; - var newFuture = future.slice(index + 1); - return { past: newPast, present: newPresent, future: newFuture }; - } - function jumpToPast(state, index) { - if (index < 0 || index >= state.past.length) { - return state; - } - var past = state.past, future = state.future, present = state.present; - var newPast = past.slice(0, index); - var newFuture = past.slice(index + 1).concat([present], future); - var newPresent = past[index]; - return { past: newPast, present: newPresent, future: newFuture }; - } - function nextStateHistory(presentStateHistory, nextPresent) { - return Object.assign({}, presentStateHistory, { - past: presentStateHistory.past.concat([presentStateHistory.present]), - present: nextPresent, - future: [] - }); - } - function applyLimits(state, limit) { - if (isStateHistory(state)) { - if (state.past.length > limit) { - state.past = state.past.slice(state.past.length - limit); - } - if (state.future.length > limit) { - state.future = state.future.slice(0, limit); - } - } - return state; - } - function isStateHistory(history) { - return typeof history.present !== "undefined" && - typeof history.future !== "undefined" && - typeof history.past !== "undefined" && - Array.isArray(history.future) && - Array.isArray(history.past); - } + function jump(state, n) { + if (!isStateHistory(state)) { + return state; + } + if (n > 0) + return jumpToFuture(state, n - 1); + if (n < 0) + return jumpToPast(state, state.past.length + n); + return state; + } + function jumpToFuture(state, index) { + if (index < 0 || index >= state.future.length) { + return state; + } + var past = state.past, future = state.future, present = state.present; + var newPast = past.concat([present], future.slice(0, index)); + var newPresent = future[index]; + var newFuture = future.slice(index + 1); + return { past: newPast, present: newPresent, future: newFuture }; + } + function jumpToPast(state, index) { + if (index < 0 || index >= state.past.length) { + return state; + } + var past = state.past, future = state.future, present = state.present; + var newPast = past.slice(0, index); + var newFuture = past.slice(index + 1).concat([present], future); + var newPresent = past[index]; + return { past: newPast, present: newPresent, future: newFuture }; + } + function nextStateHistory(presentStateHistory, nextPresent) { + return Object.assign({}, presentStateHistory, { + past: presentStateHistory.past.concat([presentStateHistory.present]), + present: nextPresent, + future: [] + }); + } + function applyLimits(state, limit) { + if (isStateHistory(state)) { + if (state.past.length > limit) { + state.past = state.past.slice(state.past.length - limit); + } + if (state.future.length > limit) { + state.future = state.future.slice(0, limit); + } + } + return state; + } + function isStateHistory(history) { + return typeof history.present !== "undefined" && + typeof history.future !== "undefined" && + typeof history.past !== "undefined" && + Array.isArray(history.future) && + Array.isArray(history.past); + } - var DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; - (function (MiddlewarePlacement) { - MiddlewarePlacement["Before"] = "before"; - MiddlewarePlacement["After"] = "after"; - })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); - function logMiddleware(state, _, settings) { - var logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; - console[logType]("New state: ", state); - } - function localStorageMiddleware(state, _, settings) { - if (aureliaPal.PLATFORM.global.localStorage) { - var key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; - aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); - } - } - function rehydrateFromLocalStorage(state, key) { - if (!aureliaPal.PLATFORM.global.localStorage) { - return state; - } - var storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); - if (!storedState) { - return state; - } - try { - return JSON.parse(storedState); - } - catch (e) { } - return state; - } + var DEFAULT_LOCAL_STORAGE_KEY = "aurelia-store-state"; + (function (MiddlewarePlacement) { + MiddlewarePlacement["Before"] = "before"; + MiddlewarePlacement["After"] = "after"; + })(exports.MiddlewarePlacement || (exports.MiddlewarePlacement = {})); + function logMiddleware(state, _, settings) { + var logType = settings && settings.logType && console.hasOwnProperty(settings.logType) ? settings.logType : "log"; + console[logType]("New state: ", state); + } + function localStorageMiddleware(state, _, settings) { + if (aureliaPal.PLATFORM.global.localStorage) { + var key = settings && settings.key || DEFAULT_LOCAL_STORAGE_KEY; + aureliaPal.PLATFORM.global.localStorage.setItem(key, JSON.stringify(state)); + } + } + function rehydrateFromLocalStorage(state, key) { + if (!aureliaPal.PLATFORM.global.localStorage) { + return state; + } + var storedState = aureliaPal.PLATFORM.global.localStorage.getItem(key || DEFAULT_LOCAL_STORAGE_KEY); + if (!storedState) { + return state; + } + try { + return JSON.parse(storedState); + } + catch (e) { } + return state; + } - (function (LogLevel) { - LogLevel["trace"] = "trace"; - LogLevel["debug"] = "debug"; - LogLevel["info"] = "info"; - LogLevel["log"] = "log"; - LogLevel["warn"] = "warn"; - LogLevel["error"] = "error"; - })(exports.LogLevel || (exports.LogLevel = {})); - var LoggerIndexed = /** @class */ (function (_super) { - __extends(LoggerIndexed, _super); - function LoggerIndexed() { - return _super !== null && _super.apply(this, arguments) || this; - } - return LoggerIndexed; - }(aureliaLogging.Logger)); - function getLogType(options, definition, defaultLevel) { - if (definition && - options.logDefinitions && - options.logDefinitions.hasOwnProperty(definition) && - options.logDefinitions[definition] && - Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { - return options.logDefinitions[definition]; - } - return defaultLevel; - } + (function (LogLevel) { + LogLevel["trace"] = "trace"; + LogLevel["debug"] = "debug"; + LogLevel["info"] = "info"; + LogLevel["log"] = "log"; + LogLevel["warn"] = "warn"; + LogLevel["error"] = "error"; + })(exports.LogLevel || (exports.LogLevel = {})); + var LoggerIndexed = /** @class */ (function (_super) { + __extends(LoggerIndexed, _super); + function LoggerIndexed() { + return _super !== null && _super.apply(this, arguments) || this; + } + return LoggerIndexed; + }(aureliaLogging.Logger)); + function getLogType(options, definition, defaultLevel) { + if (definition && + options.logDefinitions && + options.logDefinitions.hasOwnProperty(definition) && + options.logDefinitions[definition] && + Object.values(exports.LogLevel).includes(options.logDefinitions[definition])) { + return options.logDefinitions[definition]; + } + return defaultLevel; + } - (function (PerformanceMeasurement) { - PerformanceMeasurement["StartEnd"] = "startEnd"; - PerformanceMeasurement["All"] = "all"; - })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); - var Store = /** @class */ (function () { - function Store(initialState, options) { - this.initialState = initialState; - this.logger = aureliaLogging.getLogger("aurelia-store"); - this.devToolsAvailable = false; - this.actions = new Map(); - this.middlewares = new Map(); - this.dispatchQueue = []; - this.options = options || {}; - var isUndoable = this.options.history && this.options.history.undoable === true; - this._state = new rxjs.BehaviorSubject(initialState); - this.state = this._state.asObservable(); - if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { - this.setupDevTools(); - } - if (isUndoable) { - this.registerHistoryMethods(); - } - } - Store.prototype.registerMiddleware = function (reducer, placement, settings) { - this.middlewares.set(reducer, { placement: placement, settings: settings }); - }; - Store.prototype.unregisterMiddleware = function (reducer) { - if (this.middlewares.has(reducer)) { - this.middlewares.delete(reducer); - } - }; - Store.prototype.isMiddlewareRegistered = function (middleware) { - return this.middlewares.has(middleware); - }; - Store.prototype.registerAction = function (name, reducer) { - if (reducer.length === 0) { - throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); - } - this.actions.set(reducer, { type: name }); - }; - Store.prototype.unregisterAction = function (reducer) { - if (this.actions.has(reducer)) { - this.actions.delete(reducer); - } - }; - Store.prototype.isActionRegistered = function (reducer) { - if (typeof reducer === "string") { - return Array.from(this.actions).find(function (action) { return action[1].type === reducer; }) !== undefined; - } - return this.actions.has(reducer); - }; - Store.prototype.resetToState = function (state) { - this._state.next(state); - }; - Store.prototype.dispatch = function (reducer) { - var _this = this; - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - var action; - if (typeof reducer === "string") { - var result = Array.from(this.actions) - .find(function (val) { return val[1].type === reducer; }); - if (result) { - action = result[0]; - } - } - else { - action = reducer; - } - return new Promise(function (resolve, reject) { - _this.dispatchQueue.push({ reducer: action, params: params, resolve: resolve, reject: reject }); - if (_this.dispatchQueue.length === 1) { - _this.handleQueue(); - } - }); - }; - Store.prototype.handleQueue = function () { - return __awaiter(this, void 0, void 0, function () { - var queueItem, e_1; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!(this.dispatchQueue.length > 0)) return [3 /*break*/, 5]; - queueItem = this.dispatchQueue[0]; - _a.label = 1; - case 1: - _a.trys.push([1, 3, , 4]); - return [4 /*yield*/, this.internalDispatch.apply(this, [queueItem.reducer].concat(queueItem.params))]; - case 2: - _a.sent(); - queueItem.resolve(); - return [3 /*break*/, 4]; - case 3: - e_1 = _a.sent(); - queueItem.reject(e_1); - return [3 /*break*/, 4]; - case 4: - this.dispatchQueue.shift(); - this.handleQueue(); - _a.label = 5; - case 5: return [2 /*return*/]; - } - }); - }); - }; - Store.prototype.internalDispatch = function (reducer) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - return __awaiter(this, void 0, void 0, function () { - var action, beforeMiddleswaresResult, result, resultingState, measures, marks, totalDuration; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!this.actions.has(reducer)) { - throw new Error("Tried to dispatch an unregistered action" + (reducer ? " " + reducer.name : "")); - } - aureliaPal.PLATFORM.performance.mark("dispatch-start"); - action = __assign({}, this.actions.get(reducer), { params: params }); - if (this.options.logDispatchedActions) { - this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + action.type); - } - return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, { - name: action.type, - params: params - })]; - case 1: - beforeMiddleswaresResult = _a.sent(); - if (beforeMiddleswaresResult === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - return [4 /*yield*/, reducer.apply(void 0, [beforeMiddleswaresResult].concat(params))]; - case 2: - result = _a.sent(); - if (result === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); - if (!result && typeof result !== "object") { - throw new Error("The reducer has to return a new state"); - } - return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, { - name: action.type, - params: params - })]; - case 3: - resultingState = _a.sent(); - if (resultingState === false) { - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - return [2 /*return*/]; - } - if (isStateHistory(resultingState) && - this.options.history && - this.options.history.limit) { - resultingState = applyLimits(resultingState, this.options.history.limit); - } - this._state.next(resultingState); - aureliaPal.PLATFORM.performance.mark("dispatch-end"); - if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { - aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); - measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + action.type + ":", measures); - } - else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { - marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); - totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; - this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + action.type + ":", marks); - } - aureliaPal.PLATFORM.performance.clearMarks(); - aureliaPal.PLATFORM.performance.clearMeasures(); - this.updateDevToolsState(action, resultingState); - return [2 /*return*/]; - } - }); - }); - }; - Store.prototype.executeMiddlewares = function (state, placement, action) { - var _this = this; - return Array.from(this.middlewares) - .filter(function (middleware) { return middleware[1].placement === placement; }) - .reduce(function (prev, curr, _, _arr) { return __awaiter(_this, void 0, void 0, function () { - var result, _a, _b, _c, e_2; - return __generator(this, function (_d) { - switch (_d.label) { - case 0: - _d.trys.push([0, 5, 7, 8]); - _b = (_a = curr)[0]; - return [4 /*yield*/, prev]; - case 1: return [4 /*yield*/, _b.apply(_a, [_d.sent(), this._state.getValue(), curr[1].settings, action])]; - case 2: - result = _d.sent(); - if (result === false) { - _arr = []; - return [2 /*return*/, false]; - } - _c = result; - if (_c) return [3 /*break*/, 4]; - return [4 /*yield*/, prev]; - case 3: - _c = (_d.sent()); - _d.label = 4; - case 4: return [2 /*return*/, _c]; - case 5: - e_2 = _d.sent(); - if (this.options.propagateError) { - _arr = []; - throw e_2; - } - return [4 /*yield*/, prev]; - case 6: return [2 /*return*/, _d.sent()]; - case 7: - aureliaPal.PLATFORM.performance.mark("dispatch-" + placement + "-" + curr[0].name); - return [7 /*endfinally*/]; - case 8: return [2 /*return*/]; - } - }); - }); }, state); - }; - Store.prototype.setupDevTools = function () { - var _this = this; - if (aureliaPal.PLATFORM.global.devToolsExtension) { - this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); - this.devToolsAvailable = true; - this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); - this.devTools.init(this.initialState); - this.devTools.subscribe(function (message) { - _this.logger[getLogType(_this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools sent change " + message.type); - if (message.type === "DISPATCH") { - _this._state.next(JSON.parse(message.state)); - } - }); - } - }; - Store.prototype.updateDevToolsState = function (action, state) { - if (this.devToolsAvailable) { - this.devTools.send(action, state); - } - }; - Store.prototype.registerHistoryMethods = function () { - this.registerAction("jump", jump); - }; - return Store; - }()); - function dispatchify(action) { - var store = aureliaDependencyInjection.Container.instance.get(Store); - return function () { - var params = []; - for (var _i = 0; _i < arguments.length; _i++) { - params[_i] = arguments[_i]; - } - return store.dispatch.apply(store, [action].concat(params)); - }; - } + (function (PerformanceMeasurement) { + PerformanceMeasurement["StartEnd"] = "startEnd"; + PerformanceMeasurement["All"] = "all"; + })(exports.PerformanceMeasurement || (exports.PerformanceMeasurement = {})); + var UnregisteredActionError = /** @class */ (function (_super) { + __extends(UnregisteredActionError, _super); + function UnregisteredActionError(reducer) { + return _super.call(this, "Tried to dispatch an unregistered action " + (reducer && (typeof reducer === "string" ? reducer : reducer.name))) || this; + } + return UnregisteredActionError; + }(Error)); + var Store = /** @class */ (function () { + function Store(initialState, options) { + this.initialState = initialState; + this.logger = aureliaLogging.getLogger("aurelia-store"); + this.devToolsAvailable = false; + this.actions = new Map(); + this.middlewares = new Map(); + this.dispatchQueue = []; + this.options = options || {}; + var isUndoable = this.options.history && this.options.history.undoable === true; + this._state = new rxjs.BehaviorSubject(initialState); + this.state = this._state.asObservable(); + if (!this.options.devToolsOptions || this.options.devToolsOptions.disable !== true) { + this.setupDevTools(); + } + if (isUndoable) { + this.registerHistoryMethods(); + } + } + Store.prototype.registerMiddleware = function (reducer, placement, settings) { + this.middlewares.set(reducer, { placement: placement, settings: settings }); + }; + Store.prototype.unregisterMiddleware = function (reducer) { + if (this.middlewares.has(reducer)) { + this.middlewares.delete(reducer); + } + }; + Store.prototype.isMiddlewareRegistered = function (middleware) { + return this.middlewares.has(middleware); + }; + Store.prototype.registerAction = function (name, reducer) { + if (reducer.length === 0) { + throw new Error("The reducer is expected to have one or more parameters, where the first will be the present state"); + } + this.actions.set(reducer, { type: name }); + }; + Store.prototype.unregisterAction = function (reducer) { + if (this.actions.has(reducer)) { + this.actions.delete(reducer); + } + }; + Store.prototype.isActionRegistered = function (reducer) { + if (typeof reducer === "string") { + return Array.from(this.actions).find(function (action) { return action[1].type === reducer; }) !== undefined; + } + return this.actions.has(reducer); + }; + Store.prototype.resetToState = function (state) { + this._state.next(state); + }; + Store.prototype.dispatch = function (reducer) { + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var action = this.lookupAction(reducer); + if (!action) { + return Promise.reject(new UnregisteredActionError(reducer)); + } + return this.queueDispatch([{ + reducer: action, + params: params + }]); + }; + Store.prototype.pipe = function (reducer) { + var _this = this; + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + var pipeline = []; + var dispatchPipe = { + dispatch: function () { return _this.queueDispatch(pipeline); }, + pipe: function (nextReducer) { + var nextParams = []; + for (var _i = 1; _i < arguments.length; _i++) { + nextParams[_i - 1] = arguments[_i]; + } + var action = _this.lookupAction(nextReducer); + if (!action) { + throw new UnregisteredActionError(reducer); + } + pipeline.push({ reducer: action, params: nextParams }); + return dispatchPipe; + } + }; + return dispatchPipe.pipe.apply(dispatchPipe, [reducer].concat(params)); + }; + Store.prototype.lookupAction = function (reducer) { + if (typeof reducer === "string") { + var result = Array.from(this.actions).find(function (_a) { + var _ = _a[0], action = _a[1]; + return action.type === reducer; + }); + if (result) { + return result[0]; + } + } + else if (this.actions.has(reducer)) { + return reducer; + } + return undefined; + }; + Store.prototype.queueDispatch = function (actions) { + var _this = this; + return new Promise(function (resolve, reject) { + _this.dispatchQueue.push({ actions: actions, resolve: resolve, reject: reject }); + if (_this.dispatchQueue.length === 1) { + _this.handleQueue(); + } + }); + }; + Store.prototype.handleQueue = function () { + return __awaiter(this, void 0, void 0, function () { + var queueItem, e_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(this.dispatchQueue.length > 0)) return [3 /*break*/, 5]; + queueItem = this.dispatchQueue[0]; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.internalDispatch(queueItem.actions)]; + case 2: + _a.sent(); + queueItem.resolve(); + return [3 /*break*/, 4]; + case 3: + e_1 = _a.sent(); + queueItem.reject(e_1); + return [3 /*break*/, 4]; + case 4: + this.dispatchQueue.shift(); + this.handleQueue(); + _a.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + }; + Store.prototype.internalDispatch = function (actions) { + return __awaiter(this, void 0, void 0, function () { + var unregisteredAction, pipedActions, callingAction, beforeMiddleswaresResult, result, _i, pipedActions_1, action, resultingState, measures, marks, totalDuration; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + unregisteredAction = actions.find(function (a) { return !_this.actions.has(a.reducer); }); + if (unregisteredAction) { + throw new UnregisteredActionError(unregisteredAction.reducer); + } + aureliaPal.PLATFORM.performance.mark("dispatch-start"); + pipedActions = actions.map(function (a) { return ({ + type: _this.actions.get(a.reducer).type, + params: a.params, + reducer: a.reducer + }); }); + callingAction = { + name: pipedActions.map(function (a) { return a.type; }).join("->"), + params: pipedActions.reduce(function (p, a) { return p.concat(a.params); }, []), + pipedActions: pipedActions.map(function (a) { return ({ + name: a.type, + params: a.params + }); }) + }; + if (this.options.logDispatchedActions) { + this.logger[getLogType(this.options, "dispatchedActions", exports.LogLevel.info)]("Dispatching: " + callingAction.name); + } + return [4 /*yield*/, this.executeMiddlewares(this._state.getValue(), exports.MiddlewarePlacement.Before, callingAction)]; + case 1: + beforeMiddleswaresResult = _a.sent(); + if (beforeMiddleswaresResult === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + result = beforeMiddleswaresResult; + _i = 0, pipedActions_1 = pipedActions; + _a.label = 2; + case 2: + if (!(_i < pipedActions_1.length)) return [3 /*break*/, 5]; + action = pipedActions_1[_i]; + return [4 /*yield*/, action.reducer.apply(action, [result].concat(action.params))]; + case 3: + result = _a.sent(); + if (result === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + aureliaPal.PLATFORM.performance.mark("dispatch-after-reducer-" + action.type); + if (!result && typeof result !== "object") { + throw new Error("The reducer has to return a new state"); + } + _a.label = 4; + case 4: + _i++; + return [3 /*break*/, 2]; + case 5: return [4 /*yield*/, this.executeMiddlewares(result, exports.MiddlewarePlacement.After, callingAction)]; + case 6: + resultingState = _a.sent(); + if (resultingState === false) { + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + return [2 /*return*/]; + } + if (isStateHistory(resultingState) && + this.options.history && + this.options.history.limit) { + resultingState = applyLimits(resultingState, this.options.history.limit); + } + this._state.next(resultingState); + aureliaPal.PLATFORM.performance.mark("dispatch-end"); + if (this.options.measurePerformance === exports.PerformanceMeasurement.StartEnd) { + aureliaPal.PLATFORM.performance.measure("startEndDispatchDuration", "dispatch-start", "dispatch-end"); + measures = aureliaPal.PLATFORM.performance.getEntriesByName("startEndDispatchDuration"); + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + measures[0].duration + " of dispatched action " + callingAction.name + ":", measures); + } + else if (this.options.measurePerformance === exports.PerformanceMeasurement.All) { + marks = aureliaPal.PLATFORM.performance.getEntriesByType("mark"); + totalDuration = marks[marks.length - 1].startTime - marks[0].startTime; + this.logger[getLogType(this.options, "performanceLog", exports.LogLevel.info)]("Total duration " + totalDuration + " of dispatched action " + callingAction.name + ":", marks); + } + aureliaPal.PLATFORM.performance.clearMarks(); + aureliaPal.PLATFORM.performance.clearMeasures(); + this.updateDevToolsState({ type: callingAction.name, params: callingAction.params }, resultingState); + return [2 /*return*/]; + } + }); + }); + }; + Store.prototype.executeMiddlewares = function (state, placement, action) { + var _this = this; + return Array.from(this.middlewares) + .filter(function (middleware) { return middleware[1].placement === placement; }) + .reduce(function (prev, curr, _, _arr) { return __awaiter(_this, void 0, void 0, function () { + var result, _a, _b, _c, e_2; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + _d.trys.push([0, 5, 7, 8]); + _b = (_a = curr)[0]; + return [4 /*yield*/, prev]; + case 1: return [4 /*yield*/, _b.apply(_a, [_d.sent(), this._state.getValue(), curr[1].settings, action])]; + case 2: + result = _d.sent(); + if (result === false) { + _arr = []; + return [2 /*return*/, false]; + } + _c = result; + if (_c) return [3 /*break*/, 4]; + return [4 /*yield*/, prev]; + case 3: + _c = (_d.sent()); + _d.label = 4; + case 4: return [2 /*return*/, _c]; + case 5: + e_2 = _d.sent(); + if (this.options.propagateError) { + _arr = []; + throw e_2; + } + return [4 /*yield*/, prev]; + case 6: return [2 /*return*/, _d.sent()]; + case 7: + aureliaPal.PLATFORM.performance.mark("dispatch-" + placement + "-" + curr[0].name); + return [7 /*endfinally*/]; + case 8: return [2 /*return*/]; + } + }); + }); }, state); + }; + Store.prototype.setupDevTools = function () { + var _this = this; + if (aureliaPal.PLATFORM.global.devToolsExtension) { + this.logger[getLogType(this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools are available"); + this.devToolsAvailable = true; + this.devTools = aureliaPal.PLATFORM.global.__REDUX_DEVTOOLS_EXTENSION__.connect(this.options.devToolsOptions); + this.devTools.init(this.initialState); + this.devTools.subscribe(function (message) { + _this.logger[getLogType(_this.options, "devToolsStatus", exports.LogLevel.debug)]("DevTools sent change " + message.type); + if (message.type === "DISPATCH") { + _this._state.next(JSON.parse(message.state)); + } + }); + } + }; + Store.prototype.updateDevToolsState = function (action, state) { + if (this.devToolsAvailable) { + this.devTools.send(action, state); + } + }; + Store.prototype.registerHistoryMethods = function () { + this.registerAction("jump", jump); + }; + return Store; + }()); + function dispatchify(action) { + var store = aureliaDependencyInjection.Container.instance.get(Store); + return function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return store.dispatch.apply(store, [action].concat(params)); + }; + } - function executeSteps(store, shouldLogResults) { - var steps = []; - for (var _i = 2; _i < arguments.length; _i++) { - steps[_i - 2] = arguments[_i]; - } - return __awaiter(this, void 0, void 0, function () { - var logStep, tryStep, lastStep; - return __generator(this, function (_a) { - logStep = function (step, stepIdx) { return function (res) { - if (shouldLogResults) { - console.group("Step " + stepIdx); - console.log(res); - console.groupEnd(); - } - step(res); - }; }; - tryStep = function (step, reject) { - return function (res) { - try { - step(res); - } - catch (err) { - reject(err); - } - }; - }; - lastStep = function (step, resolve) { - return function (res) { - step(res); - resolve(); - }; - }; - return [2 /*return*/, new Promise(function (resolve, reject) { - var currentStep = 0; - steps.slice(0, -1).forEach(function (step) { - store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); - currentStep++; - }); - store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); - })]; - }); - }); - } + function executeSteps(store, shouldLogResults) { + var steps = []; + for (var _i = 2; _i < arguments.length; _i++) { + steps[_i - 2] = arguments[_i]; + } + return __awaiter(this, void 0, void 0, function () { + var logStep, tryStep, lastStep; + return __generator(this, function (_a) { + logStep = function (step, stepIdx) { return function (res) { + if (shouldLogResults) { + console.group("Step " + stepIdx); + console.log(res); + console.groupEnd(); + } + step(res); + }; }; + tryStep = function (step, reject) { + return function (res) { + try { + step(res); + } + catch (err) { + reject(err); + } + }; + }; + lastStep = function (step, resolve) { + return function (res) { + step(res); + resolve(); + }; + }; + return [2 /*return*/, new Promise(function (resolve, reject) { + var currentStep = 0; + steps.slice(0, -1).forEach(function (step) { + store.state.pipe(operators.skip(currentStep), operators.take(1), operators.delay(0)).subscribe(tryStep(logStep(step, currentStep), reject)); + currentStep++; + }); + store.state.pipe(operators.skip(currentStep), operators.take(1)).subscribe(lastStep(tryStep(logStep(steps[steps.length - 1], currentStep), reject), resolve)); + })]; + }); + }); + } - var defaultSelector = function (store) { return store.state; }; - function connectTo(settings) { - if (!Object.entries) { - throw new Error("You need a polyfill for Object.entries for browsers like Internet Explorer. Example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill"); - } - var $store; - // const store = Container.instance.get(Store) as Store; - var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); - function getSource(selector) { - // if for some reason getSource is invoked before setup (bind lifecycle, typically) - // then we have no choice but to get the store instance from global container instance - // otherwise, assume that $store variable in the closure would be already assigned the right - // value from created callback - // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios - var store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); - var source = selector(store); - if (source instanceof rxjs.Observable) { - return source; - } - return store.state; - } - function createSelectors() { - var _a; - var isSelectorObj = typeof _settings.selector === "object"; - var fallbackSelector = (_a = {}, - _a[_settings.target || "state"] = _settings.selector || defaultSelector, - _a); - return Object.entries(__assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(function (_a) { - var target = _a[0], selector = _a[1]; - var _b; - return ({ - targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], - selector: selector, - // numbers are the starting index to slice all the change handling args, - // which are prop name, new state and old state - changeHandlers: (_b = {}, - _b[_settings.onChanged || ""] = 1, - _b[(_settings.target || target) + "Changed"] = _settings.target ? 0 : 1, - _b["propertyChanged"] = 0, - _b) - }); - }); - } - return function (target) { - var originalCreated = target.prototype.created; - var originalSetup = typeof settings === "object" && settings.setup - ? target.prototype[settings.setup] - : target.prototype.bind; - var originalTeardown = typeof settings === "object" && settings.teardown - ? target.prototype[settings.teardown] - : target.prototype.unbind; - // only override if prototype callback is a function - if (typeof originalCreated === "function" || originalCreated === undefined) { - target.prototype.created = function created(_, view) { - // here we relies on the fact that the class Store - // has not been registered somewhere in one of child containers, instead of root container - // if there is any issue with this approach, needs to walk all the way up to resolve from root - // typically like invoking from global Container.instance - $store = view.container.get(Store); - if (originalCreated !== undefined) { - return originalCreated.call(this, _, view); - } - }; - } - target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { - var _this = this; - if (typeof settings == "object" && - typeof settings.onChanged === "string" && - !(settings.onChanged in this)) { - throw new Error("Provided onChanged handler does not exist on target VM"); - } - this._stateSubscriptions = createSelectors().map(function (s) { return getSource(s.selector).subscribe(function (state) { - var lastTargetIdx = s.targets.length - 1; - var oldState = s.targets.reduce(function (accu, curr) { - if (accu === void 0) { accu = {}; } - return accu[curr]; - }, _this); - Object.entries(s.changeHandlers).forEach(function (_a) { - var handlerName = _a[0], args = _a[1]; - if (handlerName in _this) { - _this[handlerName].apply(_this, [s.targets[lastTargetIdx], state, oldState].slice(args, 3)); - } - }); - s.targets.reduce(function (accu, curr, idx) { - accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; - return accu[curr]; - }, _this); - }); }); - if (originalSetup) { - return originalSetup.apply(this, arguments); - } - }; - target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { - if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { - this._stateSubscriptions.forEach(function (sub) { - if (sub instanceof rxjs.Subscription && sub.closed === false) { - sub.unsubscribe(); - } - }); - } - if (originalTeardown) { - return originalTeardown.apply(this, arguments); - } - }; - }; - } + var defaultSelector = function (store) { return store.state; }; + function connectTo(settings) { + var $store; + // const store = Container.instance.get(Store) as Store; + var _settings = __assign({ selector: typeof settings === "function" ? settings : defaultSelector }, settings); + function getSource(selector) { + // if for some reason getSource is invoked before setup (bind lifecycle, typically) + // then we have no choice but to get the store instance from global container instance + // otherwise, assume that $store variable in the closure would be already assigned the right + // value from created callback + // Could also be in situation where it doesn't come from custom element, or some exotic setups/scenarios + var store = $store || ($store = aureliaDependencyInjection.Container.instance.get(Store)); + var source = selector(store); + if (source instanceof rxjs.Observable) { + return source; + } + return store.state; + } + function createSelectors() { + var _a; + var isSelectorObj = typeof _settings.selector === "object"; + var fallbackSelector = (_a = {}, + _a[_settings.target || "state"] = _settings.selector || defaultSelector, + _a); + return Object.entries(__assign({}, (isSelectorObj ? _settings.selector : fallbackSelector))).map(function (_a) { + var target = _a[0], selector = _a[1]; + var _b; + return ({ + targets: _settings.target && isSelectorObj ? [_settings.target, target] : [target], + selector: selector, + // numbers are the starting index to slice all the change handling args, + // which are prop name, new state and old state + changeHandlers: (_b = {}, + _b[_settings.onChanged || ""] = 1, + _b[(_settings.target || target) + "Changed"] = _settings.target ? 0 : 1, + _b["propertyChanged"] = 0, + _b) + }); + }); + } + return function (target) { + var originalCreated = target.prototype.created; + var originalSetup = typeof settings === "object" && settings.setup + ? target.prototype[settings.setup] + : target.prototype.bind; + var originalTeardown = typeof settings === "object" && settings.teardown + ? target.prototype[settings.teardown] + : target.prototype.unbind; + // only override if prototype callback is a function + if (typeof originalCreated === "function" || originalCreated === undefined) { + target.prototype.created = function created(_, view) { + // here we relies on the fact that the class Store + // has not been registered somewhere in one of child containers, instead of root container + // if there is any issue with this approach, needs to walk all the way up to resolve from root + // typically like invoking from global Container.instance + $store = view.container.get(Store); + if (originalCreated !== undefined) { + return originalCreated.call(this, _, view); + } + }; + } + target.prototype[typeof settings === "object" && settings.setup ? settings.setup : "bind"] = function () { + var _this = this; + if (typeof settings == "object" && + typeof settings.onChanged === "string" && + !(settings.onChanged in this)) { + throw new Error("Provided onChanged handler does not exist on target VM"); + } + this._stateSubscriptions = createSelectors().map(function (s) { return getSource(s.selector).subscribe(function (state) { + var lastTargetIdx = s.targets.length - 1; + var oldState = s.targets.reduce(function (accu, curr) { + if (accu === void 0) { accu = {}; } + return accu[curr]; + }, _this); + Object.entries(s.changeHandlers).forEach(function (_a) { + var handlerName = _a[0], args = _a[1]; + if (handlerName in _this) { + _this[handlerName].apply(_this, [s.targets[lastTargetIdx], state, oldState].slice(args, 3)); + } + }); + s.targets.reduce(function (accu, curr, idx) { + accu[curr] = idx === lastTargetIdx ? state : accu[curr] || {}; + return accu[curr]; + }, _this); + }); }); + if (originalSetup) { + return originalSetup.apply(this, arguments); + } + }; + target.prototype[typeof settings === "object" && settings.teardown ? settings.teardown : "unbind"] = function () { + if (this._stateSubscriptions && Array.isArray(this._stateSubscriptions)) { + this._stateSubscriptions.forEach(function (sub) { + if (sub instanceof rxjs.Subscription && sub.closed === false) { + sub.unsubscribe(); + } + }); + } + if (originalTeardown) { + return originalTeardown.apply(this, arguments); + } + }; + }; + } - function configure(aurelia, options) { - if (!options || !options.initialState) { - throw new Error("initialState must be provided via options"); - } - var initState = options.initialState; - if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { - initState = { past: [], present: options.initialState, future: [] }; - } - delete options.initialState; - aurelia.container - .registerInstance(Store, new Store(initState, options)); - } + function configure(aurelia, options) { + if (!options || !options.initialState) { + throw new Error("initialState must be provided via options"); + } + var initState = options.initialState; + if (options && options.history && options.history.undoable && !isStateHistory(options.initialState)) { + initState = { past: [], present: options.initialState, future: [] }; + } + delete options.initialState; + aurelia.container + .registerInstance(Store, new Store(initState, options)); + } - exports.configure = configure; - exports.Store = Store; - exports.dispatchify = dispatchify; - exports.executeSteps = executeSteps; - exports.jump = jump; - exports.nextStateHistory = nextStateHistory; - exports.applyLimits = applyLimits; - exports.isStateHistory = isStateHistory; - exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; - exports.logMiddleware = logMiddleware; - exports.localStorageMiddleware = localStorageMiddleware; - exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; - exports.LoggerIndexed = LoggerIndexed; - exports.getLogType = getLogType; - exports.connectTo = connectTo; + exports.configure = configure; + exports.UnregisteredActionError = UnregisteredActionError; + exports.Store = Store; + exports.dispatchify = dispatchify; + exports.executeSteps = executeSteps; + exports.jump = jump; + exports.nextStateHistory = nextStateHistory; + exports.applyLimits = applyLimits; + exports.isStateHistory = isStateHistory; + exports.DEFAULT_LOCAL_STORAGE_KEY = DEFAULT_LOCAL_STORAGE_KEY; + exports.logMiddleware = logMiddleware; + exports.localStorageMiddleware = localStorageMiddleware; + exports.rehydrateFromLocalStorage = rehydrateFromLocalStorage; + exports.LoggerIndexed = LoggerIndexed; + exports.getLogType = getLogType; + exports.connectTo = connectTo; - Object.defineProperty(exports, '__esModule', { value: true }); + Object.defineProperty(exports, '__esModule', { value: true }); })); diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index da6ed35..cf3a0bf 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,3 +1,14 @@ + +# [1.4.0](https://github.com/aurelia/store/compare/1.3.5...1.4.0) (2019-06-12) + + +### Features + +* **dispatch:** piped dispatch ([4a78ad5](https://github.com/aurelia/store/commit/4a78ad5)) +* **polyfills:** ship with object.entries polyfill ([8ee1156](https://github.com/aurelia/store/commit/8ee1156)) + + + ## [1.3.5](https://github.com/aurelia/store/compare/1.3.4...1.3.5) (2019-05-29) diff --git a/package-lock.json b/package-lock.json index dd01322..32a5034 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6190,7 +6190,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6211,12 +6212,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6231,17 +6234,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6358,7 +6364,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6370,6 +6377,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6384,6 +6392,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6391,12 +6400,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6415,6 +6426,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6495,7 +6507,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6507,6 +6520,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6592,7 +6606,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6628,6 +6643,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6647,6 +6663,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6690,12 +6707,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index 6aec230..aaeb3a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aurelia-store", - "version": "1.3.5", + "version": "1.4.0", "description": "Aurelia single state store based on RxJS", "keywords": [ "aurelia",