diff --git a/README.md b/README.md index f397acfcc5e6880f55e6af42a6e8ff4c927907f4..c97eabda228c57d8766737c7c35bbafb00403b60 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ![](public/jsonql-logo.png) +> What is JSON:QL? JSON Query Load :) + **IMPORTANT NOTE: This repo is currently under heavy development and many of the existing module will get transfer to our new `@jsonql` npm namespace Please check out website http://jsonql.org for update to date information** diff --git a/lerna.json b/lerna.json deleted file mode 100755 index 9b535f1d73be27de779d890948e9f4ecfc2957b7..0000000000000000000000000000000000000000 --- a/lerna.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "lerna": "2.11.0", - "packages": [ - "packages/*" - ], - "version": "0.0.0" -} diff --git a/package.json b/package.json deleted file mode 100755 index bbf8292d7475aa496b01b9242b8938c5f8849af6..0000000000000000000000000000000000000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "devDependencies": { - "lerna": "^3.15.0" - }, - "dependencies": {} -} diff --git a/packages/event/README.md b/packages/event/README.md index 72daea5fb127a144cee2afa6e066ec2ae4442c5c..4cd910287d2387e88bf0364c49eaede374840c91 100644 --- a/packages/event/README.md +++ b/packages/event/README.md @@ -1,6 +1,6 @@ # @jsonql/event -This is ported from [nb-event-service]() and rewritten with Typescript +This is ported from [nb-event-service](https://npmjs.com/package/nb-event-service) and rewritten with Typescript The different is - no alias options @@ -8,25 +8,25 @@ The different is - no alias options You don't usually use this directly, this is part of the `@jsonql` modules. -But it's design to work standalone: +But it can work standalone: ```sh $ npm i @jsonql/event ``` -For directly use in browser, you can use the `dist/jsonql-event-service.umd.js` - -And to use it directly in browser you have to do this +When using it directly in browser you have to do this: ```js var eventService = new JsonqlEventService.JsonqlEventService() // the rest of the code ``` -I know it sucks big time, but that's the way how Typescript export when using name ... +I know it sucks big time, but that's the way how Typescript export when using name option with UMD ... might change in the future when I figure out a better way, such as using a `jsonql` namespace -to encapsulate all the browser modules. But for now you have to kinda suck it up, if you want -to use it directly. I would recommend that you don't and use [nb-event-service](https://npmjs.org/package/nb-event-service) instead. +to encapsulate all the browser modules. But for now you have to kinda suck it up. + +I would recommend that you don't use this module (Typescript is not as magical as most keep saying it is); +you should use [nb-event-service](https://npmjs.org/package/nb-event-service) instead. For the other that use node build tool, just do this: @@ -36,6 +36,7 @@ import { JsonqlEventService } from '@jsonql/event' ## API +Same as `nb-event-service` only without the alias. #### $on(eventName, callback, context) diff --git a/packages/event/dist/jsonql-event-service.cjs.js b/packages/event/dist/jsonql-event-service.cjs.js index 65d55bfd8aec44cca774b98d1cefba81d43c384d..2a710aa189ee75309afcce9f234bb340e39e8a2e 100644 --- a/packages/event/dist/jsonql-event-service.cjs.js +++ b/packages/event/dist/jsonql-event-service.cjs.js @@ -2,6 +2,35 @@ Object.defineProperty(exports, '__esModule', { value: true }); +/*! ***************************************************************************** +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. + +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); +}; + +function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + /** * generate a 32bit hash based on the function.toString() * _from http://stackoverflow.com/questions/7616461/generate-a-hash-_from-string-in-javascript-jquery @@ -13,21 +42,330 @@ function hashCode(fn) { return s.split("").reduce(function (a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0) + ""; } -// The main class +// break up the store related operation here +var JsonqlStoreService = /** @class */ (function () { + function JsonqlStoreService(logger) { + // hack + this.logger = typeof logger === 'function' ? logger : function () { }; + // should this be WeakMap or should they be Map? + this.normalStore = new Map(); + this.lazyStore = new Map(); + // don't keep + this.keep = false; + // place holder, should this be a WeakSet? + this.result = []; + } + /** + * return all the listener from the event + * @param {string} evtName event name + * @param {boolean} [full=false] if true then return the entire content + * @return {array|boolean} listerner(s) or false when not found + */ + JsonqlStoreService.prototype.$get = function (evt, full) { + if (full === void 0) { full = false; } + this.validateEvt(evt); + var store = this.normalStore; + this.logger('$get', full, this.normalStore); + if (store.has(evt)) { + return this + .mapToArr(store.get(evt)) + .map(function (l) { + if (full) { + return l; + } + var key = l[0], callback = l[1]; + return callback; + }); + } + return false; + }; + Object.defineProperty(JsonqlStoreService.prototype, "$done", { + /** + * @TODO is there any real use with the keep prop? + * getter for $done + * @return {*} whatever last store result + */ + get: function () { + if (this.keep) { + this.logger(this.result); + return this.result[this.result.length - 1]; + } + return this.result; + }, + //////////////////////////////// + // SETTER / GETTER // + //////////////////////////////// + /** + * store the return result from the run + * @param {*} value whatever return from callback + */ + set: function (value) { + this.logger('set $done', value); + if (this.keep) { + this.result.push(value); + } + else { + this.result = value; + } + }, + enumerable: true, + configurable: true + }); + ///////////////////////////////// + // protected / PROTECTED METHODS // + ///////////////////////////////// + /** + * @param {any} mapObj the Map object + * @return {array} transform to array to work with + */ + JsonqlStoreService.prototype.mapToArr = function (mapObj) { + return Array.from(mapObj); + }; + /** + * make sure we store the argument correctly + * @param {*} arg could be array + * @return {array} make sured + */ + JsonqlStoreService.prototype.toArray = function (arg) { + return Array.isArray(arg) ? arg : [arg]; + }; + /** + * Run the callback + * @param {function} callback function to execute + * @param {array} payload for callback + * @param {object} ctx context or null + * @return {void} the result store in $done + */ + JsonqlStoreService.prototype.run = function (callback, payload, ctx) { + this.logger('run', callback, payload, ctx); + this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); + }; + /** + * Take the content out and remove it from store id by the name + * @param {string} evt event name + * @return {object|boolean} content or false on not found + */ + JsonqlStoreService.prototype.takeFromStore = function (evt) { + var store = this.lazyStore; + if (store.has(evt)) { + var content = store.get(evt); + this.logger('takeFromStore', content); + store.delete(evt); + return content; + } + this.logger("lazyStore doesn't have " + evt); + return false; + }; + /** + * The add to store step is similar so make it generic for resuse + * @param {object} store which store to use + * @param {string} evt event name + * @param {spread} args because the lazy store and normal store store different things + * @return {array} store and the size of the store (cheated @TODO) + */ + JsonqlStoreService.prototype.addToStore = function (store, evt) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + var fnSet; + if (store.has(evt)) { + this.logger('addToStore', evt + " existed"); + fnSet = store.get(evt); + } + else { + this.logger('addToStore', "create new Set for \"" + evt + "\""); + // this is new + fnSet = new Set(); + } + // lazy only store 2 items - this is not the case in V1.6.0 anymore + // we need to check the first parameter is string or not + if (args.length > 2) { + if (Array.isArray(args[0])) { // lazy store + // check if this type of this event already register in the lazy store + var t = args[2]; + if (!this.checkTypeInLazyStore(evt, t)) { + fnSet.add(args); + } + } + else { + if (!this.checkContentExist(args, fnSet)) { + this.logger('addToStore', "insert new", args); + fnSet.add(args); + } + } + } + else { // add straight to lazy store + fnSet.add(args); + } + store.set(evt, fnSet); + // this.logger('fnSet', fnSet, store) + return [store, fnSet.size]; + }; + /** + * @param {array} args for compare + * @param {object} fnSet A Set to search from + * @return {boolean} true on exist + */ + JsonqlStoreService.prototype.checkContentExist = function (args, fnSet) { + var list = this.mapToArr(fnSet); + return !!list + .filter(function (value, index, array) { + var hash = value[0]; + return hash === args[0]; + }).length; + }; + /** + * get the existing type to make sure no mix type add to the same store + * @param {string} evtName event name + * @param {string} type the type to check + * @return {boolean} true you can add, false then you can't add this type + */ + JsonqlStoreService.prototype.checkTypeInStore = function (evtName, type) { + this.validateEvt(evtName, type); + var all = this.$get(evtName, true); + if (all === false) { + // pristine it means you can add + return true; + } + // it should only have ONE type in ONE event store + return !all + .filter(function (value, index, array) { + var t = value[3]; + return type !== t; + }).length; + }; + /** + * This is checking just the lazy store because the structure is different + * therefore we need to use a new method to check it + * @param {string} evtName event name + * @param {string} type the types of allow event + * @return {boolean} true OK + */ + JsonqlStoreService.prototype.checkTypeInLazyStore = function (evtName, type) { + this.validateEvt(evtName, type); + var store = this.lazyStore.get(evtName); + if (store) { + this.logger('checkTypeInLazyStore', store); + return !!this.mapToArr(store) + .filter(function (value, index, array) { + var t = value[2]; + return t !== type; + }).length; + } + this.logger("Store " + evtName + " is empty"); + return false; + }; + /** + * wrapper to re-use the addToStore, + * V1.3.0 add extra check to see if this type can add to this evt + * @param {string} evt event name + * @param {string} type on or once + * @param {function} callback function + * @param {object} context the context the function execute in or null + * @return {number|boolean} size of the store, or false when the check type failed + */ + JsonqlStoreService.prototype.addToNormalStore = function (evt, type, callback, context) { + if (context === void 0) { context = null; } + // @TODO we need to check the existing store for the type first! + if (this.checkTypeInStore(evt, type)) { + this.logger("addToNormalStore: " + type + " can add to \"" + evt + "\" store"); + var key = hashCode(callback); + var args = [this.normalStore, evt, key, callback, context, type]; + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG? is this the problem because it was a setter before + this.normalStore = _store; + // this.logger('after add to store', this.normalStore) + return size; + } + this.logger('addToNormalStore:', evt, type, 'NOT add to normal store', callback.toString()); + return false; + }; + /** + * Add to lazy store this get calls when the callback is not register yet + * so we only get a payload object or even nothing + * @param {string} evt event name + * @param {array} payload of arguments or empty if there is none + * @param {object} [ctx=null] the context the callback execute in + * @param {string|boolean} [type=false] register a type so no other type can add to this evt + * @return {number} size of the store + */ + JsonqlStoreService.prototype.addToLazyStore = function (evt, payload, ctx, type) { + if (payload === void 0) { payload = []; } + if (ctx === void 0) { ctx = null; } + // this is add in V1.6.0 + // when there is type then we will need to check if this already added in lazy store + // and no other type can add to this lazy store + var args = []; + args.push(this.lazyStore, evt, this.toArray(payload), ctx); + if (type) { + args.push(type); + } + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG ? this was a setter + this.lazyStore = _store; + return size; + }; + // VALIDATORS // + // This is another JOKE about Typescript, after you spend all this time on + // fixing the non-error (aka typing) it doesn't do anything during run time + // so what the fuck? + /** + * validate the event name + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validateEvt = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + args.forEach(function (arg) { + if (typeof arg !== 'string') { + throw new Error("event name must be string type! " + arg); + } + }); + return true; + }; + /** + * Simple quick check on the two main parameters + * @param {function} callback function to call + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validate = function (callback) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + if (typeof callback === 'function') { + return Reflect.apply(this.validateEvt, this, args); + } + throw new Error("callback required to be function type! " + callback); + }; + /** + * Check if this type is correct or not added in V1.5.0 + * @param {string} type for checking + * @return {boolean} true on OK + */ + JsonqlStoreService.prototype.validateType = function (type) { + var types = ['on', 'only', 'once', 'onlyOnce']; + return !!types + .filter(function (t) { return type === t; }).length; + }; + return JsonqlStoreService; +}()); + // main -var JsonqlEventService = /** @class */ (function () { +var JsonqlEventService = /** @class */ (function (_super) { + __extends(JsonqlEventService, _super); /** * Create EventService instance * @param {configObj} config configuration object * @return {void} nothing - don't do :void this fuck typescript up what a lot of shit */ function JsonqlEventService(logger) { - // hack - this.logger = typeof logger === 'function' ? logger : function () { }; - this.normalStore = new Map(); - this.lazyStore = new Map(); - this.keep = false; - this.result = []; + return _super.call(this, logger) || this; } /** * Register your evt handler, note we don't check the type here, @@ -40,6 +378,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$on = function (evt, callback, context) { var _this = this; if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'on'; // first need to check if this evt is in lazy store var lazyStoreContent = this.takeFromStore(evt); @@ -78,6 +417,7 @@ var JsonqlEventService = /** @class */ (function () { */ JsonqlEventService.prototype.$once = function (evt, callback, context) { if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'once'; var lazyStoreContent = this.takeFromStore(evt); // this is normal register before call $trigger @@ -116,6 +456,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$only = function (evt, callback, context) { var _this = this; if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'only'; // keep getting TS232, this is why typescript is a joke // it will just ended up with everything is any type and back to square one @@ -152,6 +493,7 @@ var JsonqlEventService = /** @class */ (function () { */ JsonqlEventService.prototype.$onlyOnce = function (evt, callback, context) { if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'onlyOnce'; var added = false; var lazyStoreContent = this.takeFromStore(evt); @@ -204,6 +546,7 @@ var JsonqlEventService = /** @class */ (function () { if (payload === void 0) { payload = []; } if (context === void 0) { context = null; } if (type === void 0) { type = false; } + this.validateEvt(evt); var found = 0; // first check the normal store var nStore = this.normalStore; @@ -213,6 +556,7 @@ var JsonqlEventService = /** @class */ (function () { var nSet = this.mapToArr(nStore.get(evt)); var ctn = nSet.length; var hasOnce = false; + // let hasOnly = false; for (var i = 0; i < ctn; ++i) { ++found; // this.logger('found', found) @@ -243,6 +587,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$call = function (evt, params, type, context) { if (type === void 0) { type = false; } if (context === void 0) { context = null; } + this.validateEvt(evt); var args = [evt, params]; args.push(context, type); return Reflect.apply(this.$trigger, this, args); @@ -253,6 +598,7 @@ var JsonqlEventService = /** @class */ (function () { * @return {boolean} true actually delete something */ JsonqlEventService.prototype.$off = function (evt) { + this.validateEvt(evt); var stores = [this.lazyStore, this.normalStore]; var found = false; stores.forEach(function (store) { @@ -263,78 +609,6 @@ var JsonqlEventService = /** @class */ (function () { }); return found; }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.$get = function (evt, full) { - if (full === void 0) { full = false; } - var store = this.normalStore; - if (store.has(evt)) { - return this - .mapToArr(store.get(evt)) - .map(function (l) { - if (full) { - return l; - } - var key = l[0], callback = l[1]; - return callback; - }); - } - return false; - }; - Object.defineProperty(JsonqlEventService.prototype, "$done", { - /** - * @TODO is there any real use with the keep prop? - * getter for $done - * @return {*} whatever last store result - */ - get: function () { - if (this.keep) { - this.logger(this.result); - return this.result[this.result.length - 1]; - } - return this.result; - }, - //////////////////////////////// - // SETTER / GETTER // - //////////////////////////////// - /** - * store the return result from the run - * @param {*} value whatever return from callback - */ - set: function (value) { - this.logger('set $done', value); - if (this.keep) { - this.result.push(value); - } - else { - this.result = value; - } - }, - enumerable: true, - configurable: true - }); - ///////////////////////////////// - // PRIVATE / PROTECTED METHODS // - ///////////////////////////////// - /** - * @param {any} mapObj the Map object - * @return {array} transform to array to work with - */ - JsonqlEventService.prototype.mapToArr = function (mapObj) { - return Array.from(mapObj); - }; - /** - * make sure we store the argument correctly - * @param {*} arg could be array - * @return {array} make sured - */ - JsonqlEventService.prototype.toArray = function (arg) { - return Array.isArray(arg) ? arg : [arg]; - }; /** * When using ES6 you just need this[$+type] but typescript no such luck * @param {string} type the 4 types @@ -354,194 +628,7 @@ var JsonqlEventService = /** @class */ (function () { throw new Error(type + " is not supported!"); } }; - /** - * Run the callback - * @param {function} callback function to execute - * @param {array} payload for callback - * @param {object} ctx context or null - * @return {void} the result store in $done - */ - JsonqlEventService.prototype.run = function (callback, payload, ctx) { - this.logger('run', callback, payload, ctx); - this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); - }; - /** - * Take the content out and remove it from store id by the name - * @param {string} evt event name - * @return {object|boolean} content or false on not found - */ - JsonqlEventService.prototype.takeFromStore = function (evt) { - var store = this.lazyStore; - // let store = this[storeName]; // it could be empty at this point - this.logger('takeFromStore', store); - if (store.has(evt)) { - var content = store.get(evt); - this.logger('takeFromStore', content); - store.delete(evt); - return content; - } - return false; - }; - /** - * The add to store step is similar so make it generic for resuse - * @param {object} store which store to use - * @param {string} evt event name - * @param {spread} args because the lazy store and normal store store different things - * @return {array} store and the size of the store (cheated @TODO) - */ - JsonqlEventService.prototype.addToStore = function (store, evt) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - var fnSet; - if (store.has(evt)) { - this.logger('addToStore', evt + " existed"); - fnSet = store.get(evt); - } - else { - this.logger('addToStore', "create new Set for " + evt); - // this is new - fnSet = new Set(); - } - // lazy only store 2 items - this is not the case in V1.6.0 anymore - // we need to check the first parameter is string or not - if (args.length > 2) { - if (Array.isArray(args[0])) { // lazy store - // check if this type of this event already register in the lazy store - var t = args[2]; - if (!this.checkTypeInLazyStore(evt, t)) { - fnSet.add(args); - } - } - else { - if (!this.checkContentExist(args, fnSet)) { - this.logger('addToStore', "insert new", args); - fnSet.add(args); - } - } - } - else { // add straight to lazy store - fnSet.add(args); - } - store.set(evt, fnSet); - return [store, fnSet.size]; - }; - /** - * @param {array} args for compare - * @param {object} fnSet A Set to search from - * @return {boolean} true on exist - */ - JsonqlEventService.prototype.checkContentExist = function (args, fnSet) { - var list = this.mapToArr(fnSet); - return !!list - .filter(function (value, index, array) { - var hash = value[0]; - return hash === args[0]; - }).length; - }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.getByEvt = function (evtName) { - var store = this.normalStore; - if (store.has(evtName)) { - return Array - .from(store.get(evtName)) - .map(function (l) { return l; }); - } - return false; - }; - /** - * get the existing type to make sure no mix type add to the same store - * @param {string} evtName event name - * @param {string} type the type to check - * @return {boolean} true you can add, false then you can't add this type - */ - JsonqlEventService.prototype.checkTypeInStore = function (evtName, type) { - var all = this.getByEvt(evtName); - if (all === false) { - // pristine it means you can add - return true; - } - // it should only have ONE type in ONE event store - return !all - .filter(function (value, index, array) { - var t = value[3]; - return type !== t; - }).length; - }; - /** - * This is checking just the lazy store because the structure is different - * therefore we need to use a new method to check it - * @param {string} evtName event name - * @param {string} type the types of allow event - * @return {boolean} true OK - */ - JsonqlEventService.prototype.checkTypeInLazyStore = function (evtName, type) { - var store = this.lazyStore.get(evtName); - this.logger('checkTypeInLazyStore', store); - if (store) { - return !!Array - .from(store) - .filter(function (value, index, array) { - var t = value[2]; - return t !== type; - }).length; - } - return false; - }; - /** - * wrapper to re-use the addToStore, - * V1.3.0 add extra check to see if this type can add to this evt - * @param {string} evt event name - * @param {string} type on or once - * @param {function} callback function - * @param {object} context the context the function execute in or null - * @return {number|boolean} size of the store, or false when the check type failed - */ - JsonqlEventService.prototype.addToNormalStore = function (evt, type, callback, context) { - if (context === void 0) { context = null; } - this.logger('addToNormalStore', evt, type, 'add to normal store'); - // @TODO we need to check the existing store for the type first! - if (this.checkTypeInStore(evt, type)) { - this.logger(type + " can add to " + evt + " store"); - var key = hashCode(callback); - var args = [this.normalStore, evt, key, callback, context, type]; - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.normalStore = _store; - return size; - } - return false; - }; - /** - * Add to lazy store this get calls when the callback is not register yet - * so we only get a payload object or even nothing - * @param {string} evt event name - * @param {array} payload of arguments or empty if there is none - * @param {object} [ctx=null] the context the callback execute in - * @param {string|boolean} [type=false] register a type so no other type can add to this evt - * @return {number} size of the store - */ - JsonqlEventService.prototype.addToLazyStore = function (evt, payload, ctx, type) { - if (payload === void 0) { payload = []; } - if (ctx === void 0) { ctx = null; } - // this is add in V1.6.0 - // when there is type then we will need to check if this already added in lazy store - // and no other type can add to this lazy store - var args = []; - args.push(this.lazyStore, evt, this.toArray(payload), ctx); - if (type) { - args.push(type); - } - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.lazyStore = _store; - return size; - }; return JsonqlEventService; -}()); +}(JsonqlStoreService)); exports.JsonqlEventService = JsonqlEventService; diff --git a/packages/event/dist/jsonql-event-service.es.js b/packages/event/dist/jsonql-event-service.es.js index 9c96af46f80ae140bdab0e64852894ed5e6a0b6c..b952283bfbf82d72b547426a72a871a4c6d9a0d5 100644 --- a/packages/event/dist/jsonql-event-service.es.js +++ b/packages/event/dist/jsonql-event-service.es.js @@ -1,3 +1,32 @@ +/*! ***************************************************************************** +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. + +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); +}; + +function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + /** * generate a 32bit hash based on the function.toString() * _from http://stackoverflow.com/questions/7616461/generate-a-hash-_from-string-in-javascript-jquery @@ -9,21 +38,330 @@ function hashCode(fn) { return s.split("").reduce(function (a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0) + ""; } -// The main class +// break up the store related operation here +var JsonqlStoreService = /** @class */ (function () { + function JsonqlStoreService(logger) { + // hack + this.logger = typeof logger === 'function' ? logger : function () { }; + // should this be WeakMap or should they be Map? + this.normalStore = new Map(); + this.lazyStore = new Map(); + // don't keep + this.keep = false; + // place holder, should this be a WeakSet? + this.result = []; + } + /** + * return all the listener from the event + * @param {string} evtName event name + * @param {boolean} [full=false] if true then return the entire content + * @return {array|boolean} listerner(s) or false when not found + */ + JsonqlStoreService.prototype.$get = function (evt, full) { + if (full === void 0) { full = false; } + this.validateEvt(evt); + var store = this.normalStore; + this.logger('$get', full, this.normalStore); + if (store.has(evt)) { + return this + .mapToArr(store.get(evt)) + .map(function (l) { + if (full) { + return l; + } + var key = l[0], callback = l[1]; + return callback; + }); + } + return false; + }; + Object.defineProperty(JsonqlStoreService.prototype, "$done", { + /** + * @TODO is there any real use with the keep prop? + * getter for $done + * @return {*} whatever last store result + */ + get: function () { + if (this.keep) { + this.logger(this.result); + return this.result[this.result.length - 1]; + } + return this.result; + }, + //////////////////////////////// + // SETTER / GETTER // + //////////////////////////////// + /** + * store the return result from the run + * @param {*} value whatever return from callback + */ + set: function (value) { + this.logger('set $done', value); + if (this.keep) { + this.result.push(value); + } + else { + this.result = value; + } + }, + enumerable: true, + configurable: true + }); + ///////////////////////////////// + // protected / PROTECTED METHODS // + ///////////////////////////////// + /** + * @param {any} mapObj the Map object + * @return {array} transform to array to work with + */ + JsonqlStoreService.prototype.mapToArr = function (mapObj) { + return Array.from(mapObj); + }; + /** + * make sure we store the argument correctly + * @param {*} arg could be array + * @return {array} make sured + */ + JsonqlStoreService.prototype.toArray = function (arg) { + return Array.isArray(arg) ? arg : [arg]; + }; + /** + * Run the callback + * @param {function} callback function to execute + * @param {array} payload for callback + * @param {object} ctx context or null + * @return {void} the result store in $done + */ + JsonqlStoreService.prototype.run = function (callback, payload, ctx) { + this.logger('run', callback, payload, ctx); + this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); + }; + /** + * Take the content out and remove it from store id by the name + * @param {string} evt event name + * @return {object|boolean} content or false on not found + */ + JsonqlStoreService.prototype.takeFromStore = function (evt) { + var store = this.lazyStore; + if (store.has(evt)) { + var content = store.get(evt); + this.logger('takeFromStore', content); + store.delete(evt); + return content; + } + this.logger("lazyStore doesn't have " + evt); + return false; + }; + /** + * The add to store step is similar so make it generic for resuse + * @param {object} store which store to use + * @param {string} evt event name + * @param {spread} args because the lazy store and normal store store different things + * @return {array} store and the size of the store (cheated @TODO) + */ + JsonqlStoreService.prototype.addToStore = function (store, evt) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + var fnSet; + if (store.has(evt)) { + this.logger('addToStore', evt + " existed"); + fnSet = store.get(evt); + } + else { + this.logger('addToStore', "create new Set for \"" + evt + "\""); + // this is new + fnSet = new Set(); + } + // lazy only store 2 items - this is not the case in V1.6.0 anymore + // we need to check the first parameter is string or not + if (args.length > 2) { + if (Array.isArray(args[0])) { // lazy store + // check if this type of this event already register in the lazy store + var t = args[2]; + if (!this.checkTypeInLazyStore(evt, t)) { + fnSet.add(args); + } + } + else { + if (!this.checkContentExist(args, fnSet)) { + this.logger('addToStore', "insert new", args); + fnSet.add(args); + } + } + } + else { // add straight to lazy store + fnSet.add(args); + } + store.set(evt, fnSet); + // this.logger('fnSet', fnSet, store) + return [store, fnSet.size]; + }; + /** + * @param {array} args for compare + * @param {object} fnSet A Set to search from + * @return {boolean} true on exist + */ + JsonqlStoreService.prototype.checkContentExist = function (args, fnSet) { + var list = this.mapToArr(fnSet); + return !!list + .filter(function (value, index, array) { + var hash = value[0]; + return hash === args[0]; + }).length; + }; + /** + * get the existing type to make sure no mix type add to the same store + * @param {string} evtName event name + * @param {string} type the type to check + * @return {boolean} true you can add, false then you can't add this type + */ + JsonqlStoreService.prototype.checkTypeInStore = function (evtName, type) { + this.validateEvt(evtName, type); + var all = this.$get(evtName, true); + if (all === false) { + // pristine it means you can add + return true; + } + // it should only have ONE type in ONE event store + return !all + .filter(function (value, index, array) { + var t = value[3]; + return type !== t; + }).length; + }; + /** + * This is checking just the lazy store because the structure is different + * therefore we need to use a new method to check it + * @param {string} evtName event name + * @param {string} type the types of allow event + * @return {boolean} true OK + */ + JsonqlStoreService.prototype.checkTypeInLazyStore = function (evtName, type) { + this.validateEvt(evtName, type); + var store = this.lazyStore.get(evtName); + if (store) { + this.logger('checkTypeInLazyStore', store); + return !!this.mapToArr(store) + .filter(function (value, index, array) { + var t = value[2]; + return t !== type; + }).length; + } + this.logger("Store " + evtName + " is empty"); + return false; + }; + /** + * wrapper to re-use the addToStore, + * V1.3.0 add extra check to see if this type can add to this evt + * @param {string} evt event name + * @param {string} type on or once + * @param {function} callback function + * @param {object} context the context the function execute in or null + * @return {number|boolean} size of the store, or false when the check type failed + */ + JsonqlStoreService.prototype.addToNormalStore = function (evt, type, callback, context) { + if (context === void 0) { context = null; } + // @TODO we need to check the existing store for the type first! + if (this.checkTypeInStore(evt, type)) { + this.logger("addToNormalStore: " + type + " can add to \"" + evt + "\" store"); + var key = hashCode(callback); + var args = [this.normalStore, evt, key, callback, context, type]; + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG? is this the problem because it was a setter before + this.normalStore = _store; + // this.logger('after add to store', this.normalStore) + return size; + } + this.logger('addToNormalStore:', evt, type, 'NOT add to normal store', callback.toString()); + return false; + }; + /** + * Add to lazy store this get calls when the callback is not register yet + * so we only get a payload object or even nothing + * @param {string} evt event name + * @param {array} payload of arguments or empty if there is none + * @param {object} [ctx=null] the context the callback execute in + * @param {string|boolean} [type=false] register a type so no other type can add to this evt + * @return {number} size of the store + */ + JsonqlStoreService.prototype.addToLazyStore = function (evt, payload, ctx, type) { + if (payload === void 0) { payload = []; } + if (ctx === void 0) { ctx = null; } + // this is add in V1.6.0 + // when there is type then we will need to check if this already added in lazy store + // and no other type can add to this lazy store + var args = []; + args.push(this.lazyStore, evt, this.toArray(payload), ctx); + if (type) { + args.push(type); + } + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG ? this was a setter + this.lazyStore = _store; + return size; + }; + // VALIDATORS // + // This is another JOKE about Typescript, after you spend all this time on + // fixing the non-error (aka typing) it doesn't do anything during run time + // so what the fuck? + /** + * validate the event name + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validateEvt = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + args.forEach(function (arg) { + if (typeof arg !== 'string') { + throw new Error("event name must be string type! " + arg); + } + }); + return true; + }; + /** + * Simple quick check on the two main parameters + * @param {function} callback function to call + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validate = function (callback) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + if (typeof callback === 'function') { + return Reflect.apply(this.validateEvt, this, args); + } + throw new Error("callback required to be function type! " + callback); + }; + /** + * Check if this type is correct or not added in V1.5.0 + * @param {string} type for checking + * @return {boolean} true on OK + */ + JsonqlStoreService.prototype.validateType = function (type) { + var types = ['on', 'only', 'once', 'onlyOnce']; + return !!types + .filter(function (t) { return type === t; }).length; + }; + return JsonqlStoreService; +}()); + // main -var JsonqlEventService = /** @class */ (function () { +var JsonqlEventService = /** @class */ (function (_super) { + __extends(JsonqlEventService, _super); /** * Create EventService instance * @param {configObj} config configuration object * @return {void} nothing - don't do :void this fuck typescript up what a lot of shit */ function JsonqlEventService(logger) { - // hack - this.logger = typeof logger === 'function' ? logger : function () { }; - this.normalStore = new Map(); - this.lazyStore = new Map(); - this.keep = false; - this.result = []; + return _super.call(this, logger) || this; } /** * Register your evt handler, note we don't check the type here, @@ -36,6 +374,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$on = function (evt, callback, context) { var _this = this; if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'on'; // first need to check if this evt is in lazy store var lazyStoreContent = this.takeFromStore(evt); @@ -74,6 +413,7 @@ var JsonqlEventService = /** @class */ (function () { */ JsonqlEventService.prototype.$once = function (evt, callback, context) { if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'once'; var lazyStoreContent = this.takeFromStore(evt); // this is normal register before call $trigger @@ -112,6 +452,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$only = function (evt, callback, context) { var _this = this; if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'only'; // keep getting TS232, this is why typescript is a joke // it will just ended up with everything is any type and back to square one @@ -148,6 +489,7 @@ var JsonqlEventService = /** @class */ (function () { */ JsonqlEventService.prototype.$onlyOnce = function (evt, callback, context) { if (context === void 0) { context = null; } + this.validate(callback, evt); var type = 'onlyOnce'; var added = false; var lazyStoreContent = this.takeFromStore(evt); @@ -200,6 +542,7 @@ var JsonqlEventService = /** @class */ (function () { if (payload === void 0) { payload = []; } if (context === void 0) { context = null; } if (type === void 0) { type = false; } + this.validateEvt(evt); var found = 0; // first check the normal store var nStore = this.normalStore; @@ -209,6 +552,7 @@ var JsonqlEventService = /** @class */ (function () { var nSet = this.mapToArr(nStore.get(evt)); var ctn = nSet.length; var hasOnce = false; + // let hasOnly = false; for (var i = 0; i < ctn; ++i) { ++found; // this.logger('found', found) @@ -239,6 +583,7 @@ var JsonqlEventService = /** @class */ (function () { JsonqlEventService.prototype.$call = function (evt, params, type, context) { if (type === void 0) { type = false; } if (context === void 0) { context = null; } + this.validateEvt(evt); var args = [evt, params]; args.push(context, type); return Reflect.apply(this.$trigger, this, args); @@ -249,6 +594,7 @@ var JsonqlEventService = /** @class */ (function () { * @return {boolean} true actually delete something */ JsonqlEventService.prototype.$off = function (evt) { + this.validateEvt(evt); var stores = [this.lazyStore, this.normalStore]; var found = false; stores.forEach(function (store) { @@ -259,78 +605,6 @@ var JsonqlEventService = /** @class */ (function () { }); return found; }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.$get = function (evt, full) { - if (full === void 0) { full = false; } - var store = this.normalStore; - if (store.has(evt)) { - return this - .mapToArr(store.get(evt)) - .map(function (l) { - if (full) { - return l; - } - var key = l[0], callback = l[1]; - return callback; - }); - } - return false; - }; - Object.defineProperty(JsonqlEventService.prototype, "$done", { - /** - * @TODO is there any real use with the keep prop? - * getter for $done - * @return {*} whatever last store result - */ - get: function () { - if (this.keep) { - this.logger(this.result); - return this.result[this.result.length - 1]; - } - return this.result; - }, - //////////////////////////////// - // SETTER / GETTER // - //////////////////////////////// - /** - * store the return result from the run - * @param {*} value whatever return from callback - */ - set: function (value) { - this.logger('set $done', value); - if (this.keep) { - this.result.push(value); - } - else { - this.result = value; - } - }, - enumerable: true, - configurable: true - }); - ///////////////////////////////// - // PRIVATE / PROTECTED METHODS // - ///////////////////////////////// - /** - * @param {any} mapObj the Map object - * @return {array} transform to array to work with - */ - JsonqlEventService.prototype.mapToArr = function (mapObj) { - return Array.from(mapObj); - }; - /** - * make sure we store the argument correctly - * @param {*} arg could be array - * @return {array} make sured - */ - JsonqlEventService.prototype.toArray = function (arg) { - return Array.isArray(arg) ? arg : [arg]; - }; /** * When using ES6 you just need this[$+type] but typescript no such luck * @param {string} type the 4 types @@ -350,194 +624,7 @@ var JsonqlEventService = /** @class */ (function () { throw new Error(type + " is not supported!"); } }; - /** - * Run the callback - * @param {function} callback function to execute - * @param {array} payload for callback - * @param {object} ctx context or null - * @return {void} the result store in $done - */ - JsonqlEventService.prototype.run = function (callback, payload, ctx) { - this.logger('run', callback, payload, ctx); - this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); - }; - /** - * Take the content out and remove it from store id by the name - * @param {string} evt event name - * @return {object|boolean} content or false on not found - */ - JsonqlEventService.prototype.takeFromStore = function (evt) { - var store = this.lazyStore; - // let store = this[storeName]; // it could be empty at this point - this.logger('takeFromStore', store); - if (store.has(evt)) { - var content = store.get(evt); - this.logger('takeFromStore', content); - store.delete(evt); - return content; - } - return false; - }; - /** - * The add to store step is similar so make it generic for resuse - * @param {object} store which store to use - * @param {string} evt event name - * @param {spread} args because the lazy store and normal store store different things - * @return {array} store and the size of the store (cheated @TODO) - */ - JsonqlEventService.prototype.addToStore = function (store, evt) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - var fnSet; - if (store.has(evt)) { - this.logger('addToStore', evt + " existed"); - fnSet = store.get(evt); - } - else { - this.logger('addToStore', "create new Set for " + evt); - // this is new - fnSet = new Set(); - } - // lazy only store 2 items - this is not the case in V1.6.0 anymore - // we need to check the first parameter is string or not - if (args.length > 2) { - if (Array.isArray(args[0])) { // lazy store - // check if this type of this event already register in the lazy store - var t = args[2]; - if (!this.checkTypeInLazyStore(evt, t)) { - fnSet.add(args); - } - } - else { - if (!this.checkContentExist(args, fnSet)) { - this.logger('addToStore', "insert new", args); - fnSet.add(args); - } - } - } - else { // add straight to lazy store - fnSet.add(args); - } - store.set(evt, fnSet); - return [store, fnSet.size]; - }; - /** - * @param {array} args for compare - * @param {object} fnSet A Set to search from - * @return {boolean} true on exist - */ - JsonqlEventService.prototype.checkContentExist = function (args, fnSet) { - var list = this.mapToArr(fnSet); - return !!list - .filter(function (value, index, array) { - var hash = value[0]; - return hash === args[0]; - }).length; - }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.getByEvt = function (evtName) { - var store = this.normalStore; - if (store.has(evtName)) { - return Array - .from(store.get(evtName)) - .map(function (l) { return l; }); - } - return false; - }; - /** - * get the existing type to make sure no mix type add to the same store - * @param {string} evtName event name - * @param {string} type the type to check - * @return {boolean} true you can add, false then you can't add this type - */ - JsonqlEventService.prototype.checkTypeInStore = function (evtName, type) { - var all = this.getByEvt(evtName); - if (all === false) { - // pristine it means you can add - return true; - } - // it should only have ONE type in ONE event store - return !all - .filter(function (value, index, array) { - var t = value[3]; - return type !== t; - }).length; - }; - /** - * This is checking just the lazy store because the structure is different - * therefore we need to use a new method to check it - * @param {string} evtName event name - * @param {string} type the types of allow event - * @return {boolean} true OK - */ - JsonqlEventService.prototype.checkTypeInLazyStore = function (evtName, type) { - var store = this.lazyStore.get(evtName); - this.logger('checkTypeInLazyStore', store); - if (store) { - return !!Array - .from(store) - .filter(function (value, index, array) { - var t = value[2]; - return t !== type; - }).length; - } - return false; - }; - /** - * wrapper to re-use the addToStore, - * V1.3.0 add extra check to see if this type can add to this evt - * @param {string} evt event name - * @param {string} type on or once - * @param {function} callback function - * @param {object} context the context the function execute in or null - * @return {number|boolean} size of the store, or false when the check type failed - */ - JsonqlEventService.prototype.addToNormalStore = function (evt, type, callback, context) { - if (context === void 0) { context = null; } - this.logger('addToNormalStore', evt, type, 'add to normal store'); - // @TODO we need to check the existing store for the type first! - if (this.checkTypeInStore(evt, type)) { - this.logger(type + " can add to " + evt + " store"); - var key = hashCode(callback); - var args = [this.normalStore, evt, key, callback, context, type]; - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.normalStore = _store; - return size; - } - return false; - }; - /** - * Add to lazy store this get calls when the callback is not register yet - * so we only get a payload object or even nothing - * @param {string} evt event name - * @param {array} payload of arguments or empty if there is none - * @param {object} [ctx=null] the context the callback execute in - * @param {string|boolean} [type=false] register a type so no other type can add to this evt - * @return {number} size of the store - */ - JsonqlEventService.prototype.addToLazyStore = function (evt, payload, ctx, type) { - if (payload === void 0) { payload = []; } - if (ctx === void 0) { ctx = null; } - // this is add in V1.6.0 - // when there is type then we will need to check if this already added in lazy store - // and no other type can add to this lazy store - var args = []; - args.push(this.lazyStore, evt, this.toArray(payload), ctx); - if (type) { - args.push(type); - } - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.lazyStore = _store; - return size; - }; return JsonqlEventService; -}()); +}(JsonqlStoreService)); export { JsonqlEventService }; diff --git a/packages/event/dist/jsonql-event-service.umd.js b/packages/event/dist/jsonql-event-service.umd.js index a16292687160826d05b6d590539826f84682a191..654592e2270e61d06b7e8e78243bba4c9cb5ea6f 100644 --- a/packages/event/dist/jsonql-event-service.umd.js +++ b/packages/event/dist/jsonql-event-service.umd.js @@ -1,553 +1,640 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.Jsonql = {})); + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.JsonqlEventService = {})); }(this, function (exports) { 'use strict'; - /** - * generate a 32bit hash based on the function.toString() - * _from http://stackoverflow.com/questions/7616461/generate-a-hash-_from-string-in-javascript-jquery - * @param {function} fn the converted to string function - * @return {string} the hashed function string - */ - function hashCode(fn) { - var s = fn.toString(); - return s.split("").reduce(function (a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0) + ""; - } + /*! ***************************************************************************** + 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. + + 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); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } - // The main class - // main - var JsonqlEventService = /** @class */ (function () { - /** - * Create EventService instance - * @param {configObj} config configuration object - * @return {void} nothing - don't do :void this fuck typescript up what a lot of shit - */ - function JsonqlEventService(logger) { - // hack - this.logger = typeof logger === 'function' ? logger : function () { }; - this.normalStore = new Map(); - this.lazyStore = new Map(); - this.keep = false; - this.result = []; - } - /** - * Register your evt handler, note we don't check the type here, - * we expect you to be sensible and know what you are doing. - * @param {string} evt name of event - * @param {function} callback bind method --> if it's array or not - * @param {object} [context=null] to execute this call in - * @return {number} the size of the store - */ - JsonqlEventService.prototype.$on = function (evt, callback, context) { - var _this = this; - if (context === void 0) { context = null; } - var type = 'on'; - // first need to check if this evt is in lazy store - var lazyStoreContent = this.takeFromStore(evt); - // this is normal register first then call later - if (lazyStoreContent === false) { - this.logger('$on', evt + " callback is not in lazy store"); - // @TODO we need to check if there was other listener to this - // event and are they the same type then we could solve that - // register the different type to the same event name - return this.addToNormalStore(evt, type, callback, context); - } - this.logger('$on', evt + " found in lazy store"); - // this is when they call $trigger before register this callback - var size = 0; - lazyStoreContent.forEach(function (content) { - var payload = content[0], ctx = content[1], t = content[2]; - if (t && t !== type) { - throw new Error("You are trying to register an event already been taken by other type: " + t); - } - _this.run(callback, payload, context || ctx); - var result = _this.addToNormalStore(evt, type, callback, context || ctx); - if (typeof result !== 'boolean') { - size += result; - } - }); - return size; - }; - /** - * once only registered it once, there is no overwrite option here - * @NOTE change in v1.3.0 $once can add multiple listeners - * but once the event fired, it will remove this event (see $only) - * @param {string} evt name - * @param {function} callback to execute - * @param {object} [context=null] the handler execute in - * @return {number|boolean} result - */ - JsonqlEventService.prototype.$once = function (evt, callback, context) { - if (context === void 0) { context = null; } - var type = 'once'; - var lazyStoreContent = this.takeFromStore(evt); - // this is normal register before call $trigger - var nStore = this.normalStore; - if (lazyStoreContent === false) { - this.logger('$once', evt + " not in the lazy store"); - // v1.3.0 $once now allow to add multiple listeners - return this.addToNormalStore(evt, type, callback, context); - } - else { - // now this is the tricky bit - // there is a potential bug here that cause by the developer - // if they call $trigger first, the lazy won't know it's a once call - // so if in the middle they register any call with the same evt name - // then this $once call will be fucked - add this to the documentation - this.logger('$once', lazyStoreContent); - var list = this.mapToArr(lazyStoreContent); - // should never have more than 1 - var _a = list[0], payload = _a[0], ctx = _a[1], t = _a[2]; - if (t && t !== type) { - throw new Error("You are trying to register an event already been taken by other type: " + t); - } - this.run(callback, payload, context || ctx); - // remove this evt from store - this.$off(evt); - } - return false; - }; - /** - * This one event can only bind one callbackback - * @param {string} evt event name - * @param {function} callback event handler - * @param {object} [context=null] the context the event handler execute in - * @return {boolean} true bind for first time, false already existed - */ - JsonqlEventService.prototype.$only = function (evt, callback, context) { - var _this = this; - if (context === void 0) { context = null; } - var type = 'only'; - // keep getting TS232, this is why typescript is a joke - // it will just ended up with everything is any type and back to square one - var added = false; - var lazyStoreContent = this.takeFromStore(evt); - // this is normal register before call $trigger - var nStore = this.normalStore; - if (!nStore.has(evt)) { - this.logger("$only", evt + " add to store"); - added = this.addToNormalStore(evt, type, callback, context); - } - if (lazyStoreContent !== false) { - // there are data store in lazy store - this.logger('$only', evt + " found data in lazy store to execute"); - var list = this.mapToArr(lazyStoreContent); - // $only allow to trigger this multiple time on the single handler - list.forEach(function (l) { - var payload = l[0], ctx = l[1], t = l[2]; - if (t && t !== type) { - throw new Error("You are trying to register an event already been taken by other type: " + t); - } - _this.run(callback, payload, context || ctx); - }); - } - return added; - }; - /** - * $only + $once this is because I found a very subtile bug when we pass a - * resolver, rejecter - and it never fire because that's OLD adeed in v1.4.0 - * @param {string} evt event name - * @param {function} callback to call later - * @param {object} [context=null] exeucte context - * @return {boolean} same as above - */ - JsonqlEventService.prototype.$onlyOnce = function (evt, callback, context) { - if (context === void 0) { context = null; } - var type = 'onlyOnce'; - var added = false; - var lazyStoreContent = this.takeFromStore(evt); - // this is normal register before call $trigger - var nStore = this.normalStore; - if (!nStore.has(evt)) { - this.logger("$onlyOnce", evt + " add to store"); - added = this.addToNormalStore(evt, type, callback, context); - } - if (lazyStoreContent !== false) { - // there are data store in lazy store - this.logger('$onlyOnce', lazyStoreContent); - var list = this.mapToArr(lazyStoreContent); - // should never have more than 1 - var _a = list[0], payload = _a[0], ctx = _a[1], t = _a[2]; - if (t && t !== 'onlyOnce') { - throw new Error("You are trying to register an event already been taken by other type: " + t); - } - this.run(callback, payload, context || ctx); - // remove this evt from store - this.$off(evt); - } - return added; - }; - /** - * This is a shorthand of $off + $on added in V1.5.0 - * @param {string} evt event name - * @param {function} callback to exeucte - * @param {object} [context = null] or pass a string as type - * @param {string} [type=on] what type of method to replace - * @return {any} whatever the call method return - */ - JsonqlEventService.prototype.$replace = function (evt, callback, context, type) { - if (context === void 0) { context = null; } - if (type === void 0) { type = 'on'; } - this.$off(evt); - // this will become a problem - var method = this.getMethodBy(type); - return Reflect.apply(method, this, [evt, callback, context]); - }; - /** - * trigger the event - * @param {string} evt name NOT allow array anymore! - * @param {mixed} [payload = []] pass to fn - * @param {object|string} [context = null] overwrite what stored - * @param {string} [type=false] if pass this then we need to add type to store too - * @return {number} if it has been execute how many times - */ - JsonqlEventService.prototype.$trigger = function (evt, payload, context, type) { - if (payload === void 0) { payload = []; } - if (context === void 0) { context = null; } - if (type === void 0) { type = false; } - var found = 0; - // first check the normal store - var nStore = this.normalStore; - this.logger('$trigger', nStore); - if (nStore.has(evt)) { - this.logger('$trigger', evt, 'found'); - var nSet = this.mapToArr(nStore.get(evt)); - var ctn = nSet.length; - var hasOnce = false; - for (var i = 0; i < ctn; ++i) { - ++found; - // this.logger('found', found) - var _a = nSet[i], _ = _a[0], callback = _a[1], ctx = _a[2], type_1 = _a[3]; - this.run(callback, payload, context || ctx); - if (type_1 === 'once' || type_1 === 'onlyOnce') { - hasOnce = true; - } - } - if (hasOnce) { - nStore.delete(evt); - } - return found; - } - // now this is not register yet - this.addToLazyStore(evt, payload, context, type); - return found; - }; - /** - * this is an alias to the $trigger - * @NOTE breaking change in V1.6.0 we swap the parameter around - * @param {string} evt event name - * @param {*} params pass to the callback - * @param {string} type of call - * @param {object} context what context callback execute in - * @return {*} from $trigger - */ - JsonqlEventService.prototype.$call = function (evt, params, type, context) { - if (type === void 0) { type = false; } - if (context === void 0) { context = null; } - var args = [evt, params]; - args.push(context, type); - return Reflect.apply(this.$trigger, this, args); - }; - /** - * remove the evt from all the stores - * @param {string} evt name - * @return {boolean} true actually delete something - */ - JsonqlEventService.prototype.$off = function (evt) { - var stores = [this.lazyStore, this.normalStore]; - var found = false; - stores.forEach(function (store) { - if (store.has(evt)) { - found = true; - store.delete(evt); - } - }); - return found; - }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.$get = function (evt, full) { - if (full === void 0) { full = false; } - var store = this.normalStore; - if (store.has(evt)) { - return this - .mapToArr(store.get(evt)) - .map(function (l) { - if (full) { - return l; - } - var key = l[0], callback = l[1]; - return callback; - }); - } - return false; - }; - Object.defineProperty(JsonqlEventService.prototype, "$done", { - /** - * @TODO is there any real use with the keep prop? - * getter for $done - * @return {*} whatever last store result - */ - get: function () { - if (this.keep) { - this.logger(this.result); - return this.result[this.result.length - 1]; - } - return this.result; - }, - //////////////////////////////// - // SETTER / GETTER // - //////////////////////////////// - /** - * store the return result from the run - * @param {*} value whatever return from callback - */ - set: function (value) { - this.logger('set $done', value); - if (this.keep) { - this.result.push(value); - } - else { - this.result = value; - } - }, - enumerable: true, - configurable: true - }); - ///////////////////////////////// - // PRIVATE / PROTECTED METHODS // - ///////////////////////////////// - /** - * @param {any} mapObj the Map object - * @return {array} transform to array to work with - */ - JsonqlEventService.prototype.mapToArr = function (mapObj) { - return Array.from(mapObj); - }; - /** - * make sure we store the argument correctly - * @param {*} arg could be array - * @return {array} make sured - */ - JsonqlEventService.prototype.toArray = function (arg) { - return Array.isArray(arg) ? arg : [arg]; - }; - /** - * When using ES6 you just need this[$+type] but typescript no such luck - * @param {string} type the 4 types - * @return {*} the method - */ - JsonqlEventService.prototype.getMethodBy = function (type) { - switch (type) { - case 'on': - return this.$on; - case 'only': - return this.$only; - case 'once': - return this.$once; - case 'onlyOnce': - return this.$onlyOnce; - default: - throw new Error(type + " is not supported!"); - } - }; - /** - * Run the callback - * @param {function} callback function to execute - * @param {array} payload for callback - * @param {object} ctx context or null - * @return {void} the result store in $done - */ - JsonqlEventService.prototype.run = function (callback, payload, ctx) { - this.logger('run', callback, payload, ctx); - this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); - }; - /** - * Take the content out and remove it from store id by the name - * @param {string} evt event name - * @return {object|boolean} content or false on not found - */ - JsonqlEventService.prototype.takeFromStore = function (evt) { - var store = this.lazyStore; - // let store = this[storeName]; // it could be empty at this point - this.logger('takeFromStore', store); - if (store.has(evt)) { - var content = store.get(evt); - this.logger('takeFromStore', content); - store.delete(evt); - return content; - } - return false; - }; - /** - * The add to store step is similar so make it generic for resuse - * @param {object} store which store to use - * @param {string} evt event name - * @param {spread} args because the lazy store and normal store store different things - * @return {array} store and the size of the store (cheated @TODO) - */ - JsonqlEventService.prototype.addToStore = function (store, evt) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - var fnSet; - if (store.has(evt)) { - this.logger('addToStore', evt + " existed"); - fnSet = store.get(evt); - } - else { - this.logger('addToStore', "create new Set for " + evt); - // this is new - fnSet = new Set(); - } - // lazy only store 2 items - this is not the case in V1.6.0 anymore - // we need to check the first parameter is string or not - if (args.length > 2) { - if (Array.isArray(args[0])) { // lazy store - // check if this type of this event already register in the lazy store - var t = args[2]; - if (!this.checkTypeInLazyStore(evt, t)) { - fnSet.add(args); - } - } - else { - if (!this.checkContentExist(args, fnSet)) { - this.logger('addToStore', "insert new", args); - fnSet.add(args); - } - } - } - else { // add straight to lazy store - fnSet.add(args); - } - store.set(evt, fnSet); - return [store, fnSet.size]; - }; - /** - * @param {array} args for compare - * @param {object} fnSet A Set to search from - * @return {boolean} true on exist - */ - JsonqlEventService.prototype.checkContentExist = function (args, fnSet) { - var list = this.mapToArr(fnSet); - return !!list - .filter(function (value, index, array) { - var hash = value[0]; - return hash === args[0]; - }).length; - }; - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - JsonqlEventService.prototype.getByEvt = function (evtName) { - var store = this.normalStore; - if (store.has(evtName)) { - return Array - .from(store.get(evtName)) - .map(function (l) { return l; }); - } - return false; - }; - /** - * get the existing type to make sure no mix type add to the same store - * @param {string} evtName event name - * @param {string} type the type to check - * @return {boolean} true you can add, false then you can't add this type - */ - JsonqlEventService.prototype.checkTypeInStore = function (evtName, type) { - var all = this.getByEvt(evtName); - if (all === false) { - // pristine it means you can add - return true; - } - // it should only have ONE type in ONE event store - return !all - .filter(function (value, index, array) { - var t = value[3]; - return type !== t; - }).length; - }; - /** - * This is checking just the lazy store because the structure is different - * therefore we need to use a new method to check it - * @param {string} evtName event name - * @param {string} type the types of allow event - * @return {boolean} true OK - */ - JsonqlEventService.prototype.checkTypeInLazyStore = function (evtName, type) { - var store = this.lazyStore.get(evtName); - this.logger('checkTypeInLazyStore', store); - if (store) { - return !!Array - .from(store) - .filter(function (value, index, array) { - var t = value[2]; - return t !== type; - }).length; - } - return false; - }; - /** - * wrapper to re-use the addToStore, - * V1.3.0 add extra check to see if this type can add to this evt - * @param {string} evt event name - * @param {string} type on or once - * @param {function} callback function - * @param {object} context the context the function execute in or null - * @return {number|boolean} size of the store, or false when the check type failed - */ - JsonqlEventService.prototype.addToNormalStore = function (evt, type, callback, context) { - if (context === void 0) { context = null; } - this.logger('addToNormalStore', evt, type, 'add to normal store'); - // @TODO we need to check the existing store for the type first! - if (this.checkTypeInStore(evt, type)) { - this.logger(type + " can add to " + evt + " store"); - var key = hashCode(callback); - var args = [this.normalStore, evt, key, callback, context, type]; - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.normalStore = _store; - return size; - } - return false; - }; - /** - * Add to lazy store this get calls when the callback is not register yet - * so we only get a payload object or even nothing - * @param {string} evt event name - * @param {array} payload of arguments or empty if there is none - * @param {object} [ctx=null] the context the callback execute in - * @param {string|boolean} [type=false] register a type so no other type can add to this evt - * @return {number} size of the store - */ - JsonqlEventService.prototype.addToLazyStore = function (evt, payload, ctx, type) { - if (payload === void 0) { payload = []; } - if (ctx === void 0) { ctx = null; } - // this is add in V1.6.0 - // when there is type then we will need to check if this already added in lazy store - // and no other type can add to this lazy store - var args = []; - args.push(this.lazyStore, evt, this.toArray(payload), ctx); - if (type) { - args.push(type); - } - var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; - this.lazyStore = _store; - return size; - }; - return JsonqlEventService; - }()); + /** + * generate a 32bit hash based on the function.toString() + * _from http://stackoverflow.com/questions/7616461/generate-a-hash-_from-string-in-javascript-jquery + * @param {function} fn the converted to string function + * @return {string} the hashed function string + */ + function hashCode(fn) { + var s = fn.toString(); + return s.split("").reduce(function (a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0) + ""; + } - exports.JsonqlEventService = JsonqlEventService; + // break up the store related operation here + var JsonqlStoreService = /** @class */ (function () { + function JsonqlStoreService(logger) { + // hack + this.logger = typeof logger === 'function' ? logger : function () { }; + // should this be WeakMap or should they be Map? + this.normalStore = new Map(); + this.lazyStore = new Map(); + // don't keep + this.keep = false; + // place holder, should this be a WeakSet? + this.result = []; + } + /** + * return all the listener from the event + * @param {string} evtName event name + * @param {boolean} [full=false] if true then return the entire content + * @return {array|boolean} listerner(s) or false when not found + */ + JsonqlStoreService.prototype.$get = function (evt, full) { + if (full === void 0) { full = false; } + this.validateEvt(evt); + var store = this.normalStore; + this.logger('$get', full, this.normalStore); + if (store.has(evt)) { + return this + .mapToArr(store.get(evt)) + .map(function (l) { + if (full) { + return l; + } + var key = l[0], callback = l[1]; + return callback; + }); + } + return false; + }; + Object.defineProperty(JsonqlStoreService.prototype, "$done", { + /** + * @TODO is there any real use with the keep prop? + * getter for $done + * @return {*} whatever last store result + */ + get: function () { + if (this.keep) { + this.logger(this.result); + return this.result[this.result.length - 1]; + } + return this.result; + }, + //////////////////////////////// + // SETTER / GETTER // + //////////////////////////////// + /** + * store the return result from the run + * @param {*} value whatever return from callback + */ + set: function (value) { + this.logger('set $done', value); + if (this.keep) { + this.result.push(value); + } + else { + this.result = value; + } + }, + enumerable: true, + configurable: true + }); + ///////////////////////////////// + // protected / PROTECTED METHODS // + ///////////////////////////////// + /** + * @param {any} mapObj the Map object + * @return {array} transform to array to work with + */ + JsonqlStoreService.prototype.mapToArr = function (mapObj) { + return Array.from(mapObj); + }; + /** + * make sure we store the argument correctly + * @param {*} arg could be array + * @return {array} make sured + */ + JsonqlStoreService.prototype.toArray = function (arg) { + return Array.isArray(arg) ? arg : [arg]; + }; + /** + * Run the callback + * @param {function} callback function to execute + * @param {array} payload for callback + * @param {object} ctx context or null + * @return {void} the result store in $done + */ + JsonqlStoreService.prototype.run = function (callback, payload, ctx) { + this.logger('run', callback, payload, ctx); + this.$done = Reflect.apply(callback, ctx, this.toArray(payload)); + }; + /** + * Take the content out and remove it from store id by the name + * @param {string} evt event name + * @return {object|boolean} content or false on not found + */ + JsonqlStoreService.prototype.takeFromStore = function (evt) { + var store = this.lazyStore; + if (store.has(evt)) { + var content = store.get(evt); + this.logger('takeFromStore', content); + store.delete(evt); + return content; + } + this.logger("lazyStore doesn't have " + evt); + return false; + }; + /** + * The add to store step is similar so make it generic for resuse + * @param {object} store which store to use + * @param {string} evt event name + * @param {spread} args because the lazy store and normal store store different things + * @return {array} store and the size of the store (cheated @TODO) + */ + JsonqlStoreService.prototype.addToStore = function (store, evt) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + var fnSet; + if (store.has(evt)) { + this.logger('addToStore', evt + " existed"); + fnSet = store.get(evt); + } + else { + this.logger('addToStore', "create new Set for \"" + evt + "\""); + // this is new + fnSet = new Set(); + } + // lazy only store 2 items - this is not the case in V1.6.0 anymore + // we need to check the first parameter is string or not + if (args.length > 2) { + if (Array.isArray(args[0])) { // lazy store + // check if this type of this event already register in the lazy store + var t = args[2]; + if (!this.checkTypeInLazyStore(evt, t)) { + fnSet.add(args); + } + } + else { + if (!this.checkContentExist(args, fnSet)) { + this.logger('addToStore', "insert new", args); + fnSet.add(args); + } + } + } + else { // add straight to lazy store + fnSet.add(args); + } + store.set(evt, fnSet); + // this.logger('fnSet', fnSet, store) + return [store, fnSet.size]; + }; + /** + * @param {array} args for compare + * @param {object} fnSet A Set to search from + * @return {boolean} true on exist + */ + JsonqlStoreService.prototype.checkContentExist = function (args, fnSet) { + var list = this.mapToArr(fnSet); + return !!list + .filter(function (value, index, array) { + var hash = value[0]; + return hash === args[0]; + }).length; + }; + /** + * get the existing type to make sure no mix type add to the same store + * @param {string} evtName event name + * @param {string} type the type to check + * @return {boolean} true you can add, false then you can't add this type + */ + JsonqlStoreService.prototype.checkTypeInStore = function (evtName, type) { + this.validateEvt(evtName, type); + var all = this.$get(evtName, true); + if (all === false) { + // pristine it means you can add + return true; + } + // it should only have ONE type in ONE event store + return !all + .filter(function (value, index, array) { + var t = value[3]; + return type !== t; + }).length; + }; + /** + * This is checking just the lazy store because the structure is different + * therefore we need to use a new method to check it + * @param {string} evtName event name + * @param {string} type the types of allow event + * @return {boolean} true OK + */ + JsonqlStoreService.prototype.checkTypeInLazyStore = function (evtName, type) { + this.validateEvt(evtName, type); + var store = this.lazyStore.get(evtName); + if (store) { + this.logger('checkTypeInLazyStore', store); + return !!this.mapToArr(store) + .filter(function (value, index, array) { + var t = value[2]; + return t !== type; + }).length; + } + this.logger("Store " + evtName + " is empty"); + return false; + }; + /** + * wrapper to re-use the addToStore, + * V1.3.0 add extra check to see if this type can add to this evt + * @param {string} evt event name + * @param {string} type on or once + * @param {function} callback function + * @param {object} context the context the function execute in or null + * @return {number|boolean} size of the store, or false when the check type failed + */ + JsonqlStoreService.prototype.addToNormalStore = function (evt, type, callback, context) { + if (context === void 0) { context = null; } + // @TODO we need to check the existing store for the type first! + if (this.checkTypeInStore(evt, type)) { + this.logger("addToNormalStore: " + type + " can add to \"" + evt + "\" store"); + var key = hashCode(callback); + var args = [this.normalStore, evt, key, callback, context, type]; + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG? is this the problem because it was a setter before + this.normalStore = _store; + // this.logger('after add to store', this.normalStore) + return size; + } + this.logger('addToNormalStore:', evt, type, 'NOT add to normal store', callback.toString()); + return false; + }; + /** + * Add to lazy store this get calls when the callback is not register yet + * so we only get a payload object or even nothing + * @param {string} evt event name + * @param {array} payload of arguments or empty if there is none + * @param {object} [ctx=null] the context the callback execute in + * @param {string|boolean} [type=false] register a type so no other type can add to this evt + * @return {number} size of the store + */ + JsonqlStoreService.prototype.addToLazyStore = function (evt, payload, ctx, type) { + if (payload === void 0) { payload = []; } + if (ctx === void 0) { ctx = null; } + // this is add in V1.6.0 + // when there is type then we will need to check if this already added in lazy store + // and no other type can add to this lazy store + var args = []; + args.push(this.lazyStore, evt, this.toArray(payload), ctx); + if (type) { + args.push(type); + } + var _a = Reflect.apply(this.addToStore, this, args), _store = _a[0], size = _a[1]; + // @BUG ? this was a setter + this.lazyStore = _store; + return size; + }; + // VALIDATORS // + // This is another JOKE about Typescript, after you spend all this time on + // fixing the non-error (aka typing) it doesn't do anything during run time + // so what the fuck? + /** + * validate the event name + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validateEvt = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + args.forEach(function (arg) { + if (typeof arg !== 'string') { + throw new Error("event name must be string type! " + arg); + } + }); + return true; + }; + /** + * Simple quick check on the two main parameters + * @param {function} callback function to call + * @param {string} evt event name + * @return {boolean} true when OK + */ + JsonqlStoreService.prototype.validate = function (callback) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + if (typeof callback === 'function') { + return Reflect.apply(this.validateEvt, this, args); + } + throw new Error("callback required to be function type! " + callback); + }; + /** + * Check if this type is correct or not added in V1.5.0 + * @param {string} type for checking + * @return {boolean} true on OK + */ + JsonqlStoreService.prototype.validateType = function (type) { + var types = ['on', 'only', 'once', 'onlyOnce']; + return !!types + .filter(function (t) { return type === t; }).length; + }; + return JsonqlStoreService; + }()); - Object.defineProperty(exports, '__esModule', { value: true }); + // main + var JsonqlEventService = /** @class */ (function (_super) { + __extends(JsonqlEventService, _super); + /** + * Create EventService instance + * @param {configObj} config configuration object + * @return {void} nothing - don't do :void this fuck typescript up what a lot of shit + */ + function JsonqlEventService(logger) { + return _super.call(this, logger) || this; + } + /** + * Register your evt handler, note we don't check the type here, + * we expect you to be sensible and know what you are doing. + * @param {string} evt name of event + * @param {function} callback bind method --> if it's array or not + * @param {object} [context=null] to execute this call in + * @return {number} the size of the store + */ + JsonqlEventService.prototype.$on = function (evt, callback, context) { + var _this = this; + if (context === void 0) { context = null; } + this.validate(callback, evt); + var type = 'on'; + // first need to check if this evt is in lazy store + var lazyStoreContent = this.takeFromStore(evt); + // this is normal register first then call later + if (lazyStoreContent === false) { + this.logger('$on', evt + " callback is not in lazy store"); + // @TODO we need to check if there was other listener to this + // event and are they the same type then we could solve that + // register the different type to the same event name + return this.addToNormalStore(evt, type, callback, context); + } + this.logger('$on', evt + " found in lazy store"); + // this is when they call $trigger before register this callback + var size = 0; + lazyStoreContent.forEach(function (content) { + var payload = content[0], ctx = content[1], t = content[2]; + if (t && t !== type) { + throw new Error("You are trying to register an event already been taken by other type: " + t); + } + _this.run(callback, payload, context || ctx); + var result = _this.addToNormalStore(evt, type, callback, context || ctx); + if (typeof result !== 'boolean') { + size += result; + } + }); + return size; + }; + /** + * once only registered it once, there is no overwrite option here + * @NOTE change in v1.3.0 $once can add multiple listeners + * but once the event fired, it will remove this event (see $only) + * @param {string} evt name + * @param {function} callback to execute + * @param {object} [context=null] the handler execute in + * @return {number|boolean} result + */ + JsonqlEventService.prototype.$once = function (evt, callback, context) { + if (context === void 0) { context = null; } + this.validate(callback, evt); + var type = 'once'; + var lazyStoreContent = this.takeFromStore(evt); + // this is normal register before call $trigger + var nStore = this.normalStore; + if (lazyStoreContent === false) { + this.logger('$once', evt + " not in the lazy store"); + // v1.3.0 $once now allow to add multiple listeners + return this.addToNormalStore(evt, type, callback, context); + } + else { + // now this is the tricky bit + // there is a potential bug here that cause by the developer + // if they call $trigger first, the lazy won't know it's a once call + // so if in the middle they register any call with the same evt name + // then this $once call will be fucked - add this to the documentation + this.logger('$once', lazyStoreContent); + var list = this.mapToArr(lazyStoreContent); + // should never have more than 1 + var _a = list[0], payload = _a[0], ctx = _a[1], t = _a[2]; + if (t && t !== type) { + throw new Error("You are trying to register an event already been taken by other type: " + t); + } + this.run(callback, payload, context || ctx); + // remove this evt from store + this.$off(evt); + } + return false; + }; + /** + * This one event can only bind one callbackback + * @param {string} evt event name + * @param {function} callback event handler + * @param {object} [context=null] the context the event handler execute in + * @return {boolean} true bind for first time, false already existed + */ + JsonqlEventService.prototype.$only = function (evt, callback, context) { + var _this = this; + if (context === void 0) { context = null; } + this.validate(callback, evt); + var type = 'only'; + // keep getting TS232, this is why typescript is a joke + // it will just ended up with everything is any type and back to square one + var added = false; + var lazyStoreContent = this.takeFromStore(evt); + // this is normal register before call $trigger + var nStore = this.normalStore; + if (!nStore.has(evt)) { + this.logger("$only", evt + " add to store"); + added = this.addToNormalStore(evt, type, callback, context); + } + if (lazyStoreContent !== false) { + // there are data store in lazy store + this.logger('$only', evt + " found data in lazy store to execute"); + var list = this.mapToArr(lazyStoreContent); + // $only allow to trigger this multiple time on the single handler + list.forEach(function (l) { + var payload = l[0], ctx = l[1], t = l[2]; + if (t && t !== type) { + throw new Error("You are trying to register an event already been taken by other type: " + t); + } + _this.run(callback, payload, context || ctx); + }); + } + return added; + }; + /** + * $only + $once this is because I found a very subtile bug when we pass a + * resolver, rejecter - and it never fire because that's OLD adeed in v1.4.0 + * @param {string} evt event name + * @param {function} callback to call later + * @param {object} [context=null] exeucte context + * @return {boolean} same as above + */ + JsonqlEventService.prototype.$onlyOnce = function (evt, callback, context) { + if (context === void 0) { context = null; } + this.validate(callback, evt); + var type = 'onlyOnce'; + var added = false; + var lazyStoreContent = this.takeFromStore(evt); + // this is normal register before call $trigger + var nStore = this.normalStore; + if (!nStore.has(evt)) { + this.logger("$onlyOnce", evt + " add to store"); + added = this.addToNormalStore(evt, type, callback, context); + } + if (lazyStoreContent !== false) { + // there are data store in lazy store + this.logger('$onlyOnce', lazyStoreContent); + var list = this.mapToArr(lazyStoreContent); + // should never have more than 1 + var _a = list[0], payload = _a[0], ctx = _a[1], t = _a[2]; + if (t && t !== 'onlyOnce') { + throw new Error("You are trying to register an event already been taken by other type: " + t); + } + this.run(callback, payload, context || ctx); + // remove this evt from store + this.$off(evt); + } + return added; + }; + /** + * This is a shorthand of $off + $on added in V1.5.0 + * @param {string} evt event name + * @param {function} callback to exeucte + * @param {object} [context = null] or pass a string as type + * @param {string} [type=on] what type of method to replace + * @return {any} whatever the call method return + */ + JsonqlEventService.prototype.$replace = function (evt, callback, context, type) { + if (context === void 0) { context = null; } + if (type === void 0) { type = 'on'; } + this.$off(evt); + // this will become a problem + var method = this.getMethodBy(type); + return Reflect.apply(method, this, [evt, callback, context]); + }; + /** + * trigger the event + * @param {string} evt name NOT allow array anymore! + * @param {mixed} [payload = []] pass to fn + * @param {object|string} [context = null] overwrite what stored + * @param {string} [type=false] if pass this then we need to add type to store too + * @return {number} if it has been execute how many times + */ + JsonqlEventService.prototype.$trigger = function (evt, payload, context, type) { + if (payload === void 0) { payload = []; } + if (context === void 0) { context = null; } + if (type === void 0) { type = false; } + this.validateEvt(evt); + var found = 0; + // first check the normal store + var nStore = this.normalStore; + this.logger('$trigger', nStore); + if (nStore.has(evt)) { + this.logger('$trigger', evt, 'found'); + var nSet = this.mapToArr(nStore.get(evt)); + var ctn = nSet.length; + var hasOnce = false; + // let hasOnly = false; + for (var i = 0; i < ctn; ++i) { + ++found; + // this.logger('found', found) + var _a = nSet[i], _ = _a[0], callback = _a[1], ctx = _a[2], type_1 = _a[3]; + this.run(callback, payload, context || ctx); + if (type_1 === 'once' || type_1 === 'onlyOnce') { + hasOnce = true; + } + } + if (hasOnce) { + nStore.delete(evt); + } + return found; + } + // now this is not register yet + this.addToLazyStore(evt, payload, context, type); + return found; + }; + /** + * this is an alias to the $trigger + * @NOTE breaking change in V1.6.0 we swap the parameter around + * @param {string} evt event name + * @param {*} params pass to the callback + * @param {string} type of call + * @param {object} context what context callback execute in + * @return {*} from $trigger + */ + JsonqlEventService.prototype.$call = function (evt, params, type, context) { + if (type === void 0) { type = false; } + if (context === void 0) { context = null; } + this.validateEvt(evt); + var args = [evt, params]; + args.push(context, type); + return Reflect.apply(this.$trigger, this, args); + }; + /** + * remove the evt from all the stores + * @param {string} evt name + * @return {boolean} true actually delete something + */ + JsonqlEventService.prototype.$off = function (evt) { + this.validateEvt(evt); + var stores = [this.lazyStore, this.normalStore]; + var found = false; + stores.forEach(function (store) { + if (store.has(evt)) { + found = true; + store.delete(evt); + } + }); + return found; + }; + /** + * When using ES6 you just need this[$+type] but typescript no such luck + * @param {string} type the 4 types + * @return {*} the method + */ + JsonqlEventService.prototype.getMethodBy = function (type) { + switch (type) { + case 'on': + return this.$on; + case 'only': + return this.$only; + case 'once': + return this.$once; + case 'onlyOnce': + return this.$onlyOnce; + default: + throw new Error(type + " is not supported!"); + } + }; + return JsonqlEventService; + }(JsonqlStoreService)); + + exports.JsonqlEventService = JsonqlEventService; + + Object.defineProperty(exports, '__esModule', { value: true }); })); diff --git a/packages/event/package.json b/packages/event/package.json index 838760855d38f93ccc5aa2befd6448edb02ad175..c2661d973e0ff3d90084bb58b508209eb6ee1f8c 100644 --- a/packages/event/package.json +++ b/packages/event/package.json @@ -1,6 +1,6 @@ { "name": "@jsonql/event", - "version": "1.0.0", + "version": "1.1.0", "description": "Ported from nb-event-service rewritten with Typescript", "main": "dist/jsonql-event-service.cjs.js", "browser": "dist/jsonql-event-service.umd.js", @@ -15,7 +15,8 @@ "build": "rollup -c", "dev": "rollup -cw", "test:basic": "DEBUG=nb-event-service* ava ./tests/basic.test.js", - "test:browser": "node ./tests/fixtures/browser.js" + "test:browser": "node ./tests/fixtures/browser.js", + "test:once-problem": "DEBUG=nb-event-service* ava ./tests/once-problem.test.js" }, "keywords": [ "jsonql", @@ -28,6 +29,10 @@ "type": "git", "url": "git+ssh://git@gitee.com:to1source/jsonql.git" }, + "bugs": { + "url": "https://gitee.com/to1source/jsonql/issues" + }, + "homepage": "https://gitee.com/to1source/jsonql#readme", "license": "ISC", "devDependencies": { "ava": "^2.2.0", diff --git a/packages/event/rollup.config.js b/packages/event/rollup.config.js index 7220fd531708e666dff4e32ee449bd20d0f983e6..eed255461683ae34e7ada88f39e064948addba0f 100644 --- a/packages/event/rollup.config.js +++ b/packages/event/rollup.config.js @@ -19,7 +19,7 @@ export default { } ], external: [ - ...Object.keys(pkg.depedencies || {}), + ...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {}) ], plugins: [ diff --git a/packages/event/src/event-service.js b/packages/event/src/event-service.js index a75d6c0559dbc765258d9d27a07a0b7f2b595c79..3f26fcef6778e7a38459280723069d9d7a631520 100644 --- a/packages/event/src/event-service.js +++ b/packages/event/src/event-service.js @@ -7,6 +7,8 @@ import { import genHaskKey from './hash-code.js' // export export default class EventService { + protected logger: any; + /** * class constructor */ @@ -274,13 +276,13 @@ export default class EventService { this.validateEvt(evt) let store = this.normalStore; if (store.has(evt)) { - return Array - .from(store.get(evt)) - .map( l => { + return this + .mapToArr(store.get(evt)) + .map((value: any, index: number, arr: any[]) => { if (full) { - return l; + return value; } - let [key, callback, ] = l; + let [key, callback, ] = value; return callback; }) } diff --git a/packages/event/src/event-service.ts b/packages/event/src/event-service.ts index 679ec3f796ec71c8f6f49446beeaffda0bed71e5..65be7d873f952d337b3ddc66b9392940d88a8f7a 100644 --- a/packages/event/src/event-service.ts +++ b/packages/event/src/event-service.ts @@ -1,15 +1,9 @@ // The main class import './interfaces.ts'; -import hashFnToKey from './hash-code'; +import JsonqlStoreService from './store-service'; + // main -export class JsonqlEventService { - // public props - keep: boolean; - // private props - private normalStore: Map; - private lazyStore: Map; - private logger: any; - private result: any[]; +export class JsonqlEventService extends JsonqlStoreService { /** * Create EventService instance @@ -17,14 +11,7 @@ export class JsonqlEventService { * @return {void} nothing - don't do :void this fuck typescript up what a lot of shit */ constructor(logger: any) { - // hack - this.logger = typeof logger === 'function' ? logger : () => {}; - - this.normalStore = new Map() - this.lazyStore = new Map() - - this.keep = false; - this.result = []; + super(logger) } /** @@ -36,6 +23,7 @@ export class JsonqlEventService { * @return {number} the size of the store */ public $on(evt: string, callback: myCallbackType, context: any = null): any { + this.validate(callback, evt) const type = 'on'; // first need to check if this evt is in lazy store let lazyStoreContent = this.takeFromStore(evt) @@ -75,6 +63,7 @@ export class JsonqlEventService { * @return {number|boolean} result */ public $once(evt: string , callback: myCallbackType , context: any = null): any { + this.validate(callback, evt) const type = 'once'; let lazyStoreContent = this.takeFromStore(evt) // this is normal register before call $trigger @@ -111,6 +100,7 @@ export class JsonqlEventService { * @return {boolean} true bind for first time, false already existed */ public $only(evt: string, callback: myCallbackType, context: any = null): boolean { + this.validate(callback, evt) const type = 'only'; // keep getting TS232, this is why typescript is a joke // it will just ended up with everything is any type and back to square one @@ -147,6 +137,7 @@ export class JsonqlEventService { * @return {boolean} same as above */ public $onlyOnce(evt: string, callback: myCallbackType, context: any = null): boolean { + this.validate(callback, evt) const type = 'onlyOnce'; let added: any = false; let lazyStoreContent = this.takeFromStore(evt) @@ -196,6 +187,7 @@ export class JsonqlEventService { * @return {number} if it has been execute how many times */ public $trigger(evt: string , payload: any[] = [] , context: any = null, type: string|boolean = false): number { + this.validateEvt(evt) let found = 0; // first check the normal store let nStore = this.normalStore; @@ -205,7 +197,7 @@ export class JsonqlEventService { let nSet = this.mapToArr(nStore.get(evt)) let ctn = nSet.length; let hasOnce = false; - let hasOnly = false; + // let hasOnly = false; for (let i=0; i < ctn; ++i) { ++found; // this.logger('found', found) @@ -235,6 +227,7 @@ export class JsonqlEventService { * @return {*} from $trigger */ public $call(evt: string, params: any[], type: any = false, context: any = null): any { + this.validateEvt(evt) let args = [evt, params] args.push(context, type) return Reflect.apply(this.$trigger, this, args) @@ -246,6 +239,7 @@ export class JsonqlEventService { * @return {boolean} true actually delete something */ public $off(evt: string): boolean { + this.validateEvt(evt) let stores = [ this.lazyStore, this.normalStore ] let found = false; stores.forEach(store => { @@ -257,87 +251,15 @@ export class JsonqlEventService { return found; } - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - public $get(evt: string, full: boolean = false): any { - let store = this.normalStore; - if (store.has(evt)) { - return this - .mapToArr(store.get(evt)) - .map( l => { - if (full) { - return l; - } - let [key, callback, ] = l; - return callback; - }) - } - return false; - } - - - - //////////////////////////////// - // SETTER / GETTER // - //////////////////////////////// - /** - * store the return result from the run - * @param {*} value whatever return from callback - */ - set $done(value: any) { - this.logger('set $done', value) - if (this.keep) { - this.result.push(value) - } else { - this.result = value; - } - } - /** - * @TODO is there any real use with the keep prop? - * getter for $done - * @return {*} whatever last store result - */ - get $done(): any { - if (this.keep) { - this.logger(this.result) - return this.result[this.result.length - 1] - } - return this.result; - } - - ///////////////////////////////// - // PRIVATE / PROTECTED METHODS // - ///////////////////////////////// - - /** - * @param {any} mapObj the Map object - * @return {array} transform to array to work with - */ - protected mapToArr(mapObj: any): any[] { - return Array.from(mapObj) - } - - /** - * make sure we store the argument correctly - * @param {*} arg could be array - * @return {array} make sured - */ - protected toArray(arg: any) { - return Array.isArray(arg) ? arg : [arg]; - } /** * When using ES6 you just need this[$+type] but typescript no such luck * @param {string} type the 4 types * @return {*} the method */ - private getMethodBy(type: string): any { + protected getMethodBy(type: string): any { switch(type) { case 'on': return this.$on; @@ -352,189 +274,4 @@ export class JsonqlEventService { } } - /** - * Run the callback - * @param {function} callback function to execute - * @param {array} payload for callback - * @param {object} ctx context or null - * @return {void} the result store in $done - */ - private run(callback: myCallbackType, payload: any[], ctx: any): void { - this.logger('run', callback, payload, ctx) - this.$done = Reflect.apply(callback, ctx, this.toArray(payload)) - } - - /** - * Take the content out and remove it from store id by the name - * @param {string} evt event name - * @return {object|boolean} content or false on not found - */ - private takeFromStore(evt: string) { - const store = this.lazyStore; - // let store = this[storeName]; // it could be empty at this point - this.logger('takeFromStore', store) - if (store.has(evt)) { - let content = store.get(evt) - this.logger('takeFromStore', content) - store.delete(evt) - return content; - } - return false; - } - - /** - * The add to store step is similar so make it generic for resuse - * @param {object} store which store to use - * @param {string} evt event name - * @param {spread} args because the lazy store and normal store store different things - * @return {array} store and the size of the store (cheated @TODO) - */ - private addToStore(store: any, evt: string, ...args: any[]): any[] { - let fnSet; - if (store.has(evt)) { - this.logger('addToStore', `${evt} existed`) - fnSet = store.get(evt) - } else { - this.logger('addToStore', `create new Set for ${evt}`) - // this is new - fnSet = new Set() - } - // lazy only store 2 items - this is not the case in V1.6.0 anymore - // we need to check the first parameter is string or not - if (args.length > 2) { - if (Array.isArray(args[0])) { // lazy store - // check if this type of this event already register in the lazy store - let [,,t] = args; - if (!this.checkTypeInLazyStore(evt, t)) { - fnSet.add(args) - } - } else { - if (!this.checkContentExist(args, fnSet)) { - this.logger('addToStore', `insert new`, args) - fnSet.add(args) - } - } - } else { // add straight to lazy store - fnSet.add(args) - } - store.set(evt, fnSet) - return [store, fnSet.size] - } - - /** - * @param {array} args for compare - * @param {object} fnSet A Set to search from - * @return {boolean} true on exist - */ - private checkContentExist(args: any[], fnSet: Map): boolean { - let list = this.mapToArr(fnSet) - return !!list - .filter((value: any, index: number, array: any[]): boolean => { - let [hash,] = value; - return hash === args[0]; - }).length; - } - - /** - * return all the listener from the event - * @param {string} evtName event name - * @param {boolean} [full=false] if true then return the entire content - * @return {array|boolean} listerner(s) or false when not found - */ - private getByEvt(evtName: string): any { - let store = this.normalStore; - if (store.has(evtName)) { - return Array - .from(store.get(evtName)) - .map((l: any): any[] => l) - } - return false; - } - - /** - * get the existing type to make sure no mix type add to the same store - * @param {string} evtName event name - * @param {string} type the type to check - * @return {boolean} true you can add, false then you can't add this type - */ - private checkTypeInStore(evtName: string, type: string): boolean { - let all = this.getByEvt(evtName) - if (all === false) { - // pristine it means you can add - return true; - } - // it should only have ONE type in ONE event store - return !all - .filter( (value: any, index: number, array: any[]): boolean => { - let [ ,,,t ] = value; - return type !== t; - }).length; - } - - /** - * This is checking just the lazy store because the structure is different - * therefore we need to use a new method to check it - * @param {string} evtName event name - * @param {string} type the types of allow event - * @return {boolean} true OK - */ - private checkTypeInLazyStore(evtName: string, type: string): boolean { - let store = this.lazyStore.get(evtName) - this.logger('checkTypeInLazyStore', store) - if (store) { - return !!Array - .from(store) - .filter((value: any, index: number, array: any[]): boolean => { - let [,,t] = value; - return t !== type; - }).length - } - return false; - } - - /** - * wrapper to re-use the addToStore, - * V1.3.0 add extra check to see if this type can add to this evt - * @param {string} evt event name - * @param {string} type on or once - * @param {function} callback function - * @param {object} context the context the function execute in or null - * @return {number|boolean} size of the store, or false when the check type failed - */ - private addToNormalStore(evt: string, type: any, callback: myCallbackType, context: any = null): number | boolean { - this.logger('addToNormalStore', evt, type, 'add to normal store') - // @TODO we need to check the existing store for the type first! - if (this.checkTypeInStore(evt, type)) { - this.logger(`${type} can add to ${evt} store`) - let key = hashFnToKey(callback) - let args = [this.normalStore, evt, key, callback, context, type] - let [_store, size] = Reflect.apply(this.addToStore, this, args) - this.normalStore = _store; - return size; - } - return false; - } - - /** - * Add to lazy store this get calls when the callback is not register yet - * so we only get a payload object or even nothing - * @param {string} evt event name - * @param {array} payload of arguments or empty if there is none - * @param {object} [ctx=null] the context the callback execute in - * @param {string|boolean} [type=false] register a type so no other type can add to this evt - * @return {number} size of the store - */ - private addToLazyStore(evt: string, payload: any[] = [], ctx: any = null, type: string|boolean): number { - // this is add in V1.6.0 - // when there is type then we will need to check if this already added in lazy store - // and no other type can add to this lazy store - let args = []; - args.push(this.lazyStore, evt, this.toArray(payload), ctx) - if (type) { - args.push(type) - } - let [_store, size] = Reflect.apply(this.addToStore, this, args) - this.lazyStore = _store; - return size; - } } diff --git a/packages/event/src/store-service.ts b/packages/event/src/store-service.ts new file mode 100644 index 0000000000000000000000000000000000000000..1509457d8462ea7a864aadc743e08053a4678055 --- /dev/null +++ b/packages/event/src/store-service.ts @@ -0,0 +1,324 @@ +// break up the store related operation here +// because the class is getting way too big now +import hashFnToKey from './hash-code'; + +export default class JsonqlStoreService { + // public props + keep: boolean; + // protected props + protected normalStore: Map; + protected lazyStore: Map; + protected result: any[]; + protected logger: any; + + constructor(logger: any) { + // hack + this.logger = typeof logger === 'function' ? logger : () => {}; + // should this be WeakMap or should they be Map? + this.normalStore = new Map() + this.lazyStore = new Map() + // don't keep + this.keep = false; + // place holder, should this be a WeakSet? + this.result = []; + } + + + /** + * return all the listener from the event + * @param {string} evtName event name + * @param {boolean} [full=false] if true then return the entire content + * @return {array|boolean} listerner(s) or false when not found + */ + public $get(evt: string, full: boolean = false): any { + this.validateEvt(evt) + let store = this.normalStore; + this.logger('$get', full, this.normalStore) + if (store.has(evt)) { + return this + .mapToArr(store.get(evt)) + .map( l => { + if (full) { + return l; + } + let [key, callback, ] = l; + return callback; + }) + } + return false; + } + + + //////////////////////////////// + // SETTER / GETTER // + //////////////////////////////// + + /** + * store the return result from the run + * @param {*} value whatever return from callback + */ + set $done(value: any) { + this.logger('set $done', value) + if (this.keep) { + this.result.push(value) + } else { + this.result = value; + } + } + + /** + * @TODO is there any real use with the keep prop? + * getter for $done + * @return {*} whatever last store result + */ + get $done(): any { + if (this.keep) { + this.logger(this.result) + return this.result[this.result.length - 1] + } + return this.result; + } + + ///////////////////////////////// + // protected / PROTECTED METHODS // + ///////////////////////////////// + + /** + * @param {any} mapObj the Map object + * @return {array} transform to array to work with + */ + protected mapToArr(mapObj: any): any[] { + return Array.from(mapObj) + } + + /** + * make sure we store the argument correctly + * @param {*} arg could be array + * @return {array} make sured + */ + protected toArray(arg: any) { + return Array.isArray(arg) ? arg : [arg]; + } + + /** + * Run the callback + * @param {function} callback function to execute + * @param {array} payload for callback + * @param {object} ctx context or null + * @return {void} the result store in $done + */ + protected run(callback: myCallbackType, payload: any[], ctx: any): void { + this.logger('run', callback, payload, ctx) + this.$done = Reflect.apply(callback, ctx, this.toArray(payload)) + } + + /** + * Take the content out and remove it from store id by the name + * @param {string} evt event name + * @return {object|boolean} content or false on not found + */ + protected takeFromStore(evt: string) { + const store = this.lazyStore; + if (store.has(evt)) { + let content = store.get(evt) + this.logger('takeFromStore', content) + store.delete(evt) + return content; + } + this.logger(`lazyStore doesn't have ${evt}`) + return false; + } + + /** + * The add to store step is similar so make it generic for resuse + * @param {object} store which store to use + * @param {string} evt event name + * @param {spread} args because the lazy store and normal store store different things + * @return {array} store and the size of the store (cheated @TODO) + */ + protected addToStore(store: any, evt: string, ...args: any[]): any[] { + let fnSet; + if (store.has(evt)) { + this.logger('addToStore', `${evt} existed`) + fnSet = store.get(evt) + } else { + this.logger('addToStore', `create new Set for "${evt}"`) + // this is new + fnSet = new Set() + } + // lazy only store 2 items - this is not the case in V1.6.0 anymore + // we need to check the first parameter is string or not + if (args.length > 2) { + if (Array.isArray(args[0])) { // lazy store + // check if this type of this event already register in the lazy store + let [,,t] = args; + if (!this.checkTypeInLazyStore(evt, t)) { + fnSet.add(args) + } + } else { + if (!this.checkContentExist(args, fnSet)) { + this.logger('addToStore', `insert new`, args) + fnSet.add(args) + } + } + } else { // add straight to lazy store + fnSet.add(args) + } + store.set(evt, fnSet) + // this.logger('fnSet', fnSet, store) + return [store, fnSet.size] + } + + /** + * @param {array} args for compare + * @param {object} fnSet A Set to search from + * @return {boolean} true on exist + */ + protected checkContentExist(args: any[], fnSet: Map): boolean { + let list = this.mapToArr(fnSet) + return !!list + .filter((value: any, index: number, array: any[]): boolean => { + let [hash,] = value; + return hash === args[0]; + }).length; + } + + /** + * get the existing type to make sure no mix type add to the same store + * @param {string} evtName event name + * @param {string} type the type to check + * @return {boolean} true you can add, false then you can't add this type + */ + protected checkTypeInStore(evtName: string, type: string): boolean { + this.validateEvt(evtName, type) + let all = this.$get(evtName, true) + if (all === false) { + // pristine it means you can add + return true; + } + // it should only have ONE type in ONE event store + return !all + .filter((value: any, index: number, array: any[]): boolean => { + let [ ,,,t ] = value; + return type !== t; + }).length; + } + + /** + * This is checking just the lazy store because the structure is different + * therefore we need to use a new method to check it + * @param {string} evtName event name + * @param {string} type the types of allow event + * @return {boolean} true OK + */ + protected checkTypeInLazyStore(evtName: string, type: string): boolean { + this.validateEvt(evtName, type) + let store = this.lazyStore.get(evtName) + if (store) { + this.logger('checkTypeInLazyStore', store) + return !!this.mapToArr(store) + .filter((value: any, index: number, array: any[]): boolean => { + let [,,t] = value; + return t !== type; + }).length + } + this.logger(`Store ${evtName} is empty`) + return false; + } + + /** + * wrapper to re-use the addToStore, + * V1.3.0 add extra check to see if this type can add to this evt + * @param {string} evt event name + * @param {string} type on or once + * @param {function} callback function + * @param {object} context the context the function execute in or null + * @return {number|boolean} size of the store, or false when the check type failed + */ + protected addToNormalStore(evt: string, type: any, callback: myCallbackType, context: any = null): number | boolean { + // @TODO we need to check the existing store for the type first! + if (this.checkTypeInStore(evt, type)) { + this.logger(`addToNormalStore: ${type} can add to "${evt}" store`) + let key = hashFnToKey(callback) + let args = [this.normalStore, evt, key, callback, context, type] + let [_store, size] = Reflect.apply(this.addToStore, this, args) + // @BUG? is this the problem because it was a setter before + this.normalStore = _store; + // this.logger('after add to store', this.normalStore) + return size; + } + this.logger('addToNormalStore:', evt, type, 'NOT add to normal store', callback.toString()) + return false; + } + + /** + * Add to lazy store this get calls when the callback is not register yet + * so we only get a payload object or even nothing + * @param {string} evt event name + * @param {array} payload of arguments or empty if there is none + * @param {object} [ctx=null] the context the callback execute in + * @param {string|boolean} [type=false] register a type so no other type can add to this evt + * @return {number} size of the store + */ + protected addToLazyStore(evt: string, payload: any[] = [], ctx: any = null, type: string|boolean): number { + // this is add in V1.6.0 + // when there is type then we will need to check if this already added in lazy store + // and no other type can add to this lazy store + let args = []; + args.push(this.lazyStore, evt, this.toArray(payload), ctx) + if (type) { + args.push(type) + } + let [_store, size] = Reflect.apply(this.addToStore, this, args) + // @BUG ? this was a setter + this.lazyStore = _store; + return size; + } + + // VALIDATORS // + // This is another JOKE about Typescript, after you spend all this time on + // fixing the non-error (aka typing) it doesn't do anything during run time + // so what the fuck? + + /** + * validate the event name + * @param {string} evt event name + * @return {boolean} true when OK + */ + protected validateEvt(...args: string[]): boolean { + args.forEach((arg: string) => { + if (typeof arg !== 'string') { + throw new Error(`event name must be string type! ${arg}`) + } + }) + return true; + } + + /** + * Simple quick check on the two main parameters + * @param {function} callback function to call + * @param {string} evt event name + * @return {boolean} true when OK + */ + protected validate(callback: myCallbackType, ...args: string[]): boolean { + if (typeof callback === 'function') { + return Reflect.apply(this.validateEvt, this, args) + } + throw new Error(`callback required to be function type! ${callback}`) + } + + /** + * Check if this type is correct or not added in V1.5.0 + * @param {string} type for checking + * @return {boolean} true on OK + */ + protected validateType(type: string): boolean { + const types = ['on', 'only', 'once', 'onlyOnce'] + return !!types + .filter( + (t: any): boolean => type === t + ).length; + } + + +} diff --git a/packages/event/tests/basic.test.js b/packages/event/tests/basic.test.js index d7f37a6024f9b9cf6d59e0a9ff343367124aff94..139c4e020e058714a5ba90c5f52cad040b4f9edb 100644 --- a/packages/event/tests/basic.test.js +++ b/packages/event/tests/basic.test.js @@ -11,7 +11,7 @@ test.before( t => { t.context.evtSrv = new JsonqlEventService(logger) }) -test.skip('It should able to validate the evt', t => { +test('It should able to validate the evt', t => { let evtSrv = t.context.evtSrv; let fn = (...args) => Reflect.apply(evtSrv.$on, evtSrv, args) diff --git a/packages/event/tests/once-problem.test.js b/packages/event/tests/once-problem.test.js index 88cb5f0e723c6b77d981077fb00c8af72ffdff1c..84145629ff8bc3f1d6c9722c8ffc284d67199729 100644 --- a/packages/event/tests/once-problem.test.js +++ b/packages/event/tests/once-problem.test.js @@ -53,7 +53,7 @@ test.cb('$once should allow to add more than one listner', t => { }) -test.only('It should be fixed with the check type before adding to the store, but the $done value can be unpredictable', t => { +test('It should be fixed with the check type before adding to the store, but the $done value can be unpredictable', t => { let evtName = 'once-problem'; let evtSrv = t.context.evtSrv; @@ -77,7 +77,7 @@ test.only('It should be fixed with the check type before adding to the store, bu t.is(evtSrv.$done, 1200) evtSrv.$trigger(evtName, 2000) - // but this work as expecte because the order of adding to it + // but this work as expected because the order of adding to it // where the last one was taken out from the lazyStore t.is(evtSrv.$done, 2600) diff --git a/packages/event/tsconfig.json b/packages/event/tsconfig.json index 5909d0cbad43154552e23662ccabd5e41f6e9465..37a8428ccde73cbe04741899b21166bc35ceb512 100644 --- a/packages/event/tsconfig.json +++ b/packages/event/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */