diff --git a/.gitignore b/.gitignore index 37ec9471..d68a5405 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ coverage node_modules npm-debug.log test/browser/tests.js -lib utils/* flux.js flux-build.js diff --git a/lib/actions/index.js b/lib/actions/index.js new file mode 100644 index 00000000..489d9a38 --- /dev/null +++ b/lib/actions/index.js @@ -0,0 +1,82 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = makeAction; + +var _isPromise = _interopRequireDefault(require("is-promise")); + +var fn = _interopRequireWildcard(require("../functions")); + +var utils = _interopRequireWildcard(require("../utils/AltUtils")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function makeAction(alt, namespace, name, implementation, obj) { + var id = utils.uid(alt._actionsRegistry, "".concat(namespace, ".").concat(name)); + alt._actionsRegistry[id] = 1; + var data = { + id: id, + namespace: namespace, + name: name + }; + + var dispatch = function dispatch(payload) { + return alt.dispatch(id, payload, data); + }; // the action itself + + + var action = function action() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var invocationResult = implementation.apply(obj, args); + var actionResult = invocationResult; // async functions that return promises should not be dispatched + + if (invocationResult !== undefined && !(0, _isPromise["default"])(invocationResult)) { + if (fn.isFunction(invocationResult)) { + // inner function result should be returned as an action result + actionResult = invocationResult(dispatch, alt); + } else { + dispatch(invocationResult); + } + } + + if (invocationResult === undefined) { + utils.warn('An action was called but nothing was dispatched'); + } + + return actionResult; + }; + + action.defer = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return setTimeout(function () { + return action.apply(null, args); + }); + }; + + action.id = id; + action.data = data; // ensure each reference is unique in the namespace + + var container = alt.actions[namespace]; + var namespaceId = utils.uid(container, name); + container[namespaceId] = action; // generate a constant + + var constant = utils.formatAsConstant(namespaceId); + container[constant] = id; + return action; +} + +module.exports = exports.default; \ No newline at end of file diff --git a/lib/functions.js b/lib/functions.js new file mode 100644 index 00000000..fb6cecb0 --- /dev/null +++ b/lib/functions.js @@ -0,0 +1,39 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.assign = assign; +exports.eachObject = eachObject; +exports.isFunction = void 0; +exports.isMutableObject = isMutableObject; + +var isFunction = function isFunction(x) { + return typeof x === 'function'; +}; + +exports.isFunction = isFunction; + +function isMutableObject(target) { + var Ctor = target.constructor; + return !!target && Object.prototype.toString.call(target) === '[object Object]' && isFunction(Ctor) && !Object.isFrozen(target) && (Ctor instanceof Ctor || target.type === 'AltStore'); +} + +function eachObject(f, o) { + o.forEach(function (from) { + Object.keys(Object(from)).forEach(function (key) { + f(key, from[key]); + }); + }); +} + +function assign(target) { + for (var _len = arguments.length, source = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + source[_key - 1] = arguments[_key]; + } + + eachObject(function (key, value) { + return target[key] = value; + }, source); + return target; +} \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 00000000..259d892f --- /dev/null +++ b/lib/index.js @@ -0,0 +1,335 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = void 0; + +var _construct2 = _interopRequireDefault(require("@babel/runtime/helpers/construct")); + +var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); + +var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); + +var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); + +var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); + +var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); + +var _flux = require("flux"); + +var StateFunctions = _interopRequireWildcard(require("./utils/StateFunctions")); + +var fn = _interopRequireWildcard(require("./functions")); + +var store = _interopRequireWildcard(require("./store")); + +var utils = _interopRequireWildcard(require("./utils/AltUtils")); + +var _actions = _interopRequireDefault(require("./actions")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +var Alt = /*#__PURE__*/function () { + function Alt() { + var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + (0, _classCallCheck2["default"])(this, Alt); + this.config = config; + this.serialize = config.serialize || JSON.stringify; + this.deserialize = config.deserialize || JSON.parse; + this.dispatcher = config.dispatcher || new _flux.Dispatcher(); + + this.batchingFunction = config.batchingFunction || function (callback) { + return callback(); + }; + + this.actions = { + global: {} + }; + this.stores = {}; + this.storeTransforms = config.storeTransforms || []; + this.trapAsync = false; + this._actionsRegistry = {}; + this._initSnapshot = {}; + this._lastSnapshot = {}; + } + + (0, _createClass2["default"])(Alt, [{ + key: "dispatch", + value: function dispatch(action, data, details) { + var _this = this; + + this.batchingFunction(function () { + var id = Math.random().toString(18).substr(2, 16); // support straight dispatching of FSA-style actions + + if (action.hasOwnProperty('type') && action.hasOwnProperty('payload')) { + var fsaDetails = { + id: action.type, + namespace: action.type, + name: action.type + }; + return _this.dispatcher.dispatch(utils.fsa(id, action.type, action.payload, fsaDetails)); + } + + if (action.id && action.dispatch) { + return utils.dispatch(id, action, data, _this); + } + + return _this.dispatcher.dispatch(utils.fsa(id, action, data, details)); + }); + } + }, { + key: "createUnsavedStore", + value: function createUnsavedStore(StoreModel) { + var key = StoreModel.displayName || ''; + store.createStoreConfig(this.config, StoreModel); + var Store = store.transformStore(this.storeTransforms, StoreModel); + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return fn.isFunction(Store) ? store.createStoreFromClass.apply(store, [this, Store, key].concat(args)) : store.createStoreFromObject(this, Store, key); + } + }, { + key: "createStore", + value: function createStore(StoreModel, iden) { + var key = iden || StoreModel.displayName || StoreModel.name || ''; + store.createStoreConfig(this.config, StoreModel); + var Store = store.transformStore(this.storeTransforms, StoreModel); + /* istanbul ignore next */ + + if (module.hot) delete this.stores[key]; + + if (this.stores[key] || !key) { + if (this.stores[key]) { + utils.warn("A store named ".concat(key, " already exists, double check your store ") + "names or pass in your own custom identifier for each store"); + } else { + utils.warn('Store name was not specified'); + } + + key = utils.uid(this.stores, key); + } + + for (var _len2 = arguments.length, args = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { + args[_key2 - 2] = arguments[_key2]; + } + + var storeInstance = fn.isFunction(Store) ? store.createStoreFromClass.apply(store, [this, Store, key].concat(args)) : store.createStoreFromObject(this, Store, key); + this.stores[key] = storeInstance; + StateFunctions.saveInitialSnapshot(this, key); + return storeInstance; + } + }, { + key: "generateActions", + value: function generateActions() { + var actions = { + name: 'global' + }; + + for (var _len3 = arguments.length, actionNames = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + actionNames[_key3] = arguments[_key3]; + } + + return this.createActions(actionNames.reduce(function (obj, action) { + obj[action] = utils.dispatchIdentity; + return obj; + }, actions)); + } + }, { + key: "createAction", + value: function createAction(name, implementation, obj) { + return (0, _actions["default"])(this, 'global', name, implementation, obj); + } + }, { + key: "createActions", + value: function createActions(ActionsClass) { + var _this2 = this; + + var exportObj = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var actions = {}; + var key = utils.uid(this._actionsRegistry, ActionsClass.displayName || ActionsClass.name || 'Unknown'); + + if (fn.isFunction(ActionsClass)) { + fn.assign(actions, utils.getPrototypeChain(ActionsClass)); + + var ActionsGenerator = /*#__PURE__*/function (_ActionsClass) { + (0, _inherits2["default"])(ActionsGenerator, _ActionsClass); + + var _super = _createSuper(ActionsGenerator); + + function ActionsGenerator() { + (0, _classCallCheck2["default"])(this, ActionsGenerator); + + for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + args[_key5] = arguments[_key5]; + } + + return _super.call.apply(_super, [this].concat(args)); + } + + (0, _createClass2["default"])(ActionsGenerator, [{ + key: "generateActions", + value: function generateActions() { + for (var _len6 = arguments.length, actionNames = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { + actionNames[_key6] = arguments[_key6]; + } + + actionNames.forEach(function (actionName) { + actions[actionName] = utils.dispatchIdentity; + }); + } + }]); + return ActionsGenerator; + }(ActionsClass); + + for (var _len4 = arguments.length, argsForConstructor = new Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) { + argsForConstructor[_key4 - 2] = arguments[_key4]; + } + + fn.assign(actions, (0, _construct2["default"])(ActionsGenerator, argsForConstructor)); + } else { + fn.assign(actions, ActionsClass); + } + + this.actions[key] = this.actions[key] || {}; + fn.eachObject(function (actionName, action) { + if (!fn.isFunction(action)) { + exportObj[actionName] = action; + return; + } // create the action + + + exportObj[actionName] = (0, _actions["default"])(_this2, key, actionName, action, exportObj); // generate a constant + + var constant = utils.formatAsConstant(actionName); + exportObj[constant] = exportObj[actionName].id; + }, [actions]); + return exportObj; + } + }, { + key: "takeSnapshot", + value: function takeSnapshot() { + for (var _len7 = arguments.length, storeNames = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { + storeNames[_key7] = arguments[_key7]; + } + + var state = StateFunctions.snapshot(this, storeNames); + fn.assign(this._lastSnapshot, state); + return this.serialize(state); + } + }, { + key: "rollback", + value: function rollback() { + StateFunctions.setAppState(this, this.serialize(this._lastSnapshot), function (storeInst) { + storeInst.lifecycle('rollback'); + storeInst.emitChange(); + }); + } + }, { + key: "recycle", + value: function recycle() { + for (var _len8 = arguments.length, storeNames = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { + storeNames[_key8] = arguments[_key8]; + } + + var initialSnapshot = storeNames.length ? StateFunctions.filterSnapshots(this, this._initSnapshot, storeNames) : this._initSnapshot; + StateFunctions.setAppState(this, this.serialize(initialSnapshot), function (storeInst) { + storeInst.lifecycle('init'); + storeInst.emitChange(); + }); + } + }, { + key: "flush", + value: function flush() { + var state = this.serialize(StateFunctions.snapshot(this)); + this.recycle(); + return state; + } + }, { + key: "bootstrap", + value: function bootstrap(data) { + StateFunctions.setAppState(this, data, function (storeInst, state) { + storeInst.lifecycle('bootstrap', state); + storeInst.emitChange(); + }); + } + }, { + key: "prepare", + value: function prepare(storeInst, payload) { + var data = {}; + + if (!storeInst.displayName) { + throw new ReferenceError('Store provided does not have a name'); + } + + data[storeInst.displayName] = payload; + return this.serialize(data); + } // Instance type methods for injecting alt into your application as context + + }, { + key: "addActions", + value: function addActions(name, ActionsClass) { + for (var _len9 = arguments.length, args = new Array(_len9 > 2 ? _len9 - 2 : 0), _key9 = 2; _key9 < _len9; _key9++) { + args[_key9 - 2] = arguments[_key9]; + } + + this.actions[name] = Array.isArray(ActionsClass) ? this.generateActions.apply(this, ActionsClass) : this.createActions.apply(this, [ActionsClass].concat(args)); + } + }, { + key: "addStore", + value: function addStore(name, StoreModel) { + for (var _len10 = arguments.length, args = new Array(_len10 > 2 ? _len10 - 2 : 0), _key10 = 2; _key10 < _len10; _key10++) { + args[_key10 - 2] = arguments[_key10]; + } + + this.createStore.apply(this, [StoreModel, name].concat(args)); + } + }, { + key: "getActions", + value: function getActions(name) { + return this.actions[name]; + } + }, { + key: "getStore", + value: function getStore(name) { + return this.stores[name]; + } + }], [{ + key: "debug", + value: function debug(name, alt, win) { + var key = 'alt.js.org'; + var context = win; + + if (!context && typeof window !== 'undefined') { + context = window; + } + + if (typeof context !== 'undefined') { + context[key] = context[key] || []; + context[key].push({ + name: name, + alt: alt + }); + } + + return alt; + } + }]); + return Alt; +}(); + +var _default = Alt; +exports["default"] = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/lib/store/AltStore.js b/lib/store/AltStore.js new file mode 100644 index 00000000..777fd302 --- /dev/null +++ b/lib/store/AltStore.js @@ -0,0 +1,165 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = void 0; + +var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); + +var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); + +var _transmitter = _interopRequireDefault(require("transmitter")); + +var fn = _interopRequireWildcard(require("../functions")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +var AltStore = /*#__PURE__*/function () { + function AltStore(alt, model, state, StoreModel) { + var _this = this; + + (0, _classCallCheck2["default"])(this, AltStore); + var lifecycleEvents = model.lifecycleEvents; + this.transmitter = (0, _transmitter["default"])(); + + this.lifecycle = function (event, x) { + if (lifecycleEvents[event]) lifecycleEvents[event].publish(x); + }; + + this.state = state; + this.alt = alt; + this.preventDefault = false; + this.displayName = model.displayName; + this.boundListeners = model.boundListeners; + this.StoreModel = StoreModel; + + this.reduce = model.reduce || function (x) { + return x; + }; + + this.subscriptions = []; + + var output = model.output || function (x) { + return x; + }; + + this.emitChange = function () { + return _this.transmitter.publish(output(_this.state)); + }; + + var handleDispatch = function handleDispatch(f, payload) { + try { + return f(); + } catch (e) { + if (model.handlesOwnErrors) { + _this.lifecycle('error', { + error: e, + payload: payload, + state: _this.state + }); + + return false; + } + + throw e; + } + }; + + fn.assign(this, model.publicMethods); // Register dispatcher + + this.dispatchToken = alt.dispatcher.register(function (payload) { + _this.preventDefault = false; + + _this.lifecycle('beforeEach', { + payload: payload, + state: _this.state + }); + + var actionHandlers = model.actionListeners[payload.action]; + + if (actionHandlers || model.otherwise) { + var result; + + if (actionHandlers) { + result = handleDispatch(function () { + return actionHandlers.filter(Boolean).every(function (handler) { + return handler.call(model, payload.data, payload.action) !== false; + }); + }, payload); + } else { + result = handleDispatch(function () { + return model.otherwise(payload.data, payload.action); + }, payload); + } + + if (result !== false && !_this.preventDefault) _this.emitChange(); + } + + if (model.reduce) { + handleDispatch(function () { + var value = model.reduce(_this.state, payload); + if (value !== undefined) _this.state = value; + }, payload); + if (!_this.preventDefault) _this.emitChange(); + } + + _this.lifecycle('afterEach', { + payload: payload, + state: _this.state + }); + }); + this.lifecycle('init'); + } + + (0, _createClass2["default"])(AltStore, [{ + key: "listen", + value: function listen(cb) { + var _this2 = this; + + if (!fn.isFunction(cb)) throw new TypeError('listen expects a function'); + + var _this$transmitter$sub = this.transmitter.subscribe(cb), + dispose = _this$transmitter$sub.dispose; + + this.subscriptions.push({ + cb: cb, + dispose: dispose + }); + return function () { + _this2.lifecycle('unlisten'); + + dispose(); + }; + } + }, { + key: "unlisten", + value: function unlisten(cb) { + this.lifecycle('unlisten'); + this.subscriptions = this.subscriptions.filter(function (subscription) { + if (subscription.cb === cb) { + subscription.dispose(); + return false; + } + + return true; + }); + } + }, { + key: "getState", + value: function getState() { + return this.StoreModel.config.getState.call(this, this.state); + } + }]); + return AltStore; +}(); + +var _default = AltStore; +exports["default"] = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/lib/store/StoreMixin.js b/lib/store/StoreMixin.js new file mode 100644 index 00000000..87194c39 --- /dev/null +++ b/lib/store/StoreMixin.js @@ -0,0 +1,193 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = void 0; + +var _transmitter = _interopRequireDefault(require("transmitter")); + +var fn = _interopRequireWildcard(require("../functions")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +var StoreMixin = { + waitFor: function waitFor() { + for (var _len = arguments.length, sources = new Array(_len), _key = 0; _key < _len; _key++) { + sources[_key] = arguments[_key]; + } + + if (!sources.length) { + throw new ReferenceError('Dispatch tokens not provided'); + } + + var sourcesArray = sources; + + if (sources.length === 1) { + sourcesArray = Array.isArray(sources[0]) ? sources[0] : sources; + } + + var tokens = sourcesArray.map(function (source) { + return source.dispatchToken || source; + }); + this.dispatcher.waitFor(tokens); + }, + exportAsync: function exportAsync(asyncMethods) { + this.registerAsync(asyncMethods); + }, + registerAsync: function registerAsync(asyncDef) { + var _this = this; + + var loadCounter = 0; + var asyncMethods = fn.isFunction(asyncDef) ? asyncDef(this.alt) : asyncDef; + var toExport = Object.keys(asyncMethods).reduce(function (publicMethods, methodName) { + var desc = asyncMethods[methodName]; + var spec = fn.isFunction(desc) ? desc(_this) : desc; + var validHandlers = ['success', 'error', 'loading']; + validHandlers.forEach(function (handler) { + if (spec[handler] && !spec[handler].id) { + throw new Error("".concat(handler, " handler must be an action function")); + } + }); + + publicMethods[methodName] = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + var state = _this.getInstance().getState(); + + var value = spec.local && spec.local.apply(spec, [state].concat(args)); + var shouldFetch = spec.shouldFetch ? spec.shouldFetch.apply(spec, [state].concat(args)) + /* eslint-disable */ + : value == null; + /* eslint-enable */ + + var intercept = spec.interceptResponse || function (x) { + return x; + }; + + var makeActionHandler = function makeActionHandler(action, isError) { + return function (x) { + var fire = function fire() { + loadCounter -= 1; + action(intercept(x, action, args)); + if (isError) throw x; + return x; + }; + + return _this.alt.trapAsync ? function () { + return fire(); + } : fire(); + }; + }; // if we don't have it in cache then fetch it + + + if (shouldFetch) { + loadCounter += 1; + /* istanbul ignore else */ + + if (spec.loading) spec.loading(intercept(null, spec.loading, args)); + return spec.remote.apply(spec, [state].concat(args)).then(makeActionHandler(spec.success), makeActionHandler(spec.error, 1)); + } // otherwise emit the change now + + + _this.emitChange(); + + return value; + }; + + return publicMethods; + }, {}); + this.exportPublicMethods(toExport); + this.exportPublicMethods({ + isLoading: function isLoading() { + return loadCounter > 0; + } + }); + }, + exportPublicMethods: function exportPublicMethods(methods) { + var _this2 = this; + + fn.eachObject(function (methodName, value) { + if (!fn.isFunction(value)) { + throw new TypeError('exportPublicMethods expects a function'); + } + + _this2.publicMethods[methodName] = value; + }, [methods]); + }, + emitChange: function emitChange() { + this.getInstance().emitChange(); + }, + on: function on(lifecycleEvent, handler) { + if (lifecycleEvent === 'error') this.handlesOwnErrors = true; + var bus = this.lifecycleEvents[lifecycleEvent] || (0, _transmitter["default"])(); + this.lifecycleEvents[lifecycleEvent] = bus; + return bus.subscribe(handler.bind(this)); + }, + bindAction: function bindAction(symbol, handler) { + if (!symbol) { + throw new ReferenceError('Invalid action reference passed in'); + } + + if (!fn.isFunction(handler)) { + throw new TypeError('bindAction expects a function'); + } // You can pass in the constant or the function itself + + + var key = symbol.id ? symbol.id : symbol; + this.actionListeners[key] = this.actionListeners[key] || []; + this.actionListeners[key].push(handler.bind(this)); + this.boundListeners.push(key); + }, + bindActions: function bindActions(actions) { + var _this3 = this; + + fn.eachObject(function (action, symbol) { + var matchFirstCharacter = /./; + var assumedEventHandler = action.replace(matchFirstCharacter, function (x) { + return "on".concat(x[0].toUpperCase()); + }); + + if (_this3[action] && _this3[assumedEventHandler]) { + // If you have both action and onAction + throw new ReferenceError("You have multiple action handlers bound to an action: " + "".concat(action, " and ").concat(assumedEventHandler)); + } + + var handler = _this3[action] || _this3[assumedEventHandler]; + + if (handler) { + _this3.bindAction(symbol, handler); + } + }, [actions]); + }, + bindListeners: function bindListeners(obj) { + var _this4 = this; + + fn.eachObject(function (methodName, symbol) { + var listener = _this4[methodName]; + + if (!listener) { + throw new ReferenceError("".concat(methodName, " defined but does not exist in ").concat(_this4.displayName)); + } + + if (Array.isArray(symbol)) { + symbol.forEach(function (action) { + _this4.bindAction(action, listener); + }); + } else { + _this4.bindAction(symbol, listener); + } + }, [obj]); + } +}; +var _default = StoreMixin; +exports["default"] = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/lib/store/index.js b/lib/store/index.js new file mode 100644 index 00000000..747c698e --- /dev/null +++ b/lib/store/index.js @@ -0,0 +1,189 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createStoreConfig = createStoreConfig; +exports.createStoreFromClass = createStoreFromClass; +exports.createStoreFromObject = createStoreFromObject; +exports.transformStore = transformStore; + +var _construct2 = _interopRequireDefault(require("@babel/runtime/helpers/construct")); + +var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); + +var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); + +var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); + +var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); + +var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); + +var utils = _interopRequireWildcard(require("../utils/AltUtils")); + +var fn = _interopRequireWildcard(require("../functions")); + +var _AltStore = _interopRequireDefault(require("./AltStore")); + +var _StoreMixin = _interopRequireDefault(require("./StoreMixin")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function doSetState(store, storeInstance, state) { + if (!state) { + return; + } + + var config = storeInstance.StoreModel.config; + var nextState = fn.isFunction(state) ? state(storeInstance.state) : state; + storeInstance.state = config.setState.call(store, storeInstance.state, nextState); + + if (!store.alt.dispatcher.isDispatching()) { + store.emitChange(); + } +} + +function createPrototype(proto, alt, key, extras) { + return fn.assign(proto, _StoreMixin["default"], { + displayName: key, + alt: alt, + dispatcher: alt.dispatcher, + preventDefault: function preventDefault() { + this.getInstance().preventDefault = true; + }, + boundListeners: [], + lifecycleEvents: {}, + actionListeners: {}, + publicMethods: {}, + handlesOwnErrors: false + }, extras); +} + +function createStoreConfig(globalConfig, StoreModel) { + StoreModel.config = fn.assign({ + getState: function getState(state) { + if (Array.isArray(state)) { + return state.slice(); + } else if (fn.isMutableObject(state)) { + return fn.assign({}, state); + } + + return state; + }, + setState: function setState(currentState, nextState) { + if (fn.isMutableObject(nextState)) { + return fn.assign(currentState, nextState); + } + + return nextState; + } + }, globalConfig, StoreModel.config); +} + +function transformStore(transforms, StoreModel) { + return transforms.reduce(function (Store, transform) { + return transform(Store); + }, StoreModel); +} + +function createStoreFromObject(alt, StoreModel, key) { + var storeInstance; + var StoreProto = createPrototype({}, alt, key, fn.assign({ + getInstance: function getInstance() { + return storeInstance; + }, + setState: function setState(nextState) { + doSetState(this, storeInstance, nextState); + } + }, StoreModel)); // bind the store listeners + + /* istanbul ignore else */ + + if (StoreProto.bindListeners) { + _StoreMixin["default"].bindListeners.call(StoreProto, StoreProto.bindListeners); + } + /* istanbul ignore else */ + + + if (StoreProto.observe) { + _StoreMixin["default"].bindListeners.call(StoreProto, StoreProto.observe(alt)); + } // bind the lifecycle events + + /* istanbul ignore else */ + + + if (StoreProto.lifecycle) { + fn.eachObject(function (eventName, event) { + _StoreMixin["default"].on.call(StoreProto, eventName, event); + }, [StoreProto.lifecycle]); + } // create the instance and fn.assign the public methods to the instance + + + storeInstance = fn.assign(new _AltStore["default"](alt, StoreProto, StoreProto.state !== undefined ? StoreProto.state : {}, StoreModel), StoreProto.publicMethods, { + displayName: key, + config: StoreModel.config + }); + return storeInstance; +} + +function createStoreFromClass(alt, StoreModel, key) { + var storeInstance; + var config = StoreModel.config; // Creating a class here so we don't overload the provided store's + // prototype with the mixin behaviour and I'm extending from StoreModel + // so we can inherit any extensions from the provided store. + + var Store = /*#__PURE__*/function (_StoreModel) { + (0, _inherits2["default"])(Store, _StoreModel); + + var _super = _createSuper(Store); + + function Store() { + (0, _classCallCheck2["default"])(this, Store); + + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return _super.call.apply(_super, [this].concat(args)); + } + + return (0, _createClass2["default"])(Store); + }(StoreModel); + + createPrototype(Store.prototype, alt, key, { + type: 'AltStore', + getInstance: function getInstance() { + return storeInstance; + }, + setState: function setState(nextState) { + doSetState(this, storeInstance, nextState); + } + }); + + for (var _len = arguments.length, argsForClass = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + argsForClass[_key - 3] = arguments[_key]; + } + + var store = (0, _construct2["default"])(Store, argsForClass); + /* istanbul ignore next */ + + if (config.bindListeners) store.bindListeners(config.bindListeners); + /* istanbul ignore next */ + + if (config.datasource) store.registerAsync(config.datasource); + storeInstance = fn.assign(new _AltStore["default"](alt, store, store.state !== undefined ? store.state : store, StoreModel), utils.getInternalMethods(StoreModel), config.publicMethods, { + displayName: key + }); + return storeInstance; +} \ No newline at end of file diff --git a/lib/utils/AltUtils.js b/lib/utils/AltUtils.js new file mode 100644 index 00000000..3d6cc65c --- /dev/null +++ b/lib/utils/AltUtils.js @@ -0,0 +1,129 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.dispatch = dispatch; +exports.dispatchIdentity = dispatchIdentity; +exports.formatAsConstant = formatAsConstant; +exports.fsa = fsa; +exports.getInternalMethods = getInternalMethods; +exports.getPrototypeChain = getPrototypeChain; +exports.uid = uid; +exports.warn = warn; + +var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); + +var fn = _interopRequireWildcard(require("../functions")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + +/* eslint-disable */ +var builtIns = Object.getOwnPropertyNames(NoopClass); +var builtInProto = Object.getOwnPropertyNames(NoopClass.prototype); +/* eslint-enable */ + +function getInternalMethods(Obj, isProto) { + var excluded = isProto ? builtInProto : builtIns; + var obj = isProto ? Obj.prototype : Obj; + return Object.getOwnPropertyNames(obj).reduce(function (value, m) { + if (excluded.indexOf(m) !== -1) { + return value; + } + + value[m] = obj[m]; + return value; + }, {}); +} + +function getPrototypeChain(Obj) { + var methods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + return Obj === Function.prototype ? methods : getPrototypeChain(Object.getPrototypeOf(Obj), fn.assign(getInternalMethods(Obj, true), methods)); +} + +function warn(msg) { + /* istanbul ignore else */ + + /* eslint-disable */ + if (typeof console !== 'undefined') { + console.warn(new ReferenceError(msg)); + } + /* eslint-enable */ + +} + +function uid(container, name) { + var count = 0; + var key = name; + + while (Object.hasOwnProperty.call(container, key)) { + key = name + String(++count); + } + + return key; +} + +function formatAsConstant(name) { + return name.replace(/[a-z]([A-Z])/g, function (i) { + return "".concat(i[0], "_").concat(i[1].toLowerCase()); + }).toUpperCase(); +} + +function dispatchIdentity(x) { + if (x === undefined) return null; + + for (var _len = arguments.length, a = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + a[_key - 1] = arguments[_key]; + } + + return a.length ? [x].concat(a) : x; +} + +function fsa(id, type, payload, details) { + return { + type: type, + payload: payload, + meta: _objectSpread({ + dispatchId: id + }, details), + id: id, + action: type, + data: payload, + details: details + }; +} + +function dispatch(id, actionObj, payload, alt) { + var data = actionObj.dispatch(payload); + if (data === undefined) return null; + var type = actionObj.id; + var namespace = type; + var name = type; + var details = { + id: type, + namespace: namespace, + name: name + }; + + var dispatchLater = function dispatchLater(x) { + return alt.dispatch(type, x, details); + }; + + if (fn.isFunction(data)) return data(dispatchLater, alt); // XXX standardize this + + return alt.dispatcher.dispatch(fsa(id, type, data, details)); +} +/* istanbul ignore next */ + + +function NoopClass() {} \ No newline at end of file diff --git a/lib/utils/StateFunctions.js b/lib/utils/StateFunctions.js new file mode 100644 index 00000000..fcab1d94 --- /dev/null +++ b/lib/utils/StateFunctions.js @@ -0,0 +1,74 @@ +"use strict"; + +var _typeof = require("@babel/runtime/helpers/typeof"); + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.filterSnapshots = filterSnapshots; +exports.saveInitialSnapshot = saveInitialSnapshot; +exports.setAppState = setAppState; +exports.snapshot = snapshot; + +var fn = _interopRequireWildcard(require("../functions")); + +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function setAppState(instance, data, onStore) { + var obj = instance.deserialize(data); + fn.eachObject(function (key, value) { + var store = instance.stores[key]; + + if (store) { + var config = store.StoreModel.config; + var state = store.state; + if (config.onDeserialize) obj[key] = config.onDeserialize(value) || value; + + if (fn.isMutableObject(state)) { + fn.eachObject(function (k) { + return delete state[k]; + }, [state]); + fn.assign(state, obj[key]); + } else { + store.state = obj[key]; + } + + onStore(store, store.state); + } + }, [obj]); +} + +function snapshot(instance) { + var storeNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var stores = storeNames.length ? storeNames : Object.keys(instance.stores); + return stores.reduce(function (obj, storeHandle) { + var storeName = storeHandle.displayName || storeHandle; + var store = instance.stores[storeName]; + var config = store.StoreModel.config; + store.lifecycle('snapshot'); + var customSnapshot = config.onSerialize && config.onSerialize(store.state); + obj[storeName] = customSnapshot ? customSnapshot : store.getState(); + return obj; + }, {}); +} + +function saveInitialSnapshot(instance, key) { + var state = instance.deserialize(instance.serialize(instance.stores[key].state)); + instance._initSnapshot[key] = state; + instance._lastSnapshot[key] = state; +} + +function filterSnapshots(instance, state, stores) { + return stores.reduce(function (obj, store) { + var storeName = store.displayName || store; + + if (!state[storeName]) { + throw new ReferenceError("".concat(storeName, " is not a valid store")); + } + + obj[storeName] = state[storeName]; + return obj; + }, {}); +} \ No newline at end of file diff --git a/package.json b/package.json index 120a9d53..4e7d345c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.16.5", "@babel/eslint-parser": "^7.16.5", "@babel/node": "^7.16.5", + "@babel/plugin-external-helpers": "^7.16.5", "@babel/plugin-transform-classes": "^7.16.5", "@babel/preset-env": "^7.16.5", "@babel/preset-react": "^7.16.5", @@ -73,9 +74,9 @@ "release": "yarn run build && mversion patch -m", "size": "yarn run transpile; browserify flux.js > flux-build.js; uglifyjs -m -c 'comparisons=false,keep_fargs=true,unsafe=true,unsafe_comps=true,warnings=false' flux-build.js > flux-build.min.js", "test": "yarn run test-node", - "test-node": "babel-node node_modules/.bin/_mocha -u exports -R nyan test --verbose", + "test-node": "babel-node node_modules/.bin/_mocha -u exports -R nyan test", "transpile": "babel src --out-dir lib", - "transpile-cover": "babel src --out-dir lib --plugins external-helpers", + "transpile-cover": "babel src --out-dir lib --plugins @babel/plugin-external-helpers", "version": "yarn run build" }, "files": [ diff --git a/yarn.lock b/yarn.lock index 114a6e0c..6b97afb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -326,6 +326,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.0" +"@babel/plugin-external-helpers@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.16.5.tgz#923532490f4a4276a6952cb62544fd6fde3a5c2d" + integrity sha512-/YY58sy+Y+Oj5bbVyVRF+bqQnSKlY54XuavbdMW6SH/Duet45wHnRnO6hQFN+fqF/Irq7TcXGVl3m9gMhYt7qA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.5" + "@babel/plugin-proposal-async-generator-functions@^7.16.5": version "7.16.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.5.tgz#fd3bd7e0d98404a3d4cbca15a72d533f8c9a2f67"