From fece9ec8355e58fe6c86ad7ab0eb432f28886896 Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Thu, 14 Apr 2022 13:24:09 +0200 Subject: [PATCH 1/7] Multicontroller semaphore support --- .nvmrc | 2 +- README.md | 5 +- src/Cache.js | 4 ++ src/DataBuffer.js | 53 +++++++++++++++++- src/DataBufferController.js | 19 +++++-- src/__tests__/databuffercontroller.unit.js | 62 ++++++++++++++++++--- src/__tests__/databuffercontroller2.unit.js | 13 +++-- src/example/default.example.js | 2 +- src/example/multiController.example.js | 34 +++++++++++ src/example/redis.example.js | 2 +- 10 files changed, 169 insertions(+), 27 deletions(-) create mode 100644 src/example/multiController.example.js diff --git a/.nvmrc b/.nvmrc index a1fe187..0b77208 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.14.2 \ No newline at end of file +16.14.0 \ No newline at end of file diff --git a/README.md b/README.md index 3f87bcc..7d2b45b 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ A way to buffer and cache data to minimize requests ```javascript -import DataBufferController from './DataBufferController.js' +import {DataBufferController, Cache} from '@pondigitalsolutions/data-buffer-cache' -const controller = new DataBufferController(null, console ) +const cache = new Cache() +const controller = await DataBufferController.create({cache, logger: console}) const val = await controller.get('test') console.log('Should be undefined', val) // undefined diff --git a/src/Cache.js b/src/Cache.js index 8c23e72..f132526 100644 --- a/src/Cache.js +++ b/src/Cache.js @@ -92,4 +92,8 @@ export default class Cache extends EventEmitter { } return false } + + get items () { + return this.#items + } } diff --git a/src/DataBuffer.js b/src/DataBuffer.js index fd701c0..b6457fb 100644 --- a/src/DataBuffer.js +++ b/src/DataBuffer.js @@ -32,6 +32,11 @@ export default class DataBuffer extends EventEmitter { #currentStatus #cache + #semaphore = 'DIBS' + #closing = false + #foundSemaphore = false + #semaphoreChecking = false + /** * Initialize the DataBuffer * @@ -56,6 +61,12 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.init } + close() { + this.logger.debug('Closing DataBuffer') + this.removeAllListeners() + this.#closing = true + } + get ttl () { return this.#stdTTL } @@ -99,6 +110,11 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.finished } } catch (e) { } + + if(this.#currentStatus === this.#status.init) { + this.#cache.set(this.key, this.#semaphore) + } + return this.waitForResponse() } @@ -115,17 +131,36 @@ export default class DataBuffer extends EventEmitter { } const result = await this.#cache.set(this.key, JSON.stringify(value), { EX: ttl }) + this.triggerItemSet(ttl) + return result + } + + async checkSemaphore() { + this.#semaphoreChecking = true + await new Promise(resolve => setTimeout(resolve, this.#allowedRaceTimeMs/10)) + this.logger.debug('checking semaphore') + const data = await this.#cache.get(this.key) + if( data !== this.#semaphore ) { + this.logger.debug('semaphore is replaced with real data!') + this.triggerItemSet() + this.#semaphoreChecking = false + this.#foundSemaphore = false + return true + } + return this.checkSemaphore() + } + + triggerItemSet(ttl = this.#stdTTL) { this.#currentStatus = this.#status.finished this.setExpiry(ttl) // reset expiry this.emit(this.#status.finished) // notify all observers waiting for this request - return result } /** * Returns a response based on the current status of the this object * @returns {Promise} */ - async waitForResponse () { + async waitForResponse () { // the status is on init, set the status to running and let the first call set the value if (this.#currentStatus === this.#status.init) { this.#currentStatus = this.#status.running @@ -136,6 +171,14 @@ export default class DataBuffer extends EventEmitter { if (this.#currentStatus === this.#status.finished) { const data = await this.#cache.get(this.key) + if (data === this.#semaphore) { + this.#currentStatus = this.#status.running + this.#foundSemaphore = true + this.logger.debug('Semaphore found') + if(this.#semaphoreChecking === false ) this.checkSemaphore() + return this.waitForResponse() + } + // it is possible that the cache is expired between exists call and the get call // if that happens restart the process if (data === undefined || data === null) { @@ -143,10 +186,14 @@ export default class DataBuffer extends EventEmitter { return undefined } - this.logger.trace(`Cache hit for key: ${this.key}`) + this.logger.debug(`Cache hit for key: ${this.key}`) return JSON.parse(data) } + if ( this.#foundSemaphore ) { + const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) + } + // the status is running, so we wait until the cache gets set // or till we waited long enough const dataPromise = new Promise((resolve) => { diff --git a/src/DataBufferController.js b/src/DataBufferController.js index 5894485..204a69c 100644 --- a/src/DataBufferController.js +++ b/src/DataBufferController.js @@ -56,9 +56,6 @@ export default class DataBufferController { } this.#cache = cache - // start the cache - this.cacheStart() - // remove expired caches, call every 1/5 of the stdTTL this.#intervalRef = setInterval(this.cacheCleaning.bind(this), this.ttl * 200) } @@ -66,9 +63,10 @@ export default class DataBufferController { // cleanup async close () { this.logger.debug('Stopping DataBufferController') + clearInterval(this.#intervalRef) await this.#cache.quit() + Object.values(this.#items).forEach(dataBuffer => dataBuffer.close()) this.#items = null - clearInterval(this.#intervalRef) } /** @@ -93,7 +91,7 @@ export default class DataBufferController { * @returns {Promise} */ get (key) { - return this.getBuffer(key).get() + return this.getBuffer(key).get() } // connect the cache and setup some catching @@ -144,4 +142,15 @@ export default class DataBufferController { get size () { return Object.keys(this.#items).length } + // returns the statusus of the cache, usefull for tests + get bufferStatus () { + return Object.values(this.#items).map(dataBuffer => dataBuffer.status) + } + + static async create(data) { + const dbc = new DataBufferController(data) + // start the cache + await dbc.cacheStart() + return dbc + } } diff --git a/src/__tests__/databuffercontroller.unit.js b/src/__tests__/databuffercontroller.unit.js index 055b55c..33c559d 100644 --- a/src/__tests__/databuffercontroller.unit.js +++ b/src/__tests__/databuffercontroller.unit.js @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import { expect, describe, test, afterAll } from '@jest/globals' +import { expect, describe, test } from '@jest/globals' import DataBufferController from '../DataBufferController.js' import Cache from '../Cache.js' @@ -25,13 +25,10 @@ const logger = { } const cache = new Cache() -const controller = new DataBufferController({ logger, ttl: 2, cache }) - -afterAll(() => controller.close()) describe('Test the Controller', () => { test('If the controller throws an error when the cache is not set', () => { - expect(() => new DataBufferController({ logger, ttl: 1 })).toThrow('Cache is not provided.') + expect(DataBufferController.create({ logger, ttl: 1 })).rejects.toThrow('Cache is not provided.') }) test.each([ @@ -40,15 +37,16 @@ describe('Test the Controller', () => { [{ logger, cache, raceTime: 20 }, { ttl: 300, raceTime: 20 }], [{ logger, cache, raceTime: 20, ttl: 200 }, { ttl: 200, raceTime: 20 }], [{ cache }, { ttl: 300, raceTime: 30000 }] - ])('Basic initialization', (params, expected) => { - const dbc = new DataBufferController(params) + ])('Basic initialization', async (params, expected) => { + const dbc = await DataBufferController.create(params) expect(dbc.ttl).toEqual(expected.ttl) expect(dbc.raceTime).toEqual(expected.raceTime) expect(dbc.logger).toBeDefined() - dbc.close() + await dbc.close() }) test('The basic functionallity', async () => { + const controller = await DataBufferController.create({ logger, ttl: 2, cache }) const key = 'controller_test' const expected = await controller.get(key) expect(expected).toEqual(undefined) @@ -56,9 +54,11 @@ describe('Test the Controller', () => { const expected2 = await controller.get(key) expect(expected2).toEqual({ found: true }) + await controller.close() }) test('The sequential requests should be queued and waiting till the cache has been set', async () => { + const controller = await DataBufferController.create({ logger, ttl: 2, cache }) const key = 'test2' const list = [ controller.get(key), @@ -77,5 +77,51 @@ describe('Test the Controller', () => { controller.set(key, { found: true }) const expected2 = await Promise.all(list) expect(expected2.filter(item => item).length).toBe(9) + await controller.close() + }) +}) + +describe('Multicontroller - single cache usecase', () => { + test('The sequential requests should be queued, also over multiple controllers, and waiting till the cache has been set', async () => { + const singleCache = new Cache() + const controllerA = await DataBufferController.create({ logger, cache: singleCache, ttl: 5 }) + const controllerB = await DataBufferController.create({ logger, cache: singleCache, ttl: 5 }) + + const key = 'multitest' + const list = [ + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key) + ] + + const expectedA = await list[0] + expect(expectedA).toBe(undefined) + expect(controllerA.bufferStatus).toEqual(['running']) + expect(controllerB.bufferStatus).toEqual([]) + + list.push(controllerB.get(key)) + //give async job a change to initialize + await new Promise(resolve => setTimeout(resolve, 500)) + + const expectedB = list[5] + expect(expectedB).not.toBe(undefined) + + list.push(controllerB.get(key)) + list.push(controllerB.get(key)) + list.push(controllerB.get(key)) + list.push(controllerB.get(key)) + + expect(controllerB.bufferStatus).toEqual(['running']) + + controllerA.set(key, {found: 42}) + + console.log(list) + const expectedList = await Promise.all(list) + console.log(expectedList) + expect(expectedList.filter(item => item).length).toBe(9) + + await Promise.all([controllerA.close(), controllerB.close()]) }) }) diff --git a/src/__tests__/databuffercontroller2.unit.js b/src/__tests__/databuffercontroller2.unit.js index 097024d..2261ce2 100644 --- a/src/__tests__/databuffercontroller2.unit.js +++ b/src/__tests__/databuffercontroller2.unit.js @@ -25,13 +25,13 @@ const logger = { } describe('Test the Controller', () => { - test('The timer should call the CacheClean function periodically', () => { + test('The timer should call the CacheClean function periodically', async () => { const cache = new Cache() jest.useFakeTimers('modern') jest.setSystemTime(new Date(2020, 12, 5, 20, 0, 0)) const cleanSpy = jest.fn() const ttl = 2 - const controller = new DataBufferController({ logger, ttl, cache }) + const controller = await DataBufferController.create({ logger, ttl, cache }) controller.logger.trace = (txt) => cleanSpy(txt) expect(cleanSpy).toHaveBeenCalledTimes(0) expect(controller.size).toBe(0) @@ -48,7 +48,7 @@ describe('Test the Controller', () => { controller.close() }) - test('Test the Cache emitters', () => { + test('Test the Cache emitters', async () => { const cache = new Cache() const cleanSpy1 = jest.fn() @@ -61,11 +61,12 @@ describe('Test the Controller', () => { } const ttl = 2 - const controller = new DataBufferController({ logger: loggerSpy, cache, ttl, raceTime: 20 }) + const controller = await DataBufferController.create({ logger: loggerSpy, cache, ttl, raceTime: 20 }) - expect(cleanSpy2).toHaveBeenCalledTimes(2) + expect(cleanSpy2).toHaveBeenCalledTimes(3) expect(cleanSpy2).toHaveBeenCalledWith('Cache is Connected') - expect(cleanSpy2).toHaveBeenLastCalledWith('Cache is Ready') + expect(cleanSpy2).toHaveBeenCalledWith('Cache is Ready') + expect(cleanSpy2).toHaveBeenLastCalledWith('Cache is Setup') cache.emit('error', 'Emitted Error') expect(cleanSpy1).toHaveBeenCalledTimes(2) expect(cleanSpy1).toHaveBeenCalledWith('CACHE ERROR') diff --git a/src/example/default.example.js b/src/example/default.example.js index ebaad58..90ef2a8 100644 --- a/src/example/default.example.js +++ b/src/example/default.example.js @@ -18,7 +18,7 @@ import { Cache, DataBufferController } from '../index.js' const logger = console const inMemoryCache = new Cache() -const controller = new DataBufferController({ logger, cache: inMemoryCache }) +const controller = await DataBufferController.create({ logger, cache: inMemoryCache }) const val = await controller.get('test') logger.log('Should be undefined', val) // undefined diff --git a/src/example/multiController.example.js b/src/example/multiController.example.js new file mode 100644 index 0000000..485c109 --- /dev/null +++ b/src/example/multiController.example.js @@ -0,0 +1,34 @@ +import { Cache, DataBufferController } from '../index.js' + +const logger = console + +const singleCache = new Cache() +const controllerA = await DataBufferController.create({ logger, cache: singleCache }) +const controllerB = await DataBufferController.create({ logger, cache: singleCache }) + +const key = 'multitest' +const list = [ + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key) +] +console.log(list) +await list[0] + +console.log(singleCache.items) +list.push(controllerB.get(key)) +list.push(controllerB.get(key)) +list.push(controllerB.get(key)) +console.log(list) + +await new Promise(resolve => setTimeout(resolve, 5000)) + +controllerA.set(key, {found: 42}) +Promise.all(list).then(res => { + console.log(list) + console.log(res) + controllerA.close() + controllerB.close() +}) \ No newline at end of file diff --git a/src/example/redis.example.js b/src/example/redis.example.js index b02cad7..19e1499 100644 --- a/src/example/redis.example.js +++ b/src/example/redis.example.js @@ -19,7 +19,7 @@ import { DataBufferController } from '../index.js' const logger = console const cache = createClient() // redis -const controller = new DataBufferController({ logger, cache }) +const controller = await DataBufferController.create({ logger, cache }) const val = await controller.get('test') logger.log('Should be undefined', val) // undefined From aa65e4c8add0bcf4736e5479346851cbad032a6f Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 15 Apr 2022 09:17:21 +0200 Subject: [PATCH 2/7] cleanup, tests, linting --- src/Cache.js | 4 ---- src/DataBuffer.js | 22 +++++++---------- src/DataBufferController.js | 28 ++++++++++++++++------ src/__tests__/databuffer.unit.js | 2 +- src/__tests__/databuffercontroller.unit.js | 27 ++++++++++----------- src/example/multiController.example.js | 11 +++------ 6 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/Cache.js b/src/Cache.js index f132526..8c23e72 100644 --- a/src/Cache.js +++ b/src/Cache.js @@ -92,8 +92,4 @@ export default class Cache extends EventEmitter { } return false } - - get items () { - return this.#items - } } diff --git a/src/DataBuffer.js b/src/DataBuffer.js index 28bff7f..56a2044 100644 --- a/src/DataBuffer.js +++ b/src/DataBuffer.js @@ -61,7 +61,7 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.init } - close() { + close () { this.logger.debug('Closing DataBuffer') this.removeAllListeners() this.#closing = true @@ -71,7 +71,7 @@ export default class DataBuffer extends EventEmitter { return this.#stdTTL } - get raceTime () { + get raceTimeMs () { return this.#allowedRaceTimeMs } @@ -111,7 +111,7 @@ export default class DataBuffer extends EventEmitter { } } catch (e) { } - if(this.#currentStatus === this.#status.init) { + if (this.#currentStatus === this.#status.init) { this.#cache.set(this.key, this.#semaphore) } @@ -135,12 +135,12 @@ export default class DataBuffer extends EventEmitter { return result } - async checkSemaphore() { + async checkSemaphore () { this.#semaphoreChecking = true - await new Promise(resolve => setTimeout(resolve, this.#allowedRaceTimeMs/10)) + await new Promise(resolve => setTimeout(resolve, this.#allowedRaceTimeMs / 10)) this.logger.debug('checking semaphore') const data = await this.#cache.get(this.key) - if( data !== this.#semaphore ) { + if (data !== this.#semaphore) { this.logger.debug('semaphore is replaced with real data!') this.triggerItemSet() this.#semaphoreChecking = false @@ -150,7 +150,7 @@ export default class DataBuffer extends EventEmitter { return this.checkSemaphore() } - triggerItemSet(ttl = this.#stdTTL) { + triggerItemSet (ttl = this.#stdTTL) { this.#currentStatus = this.#status.finished this.setExpiry(ttl) // reset expiry this.emit(this.#status.finished) // notify all observers waiting for this request @@ -160,7 +160,7 @@ export default class DataBuffer extends EventEmitter { * Returns a response based on the current status of the this object * @returns {Promise} */ - async waitForResponse () { + async waitForResponse () { // the status is on init, set the status to running and let the first call set the value if (this.#currentStatus === this.#status.init) { this.#currentStatus = this.#status.running @@ -175,7 +175,7 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.running this.#foundSemaphore = true this.logger.debug('Semaphore found') - if(this.#semaphoreChecking === false ) this.checkSemaphore() + if (this.#semaphoreChecking === false) this.checkSemaphore() return this.waitForResponse() } @@ -190,10 +190,6 @@ export default class DataBuffer extends EventEmitter { return JSON.parse(data) } - if ( this.#foundSemaphore ) { - const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) - } - // the status is running, so we wait until the cache gets set // or till we waited long enough const dataPromise = new Promise((resolve) => { diff --git a/src/DataBufferController.js b/src/DataBufferController.js index 52b7127..29ff098 100644 --- a/src/DataBufferController.js +++ b/src/DataBufferController.js @@ -25,11 +25,22 @@ import DataBuffer from './DataBuffer.js' * @property {function(txt):void} error - log error information */ + + /** * A Cache definition * @typedef {import('./Cache.js').default} Cache */ +/** + * A DataBufferController Object + * @typedef {Object} DataBufferControllerObject + * @property {Cache} cache A Cache object + * @property {Logger} logger A Logger object + * @property {number} ttl Time To Live in seconds + * @property {number} raceTimeMs How long a request can be queued, before it is ignored and retried in milli-seconds + */ + export default class DataBufferController { #items #cache @@ -39,11 +50,7 @@ export default class DataBufferController { /** * Setup the Controller * - * @param {object} obj - * @param {Cache} obj.cache A Cache object - * @param {Logger} obj.logger A Logger object - * @param {number} obj.ttl Time To Live in seconds - * @param {number} obj.raceTimeMs How long a request can be queued, before it is ignored and retried in milli-seconds + * @param {DataBufferControllerObject} param * @throws {Error} When the cache is not set */ constructor ({ cache, logger = console, ttl = 300, raceTimeMs = 30000 }) { @@ -92,7 +99,7 @@ export default class DataBufferController { * @returns {Promise} */ get (key) { - return this.getBuffer(key).get() + return this.getBuffer(key).get() } // connect the cache and setup some catching @@ -144,12 +151,19 @@ export default class DataBufferController { get amountOfCachedKeys () { return Object.keys(this.#items).length } + // returns the statusus of the cache, usefull for tests get bufferStatus () { return Object.values(this.#items).map(dataBuffer => dataBuffer.status) } - static async create(data) { + /** + * Setup the Controller + * + * @param {DataBufferControllerObject} data + * @return {DataBufferController} + **/ + static async create (data) { const dbc = new DataBufferController(data) // start the cache await dbc.cacheStart() diff --git a/src/__tests__/databuffer.unit.js b/src/__tests__/databuffer.unit.js index d3a4640..8cbcfa3 100644 --- a/src/__tests__/databuffer.unit.js +++ b/src/__tests__/databuffer.unit.js @@ -33,7 +33,7 @@ describe('Test the DataBuffer', () => { ])('Basic initialization', (params, expected) => { const db = new DataBuffer(params) expect(db.ttl).toEqual(expected.ttl) - expect(db.raceTime).toEqual(expected.raceTimeMs) + expect(db.raceTimeMs).toEqual(expected.raceTimeMs) expect(db.raceTimeInSeconds).toEqual(expected.raceTimeMs / 1000) expect(db.logger).toBeDefined() db.cleanUp() diff --git a/src/__tests__/databuffercontroller.unit.js b/src/__tests__/databuffercontroller.unit.js index 1d3be66..baea4a5 100644 --- a/src/__tests__/databuffercontroller.unit.js +++ b/src/__tests__/databuffercontroller.unit.js @@ -37,10 +37,10 @@ describe('Test the Controller', () => { [{ logger, cache, raceTimeMs: 20 }, { ttl: 300, raceTimeMs: 20 }], [{ logger, cache, raceTimeMs: 20, ttl: 200 }, { ttl: 200, raceTimeMs: 20 }], [{ cache }, { ttl: 300, raceTimeMs: 30000 }] - ])('Basic initialization', (params, expected) => { - const dbc = new DataBufferController(params) + ])('Basic initialization', async (params, expected) => { + const dbc = await DataBufferController.create(params) expect(dbc.ttl).toEqual(expected.ttl) - expect(dbc.raceTime).toEqual(expected.raceTimeMs) + expect(dbc.raceTimeMs).toEqual(expected.raceTimeMs) expect(dbc.logger).toBeDefined() await dbc.close() }) @@ -84,8 +84,8 @@ describe('Test the Controller', () => { describe('Multicontroller - single cache usecase', () => { test('The sequential requests should be queued, also over multiple controllers, and waiting till the cache has been set', async () => { const singleCache = new Cache() - const controllerA = await DataBufferController.create({ logger, cache: singleCache, ttl: 5 }) - const controllerB = await DataBufferController.create({ logger, cache: singleCache, ttl: 5 }) + const controllerA = await DataBufferController.create({ logger, cache: singleCache, ttl: 10, raceTimeMs: 10000 }) + const controllerB = await DataBufferController.create({ logger, cache: singleCache, ttl: 10, raceTimeMs: 10000 }) const key = 'multitest' const list = [ @@ -97,29 +97,26 @@ describe('Multicontroller - single cache usecase', () => { ] const expectedA = await list[0] - expect(expectedA).toBe(undefined) + expect(expectedA).toBe(undefined) expect(controllerA.bufferStatus).toEqual(['running']) expect(controllerB.bufferStatus).toEqual([]) list.push(controllerB.get(key)) - //give async job a change to initialize + // give async job a change to initialize await new Promise(resolve => setTimeout(resolve, 500)) const expectedB = list[5] expect(expectedB).not.toBe(undefined) - - list.push(controllerB.get(key)) - list.push(controllerB.get(key)) - list.push(controllerB.get(key)) - list.push(controllerB.get(key)) + + for (let i = 0; i < 4; i++) { + list.push(controllerB.get(key)) + } expect(controllerB.bufferStatus).toEqual(['running']) - controllerA.set(key, {found: 42}) + controllerA.set(key, { found: 42 }) - console.log(list) const expectedList = await Promise.all(list) - console.log(expectedList) expect(expectedList.filter(item => item).length).toBe(9) await Promise.all([controllerA.close(), controllerB.close()]) diff --git a/src/example/multiController.example.js b/src/example/multiController.example.js index 485c109..7ad61ee 100644 --- a/src/example/multiController.example.js +++ b/src/example/multiController.example.js @@ -12,23 +12,18 @@ const list = [ controllerA.get(key), controllerA.get(key), controllerA.get(key), - controllerA.get(key) + controllerA.get(key) ] -console.log(list) await list[0] -console.log(singleCache.items) list.push(controllerB.get(key)) list.push(controllerB.get(key)) list.push(controllerB.get(key)) -console.log(list) await new Promise(resolve => setTimeout(resolve, 5000)) -controllerA.set(key, {found: 42}) +controllerA.set(key, { found: 42 }) Promise.all(list).then(res => { - console.log(list) - console.log(res) controllerA.close() controllerB.close() -}) \ No newline at end of file +}) From 3aea7c892ce13eb23d2738d4e7d0dacb82df464e Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 15 Apr 2022 12:51:00 +0200 Subject: [PATCH 3/7] whitespace --- src/DataBufferController.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DataBufferController.js b/src/DataBufferController.js index 29ff098..92fbd22 100644 --- a/src/DataBufferController.js +++ b/src/DataBufferController.js @@ -25,8 +25,6 @@ import DataBuffer from './DataBuffer.js' * @property {function(txt):void} error - log error information */ - - /** * A Cache definition * @typedef {import('./Cache.js').default} Cache From a8ed0d2172dd9ad6ff65ef411dff341ba875404a Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 15 Apr 2022 14:18:50 +0200 Subject: [PATCH 4/7] examples --- src/example/default.example.js | 2 +- src/example/multiController.example.js | 2 ++ src/example/redis.example.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/example/default.example.js b/src/example/default.example.js index 90ef2a8..4af2e8c 100644 --- a/src/example/default.example.js +++ b/src/example/default.example.js @@ -46,4 +46,4 @@ await controller.set('test_multi', { data: 'Buffer' }) logger.log(await Promise.all(vals)) -controller.close() +await controller.close() diff --git a/src/example/multiController.example.js b/src/example/multiController.example.js index 7ad61ee..0d845d2 100644 --- a/src/example/multiController.example.js +++ b/src/example/multiController.example.js @@ -23,7 +23,9 @@ list.push(controllerB.get(key)) await new Promise(resolve => setTimeout(resolve, 5000)) controllerA.set(key, { found: 42 }) + Promise.all(list).then(res => { + logger.info(res) controllerA.close() controllerB.close() }) diff --git a/src/example/redis.example.js b/src/example/redis.example.js index 19e1499..b38a96d 100644 --- a/src/example/redis.example.js +++ b/src/example/redis.example.js @@ -47,4 +47,4 @@ await controller.set('test_multi', { data: 'Buffer' }) logger.log(await Promise.all(vals)) -controller.close() +await controller.close() From 857b953d688747fbbd5ee89dbc12a49ca60d8e18 Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 29 Apr 2022 17:12:59 +0200 Subject: [PATCH 5/7] refinements --- package-lock.json | 419 ++++++++++++--------- package.json | 2 +- src/DataBuffer.js | 55 ++- src/DataBufferController.js | 1 + src/__tests__/databuffercontroller.unit.js | 38 +- 5 files changed, 336 insertions(+), 179 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87aac4e..9e678e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pondigitalsolutions/data-buffer-cache", - "version": "0.0.1", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@pondigitalsolutions/data-buffer-cache", - "version": "0.0.1", + "version": "1.0.0", "license": "GPL-3.0", "devDependencies": { "@babel/core": "^7.12.10", @@ -34,12 +34,13 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1677,9 +1678,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2403,10 +2404,32 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", + "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2419,9 +2442,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -2572,9 +2595,9 @@ } }, "node_modules/@pondevelopment/ponstandard": { - "version": "1.0.1", - "resolved": "https://npm.pkg.github.com/download/@pondevelopment/ponstandard/1.0.1/203cb33375e453dc0bb7058bff4ec9b46289ffc11fdba97d5d80e7a28a391580", - "integrity": "sha512-rTk9+61SuOUYpGFGq/oYj29IJeU+Rqp/nnPp80eqmCLKOGuiQ/1HTC072phD+lIfjCIGxP5RsKeAvUNfPsUofQ==", + "version": "1.0.2", + "resolved": "https://npm.pkg.github.com/download/@pondevelopment/ponstandard/1.0.2/2ab98967dba3ee74b4f023c081fdcb91d254ba4ef8975bdff9f2868594ffa0de", + "integrity": "sha512-V+Z8u6fIPEe257qnIhya/r9hSa5EUrM60OUzALLCBnNvRILDJoj/RsOnoCKIOMzLw4ulbrax5cv8eVbRp8w5Gw==", "dev": true, "license": "GPL-3", "dependencies": { @@ -2999,9 +3022,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", "dev": true, "dependencies": { "@babel/types": "^7.3.0" @@ -3047,9 +3070,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", - "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "version": "17.0.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.30.tgz", + "integrity": "sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw==", "dev": true }, "node_modules/@types/prettier": { @@ -3080,15 +3103,15 @@ "dev": true }, "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3711,9 +3734,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", "dev": true, "funding": [ { @@ -3726,10 +3749,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", "escalade": "^3.1.1", - "node-releases": "^2.0.2", + "node-releases": "^2.0.3", "picocolors": "^1.0.0" }, "bin": { @@ -3804,9 +3827,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001334", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz", + "integrity": "sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw==", "dev": true, "funding": [ { @@ -4011,12 +4034,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", - "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.3.tgz", + "integrity": "sha512-wliMbvPI2idgFWpFe7UEyHMvu6HWgW8WA+HnDRtgzoSDYvXFMpoGX1H3tPDDXrcfUSyXafCLDd7hOeMQHEZxGw==", "dev": true, "dependencies": { - "browserslist": "^4.19.1", + "browserslist": "^4.20.3", "semver": "7.0.0" }, "funding": { @@ -4167,15 +4190,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { @@ -4255,9 +4282,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.107", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", - "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", + "version": "1.4.127", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz", + "integrity": "sha512-nhD6S8nKI0O2MueC6blNOEZio+/PWppE/pevnf3LOlQA/fKPCrDp2Ao4wx4LFwmIkJpVdFdn2763YWLy9ENIZg==", "dev": true }, "node_modules/emittery": { @@ -4479,12 +4506,12 @@ } }, "node_modules/eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", - "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.1", + "@eslint/eslintrc": "^1.2.2", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -5355,6 +5382,15 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -5567,9 +5603,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5584,6 +5620,18 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5665,9 +5713,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { "agent-base": "6", @@ -5846,9 +5894,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -6097,9 +6145,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -6891,9 +6939,9 @@ } }, "node_modules/jest-junit": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-13.1.0.tgz", - "integrity": "sha512-ECbhzEG3Oe2IH3Mnwcv2vAXM4qTbcObN/gTUzwKPlpaNsf2G/zlj/teEUqRGV17YQiQ4AqzTf3pCO7W59DKVIw==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-13.2.0.tgz", + "integrity": "sha512-B0XNlotl1rdsvFZkFfoa19mc634+rrd8E4Sskb92Bb8MmSXeWV9XJGUyctunZS1W410uAxcyYuPUGVnbcOH8cg==", "dev": true, "dependencies": { "mkdirp": "^1.0.4", @@ -8451,9 +8499,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", - "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", "dev": true }, "node_modules/normalize-package-data": { @@ -9490,13 +9538,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.2.tgz", - "integrity": "sha512-Ynz8fTQW5/1elh+jWU2EDDzeoNbD0OQ0R+D1VJU5ATOkUaro4A9YEkdN2ODQl/8UQFPPpZNw91fOcLFamM7Pww==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -11076,9 +11125,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/tunnel-agent": { @@ -11142,14 +11191,14 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -11615,12 +11664,13 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { @@ -12755,9 +12805,9 @@ "optional": true }, "@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -13305,10 +13355,26 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", + "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", + "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==", "dev": true }, "@jridgewell/sourcemap-codec": { @@ -13318,9 +13384,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -13449,9 +13515,9 @@ } }, "@pondevelopment/ponstandard": { - "version": "1.0.1", - "resolved": "https://npm.pkg.github.com/download/@pondevelopment/ponstandard/1.0.1/203cb33375e453dc0bb7058bff4ec9b46289ffc11fdba97d5d80e7a28a391580", - "integrity": "sha512-rTk9+61SuOUYpGFGq/oYj29IJeU+Rqp/nnPp80eqmCLKOGuiQ/1HTC072phD+lIfjCIGxP5RsKeAvUNfPsUofQ==", + "version": "1.0.2", + "resolved": "https://npm.pkg.github.com/download/@pondevelopment/ponstandard/1.0.2/2ab98967dba3ee74b4f023c081fdcb91d254ba4ef8975bdff9f2868594ffa0de", + "integrity": "sha512-V+Z8u6fIPEe257qnIhya/r9hSa5EUrM60OUzALLCBnNvRILDJoj/RsOnoCKIOMzLw4ulbrax5cv8eVbRp8w5Gw==", "dev": true, "requires": { "@babel/core": "^7.17.5", @@ -13764,9 +13830,9 @@ } }, "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -13812,9 +13878,9 @@ "dev": true }, "@types/node": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", - "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==", + "version": "17.0.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.30.tgz", + "integrity": "sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw==", "dev": true }, "@types/prettier": { @@ -13845,15 +13911,15 @@ "dev": true }, "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", "dev": true }, "acorn-globals": { @@ -14329,15 +14395,15 @@ "dev": true }, "browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", "escalade": "^3.1.1", - "node-releases": "^2.0.2", + "node-releases": "^2.0.3", "picocolors": "^1.0.0" } }, @@ -14391,9 +14457,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001332", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", - "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", + "version": "1.0.30001334", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz", + "integrity": "sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw==", "dev": true }, "caseless": { @@ -14554,12 +14620,12 @@ } }, "core-js-compat": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", - "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.3.tgz", + "integrity": "sha512-wliMbvPI2idgFWpFe7UEyHMvu6HWgW8WA+HnDRtgzoSDYvXFMpoGX1H3tPDDXrcfUSyXafCLDd7hOeMQHEZxGw==", "dev": true, "requires": { - "browserslist": "^4.19.1", + "browserslist": "^4.20.3", "semver": "7.0.0" }, "dependencies": { @@ -14678,12 +14744,13 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "delayed-stream": { @@ -14747,9 +14814,9 @@ } }, "electron-to-chromium": { - "version": "1.4.107", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.107.tgz", - "integrity": "sha512-Huen6taaVrUrSy8o7mGStByba8PfOWWluHNxSHGBrCgEdFVLtvdQDBr9LBCF9Uci8SYxh28QNNMO0oC17wbGAg==", + "version": "1.4.127", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz", + "integrity": "sha512-nhD6S8nKI0O2MueC6blNOEZio+/PWppE/pevnf3LOlQA/fKPCrDp2Ao4wx4LFwmIkJpVdFdn2763YWLy9ENIZg==", "dev": true }, "emittery": { @@ -14919,12 +14986,12 @@ } }, "eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", - "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.1", + "@eslint/eslintrc": "^1.2.2", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -15596,6 +15663,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "generic-pool": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz", @@ -15746,9 +15819,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-flag": { @@ -15757,6 +15830,15 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -15816,9 +15898,9 @@ } }, "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { "agent-base": "6", @@ -15948,9 +16030,9 @@ "dev": true }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" @@ -16126,9 +16208,9 @@ "dev": true }, "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", "dev": true, "requires": { "@babel/core": "^7.12.3", @@ -16718,9 +16800,9 @@ } }, "jest-junit": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-13.1.0.tgz", - "integrity": "sha512-ECbhzEG3Oe2IH3Mnwcv2vAXM4qTbcObN/gTUzwKPlpaNsf2G/zlj/teEUqRGV17YQiQ4AqzTf3pCO7W59DKVIw==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-13.2.0.tgz", + "integrity": "sha512-B0XNlotl1rdsvFZkFfoa19mc634+rrd8E4Sskb92Bb8MmSXeWV9XJGUyctunZS1W410uAxcyYuPUGVnbcOH8cg==", "dev": true, "requires": { "mkdirp": "^1.0.4", @@ -17913,9 +17995,9 @@ "dev": true }, "node-releases": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", - "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", "dev": true }, "normalize-package-data": { @@ -18732,13 +18814,14 @@ } }, "regexp.prototype.flags": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.2.tgz", - "integrity": "sha512-Ynz8fTQW5/1elh+jWU2EDDzeoNbD0OQ0R+D1VJU5ATOkUaro4A9YEkdN2ODQl/8UQFPPpZNw91fOcLFamM7Pww==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { @@ -19911,9 +19994,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "tunnel-agent": { @@ -19962,14 +20045,14 @@ } }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, diff --git a/package.json b/package.json index 75998e6..db039ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pondigitalsolutions/data-buffer-cache", - "version": "0.0.1", + "version": "1.0.0", "description": "Prevent bulk requests to your backend, buffer and cache simultaneous requests.", "type": "module", "main": "src/index.js", diff --git a/src/DataBuffer.js b/src/DataBuffer.js index 56a2044..ba2a62e 100644 --- a/src/DataBuffer.js +++ b/src/DataBuffer.js @@ -36,6 +36,8 @@ export default class DataBuffer extends EventEmitter { #closing = false #foundSemaphore = false #semaphoreChecking = false + #semaphoreAmountChecks = 10 + #semaphoreCheckCounter = 0 /** * Initialize the DataBuffer @@ -101,9 +103,10 @@ export default class DataBuffer extends EventEmitter { * Return a promise that resolves with the data * or undefined when it was expired, not available or timed out * - * @returns {Promise} + * @returns {Promise} */ async get () { + // If cache exists, set the status and proceed try { const result = await this.#cache.exists(this.key) if (result !== false) { @@ -111,6 +114,7 @@ export default class DataBuffer extends EventEmitter { } } catch (e) { } + // if the status is still set on initializing set a semaphore if (this.#currentStatus === this.#status.init) { this.#cache.set(this.key, this.#semaphore) } @@ -136,17 +140,27 @@ export default class DataBuffer extends EventEmitter { } async checkSemaphore () { - this.#semaphoreChecking = true - await new Promise(resolve => setTimeout(resolve, this.#allowedRaceTimeMs / 10)) - this.logger.debug('checking semaphore') + await new Promise(resolve => setTimeout(resolve, this.#allowedRaceTimeMs / this.#semaphoreAmountChecks)) + this.logger.debug('Checking semaphore') + this.#semaphoreCheckCounter++ const data = await this.#cache.get(this.key) if (data !== this.#semaphore) { - this.logger.debug('semaphore is replaced with real data!') + this.logger.debug('Semaphore is replaced with real data!') this.triggerItemSet() this.#semaphoreChecking = false this.#foundSemaphore = false + this.#semaphoreCheckCounter = 0 return true } + + // Jump out of the recursion if this variable is set to false + // or when the racetime has passed + if(this.#semaphoreChecking === false || this.#semaphoreCheckCounter > this.#semaphoreAmountChecks) { + this.logger.debug('Semaphore is taking too long, aborting!') + this.#semaphoreCheckCounter = 0 + this.#semaphoreChecking = false + return false + } return this.checkSemaphore() } @@ -167,7 +181,7 @@ export default class DataBuffer extends EventEmitter { return undefined } - // the status is finished if the data is still there return it + // the status is finished and if the data is still there return it if (this.#currentStatus === this.#status.finished) { const data = await this.#cache.get(this.key) @@ -175,19 +189,24 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.running this.#foundSemaphore = true this.logger.debug('Semaphore found') - if (this.#semaphoreChecking === false) this.checkSemaphore() + if (this.#semaphoreChecking === false) { + this.#semaphoreChecking = true + this.checkSemaphore() + } return this.waitForResponse() } // it is possible that the cache is expired between exists call and the get call // if that happens restart the process - if (data === undefined || data === null) { + const parsedJSON = this.tryParseJSONObject(data) + + if (parsedJSON === false) { this.#currentStatus = this.#status.running return undefined } this.logger.debug(`Cache hit for key: ${this.key}`) - return JSON.parse(data) + return parsedJSON } // the status is running, so we wait until the cache gets set @@ -216,4 +235,22 @@ export default class DataBuffer extends EventEmitter { // return who's done first return Promise.race([dataPromise, timeoutPromise]) } + + // StackOverflow: https://stackoverflow.com/a/20392392 + tryParseJSONObject (jsonString){ + try { + var o = JSON.parse(jsonString); + + // Handle non-exception-throwing cases: + // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking, + // but... JSON.parse(null) returns null, and typeof null === "object", + // so we must check for that, too. Thankfully, null is falsey, so this suffices: + if (o && typeof o === "object") { + return o; + } + } + catch (e) { } + + return false; + } } diff --git a/src/DataBufferController.js b/src/DataBufferController.js index 92fbd22..f36a047 100644 --- a/src/DataBufferController.js +++ b/src/DataBufferController.js @@ -73,6 +73,7 @@ export default class DataBufferController { await this.#cache.quit() Object.values(this.#items).forEach(dataBuffer => dataBuffer.close()) this.#items = null + return 'bye' } /** diff --git a/src/__tests__/databuffercontroller.unit.js b/src/__tests__/databuffercontroller.unit.js index baea4a5..a543537 100644 --- a/src/__tests__/databuffercontroller.unit.js +++ b/src/__tests__/databuffercontroller.unit.js @@ -119,6 +119,42 @@ describe('Multicontroller - single cache usecase', () => { const expectedList = await Promise.all(list) expect(expectedList.filter(item => item).length).toBe(9) - await Promise.all([controllerA.close(), controllerB.close()]) + const result = await Promise.all([controllerA.close(), controllerB.close()]) + expect(result).toEqual(['bye', 'bye']) + }) + + test('Multiple controllers, test the semaphore checker', async () => { + const singleCache = new Cache() + const controllerA = await DataBufferController.create({ logger, cache: singleCache, ttl: 10, raceTimeMs: 5000 }) + const controllerB = await DataBufferController.create({ logger, cache: singleCache, ttl: 10, raceTimeMs: 2000 }) + const debugSpy = jest.fn() + controllerB.logger.debug = (txt) => debugSpy(txt) + + const key = 'semaphore-checker' + const list = [ + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key), + controllerA.get(key) + ] + const expectedA = await list[0] + expect(expectedA).toBe(undefined) + + list.push(controllerB.get(key)) + // give async job a change to initialize + await new Promise(resolve => setTimeout(resolve, 2500)) + expect(debugSpy).toBeCalledTimes(13) + expect(debugSpy).toHaveBeenCalledWith('Semaphore found') + expect(debugSpy).toHaveBeenNthCalledWith(10, 'Checking semaphore') + expect(debugSpy).toHaveBeenLastCalledWith('Semaphore is taking too long, aborting!') + + const expectedB = await list[5] + expect(expectedB).toBe(undefined) + + const result = await Promise.all([controllerA.close(), controllerB.close()]) + expect(result).toEqual(['bye', 'bye']) + expect(debugSpy).toHaveBeenCalledWith('Closing DataBuffer') + expect(debugSpy).toHaveBeenCalledWith('Stopping DataBufferController') }) }) From c4a833813703cf706f6ee4dfd736af967b07561d Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 29 Apr 2022 17:44:49 +0200 Subject: [PATCH 6/7] linter --- src/DataBuffer.js | 29 +++++++++++----------- src/__tests__/databuffercontroller.unit.js | 4 ++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/DataBuffer.js b/src/DataBuffer.js index ba2a62e..7a58f0b 100644 --- a/src/DataBuffer.js +++ b/src/DataBuffer.js @@ -13,6 +13,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +/* eslint max-statements: ["error", 25] */ + import EventEmitter from 'events' /** @@ -155,7 +157,7 @@ export default class DataBuffer extends EventEmitter { // Jump out of the recursion if this variable is set to false // or when the racetime has passed - if(this.#semaphoreChecking === false || this.#semaphoreCheckCounter > this.#semaphoreAmountChecks) { + if (this.#semaphoreChecking === false || this.#semaphoreCheckCounter > this.#semaphoreAmountChecks) { this.logger.debug('Semaphore is taking too long, aborting!') this.#semaphoreCheckCounter = 0 this.#semaphoreChecking = false @@ -237,20 +239,19 @@ export default class DataBuffer extends EventEmitter { } // StackOverflow: https://stackoverflow.com/a/20392392 - tryParseJSONObject (jsonString){ + tryParseJSONObject (jsonString) { try { - var o = JSON.parse(jsonString); - - // Handle non-exception-throwing cases: - // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking, - // but... JSON.parse(null) returns null, and typeof null === "object", - // so we must check for that, too. Thankfully, null is falsey, so this suffices: - if (o && typeof o === "object") { - return o; - } - } - catch (e) { } + const o = JSON.parse(jsonString) + + // Handle non-exception-throwing cases: + // Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking, + // but... JSON.parse(null) returns null, and typeof null === "object", + // so we must check for that, too. Thankfully, null is falsey, so this suffices: + if (o && typeof o === 'object') { + return o + } + } catch (e) { } - return false; + return false } } diff --git a/src/__tests__/databuffercontroller.unit.js b/src/__tests__/databuffercontroller.unit.js index a543537..9138bd1 100644 --- a/src/__tests__/databuffercontroller.unit.js +++ b/src/__tests__/databuffercontroller.unit.js @@ -13,7 +13,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import { expect, describe, test } from '@jest/globals' +/* eslint max-statements: ["error", 25] */ + +import { expect, describe, test, jest } from '@jest/globals' import DataBufferController from '../DataBufferController.js' import Cache from '../Cache.js' From 0cdbf76bd2c4ecee3e9db060e1c88a8da25e3683 Mon Sep 17 00:00:00 2001 From: Berry Piest Date: Fri, 29 Apr 2022 17:49:00 +0200 Subject: [PATCH 7/7] Added comment --- src/DataBuffer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DataBuffer.js b/src/DataBuffer.js index 7a58f0b..262ecc9 100644 --- a/src/DataBuffer.js +++ b/src/DataBuffer.js @@ -191,6 +191,7 @@ export default class DataBuffer extends EventEmitter { this.#currentStatus = this.#status.running this.#foundSemaphore = true this.logger.debug('Semaphore found') + // first one will start the checking, so there will only be one checkSemaphore process per databuffer if (this.#semaphoreChecking === false) { this.#semaphoreChecking = true this.checkSemaphore()