instrumentality 0.0.7 → 0.0.9

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/package.json CHANGED
@@ -5,12 +5,14 @@
5
5
  "url": "https://github.com/clerkburk/instrumentality.git"
6
6
  },
7
7
  "type": "module",
8
- "version": "0.0.7",
8
+ "version": "0.0.9",
9
9
  "main": "./dist/base.js",
10
10
  "types": "./dist/base.d.ts",
11
11
  "devDependencies": {
12
12
  "@types/node": "^26.1.1",
13
- "typescript": "^7.0.2"
13
+ "@vitest/coverage-v8": "^4.1.11",
14
+ "typescript": "^7.0.2",
15
+ "vitest": "^4.1.11"
14
16
  },
15
17
  "exports": {
16
18
  ".": {
@@ -36,6 +38,6 @@
36
38
  "prepublishOnly": "npm run build",
37
39
  "prepare": "npm run build",
38
40
  "test": "vitest run",
39
- "test:watch": "vitest"
41
+ "test:watch": "vitest watch"
40
42
  }
41
43
  }
package/src/base.ts CHANGED
@@ -1,61 +1,41 @@
1
1
  /** Subclass of {@link Error} that represents an error thrown from this library, providing a specific name for easier identification. */
2
- export class InsErr extends Error { override name = "Instrumentality-Error" }
3
-
2
+ export class Err extends Error { override name = "Instrumentality-Error" }
3
+ export { Err as InsErr }
4
4
 
5
5
 
6
6
 
7
7
  /**
8
- * Retries a function multiple times with optional error handling and abort signal.
8
+ * Retries a function multiple times until it succeeds or the maximum number of attempts is reached, or the error callback indicates to stop.
9
9
  *
10
10
  * @param fn_ - The function to be retried.
11
11
  * @param maxAttempts_ - The maximum number of attempts to execute the function.
12
- * @param cbErr_ - An optional callback function to be executed after each failed attempt.
13
- * @param abs_ - An optional AbortSignal to abort the retry process.
12
+ * @param cbErr_ - An optional callback function that is invoked when an error occurs. It receives the error and the remaining number of attempts as arguments and should return `true` to continue retrying or `false` to stop.
14
13
  * @returns The result of {@link fn_} if it succeeds within the allowed attempts.
15
- * @throws {unknown} If {@link fn_} fails after the maximum attempts, the last error thrown by {@link fn_} is re-thrown.
16
- * @throws {InsErr} If the maximum attempts is less than 1 or if the operation is aborted.
14
+ * @throws the last error thrown by {@link fn_} if it fails after the maximum attempts or if the error callback indicates to stop.
15
+ * @throws If {@link cbErr_} throws or returns a rejected promise, the error is propagated immediately.
16
+ * @throws {InsErr} If {@link maxAttempts_} isn't a positive integer.
17
17
  */
18
- export async function retry<T>(fn_: () => T, maxAttempts_: number, cbErr_?: () => unknown, abs_?: AbortSignal): Promise<T> {
19
- while (--maxAttempts_ >= 0 && !(abs_?.aborted ?? false))
18
+ export async function retry<T>(fn_: () => T | PromiseLike<T>, maxAttempts_: number, cbErr_?: (err: unknown, remainingAttempts: number) => boolean | PromiseLike<boolean>): Promise<T> {
19
+ if (!Number.isInteger(maxAttempts_) || maxAttempts_ < 1)
20
+ throw new Err("Max attempts isn't a positive integer")
21
+ while (true)
20
22
  try {
21
23
  return await fn_()
22
24
  } catch (err: unknown) {
23
- if (maxAttempts_ <= 0)
25
+ if (--maxAttempts_ <= 0 || (cbErr_ && !(await cbErr_(err, maxAttempts_))))
24
26
  throw err
25
- await cbErr_?.()
26
27
  }
27
- if (maxAttempts_ < 0)
28
- throw new InsErr("Max attempts must be at least 1")
29
- else
30
- throw new InsErr("Operation aborted")
31
28
  }
32
29
 
33
30
 
34
31
 
35
32
  /**
36
- * Asynchronously sleep.
33
+ * Alias for `setTimeout`.
37
34
  *
38
35
  * @param ms_ - The number of milliseconds to sleep.
39
- * @param abs_ - An optional AbortSignal to abort the sleep.
40
- * @throws {InsErr} If the sleep is aborted before or during the wait.
36
+ * @remarks The delay is a minimum, not an exact duration. The actual elapsed time also includes timer scheduling, event-loop latency, and promise-continuation overhead, so it may exceed {@link ms_}, especially for short delays.
41
37
  */
42
- export async function sleep(ms_: number, abs_?: AbortSignal): Promise<void> {
43
- if (abs_?.aborted)
44
- return Promise.reject(new InsErr("Sleep aborted before start"))
45
- return new Promise((resolve, reject) => {
46
- const timeout = setTimeout(() => {
47
- abs_?.removeEventListener("abort", onAbort)
48
- resolve()
49
- }, ms_)
50
-
51
- function onAbort() {
52
- clearTimeout(timeout)
53
- abs_?.removeEventListener("abort", onAbort)
54
- reject(new InsErr("Sleep aborted during wait"))
55
- }
56
- abs_?.addEventListener("abort", onAbort, { once: true })
57
- })
58
- }
38
+ export function sleep(ms_: number): Promise<void> { return new Promise<void>(r => setTimeout(r, ms_)) }
59
39
 
60
40
 
61
41
 
@@ -202,12 +182,12 @@ export const BASE122_SHORT = 0b111 as const
202
182
  *
203
183
  * @param data_ - An array-like object containing the data to be encoded.
204
184
  * @returns A string representing the base-122 encoded data.
205
- * @throws If somehow malformed UTF-8 data is generated, the {@link TextDecoder} will throw an error (shouldn't happen if the input is valid).
206
185
  * @remarks The high density might not be suitable for all use cases, especially if the medium used to transmit the data has limitations on character sets or encoding.
207
186
  * @see {@link TextDecoder} how the output string is generated from the byte array (this step is necessary for accurate translation to a string).
208
187
  */
209
188
  export function encode122(data_: ArrayLike<number>): string {
210
- const out: number[] = []
189
+ const out = new Uint8Array(2 * Math.ceil(data_.length * 8 / 7))
190
+ let outIndex = 0
211
191
  let byteIndex = 0
212
192
  let bitIndex = 0
213
193
 
@@ -230,17 +210,16 @@ export function encode122(data_: ArrayLike<number>): string {
230
210
  for (let value = next7(); value !== undefined; value = next7()) {
231
211
  const illegalIndex = BASE122_ILLEGAL_INDEX[value]
232
212
  if (illegalIndex === undefined)
233
- out.push(value)
213
+ out[outIndex++] = value
234
214
  else {
235
215
  const next = next7()
236
216
  const payload = next ?? value
237
- out.push(
217
+ out[outIndex++] =
238
218
  0b11000010 | ((next === undefined ? BASE122_SHORT : illegalIndex) << 2) | (payload >>> 6),
239
- 0b10000000 | (payload & 0b00111111),
240
- )
219
+ out[outIndex++] = 0b10000000 | (payload & 0b00111111)
241
220
  }
242
221
  }
243
- return new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(out))
222
+ return new TextDecoder("utf-8", { fatal: true }).decode(out.subarray(0, outIndex))
244
223
  }
245
224
 
246
225
 
@@ -278,7 +257,7 @@ export function decode122(base122_: string) {
278
257
  if (illegalIndex < BASE122_ILLEGAL.length)
279
258
  push7(BASE122_ILLEGAL[illegalIndex]!)
280
259
  else if (illegalIndex !== BASE122_SHORT)
281
- throw new InsErr(`Invalid base-122 illegal index ${illegalIndex} at position ${i}`)
260
+ throw new Err(`Invalid base-122 illegal index ${illegalIndex} at position ${i}`)
282
261
  push7(code & 0b01111111)
283
262
  }
284
263
 
package/src/dom.ts CHANGED
@@ -1,12 +1,3 @@
1
- import * as bs from "./base.ts"
2
-
3
-
4
-
5
- /** Subclass of {@link bs.InsErr} that represents an error thrown from this specific module of the library */
6
- export class DomErr extends bs.InsErr { override name = "Instrumentality-DOM-Error" }
7
-
8
-
9
-
10
1
  /**
11
2
  * Resolves on `DOMContentLoaded` or immediately if the document is already ready.
12
3
  */
@@ -37,7 +28,7 @@ export function byId<T extends HTMLElement>(id_: string, type_: new () => T): T
37
28
  * Typed accessor for {@link document.getElementsByClassName}
38
29
  *
39
30
  * @param className_ - The class name of the elements to retrieve.
40
- * @param type_ - An optional constructor function for the expected element type.
31
+ * @param type_ - The constructor function for the expected element type.
41
32
  * @returns All elements with the specified class name and type.
42
33
  */
43
34
  export function byClass<T extends HTMLElement>(className_: string, type_: new () => T): T[] {