diff --git a/packages/client/package.json b/packages/client/package.json old mode 100755 new mode 100644 index 96eb533b984010cf92632706365d1cb874ae1cc4..16cdbc9c809565dc9861cdd095d8c859ea584512 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,10 +1,10 @@ { - "name": "jsonql-client", - "version": "1.2.0-beta.4", - "description": "json:ql client package for javascript using superagent", + "name": "@jsonql/client", + "version": "1.0.0-alpha.1", + "description": "jsonql combine client for http / ws develop with Typescript", "main": "index.js", - "module": "src/index.js", - "browser": "lib/jsonql-client.js", + "module": "src/index.ts", + "browser": "dist/jsonql-client.js", "scripts": { "test": "npm run build && DEBUG=jsonql* node ./tests/browser/run-qunit.js", "prepare": "npm run build", @@ -37,10 +37,7 @@ "index.js", "oop.js" ], - "author": "to1source ", - "contributors": [ - "NEWBRAN LTD " - ], + "author": "Joel Chu ", "repository": { "type": "git", "url": "git+ssh://git@gitee.com:to1source/jsonql.git" diff --git a/packages/event/.gitignore b/packages/event/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a761871b6ef38bd19a7f27618fb05cfe9fead1e3 --- /dev/null +++ b/packages/event/.gitignore @@ -0,0 +1 @@ +.rpt2_cache/ diff --git a/packages/event/README.md b/packages/event/README.md new file mode 100644 index 0000000000000000000000000000000000000000..72daea5fb127a144cee2afa6e066ec2ae4442c5c --- /dev/null +++ b/packages/event/README.md @@ -0,0 +1,177 @@ +# @jsonql/event + +This is ported from [nb-event-service]() and rewritten with Typescript + +The different is - no alias options + +## Installation + +You don't usually use this directly, this is part of the `@jsonql` modules. + +But it's design to 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 + +```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 ... +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. + +For the other that use node build tool, just do this: + +```js +import { JsonqlEventService } from '@jsonql/event' +``` + +## API + + +#### $on(eventName, callback, context) + +* eventName (string) The event name you want to handle. You can call this multiple times to add different listeners +* callback (function) it will receive the `params` that call +* context (object|null) optional, we will pass it like this `Reflect.apply(callback, context, args)` + +It will return the total number of events that get registered. + +#### $once(eventName , callback, context) + +* eventName (string) the event you want to listen to once, you can call this more than once to add more listener +* callback (function) it will receive the `params` that call +* context (object|null) optional same as above + +$once allow you to bind one or more listener to the same event. But once this event fired (triggered) +it will remove itself from the event store, and no longer available. This behavior is changed in V1.3.0. + +There is a potential problem with $once you can see below. It's no really a bug per se, but due to our +own unique feature that can call event before even it existed (yeah, it's magic) + + +```js +// trigger event before it register with a handler +ee.$trigger('someEvent') +// now it register with a regular $on +ee.$on('someEvent', function() { + console.log('call me second') +}) +// but some where else you try to register it with $once +ee.$once('someEvent', function() { + console.log('call me first') +}) +``` + +In v1.3.0 we change the behavior of $once, now you can register more than one handler. +But if you look at the above example, you register it with `$on` then `$once`. + +What happen is, the `$once` call execute the `$trigger` from the earlier call, then it will +remove this event from the event handler store. Therefore, you `$on` will never fire again. + +So you have to make sure which event you **REALLY** want to register with what. + +#### $only(eventName , callback, context) + +This is a new method in v1.3.0 + +* eventName (string) the event you want to listen to once, this is first come first serve, and only **ONE** listener +* callback (function) it will receive the `params` that call +* context (object|null) optional same as above + +Example: + +```js +es.$only('only-event', function(message) { + console.log('ONLY', message) +}) +// now if you try to add another +es.$only('only-event', function(message) { + console.log('AGAIN', message) +}) + +// execute it +es.$trigger('only-event', 'A little cat jumping through the window') + +``` + +You will only get `ONLY A little cat jumping through the window` but the second callback never add to the event store. +Although we develop this feature purposely for our other library to use, but it has a lot real world usage. + +#### $onlyOnce(eventName , callback, context) + +Just like what it said on the tin; its `$only` + `$once`. You should able to figure out what it does. + +#### $off(eventName) + +* eventName (string) event to remove from internal store + +It will return + +* true - event been clear +* false - such even doesn't exist + +#### $replace(eventName, callback, context = null, type = 'on') + +This is `$off` + event register function + +Type can be `on`, `only`, `once`, `onlyOnce` default value is `on` + +#### $trigger(eventName, params , context, type) + +* eventName (string) this will trigger the callback that register with this `eventName` whether that actually exist or not +* params (mixed) optional - data you want to pass to your callback method +* context (object || null) optional - When we execute the callback, we will add this context to the `Reflect.apply` or default to null +* type (string) available types are `on`, `only`, `once`, `onlyOnce` this is for trigger event before it get register and prevent other type to register it + +This method will return + +* false - if there is nothing to call +* i - the total events been called + +#### $call(eventName, params, type, context) + +* eventName (string) this will trigger the callback that register with this `eventName` whether that actually exist or not +* params (mixed) optional - data you want to pass to your callback method +* type (string) available types are `on`, `only`, `once`, `onlyOnce` this is for trigger event before it get register and prevent other type to register it +* context (object || null) optional - When we execute the callback, we will add this context to the `Reflect.apply` or default to null + +This basically it's a shorthand of `$trigger` if you know that your callback only execute in `null` and purposely register a type to prevent +other to register it later + +This is useful shorthand, also trigger event before its register + +Example: + +```js +// call before event register +es.$call('some-event', 1001, 'only') +// now try to register it with a different event handler +es.$on('some-event', function(num) { + return ++num; +}) +// it will throw Error that tells you it has been register with `only` type already + +``` + +#### $get(evt) + +* return all the listeners for that particular event name from the internal store. Handy for debug. + +Or it will return `false` if there is nothing + + + +--- + +ISC + +[Joel Chu](https://joelchu.com) (c) 2019 - [NEWBRAN LTD UK](https://newbran.ch) / [TO1SOURCE CN](https://to1source.cn) diff --git a/packages/event/dist/jsonql-event-service.cjs.js b/packages/event/dist/jsonql-event-service.cjs.js new file mode 100644 index 0000000000000000000000000000000000000000..65d55bfd8aec44cca774b98d1cefba81d43c384d --- /dev/null +++ b/packages/event/dist/jsonql-event-service.cjs.js @@ -0,0 +1,547 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +/** + * 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) + ""; +} + +// 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; +}()); + +exports.JsonqlEventService = JsonqlEventService; diff --git a/packages/event/dist/jsonql-event-service.es.js b/packages/event/dist/jsonql-event-service.es.js new file mode 100644 index 0000000000000000000000000000000000000000..9c96af46f80ae140bdab0e64852894ed5e6a0b6c --- /dev/null +++ b/packages/event/dist/jsonql-event-service.es.js @@ -0,0 +1,543 @@ +/** + * 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) + ""; +} + +// 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; +}()); + +export { JsonqlEventService }; diff --git a/packages/event/dist/jsonql-event-service.umd.js b/packages/event/dist/jsonql-event-service.umd.js new file mode 100644 index 0000000000000000000000000000000000000000..a16292687160826d05b6d590539826f84682a191 --- /dev/null +++ b/packages/event/dist/jsonql-event-service.umd.js @@ -0,0 +1,553 @@ +(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 = {})); +}(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) + ""; + } + + // 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; + }()); + + exports.JsonqlEventService = JsonqlEventService; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/packages/event/index.ts b/packages/event/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..879d21b2b45dbe95afcc7785ac1c2bd5098ba64e --- /dev/null +++ b/packages/event/index.ts @@ -0,0 +1,5 @@ +// The top level for @jsonql/event + +// The class file has no default export therefore here you will ended up with named export +export * from './src/event-service'; +// what a mess! you can just do a direct export! if you use default you ended up with a stupid .default name diff --git a/packages/event/package.json b/packages/event/package.json new file mode 100644 index 0000000000000000000000000000000000000000..838760855d38f93ccc5aa2befd6448edb02ad175 --- /dev/null +++ b/packages/event/package.json @@ -0,0 +1,61 @@ +{ + "name": "@jsonql/event", + "version": "1.0.0", + "description": "Ported from nb-event-service rewritten with Typescript", + "main": "dist/jsonql-event-service.cjs.js", + "browser": "dist/jsonql-event-service.umd.js", + "module": "dist/jsonql-event-service.es.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "index.ts" + ], + "scripts": { + "test": "DEBUG=nb-event-service* ava --verbose", + "build": "rollup -c", + "dev": "rollup -cw", + "test:basic": "DEBUG=nb-event-service* ava ./tests/basic.test.js", + "test:browser": "node ./tests/fixtures/browser.js" + }, + "keywords": [ + "jsonql", + "event", + "nb-event-service", + "eventEmitter" + ], + "author": "Joel Chu ", + "repository": { + "type": "git", + "url": "git+ssh://git@gitee.com:to1source/jsonql.git" + }, + "license": "ISC", + "devDependencies": { + "ava": "^2.2.0", + "debug": "^4.1.1", + "rollup": "^1.16.7", + "rollup-plugin-typescript2": "^0.22.0", + "server-io-core": "^1.2.0-beta.2", + "ts-node": "^8.3.0", + "typescript": "^3.5.3" + }, + "dependencies": {}, + "ava": { + "files": [ + "tests/*.test.js", + "!tests/helpers/*.*", + "!tests/fixtures/*.*" + ], + "cache": true, + "concurrency": 5, + "failFast": true, + "failWithoutAssertions": false, + "tap": false, + "compileEnhancements": false, + "extensions": [ + "ts" + ], + "require": [ + "ts-node/register" + ] + } +} diff --git a/packages/event/rollup.config.js b/packages/event/rollup.config.js new file mode 100644 index 0000000000000000000000000000000000000000..7220fd531708e666dff4e32ee449bd20d0f983e6 --- /dev/null +++ b/packages/event/rollup.config.js @@ -0,0 +1,30 @@ +import typescript from 'rollup-plugin-typescript2' +import pkg from './package.json' + +export default { + input: 'index.ts', + output: [ + { + file: pkg.main, + format: 'cjs' + }, + { + file: pkg.module, + format: 'es' + }, + { + file: pkg.browser, + format: 'umd', + name: 'JsonqlEventService' + } + ], + external: [ + ...Object.keys(pkg.depedencies || {}), + ...Object.keys(pkg.peerDependencies || {}) + ], + plugins: [ + typescript({ + typescript: require('typescript') + }) + ] +} diff --git a/packages/event/src/event-service.js b/packages/event/src/event-service.js new file mode 100644 index 0000000000000000000000000000000000000000..a75d6c0559dbc765258d9d27a07a0b7f2b595c79 --- /dev/null +++ b/packages/event/src/event-service.js @@ -0,0 +1,598 @@ +// this is the new implementation without the hash key +// only using Map and Set instead +import { + NB_EVENT_SERVICE_PRIVATE_STORE, + NB_EVENT_SERVICE_PRIVATE_LAZY +} from './store' +import genHaskKey from './hash-code.js' +// export +export default class EventService { + /** + * class constructor + */ + constructor(config = {}) { + if (config.logger && typeof config.logger === 'function') { + this.logger = config.logger; + } + this.keep = config.keep; + this.suspend = false; + // for the $done setter + this.result = config.keep ? [] : null; + // we need to init the store first otherwise it could be a lot of checking later + this.normalStore = new Map() + this.lazyStore = new Map() + } + + /** + * logger function for overwrite + */ + logger() {} + + ////////////////////////// + // PUBLIC METHODS // + ////////////////////////// + + /** + * 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 + */ + $on(evt , callback , context = null) { + const type = 'on'; + this.validate(evt, callback) + // first need to check if this evt is in lazy store + let 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 + let size = 0; + lazyStoreContent.forEach(content => { + let [ payload, ctx, t ] = content; + 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) + size += this.addToNormalStore(evt, type, callback, context || ctx) + }) + 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 {boolean} result + */ + $once(evt , callback , context = null) { + this.validate(evt, callback) + const type = 'once'; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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) + const list = Array.from(lazyStoreContent) + // should never have more than 1 + const [ payload, ctx, t ] = list[0] + 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) + } + } + + /** + * 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 + */ + $only(evt, callback, context = null) { + this.validate(evt, callback) + const type = 'only'; + let added = false; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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`) + const list = Array.from(lazyStoreContent) + // $only allow to trigger this multiple time on the single handler + list.forEach( l => { + const [ payload, ctx, t ] = l; + 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 {void} + */ + $onlyOnce(evt, callback, context = null) { + this.validate(evt, callback) + const type = 'onlyOnce'; + let added = false; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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) + const list = Array.from(lazyStoreContent) + // should never have more than 1 + const [ payload, ctx, t ] = list[0] + 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 {} + */ + $replace(evt, callback, context = null, type = 'on') { + if (this.validateType(type)) { + this.$off(evt) + let method = this['$' + type] + return Reflect.apply(method, this, [evt, callback, context]) + } + throw new Error(`${type} is not supported!`) + } + + /** + * 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 + */ + $trigger(evt , payload = [] , context = null, type = false) { + this.validateEvt(evt) + let found = 0; + // first check the normal store + let nStore = this.normalStore; + this.logger('$trigger', nStore) + if (nStore.has(evt)) { + this.logger('$trigger', evt, 'found') + let nSet = Array.from(nStore.get(evt)) + let ctn = nSet.length; + let hasOnce = false; + let hasOnly = false; + for (let i=0; i < ctn; ++i) { + ++found; + // this.logger('found', found) + let [ _, callback, ctx, type ] = nSet[i] + this.run(callback, payload, context || ctx) + if (type === 'once' || type === '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 + */ + $call(evt, params, type = false, context = null) { + let 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 + */ + $off(evt) { + this.validateEvt(evt) + let stores = [ this.lazyStore, this.normalStore ] + let found = false; + stores.forEach(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 + */ + $get(evt, full = false) { + this.validateEvt(evt) + let store = this.normalStore; + if (store.has(evt)) { + return Array + .from(store.get(evt)) + .map( l => { + if (full) { + return l; + } + let [key, callback, ] = l; + return callback; + }) + } + return false; + } + + /** + * Holding off all the event firing and put them back into the lazy store + * until the suspend been lifted + * @param {string} [type=all] what type of event should be suspended + * @return {void} + */ + $suspend(type = 'all') { + this.suspend = type === 'all' ? true : this.validateType(type); + } + + /** + * Lifted the suspend + * @return {void} + */ + $resume() { + this.suspend = false; + } + + /** + * store the return result from the run + * @param {*} value whatever return from callback + */ + set $done(value) { + 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() { + if (this.keep) { + this.logger(this.result) + return this.result[this.result.length - 1] + } + return this.result; + } + + ///////////////////////////// + // PRIVATE METHODS // + ///////////////////////////// + + /** + * validate the event name + * @param {string} evt event name + * @return {boolean} true when OK + */ + validateEvt(evt) { + if (typeof evt === 'string') { + return true; + } + throw new Error(`event name must be string type!`) + } + + /** + * Simple quick check on the two main parameters + * @param {string} evt event name + * @param {function} callback function to call + * @return {boolean} true when OK + */ + validate(evt, callback) { + if (this.validateEvt(evt)) { + if (typeof callback === 'function') { + return true; + } + } + throw new Error(`callback required to be function type!`) + } + + /** + * Check if this type is correct or not added in V1.5.0 + * @param {string} type for checking + * @return {boolean} true on OK + */ + validateType(type) { + const types = ['on', 'only', 'once', 'onlyOnce'] + return !!types.filter(t => type === t).length; + } + + /** + * 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 + */ + run(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 + * @param {string} [storeName = lazyStore] name of store + * @return {object|boolean} content or false on not found + */ + takeFromStore(evt, storeName = 'lazyStore') { + let store = this[storeName]; // it could be empty at this point + if (store) { + this.logger('takeFromStore', storeName, store) + if (store.has(evt)) { + let content = store.get(evt) + this.logger('takeFromStore', content) + store.delete(evt) + return content; + } + return false; + } + throw new Error(`${storeName} is not supported!`) + } + + /** + * 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 + */ + addToStore(store, evt, ...args) { + 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 + */ + checkContentExist(args, fnSet) { + let list = Array.from(fnSet) + return !!list.filter(l => { + let [hash,] = l; + if (hash === args[0]) { + return true; + } + return false; + }).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 + */ + checkTypeInStore(evtName, type) { + this.validateEvt(evtName) + this.validateEvt(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(list => { + let [ ,,,t ] = list; + 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 + */ + checkTypeInLazyStore(evtName, type) { + this.validateEvt(evtName) + this.validateEvt(type) + let store = this.lazyStore.get(evtName) + this.logger('checkTypeInLazyStore', store) + if (store) { + return !!Array + .from(store) + .filter(l => { + let [,,t] = l; + 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} size of the store + */ + addToNormalStore(evt, type, callback, 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`) + let key = this.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} [context=null] the context the callback execute in + * @param {string} [type=false] register a type so no other type can add to this evt + * @return {number} size of the store + */ + addToLazyStore(evt, payload = [], context = null, type = false) { + // 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 = [this.lazyStore, evt, this.toArray(payload), context] + if (type) { + args.push(type) + } + let [_store, size] = Reflect.apply(this.addToStore, this, args) + this.lazyStore = _store; + return size; + } + + /** + * make sure we store the argument correctly + * @param {*} arg could be array + * @return {array} make sured + */ + toArray(arg) { + return Array.isArray(arg) ? arg : [arg]; + } + + /** + * setter to store the Set in private + * @param {object} obj a Set + */ + set normalStore(obj) { + NB_EVENT_SERVICE_PRIVATE_STORE.set(this, obj) + } + + /** + * @return {object} Set object + */ + get normalStore() { + return NB_EVENT_SERVICE_PRIVATE_STORE.get(this) + } + + /** + * setter to store the Set in lazy store + * @param {object} obj a Set + */ + set lazyStore(obj) { + NB_EVENT_SERVICE_PRIVATE_LAZY.set(this , obj) + } + + /** + * @return {object} the lazy store Set + */ + get lazyStore() { + return NB_EVENT_SERVICE_PRIVATE_LAZY.get(this) + } + + /** + * generate a hashKey to identify the function call + * The build-in store some how could store the same values! + * @param {function} fn the converted to string function + * @return {string} hashKey + */ + hashFnToKey(fn) { + return genHaskKey(fn.toString()) + ''; + } + +} diff --git a/packages/event/src/event-service.ts b/packages/event/src/event-service.ts new file mode 100644 index 0000000000000000000000000000000000000000..679ec3f796ec71c8f6f49446beeaffda0bed71e5 --- /dev/null +++ b/packages/event/src/event-service.ts @@ -0,0 +1,540 @@ +// The main class +import './interfaces.ts'; +import hashFnToKey from './hash-code'; +// main +export class JsonqlEventService { + // public props + keep: boolean; + // private props + private normalStore: Map; + private lazyStore: Map; + private logger: any; + private result: any[]; + + /** + * Create EventService instance + * @param {configObj} config configuration object + * @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 = []; + } + + /** + * 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 + */ + public $on(evt: string, callback: myCallbackType, context: any = null): any { + const type = 'on'; + // first need to check if this evt is in lazy store + let 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 + let size = 0; + lazyStoreContent.forEach((content: any[]): void => { + let [ payload, ctx, t ] = content; + 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) + let 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 + */ + public $once(evt: string , callback: myCallbackType , context: any = null): any { + const type = 'once'; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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) + const list = this.mapToArr(lazyStoreContent) + // should never have more than 1 + const [ payload, ctx, t ] = list[0] + 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 + */ + public $only(evt: string, callback: myCallbackType, context: any = null): boolean { + 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 + let added: any = false; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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`) + const list = this.mapToArr(lazyStoreContent) + // $only allow to trigger this multiple time on the single handler + list.forEach( l => { + const [ payload, ctx, t ] = l; + 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 + */ + public $onlyOnce(evt: string, callback: myCallbackType, context: any = null): boolean { + const type = 'onlyOnce'; + let added: any = false; + let lazyStoreContent = this.takeFromStore(evt) + // this is normal register before call $trigger + let 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) + const list = this.mapToArr(lazyStoreContent) + // should never have more than 1 + const [ payload, ctx, t ] = list[0] + 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 + */ + public $replace(evt: string, callback: myCallbackType, context: any = null, type: string = 'on'): any { + this.$off(evt) + // this will become a problem + let 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 + */ + public $trigger(evt: string , payload: any[] = [] , context: any = null, type: string|boolean = false): number { + let found = 0; + // first check the normal store + let nStore = this.normalStore; + this.logger('$trigger', nStore) + if (nStore.has(evt)) { + this.logger('$trigger', evt, 'found') + let nSet = this.mapToArr(nStore.get(evt)) + let ctn = nSet.length; + let hasOnce = false; + let hasOnly = false; + for (let i=0; i < ctn; ++i) { + ++found; + // this.logger('found', found) + let [ _, callback, ctx, type ] = nSet[i] + this.run(callback, payload, context || ctx) + if (type === 'once' || type === '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 + */ + public $call(evt: string, params: any[], type: any = false, context: any = null): any { + let 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 + */ + public $off(evt: string): boolean { + let stores = [ this.lazyStore, this.normalStore ] + let found = false; + stores.forEach(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 + */ + 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 { + 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 + */ + 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/hash-code.ts b/packages/event/src/hash-code.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b1cc8a8d2f4fe590b92dede7b4343baa11bb280 --- /dev/null +++ b/packages/event/src/hash-code.ts @@ -0,0 +1,10 @@ +/** + * 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 + */ +export default function hashCode(fn: Function): String { + let s = fn.toString() + return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0) + "" +} diff --git a/packages/event/src/interfaces.ts b/packages/event/src/interfaces.ts new file mode 100644 index 0000000000000000000000000000000000000000..82154f0104aec72bd087d855c5a3c339f851d0e8 --- /dev/null +++ b/packages/event/src/interfaces.ts @@ -0,0 +1,14 @@ +// declare all the interfaces here +// then re-use inside the class +interface myCallbackType { + (myArgument: string): void +} + +/* +@TODO +interface configObj { + logger?: any, // this is the shitty thing about TS + keep? : Boolean, + suspend?: Boolean +} +*/ diff --git a/packages/event/tests/basic.test.js b/packages/event/tests/basic.test.js new file mode 100644 index 0000000000000000000000000000000000000000..d7f37a6024f9b9cf6d59e0a9ff343367124aff94 --- /dev/null +++ b/packages/event/tests/basic.test.js @@ -0,0 +1,150 @@ +const test = require('ava') +// import the cjs version for testing +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:basic') +let value = 1000; + +debug(JsonqlEventService) + +test.before( t => { + t.context.evtSrv = new JsonqlEventService(logger) +}) + +test.skip('It should able to validate the evt', t => { + let evtSrv = t.context.evtSrv; + let fn = (...args) => Reflect.apply(evtSrv.$on, evtSrv, args) + + t.throws(() => fn('some', false) , Error, 'Should throw error because callback is not a function') +}) + +test.cb('It should able to bind a simple test and callback', t => { + t.plan(1) + let evtName = 'simple' + t.context.evtSrv.$on(evtName, function(num) { + t.is(num, value) + t.end() + }) + t.context.evtSrv.$trigger(evtName, value) +}) + +test.cb('It should able to emit the event before register the listener', t => { + t.plan(1) + let evtName = 'simple-reverse' + + t.context.evtSrv.$trigger(evtName, value) + + t.context.evtSrv.$on(evtName, function(num) { + t.is(num, value) + t.end() + }) +}) + +test.cb('It should able to add more than one listerner to the same event', t => { + t.plan(2) + + let evtName = 'multiple' + let letter = 'again' + + t.context.evtSrv.$on(evtName, function(a) { + t.is(a , letter) + }) + + t.context.evtSrv.$on(evtName, function(b) { + t.is(b, letter) + t.end() + }) + + t.context.evtSrv.$trigger(evtName, letter) + +}) + +test('It should not allow to add the same function again', t => { + let evtName = 'add-once' + const callback = (x) => { + debug(x) + } + + t.context.evtSrv.$on(evtName, callback) + t.context.evtSrv.$on(evtName, callback) + + let ctn = t.context.evtSrv.$get(evtName) + + t.is(ctn.length, 1) +}) + +test('It should only call once if we use the $once option', t => { + let evtName = 'once-call' + let ctn = 0; + + const callback = () => { + ++ctn; + debug(ctn) + } + + const callback2 = () => { + ++ctn; + debug(ctn) + } + + t.context.evtSrv.$once(evtName, callback) + t.context.evtSrv.$once(evtName, callback2) + + t.context.evtSrv.$trigger(evtName) + t.context.evtSrv.$trigger(evtName) + + t.is(ctn, 1) + +}) + +test('Using the $call alias to $trigger should do the same thing', t => { + let evtName = 'alias' + let ctn = 0; + + const callback = () => { + ++ctn; + debug(ctn) + } + t.context.evtSrv.$once(evtName, callback) + t.context.evtSrv.$trigger(evtName) + t.context.evtSrv.$call(evtName) + + t.is(ctn, 1) +}) + +test('Using $trigger and $call should make the callback run again', t => { + let evtName = 'alias-two' + let ctn = 0; + + const callback = () => { + ++ctn; + debug(ctn) + } + + t.context.evtSrv.$trigger(evtName) + t.context.evtSrv.$call(evtName) + + t.context.evtSrv.$on(evtName, callback) + + t.is(ctn, 2) + + t.pass() +}) + +test('Should not able to call the method once the $off is called', t => { + + let evtName = 'off-event' + + const callback = (l) => { + debug(`${l}`) + } + + t.context.evtSrv.$on(evtName, callback) + + t.context.evtSrv.$off(evtName) + + t.context.evtSrv.$trigger(evtName) + + t.false(t.context.evtSrv.$get(evtName)) + +}) diff --git a/packages/event/tests/fixtures/browser.js b/packages/event/tests/fixtures/browser.js new file mode 100644 index 0000000000000000000000000000000000000000..5eaf04770fb624afd51c35731e126d61e46bf625 --- /dev/null +++ b/packages/event/tests/fixtures/browser.js @@ -0,0 +1,9 @@ +const serverIoCore = require('server-io-core') +const { join } = require('path') + +serverIoCore({ + webroot: [ + join(__dirname), + join(__dirname, '..', '..', 'dist') + ] +}) diff --git a/packages/event/tests/fixtures/index.html b/packages/event/tests/fixtures/index.html new file mode 100644 index 0000000000000000000000000000000000000000..1006f42d008ff620e27cc663fbb55d60e2d256fe --- /dev/null +++ b/packages/event/tests/fixtures/index.html @@ -0,0 +1,25 @@ + + + + JsonqlEventService test in browser + + +

JsonqlEventService test in browser

+

+ Just look at the console log +

+ + + + diff --git a/packages/event/tests/lazy.test.js b/packages/event/tests/lazy.test.js new file mode 100644 index 0000000000000000000000000000000000000000..a38f8cef942986d7b33ac20ba02ffb69ce43a123 --- /dev/null +++ b/packages/event/tests/lazy.test.js @@ -0,0 +1,55 @@ +// testing the lazy store with a type using the context parameter +const test = require('ava') + +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:lazy') + +test.before(t => { + t.context.evtSrv = new JsonqlEventService({ + logger + }) +}) + +test('when using the context as type in lazy store, other type should not able to add to lazystore', t => { + let es = t.context.evtSrv; + let evt = 'not-here-yet' + // first we trigger an non-exist event + + es.$trigger(evt, 1, null, 'on') + es.$trigger(evt, 2, null, 'only') + + // now the lazy store should have only one item + let content = es.takeFromStore(evt) + + debug(content) + + t.is(content.size, 1) +}) + +test('It should throw an error if the event been trigger with one type but try to register with another', t => { + let es = t.context.evtSrv; + let evt = 'some-event' + + es.$trigger(evt, 1, null, 'on') + + const fn = (num) => es.$only(evt, num => { + debug(num) + }) + + t.throws(() => fn(), Error, 'It should throw error because its different type') +}) + +test('using $call should able to pass the type without passing the context', t => { + let es = t.context.evtSrv; + let evt = 'just-calling' + + es.$call(evt, 100, 'only') + + const fn = (num) => es.$on(evt, num => { + debug(num) + }) + + t.throws(() => fn(), Error, 'It should throw error because already register with $only') + +}) diff --git a/packages/event/tests/map-set.test.js b/packages/event/tests/map-set.test.js new file mode 100644 index 0000000000000000000000000000000000000000..fcadf1c4d9545491f96cf30b0fff71edea95e527 --- /dev/null +++ b/packages/event/tests/map-set.test.js @@ -0,0 +1,108 @@ +// testing the using Map and Set +const test = require('ava') +const debug = require('debug')('nb-event-service:test:map-set') + +test('A Set should not allow the same thing add twice', t => { + let ctn = 0; + let testSet = new Set() + const testFn = () => { + debug(`call me ${++ctn}`) + } + // problem - can only store primitive type that id is the same + testSet.add(testFn) + testSet.add(testFn) + t.is(testSet.size, 1) +}) + +test('It should able to store a Set inside a Map', t => { + let ctn = 0; + const testFnA = () => { + debug(`call A ${++ctn}`) + } + + const testFnB = () => { + debug(`call B ${++ctn}`) + } + + let testMap = new Map() + let testSet = new Set() + + testMap.set('key-1', testSet) + let ts1 = testMap.get('key-1') + ts1.add(testFnA) + ts1.add(testFnB) + + t.is(testMap.get('key-1').size, 2) + + for (let fn of ts1) { + fn() + } + t.pass() +}) + +test('It should able to store multiple level within a class structure and act like a private property', t => { + const testFnB = () => { + debug(`call B ${++ctn}`) + } + + let PRIVATE_STORE = new Map() + class Dummy { + + add(item) { + PRIVATE_STORE.set(this, item) + } + + get() { + return PRIVATE_STORE.get(this) + } + } + + let dummyIns = new Dummy() + + let mapA = new Map() + let toAdd1 = new Set() + mapA.set('a', toAdd1) + let toAdd1a = mapA.get('a') + toAdd1a.add(testFnB) + toAdd1a.add(testFnB) + dummyIns.add(mapA) + + let dummyValue1 = dummyIns.get() + + t.is( dummyValue1.get('a').size, 1 ) + + t.pass() +}) + +test('It should store one item if its adding different thing to the same key', t => { + let ctn = 0; + const testFnA = () => { + debug(`call A ${++ctn}`) + } + const testFnB = () => { + debug(`call B ${++ctn}`) + } + let PRIVATE_STORE = new Map() + class Dummy { + add(item) { + PRIVATE_STORE.set(this, item) + } + get() { + return PRIVATE_STORE.get(this) + } + } + + let dummyInstance = new Dummy() + + let mapA = new Map(); + mapA.set('key-1', testFnA) + let mapB = new Map(); + mapB.set('key-2', testFnB) + + dummyInstance.add(mapA) + dummyInstance.add(mapB) + + t.truthy( dummyInstance.get().has('key-2') ) + + +}) diff --git a/packages/event/tests/once-problem.test.js b/packages/event/tests/once-problem.test.js new file mode 100644 index 0000000000000000000000000000000000000000..88cb5f0e723c6b77d981077fb00c8af72ffdff1c --- /dev/null +++ b/packages/event/tests/once-problem.test.js @@ -0,0 +1,84 @@ +const test = require('ava') + +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:only-problem') +let value = 1000; + +test.before( t => { + t.context.evtSrv = new JsonqlEventService(logger) +}) + +test('This is how $once normally works', t => { + // problem is when you call $trigger before you register it with $once + let evtName = 'once-normal'; + let evtSrv = t.context.evtSrv; + + evtSrv.$trigger(evtName, 1000) + + evtSrv.$once(evtName, function(val) { + debug(val) + return (val*0.1) + val; + }) + + t.is(evtSrv.$done, 1100, 'The first time call $done getter') + + evtSrv.$trigger(evtName) + // it should be the same because it never get call again + t.is(evtSrv.$done, 1100, 'The second time call $done getter') + +}) + +test.cb('$once should allow to add more than one listner', t => { + t.plan(3) + let evtName = 'more-once'; + let evtSrv = t.context.evtSrv; + + evtSrv.$once(evtName, function() { + debug('$once', 'First listener') + t.pass() + return 1; + }) + + evtSrv.$once(evtName, function() { + debug('$once', 'Second listener') + t.pass() + t.end() + return 2; + }) + + evtSrv.$call(evtName) + + t.is(evtSrv.$done, 2) + +}) + +test.only('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; + + evtSrv.$trigger(evtName, 1000) + + evtSrv.$on(evtName, function(val) { + return (val*0.2) + val; + }) + // this should not add + evtSrv.$once(evtName, function(val) { + return (val*0.1) + val; + }) + + evtSrv.$on(evtName, function(val) { + return (val*0.3) + val; + }) + + // now the first $on call hijacked the evt + // I thought it will be 1300 but its 1200 + t.is(evtSrv.$done, 1200) + + evtSrv.$trigger(evtName, 2000) + // but this work as expecte 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/tests/only-once.test.js b/packages/event/tests/only-once.test.js new file mode 100644 index 0000000000000000000000000000000000000000..390548b1c42a549145dc2ce66b28bfd12ac90be5 --- /dev/null +++ b/packages/event/tests/only-once.test.js @@ -0,0 +1,60 @@ +const test = require('ava') + +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:only-once') +let value = 2000; + +test.before(t => { + t.context.evtSrv = new JsonqlEventService(logger) +}) + +test('It should only add one callback and fire once with $onlyOnce', t => { + + let evtSrv = t.context.evtSrv; + let evt = 'only-once-evt' + evtSrv.$onlyOnce(evt, function() { + debug('call me first') + return 'first' + }) + evtSrv.$onlyOnce(evt, function() { + debug('call me again') + return 'again' + }) + + evtSrv.$trigger(evt) + + t.is(evtSrv.$done, 'first') + + t.is(evtSrv.$trigger(evt), 0) + +}) + +test('It should only get trigger once with only callback', t => { + + let evtSrv = t.context.evtSrv; + let evt = 'only-once-evt-call' + + evtSrv.$trigger(evt) + + evtSrv.$onlyOnce(evt, function() { + debug('call me call me') + return 'me' + }) + + let list = evtSrv.$get(evt) + + t.falsy(list.length) + + evtSrv.$onlyOnce(evt, function() { + debug('call me too') + return 'too' + }) + + //evtSrv.$trigger(evt) + + t.is(evtSrv.$trigger(evt), 1) + + t.is(evtSrv.$done, 'too') + +}) diff --git a/packages/event/tests/only.test.js b/packages/event/tests/only.test.js new file mode 100644 index 0000000000000000000000000000000000000000..34ac64983aafac116547a2d506ccec9786e6ec95 --- /dev/null +++ b/packages/event/tests/only.test.js @@ -0,0 +1,70 @@ +const test = require('ava') + +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:only-problem') +let value = 1000; + +test.before( t => { + t.context.evtSrv = new JsonqlEventService(logger) +}) + +test('Test the check type method', t => { + + let evt = 'test-evt' + let evtSrv = t.context.evtSrv; + + t.is(evtSrv.checkTypeInStore(evt, 'on'), true) + + evtSrv.$on(evt, function() { + debug('call me') + }) + + t.is(evtSrv.checkTypeInStore(evt, 'on'), true) + + evtSrv.$on(evt, function() { + debug('call me again') + }) + + t.is(evtSrv.checkTypeInStore(evt, 'only'), false) +}) + +test('only should only allow to add one listner', t => { + let evt = 'test-only-evt' + let evtSrv = t.context.evtSrv; + evtSrv.$only(evt, function(num) { + return num + 10; + }) + evtSrv.$only(evt, function(num) { + return num * 10; + }) + evtSrv.$call(evt, 100) + t.is(evtSrv.$done, 110) +}) + +test('Test the trigger before call $only and see if that works the same', t => { + let ctn = 0; + let evt = 'test-only-reverse' + let evtSrv = t.context.evtSrv; + + evtSrv.$call(evt, 'x') + + let result1 = evtSrv.$only(evt, function(A) { + debug(A, ctn) + return ++ctn; + }) + + t.truthy(result1) + + evtSrv.$call(evt, 'y') + + let result2 = evtSrv.$only(evt, function(B) { + debug(B, ctn) + return --ctn; + }) + + t.false(result2) + + t.is(evtSrv.$done , 2) + +}) diff --git a/packages/event/tests/replace.test.js b/packages/event/tests/replace.test.js new file mode 100644 index 0000000000000000000000000000000000000000..a63540158b9b5462fa559b15fa6a99e92f42a7bf --- /dev/null +++ b/packages/event/tests/replace.test.js @@ -0,0 +1,39 @@ +const test = require('ava') + +const { JsonqlEventService } = require('../dist/jsonql-event-service.cjs') +const logger = require('debug')('nb-event-service') +const debug = require('debug')('nb-event-service:test:replace') + + +test.before(t => { + t.context.evtSrv = new JsonqlEventService(logger) +}) + +test('It should able to validate against the type', t => { + let evtSrv = t.context.evtSrv; + let wrongType = 'whatever' + let fn = (type) => evtSrv.$replace('some-event', () => {}, null, type) + + t.throws(() => fn(wrongType), Error, 'It should throw if we pass the wrong type') + +}) + + +test.cb('It should able to replace the event callback', t => { + let evtSrv = t.context.evtSrv; + let evt = 'same-event' + + evtSrv.$on(evt, (n) => { + debug('first callback', n) + }) + + evtSrv.$trigger(evt, 0) + + evtSrv.$replace(evt, (n) => { + t.is(n, 1) + t.end() + }) + + evtSrv.$trigger(evt, 1) + +}) diff --git a/packages/event/tsconfig.json b/packages/event/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..5909d0cbad43154552e23662ccabd5e41f6e9465 --- /dev/null +++ b/packages/event/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "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. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/client/README.md b/packages/http-client/README.md similarity index 100% rename from packages/client/README.md rename to packages/http-client/README.md diff --git a/packages/client/dist/jsonql-client.cls.js b/packages/http-client/dist/jsonql-client.cls.js similarity index 100% rename from packages/client/dist/jsonql-client.cls.js rename to packages/http-client/dist/jsonql-client.cls.js diff --git a/packages/client/dist/jsonql-client.cls.js.map b/packages/http-client/dist/jsonql-client.cls.js.map similarity index 100% rename from packages/client/dist/jsonql-client.cls.js.map rename to packages/http-client/dist/jsonql-client.cls.js.map diff --git a/packages/client/dist/jsonql-client.umd.js b/packages/http-client/dist/jsonql-client.umd.js similarity index 100% rename from packages/client/dist/jsonql-client.umd.js rename to packages/http-client/dist/jsonql-client.umd.js diff --git a/packages/client/dist/jsonql-client.umd.js.map b/packages/http-client/dist/jsonql-client.umd.js.map similarity index 100% rename from packages/client/dist/jsonql-client.umd.js.map rename to packages/http-client/dist/jsonql-client.umd.js.map diff --git a/packages/client/dist/jsonql-stores.js b/packages/http-client/dist/jsonql-stores.js similarity index 100% rename from packages/client/dist/jsonql-stores.js rename to packages/http-client/dist/jsonql-stores.js diff --git a/packages/client/dist/jsonql-stores.js.map b/packages/http-client/dist/jsonql-stores.js.map similarity index 100% rename from packages/client/dist/jsonql-stores.js.map rename to packages/http-client/dist/jsonql-stores.js.map diff --git a/packages/client/index.js b/packages/http-client/index.js similarity index 100% rename from packages/client/index.js rename to packages/http-client/index.js diff --git a/packages/client/index.ts b/packages/http-client/index.ts similarity index 100% rename from packages/client/index.ts rename to packages/http-client/index.ts diff --git a/packages/http-client/package.json b/packages/http-client/package.json new file mode 100755 index 0000000000000000000000000000000000000000..35484f6e2bf0be9b16c6ff6cacee277cdaab323a --- /dev/null +++ b/packages/http-client/package.json @@ -0,0 +1,105 @@ +{ + "name": "@jsonql/http-client", + "version": "1.0.0-beta.1", + "description": "json:ql client package for javascript using superagent", + "main": "index.js", + "module": "src/index.js", + "browser": "lib/jsonql-client.js", + "scripts": { + "test": "npm run build && DEBUG=jsonql* node ./tests/browser/run-qunit.js", + "prepare": "npm run build", + "test:node": "DEBUG=jsonql* ava --verbose", + "test:contract": "DEBUG=jsonql-* ava ./tests/contract-lock.test.js", + "test:browser": "npm run build && npm run run:browser", + "test:fn": "ava ./tests/fn.test.js", + "run:browser": "DEBUG=jsonql-* node ./tests/fixtures/run.js", + "start": "rollup -c -w --environment NODE_ENV:dev", + "build": "npm run build:production && npm run build:oop", + "build:umd": "rollup -c", + "build:production": "NODE_ENV=production rollup -c", + "build:test": "rollup -c -w", + "build:oop": "rollup --config ./rollup.oop.config.js", + "build:stores": "NODE_ENV=stores rollup --config ./rollup.oop.config.js", + "contract:file": "node ./tests/fixtures/contract.js", + "contract": "npm run contract:base && npm run contract:public", + "contract:base": "DEBUG=jsonql-contract* node ../contract-cli/cmd.js ./tests/fixtures/resolvers ./tests/fixtures/contracts/tmp", + "contract:public": "DEBUG=jsonql-contract* node ../contract-cli/cmd.js ./tests/fixtures/resolvers ./tests/fixtures/contracts/tmp --public=1", + "qunit": "npm run build:umd && DEBUG=jsonql-*,server-io-core* node ./tests/qunit/run-qunit.js" + }, + "keywords": [ + "json", + "ql", + "js" + ], + "files": [ + "lib", + "src", + "index.js", + "oop.js" + ], + "author": "to1source ", + "contributors": [ + "NEWBRAN LTD " + ], + "repository": { + "type": "git", + "url": "git+ssh://git@gitee.com:to1source/jsonql.git" + }, + "license": "MIT", + "dependencies": { + "flyio": "^0.6.14", + "jsonql-constants": "^1.7.6", + "jsonql-errors": "^1.0.9", + "jsonql-jwt": "^1.2.1", + "jsonql-params-validator": "^1.4.3", + "lodash-es": "^4.17.11", + "nb-event-service": "^1.6.0", + "store": "^2.0.12" + }, + "devDependencies": { + "ava": "^2.1.0", + "browser-env": "^3.2.6", + "debug": "^4.1.1", + "esm": "^3.2.25", + "glob": "^7.1.4", + "jsonql-koa": "^1.2.4", + "koa-favicon": "^2.0.1", + "nyc": "^14.1.1", + "promise-polyfill": "8.1.3", + "qunit": "^2.9.2", + "rollup": "^1.16.3", + "rollup-plugin-alias": "^1.5.2", + "rollup-plugin-analyzer": "^3.0.1", + "rollup-plugin-async": "^1.2.0", + "rollup-plugin-buble": "^0.19.6", + "rollup-plugin-bundle-size": "^1.0.3", + "rollup-plugin-commonjs": "^10.0.1", + "rollup-plugin-copy": "^3.0.0", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-node-globals": "^1.4.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-replace": "^2.2.0", + "rollup-plugin-serve": "^1.0.1", + "rollup-plugin-terser": "^5.0.0", + "rollup-plugin-uglify": "^6.0.2", + "server-io-core": "^1.1.0", + "window": "^4.2.6" + }, + "ava": { + "files": [ + "tests/*.test.js", + "!tests/fixtures/*.*", + "!tests/browser/*.*", + "!tests/qunit/*.*" + ], + "require": [ + "./tests/fixtures/helpers/setup-browser.js" + ], + "cache": false, + "concurrency": 5, + "failFast": true, + "failWithoutAssertions": false, + "tap": false, + "compileEnhancements": false + } +} diff --git a/packages/client/rollup.config.js b/packages/http-client/rollup.config.js similarity index 100% rename from packages/client/rollup.config.js rename to packages/http-client/rollup.config.js diff --git a/packages/client/rollup.oop.config.js b/packages/http-client/rollup.oop.config.js similarity index 100% rename from packages/client/rollup.oop.config.js rename to packages/http-client/rollup.oop.config.js diff --git a/packages/client/src/index.js b/packages/http-client/src/index.js similarity index 100% rename from packages/client/src/index.js rename to packages/http-client/src/index.js diff --git a/packages/client/src/jsonql-api.js b/packages/http-client/src/jsonql-api.js similarity index 100% rename from packages/client/src/jsonql-api.js rename to packages/http-client/src/jsonql-api.js diff --git a/packages/client/src/jsonql-client-cls.js b/packages/http-client/src/jsonql-client-cls.js similarity index 100% rename from packages/client/src/jsonql-client-cls.js rename to packages/http-client/src/jsonql-client-cls.js diff --git a/packages/client/src/lib/base/auth-cls.js b/packages/http-client/src/lib/base/auth-cls.js similarity index 100% rename from packages/client/src/lib/base/auth-cls.js rename to packages/http-client/src/lib/base/auth-cls.js diff --git a/packages/client/src/lib/base/base-cls.js b/packages/http-client/src/lib/base/base-cls.js similarity index 100% rename from packages/client/src/lib/base/base-cls.js rename to packages/http-client/src/lib/base/base-cls.js diff --git a/packages/client/src/lib/base/contract-cls.js b/packages/http-client/src/lib/base/contract-cls.js similarity index 100% rename from packages/client/src/lib/base/contract-cls.js rename to packages/http-client/src/lib/base/contract-cls.js diff --git a/packages/client/src/lib/base/flyio.js b/packages/http-client/src/lib/base/flyio.js similarity index 100% rename from packages/client/src/lib/base/flyio.js rename to packages/http-client/src/lib/base/flyio.js diff --git a/packages/client/src/lib/base/http-cls.js b/packages/http-client/src/lib/base/http-cls.js similarity index 100% rename from packages/client/src/lib/base/http-cls.js rename to packages/http-client/src/lib/base/http-cls.js diff --git a/packages/client/src/lib/base/index.js b/packages/http-client/src/lib/base/index.js similarity index 100% rename from packages/client/src/lib/base/index.js rename to packages/http-client/src/lib/base/index.js diff --git a/packages/client/src/lib/ee.js b/packages/http-client/src/lib/ee.js similarity index 100% rename from packages/client/src/lib/ee.js rename to packages/http-client/src/lib/ee.js diff --git a/packages/client/src/lib/jsonql-api-generator.js b/packages/http-client/src/lib/jsonql-api-generator.js similarity index 100% rename from packages/client/src/lib/jsonql-api-generator.js rename to packages/http-client/src/lib/jsonql-api-generator.js diff --git a/packages/client/src/lib/options/base-options.js b/packages/http-client/src/lib/options/base-options.js similarity index 100% rename from packages/client/src/lib/options/base-options.js rename to packages/http-client/src/lib/options/base-options.js diff --git a/packages/client/src/lib/options/check-options.js b/packages/http-client/src/lib/options/check-options.js similarity index 100% rename from packages/client/src/lib/options/check-options.js rename to packages/http-client/src/lib/options/check-options.js diff --git a/packages/client/src/lib/options/index.js b/packages/http-client/src/lib/options/index.js similarity index 100% rename from packages/client/src/lib/options/index.js rename to packages/http-client/src/lib/options/index.js diff --git a/packages/client/src/lib/stores/index.js b/packages/http-client/src/lib/stores/index.js similarity index 100% rename from packages/client/src/lib/stores/index.js rename to packages/http-client/src/lib/stores/index.js diff --git a/packages/client/src/lib/stores/local-store.js b/packages/http-client/src/lib/stores/local-store.js similarity index 100% rename from packages/client/src/lib/stores/local-store.js rename to packages/http-client/src/lib/stores/local-store.js diff --git a/packages/client/src/lib/stores/session-store.js b/packages/http-client/src/lib/stores/session-store.js similarity index 100% rename from packages/client/src/lib/stores/session-store.js rename to packages/http-client/src/lib/stores/session-store.js diff --git a/packages/client/src/lib/utils.js b/packages/http-client/src/lib/utils.js similarity index 100% rename from packages/client/src/lib/utils.js rename to packages/http-client/src/lib/utils.js diff --git a/packages/client/src/ts/README.md b/packages/http-client/src/ts/README.md similarity index 100% rename from packages/client/src/ts/README.md rename to packages/http-client/src/ts/README.md diff --git a/packages/client/tests/contract-lock.test.js b/packages/http-client/tests/contract-lock.test.js similarity index 100% rename from packages/client/tests/contract-lock.test.js rename to packages/http-client/tests/contract-lock.test.js diff --git a/packages/client/tests/fixtures/contract.js b/packages/http-client/tests/fixtures/contract.js similarity index 100% rename from packages/client/tests/fixtures/contract.js rename to packages/http-client/tests/fixtures/contract.js diff --git a/packages/client/tests/fixtures/dev.js b/packages/http-client/tests/fixtures/dev.js similarity index 100% rename from packages/client/tests/fixtures/dev.js rename to packages/http-client/tests/fixtures/dev.js diff --git a/packages/client/tests/fixtures/favicon.ico b/packages/http-client/tests/fixtures/favicon.ico similarity index 100% rename from packages/client/tests/fixtures/favicon.ico rename to packages/http-client/tests/fixtures/favicon.ico diff --git a/packages/client/tests/fixtures/helpers/setup-browser.js b/packages/http-client/tests/fixtures/helpers/setup-browser.js similarity index 100% rename from packages/client/tests/fixtures/helpers/setup-browser.js rename to packages/http-client/tests/fixtures/helpers/setup-browser.js diff --git a/packages/client/tests/fixtures/index.html b/packages/http-client/tests/fixtures/index.html similarity index 100% rename from packages/client/tests/fixtures/index.html rename to packages/http-client/tests/fixtures/index.html diff --git a/packages/client/tests/fixtures/obj.js b/packages/http-client/tests/fixtures/obj.js similarity index 100% rename from packages/client/tests/fixtures/obj.js rename to packages/http-client/tests/fixtures/obj.js diff --git a/packages/client/tests/fixtures/options.json b/packages/http-client/tests/fixtures/options.json similarity index 100% rename from packages/client/tests/fixtures/options.json rename to packages/http-client/tests/fixtures/options.json diff --git a/packages/client/tests/fixtures/resolvers/auth/issuer.js b/packages/http-client/tests/fixtures/resolvers/auth/issuer.js similarity index 100% rename from packages/client/tests/fixtures/resolvers/auth/issuer.js rename to packages/http-client/tests/fixtures/resolvers/auth/issuer.js diff --git a/packages/client/tests/fixtures/resolvers/auth/validator.js b/packages/http-client/tests/fixtures/resolvers/auth/validator.js similarity index 100% rename from packages/client/tests/fixtures/resolvers/auth/validator.js rename to packages/http-client/tests/fixtures/resolvers/auth/validator.js diff --git a/packages/client/tests/fixtures/resolvers/mutation/plus.js b/packages/http-client/tests/fixtures/resolvers/mutation/plus.js similarity index 100% rename from packages/client/tests/fixtures/resolvers/mutation/plus.js rename to packages/http-client/tests/fixtures/resolvers/mutation/plus.js diff --git a/packages/client/tests/fixtures/resolvers/query/get-something.js b/packages/http-client/tests/fixtures/resolvers/query/get-something.js similarity index 100% rename from packages/client/tests/fixtures/resolvers/query/get-something.js rename to packages/http-client/tests/fixtures/resolvers/query/get-something.js diff --git a/packages/client/tests/fixtures/resolvers/query/test-list.js b/packages/http-client/tests/fixtures/resolvers/query/test-list.js similarity index 100% rename from packages/client/tests/fixtures/resolvers/query/test-list.js rename to packages/http-client/tests/fixtures/resolvers/query/test-list.js diff --git a/packages/client/tests/fixtures/run.js b/packages/http-client/tests/fixtures/run.js similarity index 100% rename from packages/client/tests/fixtures/run.js rename to packages/http-client/tests/fixtures/run.js diff --git a/packages/client/tests/fixtures/server.js b/packages/http-client/tests/fixtures/server.js similarity index 100% rename from packages/client/tests/fixtures/server.js rename to packages/http-client/tests/fixtures/server.js diff --git a/packages/client/tests/fn.test.js b/packages/http-client/tests/fn.test.js similarity index 100% rename from packages/client/tests/fn.test.js rename to packages/http-client/tests/fn.test.js diff --git a/packages/client/tests/main.test.js b/packages/http-client/tests/main.test.js similarity index 100% rename from packages/client/tests/main.test.js rename to packages/http-client/tests/main.test.js diff --git a/packages/client/tests/qunit/files/base-test.js b/packages/http-client/tests/qunit/files/base-test.js similarity index 100% rename from packages/client/tests/qunit/files/base-test.js rename to packages/http-client/tests/qunit/files/base-test.js diff --git a/packages/client/tests/qunit/files/stores-test.js b/packages/http-client/tests/qunit/files/stores-test.js similarity index 100% rename from packages/client/tests/qunit/files/stores-test.js rename to packages/http-client/tests/qunit/files/stores-test.js diff --git a/packages/client/tests/qunit/run-qunit-setup.js b/packages/http-client/tests/qunit/run-qunit-setup.js similarity index 100% rename from packages/client/tests/qunit/run-qunit-setup.js rename to packages/http-client/tests/qunit/run-qunit-setup.js diff --git a/packages/client/tests/qunit/run-qunit.js b/packages/http-client/tests/qunit/run-qunit.js similarity index 100% rename from packages/client/tests/qunit/run-qunit.js rename to packages/http-client/tests/qunit/run-qunit.js diff --git a/packages/client/tests/qunit/webroot/index.html b/packages/http-client/tests/qunit/webroot/index.html similarity index 100% rename from packages/client/tests/qunit/webroot/index.html rename to packages/http-client/tests/qunit/webroot/index.html diff --git a/packages/client/tests/qunit/webroot/nb-qunit-helper.js b/packages/http-client/tests/qunit/webroot/nb-qunit-helper.js similarity index 100% rename from packages/client/tests/qunit/webroot/nb-qunit-helper.js rename to packages/http-client/tests/qunit/webroot/nb-qunit-helper.js diff --git a/packages/client/tests/validation.test.js b/packages/http-client/tests/validation.test.js similarity index 100% rename from packages/client/tests/validation.test.js rename to packages/http-client/tests/validation.test.js diff --git a/packages/util/README.md b/packages/util/README.md new file mode 100644 index 0000000000000000000000000000000000000000..44de9a659e104d802727212542a656cf65c1ba54 --- /dev/null +++ b/packages/util/README.md @@ -0,0 +1,11 @@ +# jsonql-util + +This is a dependency module for various jsonql node modules. + +Please check [jsonql.org](http://jsonql.org) for more information. + +--- + +ISC + +Joel Chu (c) 2019 diff --git a/packages/util/package.json b/packages/util/package.json new file mode 100644 index 0000000000000000000000000000000000000000..fad2b757e136d75d6435586c91be3ecfcc02769a --- /dev/null +++ b/packages/util/package.json @@ -0,0 +1,36 @@ +{ + "name": "jsonql-util", + "version": "1.0.0-beta", + "description": "This is not for generate use, it's for jsonql various modules", + "main": "index.js", + "scripts": { + "test": "ava" + }, + "keywords": [ + "jsonql", + "utils" + ], + "author": "Joel Chu ", + "license": "ISC", + "repository": { + "type": "git", + "url": "git+ssh://git@gitee.com:to1source/jsonql.git" + }, + "ava": { + "files": [ + "tests/*.test.js", + "!tests/fixtures/*.*", + "!tests/browser/*.*", + "!tests/qunit/*.*" + ], + "require": [ + "esm" + ], + "cache": false, + "concurrency": 5, + "failFast": true, + "failWithoutAssertions": false, + "tap": false, + "compileEnhancements": false + } +}