@actdim/utico 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,4 +7,4 @@ declare class AsyncMutex {
7
7
  dispatch<T>(fn: Executor<T>, timeoutMs?: number): Promise<T>;
8
8
  }
9
9
  export { AsyncMutex };
10
- //# sourceMappingURL=mutex.d.ts.map
10
+ //# sourceMappingURL=asyncMutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncMutex.d.ts","sourceRoot":"","sources":["../src/asyncMutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC,cAAM,UAAU;IACZ,OAAO,CAAC,KAAK,CAAqB;IAElC,OAAO,CAAC,MAAM,CAAS;IAEjB,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAgCnD,OAAO,IAAI,MAAM,IAAI,GAAG,IAAI;IAiBtB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAQrE;AAGD,OAAO,EACH,UAAU,EACb,CAAC"}
@@ -44,4 +44,4 @@ class n {
44
44
  export {
45
45
  n as AsyncMutex
46
46
  };
47
- //# sourceMappingURL=mutex.es.js.map
47
+ //# sourceMappingURL=asyncMutex.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncMutex.es.js","sources":["../src/asyncMutex.ts"],"sourcesContent":["import { Executor } from './typeCore';\r\n/**\r\n * Example:\r\n * const mutex = new Mutex();\r\n * await mutex.dispatch(async () => {...});\r\n */\r\n// type Executor<T> = () => Promise<T> | T;\r\n\r\nclass AsyncMutex { // or Mutex\r\n private mutex = Promise.resolve();\r\n\r\n private locked = false;\r\n \r\n async lock(timeoutMs?: number): Promise<() => void> {\r\n let begin: (unlock: () => void) => void = () => { };\r\n let timer: NodeJS.Timeout | undefined;\r\n\r\n const previous = this.mutex;\r\n this.mutex = this.mutex.then(() => new Promise(begin));\r\n this.locked = true;\r\n \r\n const lockPromise = new Promise<() => void>((resolve, reject) => {\r\n begin = resolve;\r\n\r\n if (timeoutMs) {\r\n timer = setTimeout(() => {\r\n reject(new Error(\"Mutex lock timeout\"));\r\n }, timeoutMs);\r\n }\r\n });\r\n\r\n try {\r\n const unlock = await lockPromise;\r\n clearTimeout(timer);\r\n return () => {\r\n this.locked = false;\r\n unlock();\r\n };\r\n } catch (err) {\r\n this.mutex = previous;\r\n this.locked = false;\r\n throw err;\r\n }\r\n }\r\n\r\n tryLock(): () => void | null {\r\n if (this.locked) return null;\r\n\r\n let unlock!: () => void;\r\n this.locked = true;\r\n this.mutex = this.mutex.then(\r\n () =>\r\n new Promise<void>(res => {\r\n unlock = () => {\r\n this.locked = false;\r\n res();\r\n };\r\n })\r\n );\r\n return unlock;\r\n }\r\n\r\n async dispatch<T>(fn: Executor<T>, timeoutMs?: number): Promise<T> {\r\n const unlock = await this.lock(timeoutMs);\r\n try {\r\n return await fn();\r\n } finally {\r\n unlock();\r\n }\r\n }\r\n}\r\n\r\n\r\nexport {\r\n AsyncMutex\r\n};"],"names":["AsyncMutex","timeoutMs","begin","timer","previous","lockPromise","resolve","reject","unlock","err","res","fn"],"mappings":"AAQA,MAAMA,EAAW;AAAA;AAAA,EACL,QAAQ,QAAQ,QAAA;AAAA,EAEhB,SAAS;AAAA,EAEjB,MAAM,KAAKC,GAAyC;AAChD,QAAIC,IAAsC,MAAM;AAAA,IAAE,GAC9CC;AAEJ,UAAMC,IAAW,KAAK;AACtB,SAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,IAAI,QAAQF,CAAK,CAAC,GACrD,KAAK,SAAS;AAEd,UAAMG,IAAc,IAAI,QAAoB,CAACC,GAASC,MAAW;AAC7D,MAAAL,IAAQI,GAEJL,MACAE,IAAQ,WAAW,MAAM;AACrB,QAAAI,EAAO,IAAI,MAAM,oBAAoB,CAAC;AAAA,MAC1C,GAAGN,CAAS;AAAA,IAEpB,CAAC;AAED,QAAI;AACA,YAAMO,IAAS,MAAMH;AACrB,0BAAaF,CAAK,GACX,MAAM;AACT,aAAK,SAAS,IACdK,EAAA;AAAA,MACJ;AAAA,IACJ,SAASC,GAAK;AACV,iBAAK,QAAQL,GACb,KAAK,SAAS,IACRK;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,UAA6B;AACzB,QAAI,KAAK,OAAQ,QAAO;AAExB,QAAID;AACJ,gBAAK,SAAS,IACd,KAAK,QAAQ,KAAK,MAAM;AAAA,MACpB,MACI,IAAI,QAAc,CAAAE,MAAO;AACrB,QAAAF,IAAS,MAAM;AACX,eAAK,SAAS,IACdE,EAAA;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IAAA,GAEFF;AAAA,EACX;AAAA,EAEA,MAAM,SAAYG,GAAiBV,GAAgC;AAC/D,UAAMO,IAAS,MAAM,KAAK,KAAKP,CAAS;AACxC,QAAI;AACA,aAAO,MAAMU,EAAA;AAAA,IACjB,UAAA;AACI,MAAAH,EAAA;AAAA,IACJ;AAAA,EACJ;AACJ;"}
@@ -1,4 +1,4 @@
1
- import { AsyncMutex as y } from "../mutex.es.js";
1
+ import { AsyncMutex as y } from "../asyncMutex.es.js";
2
2
  import { StoreDb as c } from "./storeDb.es.js";
3
3
  import { StructEventTarget as h } from "../structEvent.es.js";
4
4
  import n from "dexie";
@@ -1 +1 @@
1
- {"version":3,"file":"dataStore.es.js","sources":["../../src/store/dataStore.ts"],"sourcesContent":["import { AsyncMutex } from \"@/mutex\";\r\nimport { CommonPartFromSchema } from \"@/typeCore\";\r\nimport { EntryTypes, IDataItem, StoreDb } from \"./storeDb\";\r\nimport { StructEventTarget } from \"@/structEvent\";\r\nimport Dexie, { TransactionMode } from \"dexie\";\r\nimport { v4 as uuid } from \"uuid\";\r\nimport { strictSatisfies } from \"@/typeUtils\";\r\n\r\nconst mutex = new AsyncMutex();\r\n\r\n// BaseEntry/EntryBase\r\ntype CommonEntry = CommonPartFromSchema<EntryTypes>;\r\n\r\n// TODO: update\r\nexport type IDataStoreBase = {\r\n openAsync(): PromiseLike<void>;\r\n}\r\n\r\nexport class DataStore<TEntryTemplate extends keyof EntryTypes, TEventStruct extends Record<string, any> = unknown> extends StructEventTarget<TEventStruct> implements IDataStoreBase {\r\n\r\n protected _db: StoreDb<TEntryTemplate>;\r\n\r\n protected _isDisposed: boolean;\r\n\r\n protected static async $deleteAsync(name: string) {\r\n try {\r\n await mutex.dispatch(async () => {\r\n if (await StoreDb.exists(name)) {\r\n await StoreDb.delete(name);\r\n }\r\n });\r\n } catch (err) {\r\n if (err instanceof Dexie.InvalidStateError || err instanceof Dexie.VersionError) {\r\n console.warn(`[DataStore] delete(${name}) failed:`, err);\r\n } else {\r\n throw err;\r\n }\r\n }\r\n }\r\n\r\n protected static $existsAsync(name: string) {\r\n return Dexie.exists(name);\r\n }\r\n\r\n protected static async $openAsync<T extends IDataStoreBase>(name: string, factory: (name: string) => T) {\r\n return await mutex.dispatch(async () => {\r\n const store = factory(name);\r\n await store.openAsync();\r\n return store;\r\n });\r\n }\r\n\r\n constructor(name: string, entryTemplate: TEntryTemplate) {\r\n super();\r\n if (!name) {\r\n throw new Error(\"Name cannot be empty\");\r\n }\r\n this._isDisposed = false;\r\n this._db = new StoreDb(name, entryTemplate);\r\n }\r\n\r\n async openAsync() {\r\n if (!this._db.isOpen()) {\r\n try {\r\n await this._db.open();\r\n } catch (err) {\r\n if (err instanceof Dexie.OpenFailedError) {\r\n await this._db.open();\r\n } else {\r\n throw err;\r\n }\r\n }\r\n // TODO: log (this._db.verno, this._db._dbSchema etc)\r\n }\r\n }\r\n\r\n dispose() {\r\n if (!this._isDisposed) {\r\n if (this._db) {\r\n // this.exec(async () => {\r\n // \t// ...\r\n // }).then(() => {\r\n // \tthis._db = null;\r\n // });\r\n if (this._db.isOpen()) {\r\n this._db.close();\r\n }\r\n this._db = null;\r\n }\r\n this._isDisposed = true;\r\n }\r\n }\r\n\r\n protected async $execAsync<T>(action: () => Promise<T>, transactionMode: TransactionMode = \"rw\") {\r\n await this.openAsync();\r\n try {\r\n const result = await this._db.transaction(transactionMode, this._db.registry, this._db.data, async () => {\r\n return await action();\r\n });\r\n return result;\r\n } catch (err) {\r\n if (this._db.isOpen()) {\r\n // this._db.close(); // generally speaking: we don't (never) need to close a connection\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n async getKeysAsync() {\r\n return await this._db.registry.filter((_) => true).primaryKeys();\r\n }\r\n\r\n async getAsync(key: string): Promise<Readonly<EntryTypes[TEntryTemplate] & IDataItem>> {\r\n return await this.$execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n if (entry) {\r\n const data = await this._db.data.get(key);\r\n // const data = await this._db.data.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n return { ...entry, ...data };\r\n }\r\n return null;\r\n }, \"readonly\");\r\n }\r\n\r\n // getMany\r\n async bulkGetAsync(ids: string[]): Promise<{ [key: string]: Readonly<EntryTypes[TEntryTemplate] & IDataItem> }> {\r\n const result: { [key: string]: Readonly<EntryTypes[TEntryTemplate] & IDataItem> } = {};\r\n return await this.$execAsync(async () => {\r\n // const entries = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).anyOf(ids).toArray();\r\n const entries = await this._db.registry.bulkGet(ids);\r\n const entryMap: { [key: string]: EntryTypes[TEntryTemplate] } = entries.reduce((map, entry, i) => {\r\n map[entry.id] = entry;\r\n return map;\r\n }, {});\r\n\r\n // const dataItems = this._db.data.where(keyOf<IDataEntry>(\"id\")).anyOf(ids);\r\n // await dataItems.each((dataItem) => {\r\n // result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n // delete entryMap[dataItem.id];\r\n // });\r\n\r\n const dataItems = await this._db.data.bulkGet(ids);\r\n for (const dataItem of dataItems) {\r\n result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n delete entryMap[dataItem.id];\r\n }\r\n\r\n // TODO: update data entry \r\n // accessedAt,\r\n // updatedAt,\r\n // expiresAt // for sliding expiration\r\n\r\n for (const key of Object.keys(entryMap)) {\r\n // abandoned/orphaned entries:\r\n let dataItem: IDataItem = {\r\n id: key,\r\n value: undefined\r\n }\r\n result[key] = { ...entryMap[key], ...dataItem };\r\n // Object.defineProperty(result[key], keyOf<IDataItem>(\"value\"), {\r\n // writable: false,\r\n // get: function () {\r\n // throw new Error(\"Not found\");\r\n // }\r\n // });\r\n }\r\n\r\n return result;\r\n }, \"readonly\");\r\n }\r\n\r\n async containsAsync(key: string) {\r\n return await this.$execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n return entry != undefined;\r\n });\r\n }\r\n\r\n async deleteAsync(key: string) {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.delete(key);\r\n await this._db.data.delete(key);\r\n });\r\n }\r\n\r\n // deleteManyAsync\r\n async bulkDeleteAsync(keys: string[]) {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.bulkDelete(keys);\r\n await this._db.data.bulkDelete(keys);\r\n });\r\n }\r\n\r\n // upsertAsync\r\n protected async $setAsync(key: string, value: any) {\r\n return await this.$execAsync(async () => {\r\n let entry;\r\n const now = new Date().getTime();\r\n if (key) {\r\n entry = await this._db.registry.get(key);\r\n // entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n } else {\r\n key = uuid();\r\n }\r\n await this._db.registry.put(strictSatisfies<CommonEntry>()({\r\n id: key,\r\n createdAt: entry ? entry.createdAt : now,\r\n updatedAt: now\r\n }));\r\n await this._db.data.put({\r\n id: key,\r\n value: value\r\n });\r\n });\r\n }\r\n\r\n // getOrAddAsync\r\n protected async $getOrSetAsync(key: string, factory: (key: string) => any,) {\r\n return await this.$execAsync(async () => {\r\n if (!(await this.containsAsync(key))) {\r\n await this.$setAsync(key, factory(key));\r\n }\r\n return await this.getAsync(key);\r\n });\r\n }\r\n\r\n // clearAllAsync/evictAllAsync\r\n async clearAsync() {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.clear();\r\n await this._db.data.clear();\r\n });\r\n }\r\n\r\n // TODO: support bulkSetAsync\r\n}"],"names":["mutex","AsyncMutex","DataStore","StructEventTarget","name","StoreDb","err","Dexie","factory","store","entryTemplate","action","transactionMode","_","key","entry","data","ids","result","entryMap","map","i","dataItems","dataItem","keys","value","now","uuid","strictSatisfies"],"mappings":"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"}
1
+ {"version":3,"file":"dataStore.es.js","sources":["../../src/store/dataStore.ts"],"sourcesContent":["import { AsyncMutex } from \"@/asyncMutex\";\r\nimport { CommonPartFromSchema } from \"@/typeCore\";\r\nimport { EntryTypes, IDataItem, StoreDb } from \"./storeDb\";\r\nimport { StructEventTarget } from \"@/structEvent\";\r\nimport Dexie, { TransactionMode } from \"dexie\";\r\nimport { v4 as uuid } from \"uuid\";\r\nimport { strictSatisfies } from \"@/typeUtils\";\r\n\r\nconst mutex = new AsyncMutex();\r\n\r\n// BaseEntry/EntryBase\r\ntype CommonEntry = CommonPartFromSchema<EntryTypes>;\r\n\r\n// TODO: update\r\nexport type IDataStoreBase = {\r\n openAsync(): PromiseLike<void>;\r\n}\r\n\r\nexport class DataStore<TEntryTemplate extends keyof EntryTypes, TEventStruct extends Record<string, any> = unknown> extends StructEventTarget<TEventStruct> implements IDataStoreBase {\r\n\r\n protected _db: StoreDb<TEntryTemplate>;\r\n\r\n protected _isDisposed: boolean;\r\n\r\n protected static async $deleteAsync(name: string) {\r\n try {\r\n await mutex.dispatch(async () => {\r\n if (await StoreDb.exists(name)) {\r\n await StoreDb.delete(name);\r\n }\r\n });\r\n } catch (err) {\r\n if (err instanceof Dexie.InvalidStateError || err instanceof Dexie.VersionError) {\r\n console.warn(`[DataStore] delete(${name}) failed:`, err);\r\n } else {\r\n throw err;\r\n }\r\n }\r\n }\r\n\r\n protected static $existsAsync(name: string) {\r\n return Dexie.exists(name);\r\n }\r\n\r\n protected static async $openAsync<T extends IDataStoreBase>(name: string, factory: (name: string) => T) {\r\n return await mutex.dispatch(async () => {\r\n const store = factory(name);\r\n await store.openAsync();\r\n return store;\r\n });\r\n }\r\n\r\n constructor(name: string, entryTemplate: TEntryTemplate) {\r\n super();\r\n if (!name) {\r\n throw new Error(\"Name cannot be empty\");\r\n }\r\n this._isDisposed = false;\r\n this._db = new StoreDb(name, entryTemplate);\r\n }\r\n\r\n async openAsync() {\r\n if (!this._db.isOpen()) {\r\n try {\r\n await this._db.open();\r\n } catch (err) {\r\n if (err instanceof Dexie.OpenFailedError) {\r\n await this._db.open();\r\n } else {\r\n throw err;\r\n }\r\n }\r\n // TODO: log (this._db.verno, this._db._dbSchema etc)\r\n }\r\n }\r\n\r\n dispose() {\r\n if (!this._isDisposed) {\r\n if (this._db) {\r\n // this.exec(async () => {\r\n // \t// ...\r\n // }).then(() => {\r\n // \tthis._db = null;\r\n // });\r\n if (this._db.isOpen()) {\r\n this._db.close();\r\n }\r\n this._db = null;\r\n }\r\n this._isDisposed = true;\r\n }\r\n }\r\n\r\n protected async $execAsync<T>(action: () => Promise<T>, transactionMode: TransactionMode = \"rw\") {\r\n await this.openAsync();\r\n try {\r\n const result = await this._db.transaction(transactionMode, this._db.registry, this._db.data, async () => {\r\n return await action();\r\n });\r\n return result;\r\n } catch (err) {\r\n if (this._db.isOpen()) {\r\n // this._db.close(); // generally speaking: we don't (never) need to close a connection\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n async getKeysAsync() {\r\n return await this._db.registry.filter((_) => true).primaryKeys();\r\n }\r\n\r\n async getAsync(key: string): Promise<Readonly<EntryTypes[TEntryTemplate] & IDataItem>> {\r\n return await this.$execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n if (entry) {\r\n const data = await this._db.data.get(key);\r\n // const data = await this._db.data.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n return { ...entry, ...data };\r\n }\r\n return null;\r\n }, \"readonly\");\r\n }\r\n\r\n // getMany\r\n async bulkGetAsync(ids: string[]): Promise<{ [key: string]: Readonly<EntryTypes[TEntryTemplate] & IDataItem> }> {\r\n const result: { [key: string]: Readonly<EntryTypes[TEntryTemplate] & IDataItem> } = {};\r\n return await this.$execAsync(async () => {\r\n // const entries = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).anyOf(ids).toArray();\r\n const entries = await this._db.registry.bulkGet(ids);\r\n const entryMap: { [key: string]: EntryTypes[TEntryTemplate] } = entries.reduce((map, entry, i) => {\r\n map[entry.id] = entry;\r\n return map;\r\n }, {});\r\n\r\n // const dataItems = this._db.data.where(keyOf<IDataEntry>(\"id\")).anyOf(ids);\r\n // await dataItems.each((dataItem) => {\r\n // result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n // delete entryMap[dataItem.id];\r\n // });\r\n\r\n const dataItems = await this._db.data.bulkGet(ids);\r\n for (const dataItem of dataItems) {\r\n result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n delete entryMap[dataItem.id];\r\n }\r\n\r\n // TODO: update data entry \r\n // accessedAt,\r\n // updatedAt,\r\n // expiresAt // for sliding expiration\r\n\r\n for (const key of Object.keys(entryMap)) {\r\n // abandoned/orphaned entries:\r\n let dataItem: IDataItem = {\r\n id: key,\r\n value: undefined\r\n }\r\n result[key] = { ...entryMap[key], ...dataItem };\r\n // Object.defineProperty(result[key], keyOf<IDataItem>(\"value\"), {\r\n // writable: false,\r\n // get: function () {\r\n // throw new Error(\"Not found\");\r\n // }\r\n // });\r\n }\r\n\r\n return result;\r\n }, \"readonly\");\r\n }\r\n\r\n async containsAsync(key: string) {\r\n return await this.$execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n return entry != undefined;\r\n });\r\n }\r\n\r\n async deleteAsync(key: string) {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.delete(key);\r\n await this._db.data.delete(key);\r\n });\r\n }\r\n\r\n // deleteManyAsync\r\n async bulkDeleteAsync(keys: string[]) {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.bulkDelete(keys);\r\n await this._db.data.bulkDelete(keys);\r\n });\r\n }\r\n\r\n // upsertAsync\r\n protected async $setAsync(key: string, value: any) {\r\n return await this.$execAsync(async () => {\r\n let entry;\r\n const now = new Date().getTime();\r\n if (key) {\r\n entry = await this._db.registry.get(key);\r\n // entry = await this._db.registry.where(keyOf<IDataEntry>(\"id\")).equals(key).first();\r\n } else {\r\n key = uuid();\r\n }\r\n await this._db.registry.put(strictSatisfies<CommonEntry>()({\r\n id: key,\r\n createdAt: entry ? entry.createdAt : now,\r\n updatedAt: now\r\n }));\r\n await this._db.data.put({\r\n id: key,\r\n value: value\r\n });\r\n });\r\n }\r\n\r\n // getOrAddAsync\r\n protected async $getOrSetAsync(key: string, factory: (key: string) => any,) {\r\n return await this.$execAsync(async () => {\r\n if (!(await this.containsAsync(key))) {\r\n await this.$setAsync(key, factory(key));\r\n }\r\n return await this.getAsync(key);\r\n });\r\n }\r\n\r\n // clearAllAsync/evictAllAsync\r\n async clearAsync() {\r\n await this.$execAsync(async () => {\r\n await this._db.registry.clear();\r\n await this._db.data.clear();\r\n });\r\n }\r\n\r\n // TODO: support bulkSetAsync\r\n}"],"names":["mutex","AsyncMutex","DataStore","StructEventTarget","name","StoreDb","err","Dexie","factory","store","entryTemplate","action","transactionMode","_","key","entry","data","ids","result","entryMap","map","i","dataItems","dataItem","keys","value","now","uuid","strictSatisfies"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actdim/utico",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "A modern foundation toolkit for complex TypeScript apps",
5
5
  "author": "Pavel Borodaev",
6
6
  "license": "Proprietary",
@@ -1 +0,0 @@
1
- {"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../src/mutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC,cAAM,UAAU;IACZ,OAAO,CAAC,KAAK,CAAqB;IAElC,OAAO,CAAC,MAAM,CAAS;IAEjB,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAgCnD,OAAO,IAAI,MAAM,IAAI,GAAG,IAAI;IAiBtB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAQrE;AAGD,OAAO,EACH,UAAU,EACb,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mutex.es.js","sources":["../src/mutex.ts"],"sourcesContent":["import { Executor } from './typeCore';\r\n/**\r\n * Example:\r\n * const mutex = new Mutex();\r\n * await mutex.dispatch(async () => {...});\r\n */\r\n// type Executor<T> = () => Promise<T> | T;\r\n\r\nclass AsyncMutex { // or Mutex\r\n private mutex = Promise.resolve();\r\n\r\n private locked = false;\r\n \r\n async lock(timeoutMs?: number): Promise<() => void> {\r\n let begin: (unlock: () => void) => void = () => { };\r\n let timer: NodeJS.Timeout | undefined;\r\n\r\n const previous = this.mutex;\r\n this.mutex = this.mutex.then(() => new Promise(begin));\r\n this.locked = true;\r\n \r\n const lockPromise = new Promise<() => void>((resolve, reject) => {\r\n begin = resolve;\r\n\r\n if (timeoutMs) {\r\n timer = setTimeout(() => {\r\n reject(new Error(\"Mutex lock timeout\"));\r\n }, timeoutMs);\r\n }\r\n });\r\n\r\n try {\r\n const unlock = await lockPromise;\r\n clearTimeout(timer);\r\n return () => {\r\n this.locked = false;\r\n unlock();\r\n };\r\n } catch (err) {\r\n this.mutex = previous;\r\n this.locked = false;\r\n throw err;\r\n }\r\n }\r\n\r\n tryLock(): () => void | null {\r\n if (this.locked) return null;\r\n\r\n let unlock!: () => void;\r\n this.locked = true;\r\n this.mutex = this.mutex.then(\r\n () =>\r\n new Promise<void>(res => {\r\n unlock = () => {\r\n this.locked = false;\r\n res();\r\n };\r\n })\r\n );\r\n return unlock;\r\n }\r\n\r\n async dispatch<T>(fn: Executor<T>, timeoutMs?: number): Promise<T> {\r\n const unlock = await this.lock(timeoutMs);\r\n try {\r\n return await fn();\r\n } finally {\r\n unlock();\r\n }\r\n }\r\n}\r\n\r\n\r\nexport {\r\n AsyncMutex\r\n};"],"names":["AsyncMutex","timeoutMs","begin","timer","previous","lockPromise","resolve","reject","unlock","err","res","fn"],"mappings":"AAQA,MAAMA,EAAW;AAAA;AAAA,EACL,QAAQ,QAAQ,QAAA;AAAA,EAEhB,SAAS;AAAA,EAEjB,MAAM,KAAKC,GAAyC;AAChD,QAAIC,IAAsC,MAAM;AAAA,IAAE,GAC9CC;AAEJ,UAAMC,IAAW,KAAK;AACtB,SAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,IAAI,QAAQF,CAAK,CAAC,GACrD,KAAK,SAAS;AAEd,UAAMG,IAAc,IAAI,QAAoB,CAACC,GAASC,MAAW;AAC7D,MAAAL,IAAQI,GAEJL,MACAE,IAAQ,WAAW,MAAM;AACrB,QAAAI,EAAO,IAAI,MAAM,oBAAoB,CAAC;AAAA,MAC1C,GAAGN,CAAS;AAAA,IAEpB,CAAC;AAED,QAAI;AACA,YAAMO,IAAS,MAAMH;AACrB,0BAAaF,CAAK,GACX,MAAM;AACT,aAAK,SAAS,IACdK,EAAA;AAAA,MACJ;AAAA,IACJ,SAASC,GAAK;AACV,iBAAK,QAAQL,GACb,KAAK,SAAS,IACRK;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,UAA6B;AACzB,QAAI,KAAK,OAAQ,QAAO;AAExB,QAAID;AACJ,gBAAK,SAAS,IACd,KAAK,QAAQ,KAAK,MAAM;AAAA,MACpB,MACI,IAAI,QAAc,CAAAE,MAAO;AACrB,QAAAF,IAAS,MAAM;AACX,eAAK,SAAS,IACdE,EAAA;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IAAA,GAEFF;AAAA,EACX;AAAA,EAEA,MAAM,SAAYG,GAAiBV,GAAgC;AAC/D,UAAMO,IAAS,MAAM,KAAK,KAAKP,CAAS;AACxC,QAAI;AACA,aAAO,MAAMU,EAAA;AAAA,IACjB,UAAA;AACI,MAAAH,EAAA;AAAA,IACJ;AAAA,EACJ;AACJ;"}