@choksheak/ts-utils 0.2.8 → 0.3.0

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.
Files changed (275) hide show
  1. package/README.md +2 -0
  2. package/{arrayBuffer.js → arrayBuffer.cjs} +0 -1
  3. package/arrayBuffer.d.mts +10 -0
  4. package/arrayBuffer.min.cjs +2 -0
  5. package/arrayBuffer.min.cjs.map +1 -0
  6. package/arrayBuffer.min.mjs +2 -0
  7. package/{arrayBuffer.js.map → arrayBuffer.min.mjs.map} +1 -1
  8. package/arrayBuffer.mjs +14 -0
  9. package/{assert.js → assert.cjs} +3 -2
  10. package/assert.d.mts +7 -0
  11. package/assert.d.ts +1 -1
  12. package/assert.min.cjs +2 -0
  13. package/assert.min.cjs.map +1 -0
  14. package/assert.min.mjs +2 -0
  15. package/assert.min.mjs.map +1 -0
  16. package/assert.mjs +11 -0
  17. package/{average.js → average.cjs} +0 -1
  18. package/average.d.mts +7 -0
  19. package/average.min.cjs +2 -0
  20. package/{average.js.map → average.min.cjs.map} +1 -1
  21. package/average.min.mjs +2 -0
  22. package/average.min.mjs.map +1 -0
  23. package/average.mjs +20 -0
  24. package/base64Url.cjs +40 -0
  25. package/base64Url.d.mts +10 -0
  26. package/base64Url.d.ts +8 -0
  27. package/base64Url.min.cjs +2 -0
  28. package/base64Url.min.cjs.map +1 -0
  29. package/base64Url.min.mjs +2 -0
  30. package/base64Url.min.mjs.map +1 -0
  31. package/base64Url.mjs +14 -0
  32. package/{dateTimeStr.js → dateTimeStr.cjs} +0 -1
  33. package/dateTimeStr.d.mts +85 -0
  34. package/dateTimeStr.min.cjs +2 -0
  35. package/dateTimeStr.min.cjs.map +1 -0
  36. package/dateTimeStr.min.mjs +2 -0
  37. package/{dateTimeStr.js.map → dateTimeStr.min.mjs.map} +1 -1
  38. package/dateTimeStr.mjs +83 -0
  39. package/{duration.js → duration.cjs} +9 -11
  40. package/duration.d.mts +78 -0
  41. package/duration.min.cjs +2 -0
  42. package/duration.min.cjs.map +1 -0
  43. package/duration.min.mjs +2 -0
  44. package/duration.min.mjs.map +1 -0
  45. package/duration.mjs +154 -0
  46. package/{isEmpty.js → isEmpty.cjs} +0 -1
  47. package/isEmpty.d.mts +6 -0
  48. package/isEmpty.min.cjs +2 -0
  49. package/isEmpty.min.cjs.map +1 -0
  50. package/isEmpty.min.mjs +2 -0
  51. package/{isEmpty.js.map → isEmpty.min.mjs.map} +1 -1
  52. package/isEmpty.mjs +22 -0
  53. package/kvStore.cjs +333 -0
  54. package/kvStore.d.mts +97 -0
  55. package/kvStore.min.cjs +2 -0
  56. package/kvStore.min.cjs.map +1 -0
  57. package/kvStore.min.mjs +2 -0
  58. package/kvStore.min.mjs.map +1 -0
  59. package/kvStore.mjs +300 -0
  60. package/{localStorageCache.js → localStorageCache.cjs} +5 -7
  61. package/localStorageCache.d.mts +57 -0
  62. package/localStorageCache.min.cjs +2 -0
  63. package/localStorageCache.min.cjs.map +1 -0
  64. package/localStorageCache.min.mjs +2 -0
  65. package/localStorageCache.min.mjs.map +1 -0
  66. package/localStorageCache.mjs +92 -0
  67. package/{logging.js → logging.cjs} +0 -1
  68. package/logging.d.mts +4 -0
  69. package/logging.min.cjs +2 -0
  70. package/logging.min.cjs.map +1 -0
  71. package/logging.min.mjs +2 -0
  72. package/{logging.js.map → logging.min.mjs.map} +1 -1
  73. package/logging.mjs +21 -0
  74. package/{nonEmpty.js → nonEmpty.cjs} +0 -1
  75. package/nonEmpty.d.mts +10 -0
  76. package/nonEmpty.min.cjs +2 -0
  77. package/{nonEmpty.js.map → nonEmpty.min.cjs.map} +1 -1
  78. package/nonEmpty.min.mjs +2 -0
  79. package/nonEmpty.min.mjs.map +1 -0
  80. package/nonEmpty.mjs +30 -0
  81. package/{nonNil.js → nonNil.cjs} +0 -1
  82. package/{src/nonNil.ts → nonNil.d.mts} +3 -6
  83. package/nonNil.min.cjs +2 -0
  84. package/nonNil.min.cjs.map +1 -0
  85. package/nonNil.min.mjs +2 -0
  86. package/{nonNil.js.map → nonNil.min.mjs.map} +1 -1
  87. package/nonNil.mjs +10 -0
  88. package/package.json +414 -24
  89. package/{round.js → round.cjs} +0 -1
  90. package/{src/round.ts → round.d.mts} +4 -8
  91. package/round.min.cjs +2 -0
  92. package/round.min.cjs.map +1 -0
  93. package/round.min.mjs +2 -0
  94. package/{round.js.map → round.min.mjs.map} +1 -1
  95. package/round.mjs +12 -0
  96. package/{safeBtoa.js → safeBtoa.cjs} +0 -1
  97. package/safeBtoa.d.mts +6 -0
  98. package/safeBtoa.min.cjs +2 -0
  99. package/safeBtoa.min.cjs.map +1 -0
  100. package/safeBtoa.min.mjs +2 -0
  101. package/{safeBtoa.js.map → safeBtoa.min.mjs.map} +1 -1
  102. package/safeBtoa.mjs +9 -0
  103. package/{safeParseFloat.js → safeParseFloat.cjs} +0 -1
  104. package/safeParseFloat.d.mts +6 -0
  105. package/safeParseFloat.min.cjs +2 -0
  106. package/safeParseFloat.min.cjs.map +1 -0
  107. package/safeParseFloat.min.mjs +2 -0
  108. package/{safeParseFloat.js.map → safeParseFloat.min.mjs.map} +1 -1
  109. package/safeParseFloat.mjs +8 -0
  110. package/{safeParseInt.js → safeParseInt.cjs} +0 -1
  111. package/safeParseInt.d.mts +6 -0
  112. package/safeParseInt.min.cjs +2 -0
  113. package/safeParseInt.min.cjs.map +1 -0
  114. package/safeParseInt.min.mjs +2 -0
  115. package/{safeParseInt.js.map → safeParseInt.min.mjs.map} +1 -1
  116. package/safeParseInt.mjs +8 -0
  117. package/sha256.cjs +35 -0
  118. package/sha256.d.mts +6 -0
  119. package/sha256.min.cjs +2 -0
  120. package/sha256.min.cjs.map +1 -0
  121. package/sha256.min.mjs +2 -0
  122. package/{sha256.js.map → sha256.min.mjs.map} +1 -1
  123. package/sha256.mjs +10 -0
  124. package/{sleep.js → sleep.cjs} +0 -1
  125. package/{src/sleep.ts → sleep.d.mts} +3 -3
  126. package/sleep.min.cjs +2 -0
  127. package/sleep.min.cjs.map +1 -0
  128. package/sleep.min.mjs +2 -0
  129. package/{sleep.js.map → sleep.min.mjs.map} +1 -1
  130. package/sleep.mjs +7 -0
  131. package/{sum.js → sum.cjs} +0 -1
  132. package/sum.d.mts +7 -0
  133. package/sum.min.cjs +2 -0
  134. package/sum.min.cjs.map +1 -0
  135. package/sum.min.mjs +2 -0
  136. package/{sum.js.map → sum.min.mjs.map} +1 -1
  137. package/sum.mjs +13 -0
  138. package/{timeConstants.js → timeConstants.cjs} +0 -1
  139. package/timeConstants.d.mts +20 -0
  140. package/timeConstants.min.cjs +2 -0
  141. package/timeConstants.min.cjs.map +1 -0
  142. package/timeConstants.min.mjs +2 -0
  143. package/{timeConstants.js.map → timeConstants.min.mjs.map} +1 -1
  144. package/timeConstants.mjs +31 -0
  145. package/{timer.js → timer.cjs} +6 -6
  146. package/timer.d.mts +13 -0
  147. package/timer.min.cjs +2 -0
  148. package/timer.min.cjs.map +1 -0
  149. package/timer.min.mjs +2 -0
  150. package/timer.min.mjs.map +1 -0
  151. package/timer.mjs +161 -0
  152. package/assert.js.map +0 -1
  153. package/docs/.nojekyll +0 -1
  154. package/docs/assets/hierarchy.js +0 -1
  155. package/docs/assets/highlight.css +0 -22
  156. package/docs/assets/icons.js +0 -18
  157. package/docs/assets/icons.svg +0 -1
  158. package/docs/assets/main.js +0 -60
  159. package/docs/assets/navigation.js +0 -1
  160. package/docs/assets/search.js +0 -1
  161. package/docs/assets/style.css +0 -1640
  162. package/docs/classes/kvStore.KVStore.html +0 -26
  163. package/docs/classes/kvStore.KVStoreField.html +0 -7
  164. package/docs/classes/timer.Timer.html +0 -8
  165. package/docs/functions/arrayBuffer.arrayBufferToBase64.html +0 -2
  166. package/docs/functions/arrayBuffer.arrayBufferToHex.html +0 -2
  167. package/docs/functions/assert.assert.html +0 -3
  168. package/docs/functions/average.average.html +0 -3
  169. package/docs/functions/dateTimeStr.getDisplayDateTime.html +0 -3
  170. package/docs/functions/dateTimeStr.getLongMonthNameOneIndexed.html +0 -4
  171. package/docs/functions/dateTimeStr.getLongMonthNameZeroIndexed.html +0 -4
  172. package/docs/functions/dateTimeStr.getShortMonthNameOneIndexed.html +0 -4
  173. package/docs/functions/dateTimeStr.getShortMonthNameZeroIndexed.html +0 -4
  174. package/docs/functions/dateTimeStr.hhMm.html +0 -4
  175. package/docs/functions/dateTimeStr.hhMmSs.html +0 -4
  176. package/docs/functions/dateTimeStr.hhMmSsMs.html +0 -5
  177. package/docs/functions/dateTimeStr.toDate.html +0 -4
  178. package/docs/functions/dateTimeStr.tzShort.html +0 -4
  179. package/docs/functions/dateTimeStr.yyyyMm.html +0 -4
  180. package/docs/functions/dateTimeStr.yyyyMmDd.html +0 -4
  181. package/docs/functions/duration.durationOrMsToMs.html +0 -2
  182. package/docs/functions/duration.durationToMs.html +0 -2
  183. package/docs/functions/duration.elapsed.html +0 -2
  184. package/docs/functions/duration.formatDuration.html +0 -4
  185. package/docs/functions/duration.msToDuration.html +0 -5
  186. package/docs/functions/duration.readableDuration.html +0 -4
  187. package/docs/functions/isEmpty.isEmpty.html +0 -2
  188. package/docs/functions/kvStore.kvStoreItem.html +0 -2
  189. package/docs/functions/localStorageCache.storeItem.html +0 -15
  190. package/docs/functions/logging.capLength.html +0 -1
  191. package/docs/functions/logging.stringify.html +0 -1
  192. package/docs/functions/nonEmpty.nonEmpty.html +0 -5
  193. package/docs/functions/nonNil.nonNil.html +0 -5
  194. package/docs/functions/round.round.html +0 -5
  195. package/docs/functions/round.roundS.html +0 -3
  196. package/docs/functions/safeBtoa.safeBtoa.html +0 -2
  197. package/docs/functions/safeParseFloat.safeParseFloat.html +0 -2
  198. package/docs/functions/safeParseInt.safeParseInt.html +0 -2
  199. package/docs/functions/sha256.sha256.html +0 -2
  200. package/docs/functions/sleep.sleep.html +0 -3
  201. package/docs/functions/sum.sum.html +0 -3
  202. package/docs/functions/timer.timer.html +0 -2
  203. package/docs/hierarchy.html +0 -1
  204. package/docs/index.html +0 -4
  205. package/docs/modules/arrayBuffer.html +0 -1
  206. package/docs/modules/assert.html +0 -1
  207. package/docs/modules/average.html +0 -1
  208. package/docs/modules/dateTimeStr.html +0 -1
  209. package/docs/modules/duration.html +0 -1
  210. package/docs/modules/isEmpty.html +0 -1
  211. package/docs/modules/kvStore.html +0 -1
  212. package/docs/modules/localStorageCache.html +0 -1
  213. package/docs/modules/logging.html +0 -1
  214. package/docs/modules/nonEmpty.html +0 -1
  215. package/docs/modules/nonNil.html +0 -1
  216. package/docs/modules/round.html +0 -1
  217. package/docs/modules/safeBtoa.html +0 -1
  218. package/docs/modules/safeParseFloat.html +0 -1
  219. package/docs/modules/safeParseInt.html +0 -1
  220. package/docs/modules/sha256.html +0 -1
  221. package/docs/modules/sleep.html +0 -1
  222. package/docs/modules/sum.html +0 -1
  223. package/docs/modules/timeConstants.html +0 -1
  224. package/docs/modules/timer.html +0 -1
  225. package/docs/modules.html +0 -1
  226. package/docs/types/dateTimeStr.AnyDateTime.html +0 -1
  227. package/docs/types/duration.Duration.html +0 -6
  228. package/docs/types/duration.DurationStyle.html +0 -6
  229. package/docs/types/duration.DurationSuffixMap.html +0 -6
  230. package/docs/types/duration.DurationSuffixType.html +0 -1
  231. package/docs/types/duration.DurationType.html +0 -2
  232. package/docs/types/localStorageCache.StoredItem.html +0 -4
  233. package/docs/variables/duration.DURATION_STYLE_SUFFIX_MAP.html +0 -1
  234. package/docs/variables/duration.DURATION_TYPE_SEQUENCE.html +0 -2
  235. package/docs/variables/kvStore.DEFAULT_EXPIRY_DELTA_MS.html +0 -2
  236. package/docs/variables/kvStore.GC_INTERVAL_MS.html +0 -2
  237. package/docs/variables/kvStore.MILLIS_PER_DAY.html +0 -2
  238. package/docs/variables/kvStore.kvStore.html +0 -3
  239. package/docs/variables/timeConstants.HOURS_PER_DAY.html +0 -1
  240. package/docs/variables/timeConstants.HOURS_PER_WEEK.html +0 -1
  241. package/docs/variables/timeConstants.MINUTES_PER_DAY.html +0 -1
  242. package/docs/variables/timeConstants.MINUTES_PER_HOUR.html +0 -1
  243. package/docs/variables/timeConstants.MINUTES_PER_WEEK.html +0 -1
  244. package/docs/variables/timeConstants.MS_PER_DAY.html +0 -1
  245. package/docs/variables/timeConstants.MS_PER_HOUR.html +0 -1
  246. package/docs/variables/timeConstants.MS_PER_MINUTE.html +0 -1
  247. package/docs/variables/timeConstants.MS_PER_SECOND.html +0 -3
  248. package/docs/variables/timeConstants.MS_PER_WEEK.html +0 -1
  249. package/docs/variables/timeConstants.SECONDS_PER_DAY.html +0 -1
  250. package/docs/variables/timeConstants.SECONDS_PER_HOUR.html +0 -1
  251. package/docs/variables/timeConstants.SECONDS_PER_MINUTE.html +0 -1
  252. package/docs/variables/timeConstants.SECONDS_PER_WEEK.html +0 -1
  253. package/duration.js.map +0 -1
  254. package/kvStore.js +0 -383
  255. package/kvStore.js.map +0 -1
  256. package/localStorageCache.js.map +0 -1
  257. package/sha256.js +0 -58
  258. package/src/arrayBuffer.ts +0 -29
  259. package/src/assert.ts +0 -12
  260. package/src/average.ts +0 -13
  261. package/src/dateTimeStr.ts +0 -184
  262. package/src/duration.ts +0 -260
  263. package/src/isEmpty.ts +0 -32
  264. package/src/kvStore.ts +0 -444
  265. package/src/localStorageCache.ts +0 -121
  266. package/src/logging.ts +0 -26
  267. package/src/nonEmpty.ts +0 -18
  268. package/src/safeBtoa.ts +0 -15
  269. package/src/safeParseFloat.ts +0 -10
  270. package/src/safeParseInt.ts +0 -10
  271. package/src/sha256.ts +0 -13
  272. package/src/sum.ts +0 -17
  273. package/src/timeConstants.ts +0 -22
  274. package/src/timer.ts +0 -33
  275. package/timer.js.map +0 -1
package/src/kvStore.ts DELETED
@@ -1,444 +0,0 @@
1
- /**
2
- * Indexed DB key-value store with support for auto-expirations.
3
- *
4
- * Why use this?
5
- * 1. No need to worry about running out of storage.
6
- * 2. Extremely simple interface to use indexed DBs.
7
- * 3. Auto-expirations frees you from worrying about data clean-up.
8
- * 4. Any serializable data type can be stored (except undefined).
9
- *
10
- * How to use?
11
- * Just use the `kvStore` global constant like the local storage.
12
- */
13
-
14
- import { Duration, durationOrMsToMs } from "./duration";
15
-
16
- // Updating the DB name will cause all old entries to be gone.
17
- const DEFAULT_DB_NAME = "KVStore";
18
-
19
- // Updating the version will cause all old entries to be gone.
20
- const DEFAULT_DB_VERSION = 1;
21
-
22
- // Use a constant store name to keep things simple.
23
- const STORE_NAME = "kvStore";
24
-
25
- /** One day in milliseconds. */
26
- export const MILLIS_PER_DAY = 86_400_000;
27
-
28
- /** 30 days in ms. */
29
- export const DEFAULT_EXPIRY_DELTA_MS = MILLIS_PER_DAY * 30;
30
-
31
- /** Do GC once per day. */
32
- export const GC_INTERVAL_MS = MILLIS_PER_DAY;
33
-
34
- type StoredObject<T> = {
35
- key: string;
36
- value: T;
37
- storedMs: number;
38
- expiryMs: number;
39
- };
40
-
41
- /**
42
- * Parse a stored value string. Returns undefined if invalid or expired.
43
- * Throws an error if the string cannot be parsed as JSON.
44
- */
45
- function validateStoredObject<T>(
46
- obj: StoredObject<T>,
47
- ): StoredObject<T> | undefined {
48
- if (
49
- !obj ||
50
- typeof obj !== "object" ||
51
- !("key" in obj) ||
52
- typeof obj.key !== "string" ||
53
- !("value" in obj) ||
54
- !("storedMs" in obj) ||
55
- typeof obj.storedMs !== "number" ||
56
- obj.value === undefined ||
57
- !("expiryMs" in obj) ||
58
- typeof obj.expiryMs !== "number" ||
59
- Date.now() >= obj.expiryMs
60
- ) {
61
- return undefined;
62
- }
63
-
64
- return obj;
65
- }
66
-
67
- /** Add an `onerror` handler to the request. */
68
- function withOnError<T extends IDBRequest | IDBTransaction>(
69
- request: T,
70
- reject: (reason?: unknown) => void,
71
- ): T {
72
- request.onerror = (event) => {
73
- reject(event);
74
- };
75
-
76
- return request;
77
- }
78
-
79
- export class KVStoreField<T> {
80
- public constructor(
81
- public readonly store: KVStore,
82
- public readonly key: string,
83
- ) {}
84
-
85
- public get(): Promise<T | undefined> {
86
- return this.store.get(this.key);
87
- }
88
-
89
- public set(t: T): Promise<T> {
90
- return this.store.set(this.key, t);
91
- }
92
-
93
- public delete(): Promise<void> {
94
- return this.store.delete(this.key);
95
- }
96
- }
97
-
98
- /**
99
- * You can create multiple KVStores if you want, but most likely you will only
100
- * need to use the default `kvStore` instance.
101
- */
102
- export class KVStore {
103
- // We'll init the DB only on first use.
104
- private db: IDBDatabase | undefined;
105
-
106
- // Local storage key name for the last GC completed timestamp.
107
- public readonly gcMsStorageKey: string;
108
-
109
- public constructor(
110
- public readonly dbName: string,
111
- public readonly dbVersion: number,
112
- public readonly defaultExpiryDeltaMs: number,
113
- ) {
114
- this.gcMsStorageKey = `__kvStore:lastGcMs:${dbName}:v${dbVersion}:${STORE_NAME}`;
115
- }
116
-
117
- private async getOrCreateDb() {
118
- if (!this.db) {
119
- this.db = await new Promise<IDBDatabase>((resolve, reject) => {
120
- const request = withOnError(
121
- globalThis.indexedDB.open(this.dbName, this.dbVersion),
122
- reject,
123
- );
124
-
125
- request.onupgradeneeded = (event) => {
126
- const db = (event.target as unknown as { result: IDBDatabase })
127
- .result;
128
-
129
- // Create the store on DB init.
130
- const objectStore = db.createObjectStore(STORE_NAME, {
131
- keyPath: "key",
132
- });
133
-
134
- objectStore.createIndex("key", "key", {
135
- unique: true,
136
- });
137
- };
138
-
139
- request.onsuccess = (event) => {
140
- const db = (event.target as unknown as { result: IDBDatabase })
141
- .result;
142
- resolve(db);
143
- };
144
- });
145
- }
146
-
147
- return this.db;
148
- }
149
-
150
- private async transact<T>(
151
- mode: IDBTransactionMode,
152
- callback: (
153
- objectStore: IDBObjectStore,
154
- resolve: (t: T) => void,
155
- reject: (reason?: unknown) => void,
156
- ) => void,
157
- ): Promise<T> {
158
- const db = await this.getOrCreateDb();
159
-
160
- return await new Promise<T>((resolve, reject) => {
161
- const transaction = withOnError(db.transaction(STORE_NAME, mode), reject);
162
-
163
- transaction.onabort = (event) => {
164
- reject(event);
165
- };
166
-
167
- const objectStore = transaction.objectStore(STORE_NAME);
168
-
169
- callback(objectStore, resolve, reject);
170
- });
171
- }
172
-
173
- public async set<T>(
174
- key: string,
175
- value: T,
176
- expiryDeltaMs: number | Duration = this.defaultExpiryDeltaMs,
177
- ): Promise<T> {
178
- const nowMs = Date.now();
179
- const obj: StoredObject<T> = {
180
- key,
181
- value,
182
- storedMs: nowMs,
183
- expiryMs: nowMs + durationOrMsToMs(expiryDeltaMs),
184
- };
185
-
186
- return await this.transact<T>(
187
- "readwrite",
188
- (objectStore, resolve, reject) => {
189
- const request = withOnError(objectStore.put(obj), reject);
190
-
191
- request.onsuccess = () => {
192
- resolve(value);
193
-
194
- this.gc(); // check GC on every write
195
- };
196
- },
197
- );
198
- }
199
-
200
- /** Delete one or multiple keys. */
201
- public async delete(key: string | string[]): Promise<void> {
202
- return await this.transact<void>(
203
- "readwrite",
204
- (objectStore, resolve, reject) => {
205
- objectStore.transaction.oncomplete = () => {
206
- resolve();
207
- };
208
-
209
- if (typeof key === "string") {
210
- withOnError(objectStore.delete(key), reject);
211
- } else {
212
- for (const k of key) {
213
- withOnError(objectStore.delete(k), reject);
214
- }
215
- }
216
- },
217
- );
218
- }
219
-
220
- public async getStoredObject<T>(
221
- key: string,
222
- ): Promise<StoredObject<T> | undefined> {
223
- const stored = await this.transact<StoredObject<T> | undefined>(
224
- "readonly",
225
- (objectStore, resolve, reject) => {
226
- const request = withOnError(objectStore.get(key), reject);
227
-
228
- request.onsuccess = () => {
229
- resolve(request.result);
230
- };
231
- },
232
- );
233
-
234
- if (!stored) {
235
- return undefined;
236
- }
237
-
238
- try {
239
- const obj = validateStoredObject(stored);
240
- if (!obj) {
241
- await this.delete(key);
242
-
243
- this.gc(); // check GC on every read of an expired key
244
-
245
- return undefined;
246
- }
247
-
248
- return obj;
249
- } catch (e) {
250
- console.error(`Invalid kv value: ${key}=${JSON.stringify(stored)}:`, e);
251
- await this.delete(key);
252
-
253
- this.gc(); // check GC on every read of an invalid key
254
-
255
- return undefined;
256
- }
257
- }
258
-
259
- public async get<T>(key: string): Promise<T | undefined> {
260
- const obj = await this.getStoredObject<T>(key);
261
-
262
- return obj?.value;
263
- }
264
-
265
- public async forEach(
266
- callback: (
267
- key: string,
268
- value: unknown,
269
- expiryMs: number,
270
- ) => void | Promise<void>,
271
- ): Promise<void> {
272
- await this.transact<void>("readonly", (objectStore, resolve, reject) => {
273
- const request = withOnError(objectStore.openCursor(), reject);
274
-
275
- request.onsuccess = async (event) => {
276
- const cursor = (
277
- event.target as unknown as { result: IDBCursorWithValue }
278
- ).result;
279
-
280
- if (cursor) {
281
- if (cursor.key) {
282
- const obj = validateStoredObject(cursor.value);
283
- if (obj) {
284
- await callback(String(cursor.key), obj.value, obj.expiryMs);
285
- } else {
286
- await callback(String(cursor.key), undefined, 0);
287
- }
288
- }
289
- cursor.continue();
290
- } else {
291
- resolve();
292
- }
293
- };
294
- });
295
- }
296
-
297
- /** Cannot be a getter because this needs to be async. */
298
- public async size() {
299
- let count = 0;
300
- await this.forEach(() => {
301
- count++;
302
- });
303
- return count;
304
- }
305
-
306
- public async clear() {
307
- const keys: string[] = [];
308
- await this.forEach((key) => {
309
- keys.push(key);
310
- });
311
-
312
- await this.delete(keys);
313
- }
314
-
315
- /** Mainly for debugging dumps. */
316
- public async asMap(): Promise<Map<string, unknown>> {
317
- const map = new Map<string, unknown>();
318
- await this.forEach((key, value, expiryMs) => {
319
- map.set(key, { value, expiryMs });
320
- });
321
- return map;
322
- }
323
-
324
- public get lastGcMs(): number {
325
- const lastGcMsStr = globalThis.localStorage.getItem(this.gcMsStorageKey);
326
- if (!lastGcMsStr) return 0;
327
-
328
- const ms = Number(lastGcMsStr);
329
- return isNaN(ms) ? 0 : ms;
330
- }
331
-
332
- public set lastGcMs(ms: number) {
333
- globalThis.localStorage.setItem(this.gcMsStorageKey, String(ms));
334
- }
335
-
336
- /** Perform garbage-collection if due. */
337
- public async gc() {
338
- const lastGcMs = this.lastGcMs;
339
-
340
- // Set initial timestamp - no need GC now.
341
- if (!lastGcMs) {
342
- this.lastGcMs = Date.now();
343
- return;
344
- }
345
-
346
- if (Date.now() < lastGcMs + GC_INTERVAL_MS) {
347
- return; // not due for next GC yet
348
- }
349
-
350
- // GC is due now, so run it.
351
- await this.gcNow();
352
- }
353
-
354
- /** Perform garbage-collection immediately without checking. */
355
- public async gcNow() {
356
- console.log(`Starting kvStore GC on ${this.dbName} v${this.dbVersion}...`);
357
-
358
- // Prevent concurrent GC runs.
359
- this.lastGcMs = Date.now();
360
-
361
- const keysToDelete: string[] = [];
362
- await this.forEach(
363
- async (key: string, value: unknown, expiryMs: number) => {
364
- if (value === undefined || Date.now() >= expiryMs) {
365
- keysToDelete.push(key);
366
- }
367
- },
368
- );
369
-
370
- if (keysToDelete.length) {
371
- await this.delete(keysToDelete);
372
- }
373
-
374
- console.log(
375
- `Finished kvStore GC on ${this.dbName} v${this.dbVersion} ` +
376
- `- deleted ${keysToDelete.length} keys`,
377
- );
378
-
379
- // Mark the end time as last GC time.
380
- this.lastGcMs = Date.now();
381
- }
382
-
383
- /** Get an independent store item with a locked key and value type. */
384
- public field<T>(key: string) {
385
- return new KVStoreField<T>(this, key);
386
- }
387
- }
388
-
389
- /**
390
- * Default KV store ready for immediate use. You can create new instances if
391
- * you want, but most likely you will only need one store instance.
392
- */
393
- export const kvStore = new KVStore(
394
- DEFAULT_DB_NAME,
395
- DEFAULT_DB_VERSION,
396
- DEFAULT_EXPIRY_DELTA_MS,
397
- );
398
-
399
- /**
400
- * Class to represent one key in the store with a default expiration.
401
- */
402
- class KvStoreItem<T> {
403
- public constructor(
404
- public readonly key: string,
405
- public readonly defaultExpiryDeltaMs: number,
406
- public readonly store = kvStore,
407
- ) {}
408
-
409
- /**
410
- * Example usage:
411
- *
412
- * const { value, storedMs, expiryMs } = await myKvItem.getStoredObject();
413
- */
414
- public async getStoredObject(): Promise<StoredObject<T> | undefined> {
415
- return await this.store.getStoredObject(this.key);
416
- }
417
-
418
- public async get(): Promise<T | undefined> {
419
- return await this.store.get(this.key);
420
- }
421
-
422
- public async set(
423
- value: T,
424
- expiryDeltaMs: number = this.defaultExpiryDeltaMs,
425
- ): Promise<void> {
426
- await this.store.set(this.key, value, expiryDeltaMs);
427
- }
428
-
429
- public async delete(): Promise<void> {
430
- await this.store.delete(this.key);
431
- }
432
- }
433
-
434
- /**
435
- * Create a KV store item with a key and a default expiration.
436
- */
437
- export function kvStoreItem<T>(
438
- key: string,
439
- defaultExpiration: number | Duration,
440
- ): KvStoreItem<T> {
441
- const defaultExpiryDeltaMs = durationOrMsToMs(defaultExpiration);
442
-
443
- return new KvStoreItem<T>(key, defaultExpiryDeltaMs);
444
- }
@@ -1,121 +0,0 @@
1
- import { Duration, durationOrMsToMs } from "./duration";
2
-
3
- export type StoredItem<T> = { value: T; storedMs: number; expiryMs: number };
4
-
5
- /**
6
- * Simple local storage cache with support for auto-expiration.
7
- * Note that this works in the browser context only because nodejs does not
8
- * have local storage.
9
- *
10
- * Create a cache item accessor object with auto-expiration. The value will
11
- * always be stored as a string by applying JSON.stringify(), and will be
12
- * returned in the same object type by applying JSON.parse().
13
- *
14
- * In order to provide proper type-checking, please always specify the T
15
- * type parameter. E.g. const item = storeItem<string>("name", 10_000);
16
- *
17
- * @param key The store key in local storage.
18
- * @param expires Either a number in milliseconds, or a Duration object
19
- * @param logError Log an error if we found an invalid object in the store.
20
- * The invalid object is usually a string that cannot be parsed as JSON.
21
- * @param defaultValue Specify a default value to use for the object. Defaults
22
- * to undefined.
23
- */
24
- export function storeItem<T>(
25
- key: string,
26
- expires: number | Duration,
27
- logError = true,
28
- defaultValue?: T,
29
- ) {
30
- const expireDeltaMs = durationOrMsToMs(expires);
31
-
32
- return new CacheItem<T>(key, expireDeltaMs, logError, defaultValue);
33
- }
34
-
35
- class CacheItem<T> {
36
- /**
37
- * Create a cache item accessor object with auto-expiration.
38
- */
39
- public constructor(
40
- public readonly key: string,
41
- public readonly expireDeltaMs: number,
42
- public readonly logError: boolean,
43
- public readonly defaultValue: T | undefined,
44
- ) {}
45
-
46
- /**
47
- * Set the value of this item with auto-expiration.
48
- */
49
- public set(
50
- value: T,
51
- expiryDelta: number | Duration = this.expireDeltaMs,
52
- ): void {
53
- const nowMs = Date.now();
54
- const toStore: StoredItem<T> = {
55
- value,
56
- storedMs: nowMs,
57
- expiryMs: nowMs + durationOrMsToMs(expiryDelta),
58
- };
59
- const valueStr = JSON.stringify(toStore);
60
-
61
- globalThis.localStorage.setItem(this.key, valueStr);
62
- }
63
-
64
- /**
65
- * Example usage:
66
- *
67
- * const { value, storedMs, expiryMs } = await myItem.getStoredItem();
68
- */
69
- public getStoredItem(): StoredItem<T> | undefined {
70
- const jsonStr = globalThis.localStorage.getItem(this.key);
71
-
72
- if (!jsonStr) {
73
- return undefined;
74
- }
75
-
76
- try {
77
- const obj: StoredItem<T> | undefined = JSON.parse(jsonStr);
78
-
79
- if (
80
- !obj ||
81
- typeof obj !== "object" ||
82
- !("value" in obj) ||
83
- !("storedMs" in obj) ||
84
- typeof obj.storedMs !== "number" ||
85
- !("expiryMs" in obj) ||
86
- typeof obj.expiryMs !== "number" ||
87
- Date.now() >= obj.expiryMs
88
- ) {
89
- this.remove();
90
- return undefined;
91
- }
92
-
93
- return obj;
94
- } catch (e) {
95
- if (this.logError) {
96
- console.error(
97
- `Found invalid storage value: ${this.key}=${jsonStr}:`,
98
- e,
99
- );
100
- }
101
- this.remove();
102
- return undefined;
103
- }
104
- }
105
-
106
- /**
107
- * Get the value of this item, or undefined if value is not set or expired.
108
- */
109
- public get(): T | undefined {
110
- const stored = this.getStoredItem();
111
-
112
- return stored !== undefined ? stored.value : this.defaultValue;
113
- }
114
-
115
- /**
116
- * Remove the value of this item.
117
- */
118
- public remove(): void {
119
- globalThis.localStorage.removeItem(this.key);
120
- }
121
- }
package/src/logging.ts DELETED
@@ -1,26 +0,0 @@
1
- export function stringify(u: unknown): string {
2
- if (typeof u === "string") {
3
- return u;
4
- }
5
-
6
- // If the object has a custom toString(), then use it.
7
- if (
8
- u !== null &&
9
- typeof u === "object" &&
10
- u.toString !== Object.prototype.toString
11
- ) {
12
- return u.toString();
13
- }
14
-
15
- return JSON.stringify(u);
16
- }
17
-
18
- export function capLength(u: unknown, maxLength = 400): string {
19
- const s = stringify(u);
20
-
21
- if (s.length <= maxLength) {
22
- return s;
23
- }
24
-
25
- return s.slice(0, maxLength) + ` ... (${s.length - maxLength} more)`;
26
- }
package/src/nonEmpty.ts DELETED
@@ -1,18 +0,0 @@
1
- import { isEmpty } from "./isEmpty";
2
-
3
- /**
4
- * Type asserts that `t` is truthy.
5
- * Throws an error if `t` is null or undefined.
6
- *
7
- * @param varName The variable name to include in the error to throw when t is
8
- * empty. Defaults to 'value'.
9
- */
10
- export function nonEmpty<T>(
11
- t: T | null | undefined | "" | 0 | -0 | 0n | false | typeof NaN,
12
- varName = "value",
13
- ): T {
14
- if (isEmpty(t)) {
15
- throw new Error(`Empty ${varName}: ${t}`);
16
- }
17
- return t as T;
18
- }
package/src/safeBtoa.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * Base 64 encode the given input string, but safely.
3
- */
4
- export function safeBtoa(input: string): string {
5
- // Convert the string to a UTF-8 encoded binary-safe string
6
- const utf8Bytes = new TextEncoder().encode(input);
7
-
8
- // Convert the binary data to a string for btoa
9
- const binaryString = Array.from(utf8Bytes)
10
- .map((byte) => String.fromCodePoint(byte))
11
- .join("");
12
-
13
- // Use btoa to encode the binary-safe string
14
- return btoa(binaryString);
15
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Returns 0 or the given defaultValue if the string is not a valid number.
3
- */
4
- export function safeParseFloat<T>(
5
- s: string,
6
- defaultValue: T | number = 0,
7
- ): T | number {
8
- const i = Number(s);
9
- return isNaN(i) ? defaultValue : i;
10
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Returns 0 or the given defaultValue if the string is not a valid number.
3
- */
4
- export function safeParseInt<T>(
5
- s: string,
6
- defaultValue: T | number = 0,
7
- ): T | number {
8
- const i = Number(s);
9
- return isNaN(i) ? defaultValue : Math.floor(i);
10
- }
package/src/sha256.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * SHA-256 hash an input string into an ArrayBuffer.
3
- */
4
- export async function sha256(input: string): Promise<ArrayBuffer> {
5
- // Encode the input string as a Uint8Array
6
- const encoder = new TextEncoder();
7
- const uint8Array = encoder.encode(input);
8
-
9
- // Compute the SHA-256 hash using the SubtleCrypto API
10
- const arrayBuffer = await crypto.subtle.digest("SHA-256", uint8Array);
11
-
12
- return arrayBuffer;
13
- }
package/src/sum.ts DELETED
@@ -1,17 +0,0 @@
1
- /**
2
- * Add all the numbers together in the given array. Treats null, undefined and
3
- * NaN as zero.
4
- */
5
- export function sum(numbers: (number | null | undefined)[]): number {
6
- return numbers.reduce(
7
- (accumulated: number, current: number | null | undefined) => {
8
- const value =
9
- typeof current === "number" && !isNaN(current) && isFinite(current)
10
- ? current
11
- : 0;
12
-
13
- return accumulated + value;
14
- },
15
- 0,
16
- );
17
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Note that month and year do not have fixed durations, and hence are excluded
3
- * from this file.
4
- */
5
-
6
- export const MS_PER_SECOND = 1000;
7
- export const MS_PER_MINUTE = 60_000;
8
- export const MS_PER_HOUR = 3_600_000;
9
- export const MS_PER_DAY = 86_400_000;
10
- export const MS_PER_WEEK = 604_800_000;
11
-
12
- export const SECONDS_PER_MINUTE = 60;
13
- export const SECONDS_PER_HOUR = 3_600;
14
- export const SECONDS_PER_DAY = 86_400;
15
- export const SECONDS_PER_WEEK = 604_800;
16
-
17
- export const MINUTES_PER_HOUR = 60;
18
- export const MINUTES_PER_DAY = 1440;
19
- export const MINUTES_PER_WEEK = 10_080;
20
-
21
- export const HOURS_PER_DAY = 24;
22
- export const HOURS_PER_WEEK = 168;