@dr.pogodin/js-utils 0.1.0 → 0.1.1

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.
package/README.md CHANGED
@@ -45,7 +45,13 @@ and used in the same way):
45
45
  - [YEAR_MS](https://dr.pogodin.studio/docs/react-utils/docs/api/utils/time#year_ms)
46
46
  — One year expressed in milliseconds.
47
47
 
48
+ ### Types
49
+ - `ObjectKey` — **string** | **number** | **symbol** —
50
+ The most generic valid type of an object key in TypeScript.
51
+
48
52
  ### Functions
53
+ - `assertEmptyObject()` — Asserts given object is empty,
54
+ both compile- and run-time.
49
55
  - [timer()] — Creates a [Barrier] which resolves after the specified timeout.
50
56
  - [withRetries()] — Attempts a given action multiple times until its succeeds.
51
57
 
@@ -53,8 +59,9 @@ and used in the same way):
53
59
  - [Barrier] — A [Promise] with **resolve()** and **reject()** exposed as
54
60
  instance methods.
55
61
 
56
- - `Cached` — Implements a cache of (a)synchronously retrieved items with
57
- timestamp-based expiration. _To be documented_.
62
+ - [Cached](https://dr.pogodin.studio/docs/react-utils/docs/api/classes/Cached)
63
+ — A cache of (a)synchronously retrieved items with timestamp-based
64
+ expiration.
58
65
 
59
66
  - [Emitter](https://dr.pogodin.studio/docs/react-utils/docs/api/classes/Emitter)
60
67
  — Simple listeneable data emitter.
@@ -16,17 +16,17 @@ function getTimestamp(e) {
16
16
  return Array.isArray(e) ? e[1] : e.timestamp;
17
17
  }
18
18
  class Cached {
19
- data = {};
20
- oldestTimestamp = Number.MAX_SAFE_INTEGER;
19
+ pData = {};
20
+ pOldestTimestamp = Number.MAX_SAFE_INTEGER;
21
21
 
22
22
  /** For test use only. */
23
- get _data() {
24
- return this.data;
23
+ get data() {
24
+ return this.pData;
25
25
  }
26
26
 
27
27
  /** For test use only. */
28
- get _oldestTimestamp() {
29
- return this.oldestTimestamp;
28
+ get oldestTimestamp() {
29
+ return this.pOldestTimestamp;
30
30
  }
31
31
  constructor(maxage, getter) {
32
32
  this.maxage = maxage;
@@ -36,44 +36,56 @@ class Cached {
36
36
  /** Removes stale items from the cache, and updates .oldestTimestamp. */
37
37
  cleanCache() {
38
38
  const deadline = Date.now() - this.maxage;
39
- this.oldestTimestamp = Number.MAX_SAFE_INTEGER;
40
- for (const [key, entry] of Object.entries(this.data)) {
39
+ this.pOldestTimestamp = Number.MAX_SAFE_INTEGER;
40
+ for (const [key, entry] of Object.entries(this.pData)) {
41
41
  const timestamp = getTimestamp(entry);
42
- if (timestamp < deadline) delete this.data[key];else if (timestamp < this.oldestTimestamp) {
43
- this.oldestTimestamp = timestamp;
42
+ if (timestamp < deadline) delete this.pData[key];else if (timestamp < this.pOldestTimestamp) {
43
+ this.pOldestTimestamp = timestamp;
44
44
  }
45
45
  }
46
46
  }
47
47
 
48
+ /**
49
+ * Adds entry to the cache.
50
+ * NOTE: It assumes entry's timestamp is the current moment (for the cache
51
+ * cleaning purposes; if it is not, but it is a past timestamp, nothing bad
52
+ * will happen, just some cleaning operation will be skipped).
53
+ */
54
+ setEntry(id, entry) {
55
+ this.pData[id] = entry;
56
+ const timestamp = getTimestamp(entry);
57
+ if (timestamp < this.pOldestTimestamp) this.pOldestTimestamp = timestamp;else if (this.pOldestTimestamp < timestamp - this.maxage) this.cleanCache();
58
+ }
59
+
48
60
  /** Adds `datum` to the cache, and removes stale items from the cache. */
49
61
  set(id, datum) {
50
- const now = Date.now();
51
- if (this.oldestTimestamp < now - this.maxage) this.cleanCache();
52
- if (now < this.oldestTimestamp) this.oldestTimestamp = now;
53
- const res = [datum, now];
54
- this.data[id] = res;
62
+ const res = [datum, Date.now()];
63
+ this.setEntry(id, res);
55
64
  return res;
56
65
  }
57
66
 
58
- /** Retrieves envelope of the specified datum, either read from the cache,
59
- * or retrieved using the getter provided at construction time. */
60
- getEntry(id) {
67
+ /**
68
+ * Retrieves envelope of the specified datum, either read from the cache,
69
+ * or retrieved using the getter provided at construction time.
70
+ */
71
+ getEntry(id, forceRefresh) {
61
72
  const now = Date.now();
62
- let cached = this.data[id];
63
- if (cached && getTimestamp(cached) >= now - this.maxage) return cached;
73
+ if (!forceRefresh) {
74
+ const cached = this.pData[id];
75
+ if (cached && getTimestamp(cached) >= now - this.maxage) return cached;
76
+ }
64
77
  const itemOrPromise = this.getter(id);
65
78
  if (!(itemOrPromise instanceof Promise)) {
66
79
  return this.set(id, itemOrPromise);
67
80
  }
68
- cached = addTimestamp(itemOrPromise.then(item => this.set(id, item)), now);
69
- if (now < this.oldestTimestamp) this.oldestTimestamp = now;
70
- this.data[id] = cached;
71
- return cached;
81
+ const promise = addTimestamp(itemOrPromise.then(item => this.set(id, item)), now);
82
+ this.setEntry(id, promise);
83
+ return promise;
72
84
  }
73
85
 
74
86
  /** Gets item. */
75
- get(id) {
76
- const entry = this.getEntry(id);
87
+ get(id, forceRefresh) {
88
+ const entry = this.getEntry(id, forceRefresh);
77
89
  return Array.isArray(entry) ? entry[0] : entry.then(e => e[0]);
78
90
  }
79
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Cached.js","names":["addTimestamp","promise","timestamp","res","getTimestamp","e","Array","isArray","Cached","data","oldestTimestamp","Number","MAX_SAFE_INTEGER","_data","_oldestTimestamp","constructor","maxage","getter","cleanCache","deadline","Date","now","key","entry","Object","entries","set","id","datum","getEntry","cached","itemOrPromise","Promise","then","item","get","exports"],"sources":["../../src/Cached.ts"],"sourcesContent":["type CachedT<T> = [T, number];\ntype TimestampedPromise<T> = Promise<T> & { timestamp: number };\ntype EntryT<T> = CachedT<T> | TimestampedPromise<CachedT<T>>;\n\n/** Adds timestamp to the promise. */\nfunction addTimestamp<T>(\n promise: Promise<T>,\n timestamp: number,\n): TimestampedPromise<T> {\n const res = promise as TimestampedPromise<T>;\n res.timestamp = timestamp;\n return res;\n}\n\n/** Gets entry timestamp. */\nfunction getTimestamp<T>(e: EntryT<T>): number {\n return Array.isArray(e) ? e[1] : e.timestamp;\n}\n\nexport class Cached<T> {\n private data: Record<string, EntryT<T>> = {};\n private oldestTimestamp = Number.MAX_SAFE_INTEGER;\n\n /** For test use only. */\n get _data(): Readonly<Record<string, EntryT<T>>> {\n return this.data;\n }\n\n /** For test use only. */\n get _oldestTimestamp(): number {\n return this.oldestTimestamp;\n }\n\n constructor(\n public readonly maxage: number,\n private getter: (id: string) => T | Promise<T>,\n ) {}\n\n /** Removes stale items from the cache, and updates .oldestTimestamp. */\n private cleanCache() {\n const deadline = Date.now() - this.maxage;\n this.oldestTimestamp = Number.MAX_SAFE_INTEGER;\n for (const [key, entry] of Object.entries(this.data)) {\n const timestamp = getTimestamp(entry);\n if (timestamp < deadline) delete this.data[key];\n else if (timestamp < this.oldestTimestamp) {\n this.oldestTimestamp = timestamp;\n }\n }\n }\n\n /** Adds `datum` to the cache, and removes stale items from the cache. */\n private set(id: string, datum: T): CachedT<T> {\n const now = Date.now();\n if (this.oldestTimestamp < now - this.maxage) this.cleanCache();\n if (now < this.oldestTimestamp) this.oldestTimestamp = now;\n const res: CachedT<T> = [datum, now];\n this.data[id] = res;\n return res;\n }\n\n /** Retrieves envelope of the specified datum, either read from the cache,\n * or retrieved using the getter provided at construction time. */\n private getEntry(id: string): EntryT<T> {\n const now = Date.now();\n\n let cached = this.data[id];\n if (cached && getTimestamp(cached) >= now - this.maxage) return cached;\n\n const itemOrPromise = this.getter(id);\n if (!(itemOrPromise instanceof Promise)) {\n return this.set(id, itemOrPromise);\n }\n\n cached = addTimestamp(\n itemOrPromise.then((item) => this.set(id, item)),\n now,\n );\n if (now < this.oldestTimestamp) this.oldestTimestamp = now;\n this.data[id] = cached;\n return cached;\n }\n\n /** Gets item. */\n get(id: string): T | Promise<T> {\n const entry = this.getEntry(id);\n return Array.isArray(entry) ? entry[0] : entry.then((e) => e[0]);\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"Cached.js","names":["addTimestamp","promise","timestamp","res","getTimestamp","e","Array","isArray","Cached","pData","pOldestTimestamp","Number","MAX_SAFE_INTEGER","data","oldestTimestamp","constructor","maxage","getter","cleanCache","deadline","Date","now","key","entry","Object","entries","setEntry","id","set","datum","getEntry","forceRefresh","cached","itemOrPromise","Promise","then","item","get","exports"],"sources":["../../src/Cached.ts"],"sourcesContent":["type CachedT<T> = [T, number];\ntype TimestampedPromise<T> = Promise<T> & { timestamp: number };\ntype EntryT<T> = CachedT<T> | TimestampedPromise<CachedT<T>>;\n\n/** Adds timestamp to the promise. */\nfunction addTimestamp<T>(\n promise: Promise<T>,\n timestamp: number,\n): TimestampedPromise<T> {\n const res = promise as TimestampedPromise<T>;\n res.timestamp = timestamp;\n return res;\n}\n\n/** Gets entry timestamp. */\nfunction getTimestamp<T>(e: EntryT<T>): number {\n return Array.isArray(e) ? e[1] : e.timestamp;\n}\n\nexport class Cached<T> {\n private pData: Record<string, EntryT<T>> = {};\n private pOldestTimestamp = Number.MAX_SAFE_INTEGER;\n\n /** For test use only. */\n get data(): Readonly<Record<string, EntryT<T>>> {\n return this.pData;\n }\n\n /** For test use only. */\n get oldestTimestamp(): number {\n return this.pOldestTimestamp;\n }\n\n constructor(\n public readonly maxage: number,\n private getter: (id: string) => T | Promise<T>,\n ) {}\n\n /** Removes stale items from the cache, and updates .oldestTimestamp. */\n private cleanCache() {\n const deadline = Date.now() - this.maxage;\n this.pOldestTimestamp = Number.MAX_SAFE_INTEGER;\n for (const [key, entry] of Object.entries(this.pData)) {\n const timestamp = getTimestamp(entry);\n if (timestamp < deadline) delete this.pData[key];\n else if (timestamp < this.pOldestTimestamp) {\n this.pOldestTimestamp = timestamp;\n }\n }\n }\n\n /**\n * Adds entry to the cache.\n * NOTE: It assumes entry's timestamp is the current moment (for the cache\n * cleaning purposes; if it is not, but it is a past timestamp, nothing bad\n * will happen, just some cleaning operation will be skipped).\n */\n private setEntry(id: string, entry: EntryT<T>) {\n this.pData[id] = entry;\n const timestamp = getTimestamp(entry);\n if (timestamp < this.pOldestTimestamp) this.pOldestTimestamp = timestamp;\n else if (this.pOldestTimestamp < timestamp - this.maxage) this.cleanCache();\n }\n\n /** Adds `datum` to the cache, and removes stale items from the cache. */\n private set(id: string, datum: T): CachedT<T> {\n const res: CachedT<T> = [datum, Date.now()];\n this.setEntry(id, res);\n return res;\n }\n\n /**\n * Retrieves envelope of the specified datum, either read from the cache,\n * or retrieved using the getter provided at construction time.\n */\n private getEntry(id: string, forceRefresh?: boolean): EntryT<T> {\n const now = Date.now();\n\n if (!forceRefresh) {\n const cached = this.pData[id];\n if (cached && getTimestamp(cached) >= now - this.maxage) return cached;\n }\n\n const itemOrPromise = this.getter(id);\n if (!(itemOrPromise instanceof Promise)) {\n return this.set(id, itemOrPromise);\n }\n\n const promise = addTimestamp(\n itemOrPromise.then((item) => this.set(id, item)),\n now,\n );\n\n this.setEntry(id, promise);\n\n return promise;\n }\n\n /** Gets item. */\n get(id: string, forceRefresh?: boolean): T | Promise<T> {\n const entry = this.getEntry(id, forceRefresh);\n return Array.isArray(entry) ? entry[0] : entry.then((e) => e[0]);\n }\n}\n"],"mappings":";;;;;;AAIA;AACA,SAASA,YAAYA,CACnBC,OAAmB,EACnBC,SAAiB,EACM;EACvB,MAAMC,GAAG,GAAGF,OAAgC;EAC5CE,GAAG,CAACD,SAAS,GAAGA,SAAS;EACzB,OAAOC,GAAG;AACZ;;AAEA;AACA,SAASC,YAAYA,CAAIC,CAAY,EAAU;EAC7C,OAAOC,KAAK,CAACC,OAAO,CAACF,CAAC,CAAC,GAAGA,CAAC,CAAC,CAAC,CAAC,GAAGA,CAAC,CAACH,SAAS;AAC9C;AAEO,MAAMM,MAAM,CAAI;EACbC,KAAK,GAA8B,CAAC,CAAC;EACrCC,gBAAgB,GAAGC,MAAM,CAACC,gBAAgB;;EAElD;EACA,IAAIC,IAAIA,CAAA,EAAwC;IAC9C,OAAO,IAAI,CAACJ,KAAK;EACnB;;EAEA;EACA,IAAIK,eAAeA,CAAA,EAAW;IAC5B,OAAO,IAAI,CAACJ,gBAAgB;EAC9B;EAEAK,WAAWA,CACOC,MAAc,EACtBC,MAAsC,EAC9C;IAAA,KAFgBD,MAAc,GAAdA,MAAc;IAAA,KACtBC,MAAsC,GAAtCA,MAAsC;EAC7C;;EAEH;EACQC,UAAUA,CAAA,EAAG;IACnB,MAAMC,QAAQ,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAACL,MAAM;IACzC,IAAI,CAACN,gBAAgB,GAAGC,MAAM,CAACC,gBAAgB;IAC/C,KAAK,MAAM,CAACU,GAAG,EAAEC,KAAK,CAAC,IAAIC,MAAM,CAACC,OAAO,CAAC,IAAI,CAAChB,KAAK,CAAC,EAAE;MACrD,MAAMP,SAAS,GAAGE,YAAY,CAACmB,KAAK,CAAC;MACrC,IAAIrB,SAAS,GAAGiB,QAAQ,EAAE,OAAO,IAAI,CAACV,KAAK,CAACa,GAAG,CAAC,CAAC,KAC5C,IAAIpB,SAAS,GAAG,IAAI,CAACQ,gBAAgB,EAAE;QAC1C,IAAI,CAACA,gBAAgB,GAAGR,SAAS;MACnC;IACF;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;EACUwB,QAAQA,CAACC,EAAU,EAAEJ,KAAgB,EAAE;IAC7C,IAAI,CAACd,KAAK,CAACkB,EAAE,CAAC,GAAGJ,KAAK;IACtB,MAAMrB,SAAS,GAAGE,YAAY,CAACmB,KAAK,CAAC;IACrC,IAAIrB,SAAS,GAAG,IAAI,CAACQ,gBAAgB,EAAE,IAAI,CAACA,gBAAgB,GAAGR,SAAS,CAAC,KACpE,IAAI,IAAI,CAACQ,gBAAgB,GAAGR,SAAS,GAAG,IAAI,CAACc,MAAM,EAAE,IAAI,CAACE,UAAU,CAAC,CAAC;EAC7E;;EAEA;EACQU,GAAGA,CAACD,EAAU,EAAEE,KAAQ,EAAc;IAC5C,MAAM1B,GAAe,GAAG,CAAC0B,KAAK,EAAET,IAAI,CAACC,GAAG,CAAC,CAAC,CAAC;IAC3C,IAAI,CAACK,QAAQ,CAACC,EAAE,EAAExB,GAAG,CAAC;IACtB,OAAOA,GAAG;EACZ;;EAEA;AACF;AACA;AACA;EACU2B,QAAQA,CAACH,EAAU,EAAEI,YAAsB,EAAa;IAC9D,MAAMV,GAAG,GAAGD,IAAI,CAACC,GAAG,CAAC,CAAC;IAEtB,IAAI,CAACU,YAAY,EAAE;MACjB,MAAMC,MAAM,GAAG,IAAI,CAACvB,KAAK,CAACkB,EAAE,CAAC;MAC7B,IAAIK,MAAM,IAAI5B,YAAY,CAAC4B,MAAM,CAAC,IAAIX,GAAG,GAAG,IAAI,CAACL,MAAM,EAAE,OAAOgB,MAAM;IACxE;IAEA,MAAMC,aAAa,GAAG,IAAI,CAAChB,MAAM,CAACU,EAAE,CAAC;IACrC,IAAI,EAAEM,aAAa,YAAYC,OAAO,CAAC,EAAE;MACvC,OAAO,IAAI,CAACN,GAAG,CAACD,EAAE,EAAEM,aAAa,CAAC;IACpC;IAEA,MAAMhC,OAAO,GAAGD,YAAY,CAC1BiC,aAAa,CAACE,IAAI,CAAEC,IAAI,IAAK,IAAI,CAACR,GAAG,CAACD,EAAE,EAAES,IAAI,CAAC,CAAC,EAChDf,GACF,CAAC;IAED,IAAI,CAACK,QAAQ,CAACC,EAAE,EAAE1B,OAAO,CAAC;IAE1B,OAAOA,OAAO;EAChB;;EAEA;EACAoC,GAAGA,CAACV,EAAU,EAAEI,YAAsB,EAAkB;IACtD,MAAMR,KAAK,GAAG,IAAI,CAACO,QAAQ,CAACH,EAAE,EAAEI,YAAY,CAAC;IAC7C,OAAOzB,KAAK,CAACC,OAAO,CAACgB,KAAK,CAAC,GAAGA,KAAK,CAAC,CAAC,CAAC,GAAGA,KAAK,CAACY,IAAI,CAAE9B,CAAC,IAAKA,CAAC,CAAC,CAAC,CAAC,CAAC;EAClE;AACF;AAACiC,OAAA,CAAA9B,MAAA,GAAAA,MAAA","ignoreList":[]}
@@ -65,5 +65,17 @@ Object.keys(_time).forEach(function (key) {
65
65
  }
66
66
  });
67
67
  });
68
+ var _types = require("./types");
69
+ Object.keys(_types).forEach(function (key) {
70
+ if (key === "default" || key === "__esModule") return;
71
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
72
+ if (key in exports && exports[key] === _types[key]) return;
73
+ Object.defineProperty(exports, key, {
74
+ enumerable: true,
75
+ get: function () {
76
+ return _types[key];
77
+ }
78
+ });
79
+ });
68
80
  var _withRetries = _interopRequireDefault(require("./withRetries"));
69
81
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_Barrier","_interopRequireDefault","require","_Cached","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_Emitter","_Semaphore","_time","_withRetries"],"sources":["../../src/index.ts"],"sourcesContent":["export { default as Barrier } from './Barrier';\nexport * from './Cached';\nexport * from './Emitter';\nexport { default as Semaphore } from './Semaphore';\nexport * from './time';\nexport { default as withRetries } from './withRetries';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,QAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAF,OAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,OAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,OAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AACA,IAAAS,QAAA,GAAAd,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAW,QAAA,EAAAV,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAS,QAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,QAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,UAAA,GAAAhB,sBAAA,CAAAC,OAAA;AACA,IAAAgB,KAAA,GAAAhB,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAa,KAAA,EAAAZ,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAW,KAAA,CAAAX,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,KAAA,CAAAX,GAAA;IAAA;EAAA;AAAA;AACA,IAAAY,YAAA,GAAAlB,sBAAA,CAAAC,OAAA","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_Barrier","_interopRequireDefault","require","_Cached","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_Emitter","_Semaphore","_time","_types","_withRetries"],"sources":["../../src/index.ts"],"sourcesContent":["export { default as Barrier } from './Barrier';\nexport * from './Cached';\nexport * from './Emitter';\nexport { default as Semaphore } from './Semaphore';\nexport * from './time';\nexport * from './types';\nexport { default as withRetries } from './withRetries';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,QAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAF,OAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,OAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,OAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AACA,IAAAS,QAAA,GAAAd,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAW,QAAA,EAAAV,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAS,QAAA,CAAAT,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,QAAA,CAAAT,GAAA;IAAA;EAAA;AAAA;AACA,IAAAU,UAAA,GAAAhB,sBAAA,CAAAC,OAAA;AACA,IAAAgB,KAAA,GAAAhB,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAa,KAAA,EAAAZ,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAW,KAAA,CAAAX,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,KAAA,CAAAX,GAAA;IAAA;EAAA;AAAA;AACA,IAAAY,MAAA,GAAAjB,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAc,MAAA,EAAAb,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAY,MAAA,CAAAZ,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAI,MAAA,CAAAZ,GAAA;IAAA;EAAA;AAAA;AACA,IAAAa,YAAA,GAAAnB,sBAAA,CAAAC,OAAA","ignoreList":[]}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.assertEmptyObject = assertEmptyObject;
7
+ // Misc TypeScript-specific utilities.
8
+
9
+ /** The most permissive object key type. */
10
+
11
+ /** Asserts given object is empty, both compile- and run-time. */
12
+ function assertEmptyObject(object) {
13
+ if (Object.keys(object).length) throw Error('The object is not empty');
14
+ }
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":["assertEmptyObject","object","Object","keys","length","Error"],"sources":["../../src/types.ts"],"sourcesContent":["// Misc TypeScript-specific utilities.\n\n/** The most permissive object key type. */\nexport type ObjectKey = string | number | symbol;\n\n/** Asserts given object is empty, both compile- and run-time. */\nexport function assertEmptyObject(object: Record<ObjectKey, never>): void {\n if (Object.keys(object).length) throw Error('The object is not empty');\n}\n"],"mappings":";;;;;;AAAA;;AAEA;;AAGA;AACO,SAASA,iBAAiBA,CAACC,MAAgC,EAAQ;EACxE,IAAIC,MAAM,CAACC,IAAI,CAACF,MAAM,CAAC,CAACG,MAAM,EAAE,MAAMC,KAAK,CAAC,yBAAyB,CAAC;AACxE","ignoreList":[]}
@@ -10,17 +10,17 @@ function getTimestamp(e) {
10
10
  return Array.isArray(e) ? e[1] : e.timestamp;
11
11
  }
12
12
  export class Cached {
13
- data = {};
14
- oldestTimestamp = Number.MAX_SAFE_INTEGER;
13
+ pData = {};
14
+ pOldestTimestamp = Number.MAX_SAFE_INTEGER;
15
15
 
16
16
  /** For test use only. */
17
- get _data() {
18
- return this.data;
17
+ get data() {
18
+ return this.pData;
19
19
  }
20
20
 
21
21
  /** For test use only. */
22
- get _oldestTimestamp() {
23
- return this.oldestTimestamp;
22
+ get oldestTimestamp() {
23
+ return this.pOldestTimestamp;
24
24
  }
25
25
  constructor(maxage, getter) {
26
26
  this.maxage = maxage;
@@ -30,44 +30,56 @@ export class Cached {
30
30
  /** Removes stale items from the cache, and updates .oldestTimestamp. */
31
31
  cleanCache() {
32
32
  const deadline = Date.now() - this.maxage;
33
- this.oldestTimestamp = Number.MAX_SAFE_INTEGER;
34
- for (const [key, entry] of Object.entries(this.data)) {
33
+ this.pOldestTimestamp = Number.MAX_SAFE_INTEGER;
34
+ for (const [key, entry] of Object.entries(this.pData)) {
35
35
  const timestamp = getTimestamp(entry);
36
- if (timestamp < deadline) delete this.data[key];else if (timestamp < this.oldestTimestamp) {
37
- this.oldestTimestamp = timestamp;
36
+ if (timestamp < deadline) delete this.pData[key];else if (timestamp < this.pOldestTimestamp) {
37
+ this.pOldestTimestamp = timestamp;
38
38
  }
39
39
  }
40
40
  }
41
41
 
42
+ /**
43
+ * Adds entry to the cache.
44
+ * NOTE: It assumes entry's timestamp is the current moment (for the cache
45
+ * cleaning purposes; if it is not, but it is a past timestamp, nothing bad
46
+ * will happen, just some cleaning operation will be skipped).
47
+ */
48
+ setEntry(id, entry) {
49
+ this.pData[id] = entry;
50
+ const timestamp = getTimestamp(entry);
51
+ if (timestamp < this.pOldestTimestamp) this.pOldestTimestamp = timestamp;else if (this.pOldestTimestamp < timestamp - this.maxage) this.cleanCache();
52
+ }
53
+
42
54
  /** Adds `datum` to the cache, and removes stale items from the cache. */
43
55
  set(id, datum) {
44
- const now = Date.now();
45
- if (this.oldestTimestamp < now - this.maxage) this.cleanCache();
46
- if (now < this.oldestTimestamp) this.oldestTimestamp = now;
47
- const res = [datum, now];
48
- this.data[id] = res;
56
+ const res = [datum, Date.now()];
57
+ this.setEntry(id, res);
49
58
  return res;
50
59
  }
51
60
 
52
- /** Retrieves envelope of the specified datum, either read from the cache,
53
- * or retrieved using the getter provided at construction time. */
54
- getEntry(id) {
61
+ /**
62
+ * Retrieves envelope of the specified datum, either read from the cache,
63
+ * or retrieved using the getter provided at construction time.
64
+ */
65
+ getEntry(id, forceRefresh) {
55
66
  const now = Date.now();
56
- let cached = this.data[id];
57
- if (cached && getTimestamp(cached) >= now - this.maxage) return cached;
67
+ if (!forceRefresh) {
68
+ const cached = this.pData[id];
69
+ if (cached && getTimestamp(cached) >= now - this.maxage) return cached;
70
+ }
58
71
  const itemOrPromise = this.getter(id);
59
72
  if (!(itemOrPromise instanceof Promise)) {
60
73
  return this.set(id, itemOrPromise);
61
74
  }
62
- cached = addTimestamp(itemOrPromise.then(item => this.set(id, item)), now);
63
- if (now < this.oldestTimestamp) this.oldestTimestamp = now;
64
- this.data[id] = cached;
65
- return cached;
75
+ const promise = addTimestamp(itemOrPromise.then(item => this.set(id, item)), now);
76
+ this.setEntry(id, promise);
77
+ return promise;
66
78
  }
67
79
 
68
80
  /** Gets item. */
69
- get(id) {
70
- const entry = this.getEntry(id);
81
+ get(id, forceRefresh) {
82
+ const entry = this.getEntry(id, forceRefresh);
71
83
  return Array.isArray(entry) ? entry[0] : entry.then(e => e[0]);
72
84
  }
73
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Cached.js","names":["addTimestamp","promise","timestamp","res","getTimestamp","e","Array","isArray","Cached","data","oldestTimestamp","Number","MAX_SAFE_INTEGER","_data","_oldestTimestamp","constructor","maxage","getter","cleanCache","deadline","Date","now","key","entry","Object","entries","set","id","datum","getEntry","cached","itemOrPromise","Promise","then","item","get"],"sources":["../../src/Cached.ts"],"sourcesContent":["type CachedT<T> = [T, number];\ntype TimestampedPromise<T> = Promise<T> & { timestamp: number };\ntype EntryT<T> = CachedT<T> | TimestampedPromise<CachedT<T>>;\n\n/** Adds timestamp to the promise. */\nfunction addTimestamp<T>(\n promise: Promise<T>,\n timestamp: number,\n): TimestampedPromise<T> {\n const res = promise as TimestampedPromise<T>;\n res.timestamp = timestamp;\n return res;\n}\n\n/** Gets entry timestamp. */\nfunction getTimestamp<T>(e: EntryT<T>): number {\n return Array.isArray(e) ? e[1] : e.timestamp;\n}\n\nexport class Cached<T> {\n private data: Record<string, EntryT<T>> = {};\n private oldestTimestamp = Number.MAX_SAFE_INTEGER;\n\n /** For test use only. */\n get _data(): Readonly<Record<string, EntryT<T>>> {\n return this.data;\n }\n\n /** For test use only. */\n get _oldestTimestamp(): number {\n return this.oldestTimestamp;\n }\n\n constructor(\n public readonly maxage: number,\n private getter: (id: string) => T | Promise<T>,\n ) {}\n\n /** Removes stale items from the cache, and updates .oldestTimestamp. */\n private cleanCache() {\n const deadline = Date.now() - this.maxage;\n this.oldestTimestamp = Number.MAX_SAFE_INTEGER;\n for (const [key, entry] of Object.entries(this.data)) {\n const timestamp = getTimestamp(entry);\n if (timestamp < deadline) delete this.data[key];\n else if (timestamp < this.oldestTimestamp) {\n this.oldestTimestamp = timestamp;\n }\n }\n }\n\n /** Adds `datum` to the cache, and removes stale items from the cache. */\n private set(id: string, datum: T): CachedT<T> {\n const now = Date.now();\n if (this.oldestTimestamp < now - this.maxage) this.cleanCache();\n if (now < this.oldestTimestamp) this.oldestTimestamp = now;\n const res: CachedT<T> = [datum, now];\n this.data[id] = res;\n return res;\n }\n\n /** Retrieves envelope of the specified datum, either read from the cache,\n * or retrieved using the getter provided at construction time. */\n private getEntry(id: string): EntryT<T> {\n const now = Date.now();\n\n let cached = this.data[id];\n if (cached && getTimestamp(cached) >= now - this.maxage) return cached;\n\n const itemOrPromise = this.getter(id);\n if (!(itemOrPromise instanceof Promise)) {\n return this.set(id, itemOrPromise);\n }\n\n cached = addTimestamp(\n itemOrPromise.then((item) => this.set(id, item)),\n now,\n );\n if (now < this.oldestTimestamp) this.oldestTimestamp = now;\n this.data[id] = cached;\n return cached;\n }\n\n /** Gets item. */\n get(id: string): T | Promise<T> {\n const entry = this.getEntry(id);\n return Array.isArray(entry) ? entry[0] : entry.then((e) => e[0]);\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"Cached.js","names":["addTimestamp","promise","timestamp","res","getTimestamp","e","Array","isArray","Cached","pData","pOldestTimestamp","Number","MAX_SAFE_INTEGER","data","oldestTimestamp","constructor","maxage","getter","cleanCache","deadline","Date","now","key","entry","Object","entries","setEntry","id","set","datum","getEntry","forceRefresh","cached","itemOrPromise","Promise","then","item","get"],"sources":["../../src/Cached.ts"],"sourcesContent":["type CachedT<T> = [T, number];\ntype TimestampedPromise<T> = Promise<T> & { timestamp: number };\ntype EntryT<T> = CachedT<T> | TimestampedPromise<CachedT<T>>;\n\n/** Adds timestamp to the promise. */\nfunction addTimestamp<T>(\n promise: Promise<T>,\n timestamp: number,\n): TimestampedPromise<T> {\n const res = promise as TimestampedPromise<T>;\n res.timestamp = timestamp;\n return res;\n}\n\n/** Gets entry timestamp. */\nfunction getTimestamp<T>(e: EntryT<T>): number {\n return Array.isArray(e) ? e[1] : e.timestamp;\n}\n\nexport class Cached<T> {\n private pData: Record<string, EntryT<T>> = {};\n private pOldestTimestamp = Number.MAX_SAFE_INTEGER;\n\n /** For test use only. */\n get data(): Readonly<Record<string, EntryT<T>>> {\n return this.pData;\n }\n\n /** For test use only. */\n get oldestTimestamp(): number {\n return this.pOldestTimestamp;\n }\n\n constructor(\n public readonly maxage: number,\n private getter: (id: string) => T | Promise<T>,\n ) {}\n\n /** Removes stale items from the cache, and updates .oldestTimestamp. */\n private cleanCache() {\n const deadline = Date.now() - this.maxage;\n this.pOldestTimestamp = Number.MAX_SAFE_INTEGER;\n for (const [key, entry] of Object.entries(this.pData)) {\n const timestamp = getTimestamp(entry);\n if (timestamp < deadline) delete this.pData[key];\n else if (timestamp < this.pOldestTimestamp) {\n this.pOldestTimestamp = timestamp;\n }\n }\n }\n\n /**\n * Adds entry to the cache.\n * NOTE: It assumes entry's timestamp is the current moment (for the cache\n * cleaning purposes; if it is not, but it is a past timestamp, nothing bad\n * will happen, just some cleaning operation will be skipped).\n */\n private setEntry(id: string, entry: EntryT<T>) {\n this.pData[id] = entry;\n const timestamp = getTimestamp(entry);\n if (timestamp < this.pOldestTimestamp) this.pOldestTimestamp = timestamp;\n else if (this.pOldestTimestamp < timestamp - this.maxage) this.cleanCache();\n }\n\n /** Adds `datum` to the cache, and removes stale items from the cache. */\n private set(id: string, datum: T): CachedT<T> {\n const res: CachedT<T> = [datum, Date.now()];\n this.setEntry(id, res);\n return res;\n }\n\n /**\n * Retrieves envelope of the specified datum, either read from the cache,\n * or retrieved using the getter provided at construction time.\n */\n private getEntry(id: string, forceRefresh?: boolean): EntryT<T> {\n const now = Date.now();\n\n if (!forceRefresh) {\n const cached = this.pData[id];\n if (cached && getTimestamp(cached) >= now - this.maxage) return cached;\n }\n\n const itemOrPromise = this.getter(id);\n if (!(itemOrPromise instanceof Promise)) {\n return this.set(id, itemOrPromise);\n }\n\n const promise = addTimestamp(\n itemOrPromise.then((item) => this.set(id, item)),\n now,\n );\n\n this.setEntry(id, promise);\n\n return promise;\n }\n\n /** Gets item. */\n get(id: string, forceRefresh?: boolean): T | Promise<T> {\n const entry = this.getEntry(id, forceRefresh);\n return Array.isArray(entry) ? entry[0] : entry.then((e) => e[0]);\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -3,5 +3,6 @@ export * from './Cached';
3
3
  export * from './Emitter';
4
4
  export { default as Semaphore } from './Semaphore';
5
5
  export * from './time';
6
+ export * from './types';
6
7
  export { default as withRetries } from './withRetries';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["default","Barrier","Semaphore","withRetries"],"sources":["../../src/index.ts"],"sourcesContent":["export { default as Barrier } from './Barrier';\nexport * from './Cached';\nexport * from './Emitter';\nexport { default as Semaphore } from './Semaphore';\nexport * from './time';\nexport { default as withRetries } from './withRetries';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,OAAO,QAAQ,WAAW;AAC9C,cAAc,UAAU;AACxB,cAAc,WAAW;AACzB,SAASD,OAAO,IAAIE,SAAS,QAAQ,aAAa;AAClD,cAAc,QAAQ;AACtB,SAASF,OAAO,IAAIG,WAAW,QAAQ,eAAe","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["default","Barrier","Semaphore","withRetries"],"sources":["../../src/index.ts"],"sourcesContent":["export { default as Barrier } from './Barrier';\nexport * from './Cached';\nexport * from './Emitter';\nexport { default as Semaphore } from './Semaphore';\nexport * from './time';\nexport * from './types';\nexport { default as withRetries } from './withRetries';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,OAAO,QAAQ,WAAW;AAC9C,cAAc,UAAU;AACxB,cAAc,WAAW;AACzB,SAASD,OAAO,IAAIE,SAAS,QAAQ,aAAa;AAClD,cAAc,QAAQ;AACtB,cAAc,SAAS;AACvB,SAASF,OAAO,IAAIG,WAAW,QAAQ,eAAe","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ // Misc TypeScript-specific utilities.
2
+
3
+ /** The most permissive object key type. */
4
+
5
+ /** Asserts given object is empty, both compile- and run-time. */
6
+ export function assertEmptyObject(object) {
7
+ if (Object.keys(object).length) throw Error('The object is not empty');
8
+ }
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":["assertEmptyObject","object","Object","keys","length","Error"],"sources":["../../src/types.ts"],"sourcesContent":["// Misc TypeScript-specific utilities.\n\n/** The most permissive object key type. */\nexport type ObjectKey = string | number | symbol;\n\n/** Asserts given object is empty, both compile- and run-time. */\nexport function assertEmptyObject(object: Record<ObjectKey, never>): void {\n if (Object.keys(object).length) throw Error('The object is not empty');\n}\n"],"mappings":"AAAA;;AAEA;;AAGA;AACA,OAAO,SAASA,iBAAiBA,CAACC,MAAgC,EAAQ;EACxE,IAAIC,MAAM,CAACC,IAAI,CAACF,MAAM,CAAC,CAACG,MAAM,EAAE,MAAMC,KAAK,CAAC,yBAAyB,CAAC;AACxE","ignoreList":[]}
@@ -6,21 +6,30 @@ type EntryT<T> = CachedT<T> | TimestampedPromise<CachedT<T>>;
6
6
  export declare class Cached<T> {
7
7
  readonly maxage: number;
8
8
  private getter;
9
- private data;
10
- private oldestTimestamp;
9
+ private pData;
10
+ private pOldestTimestamp;
11
11
  /** For test use only. */
12
- get _data(): Readonly<Record<string, EntryT<T>>>;
12
+ get data(): Readonly<Record<string, EntryT<T>>>;
13
13
  /** For test use only. */
14
- get _oldestTimestamp(): number;
14
+ get oldestTimestamp(): number;
15
15
  constructor(maxage: number, getter: (id: string) => T | Promise<T>);
16
16
  /** Removes stale items from the cache, and updates .oldestTimestamp. */
17
17
  private cleanCache;
18
+ /**
19
+ * Adds entry to the cache.
20
+ * NOTE: It assumes entry's timestamp is the current moment (for the cache
21
+ * cleaning purposes; if it is not, but it is a past timestamp, nothing bad
22
+ * will happen, just some cleaning operation will be skipped).
23
+ */
24
+ private setEntry;
18
25
  /** Adds `datum` to the cache, and removes stale items from the cache. */
19
26
  private set;
20
- /** Retrieves envelope of the specified datum, either read from the cache,
21
- * or retrieved using the getter provided at construction time. */
27
+ /**
28
+ * Retrieves envelope of the specified datum, either read from the cache,
29
+ * or retrieved using the getter provided at construction time.
30
+ */
22
31
  private getEntry;
23
32
  /** Gets item. */
24
- get(id: string): T | Promise<T>;
33
+ get(id: string, forceRefresh?: boolean): T | Promise<T>;
25
34
  }
26
35
  export {};
@@ -3,4 +3,5 @@ export * from './Cached';
3
3
  export * from './Emitter';
4
4
  export { default as Semaphore } from './Semaphore';
5
5
  export * from './time';
6
+ export * from './types';
6
7
  export { default as withRetries } from './withRetries';
@@ -0,0 +1,4 @@
1
+ /** The most permissive object key type. */
2
+ export type ObjectKey = string | number | symbol;
3
+ /** Asserts given object is empty, both compile- and run-time. */
4
+ export declare function assertEmptyObject(object: Record<ObjectKey, never>): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dr.pogodin/js-utils",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Collection of JavaScript (TypeScript) utilities.",
5
5
  "main": "./build/module/index.js",
6
6
  "module": "./build/module/index.js",
@@ -43,11 +43,11 @@
43
43
  "@babel/plugin-transform-runtime": "^7.28.0",
44
44
  "@babel/preset-env": "^7.28.0",
45
45
  "@babel/preset-typescript": "^7.27.1",
46
- "@dr.pogodin/eslint-configs": "^0.0.8",
46
+ "@dr.pogodin/eslint-configs": "^0.0.9",
47
47
  "@tsconfig/recommended": "^1.0.10",
48
48
  "@types/jest": "^30.0.0",
49
- "babel-jest": "^30.0.4",
50
- "jest": "^30.0.4",
49
+ "babel-jest": "^30.0.5",
50
+ "jest": "^30.0.5",
51
51
  "mockdate": "^3.0.5",
52
52
  "rimraf": "^6.0.1",
53
53
  "tstyche": "^4.3.0",