@travetto/runtime 7.0.0-rc.1 → 7.0.0-rc.3

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/src/exec.ts CHANGED
@@ -1,14 +1,23 @@
1
- import { ChildProcess } from 'node:child_process';
2
- import { Readable } from 'node:stream';
1
+ import { type ChildProcess, spawn, type SpawnOptions } from 'node:child_process';
2
+ import type { Readable } from 'node:stream';
3
3
  import { createInterface } from 'node:readline/promises';
4
4
 
5
- import { castTo } from './types.ts';
6
-
7
- const MINUTE = (1000 * 60);
5
+ import { castTo, type Any } from './types.ts';
8
6
 
9
7
  const ResultSymbol = Symbol();
10
8
 
11
- interface ExecutionBaseResult {
9
+ /**
10
+ * Result of an execution
11
+ */
12
+ export interface ExecutionResult<T extends string | Buffer = string | Buffer> {
13
+ /**
14
+ * Stdout
15
+ */
16
+ stdout: T;
17
+ /**
18
+ * Stderr
19
+ */
20
+ stderr: T;
12
21
  /**
13
22
  * Exit code
14
23
  */
@@ -23,77 +32,19 @@ interface ExecutionBaseResult {
23
32
  valid: boolean;
24
33
  }
25
34
 
26
- /**
27
- * Result of an execution
28
- */
29
- export interface ExecutionResult<T extends string | Buffer = string | Buffer> extends ExecutionBaseResult {
30
- /**
31
- * Stdout
32
- */
33
- stdout: T;
34
- /**
35
- * Stderr
36
- */
37
- stderr: T;
38
- }
35
+ type ExecutionBaseResult = Omit<ExecutionResult, 'stdout' | 'stderr'>;
39
36
 
40
37
  /**
41
38
  * Standard utilities for managing executions
42
39
  */
43
40
  export class ExecUtil {
44
41
 
45
- static RESTART_EXIT_CODE = 200;
46
-
47
42
  /**
48
- * Run with automatic restart support
49
- * @param run The factory to produce the next running process
50
- * @param maxRetriesPerMinute The number of times to allow a retry within a minute
43
+ * Spawn wrapper that ensures performant invocation of trv commands
51
44
  */
52
- static async withRestart(
53
- run: () => ChildProcess,
54
- config?: { maxRetriesPerMinute?: number, relayInterrupt?: boolean }
55
- ): Promise<ExecutionResult> {
56
- const maxRetries = config?.maxRetriesPerMinute ?? 5;
57
- const relayInterrupt = config?.relayInterrupt ?? false;
58
-
59
- const restarts: number[] = [];
60
-
61
- if (!relayInterrupt) {
62
- process.removeAllListeners('SIGINT'); // Remove any existing listeners
63
- process.on('SIGINT', () => { }); // Prevents SIGINT from killing parent process, the child will handle
64
- }
65
-
66
- for (; ;) {
67
- const proc = run();
68
- const interrupt = (): void => { proc.kill('SIGINT'); };
69
- const toMessage = (v: unknown): void => { proc.send?.(v!); };
70
-
71
- // Proxy kill requests
72
- process.on('message', toMessage);
73
- if (relayInterrupt) {
74
- process.on('SIGINT', interrupt);
75
- }
76
- proc.on('message', v => process.send?.(v));
77
-
78
- const result = await this.getResult(proc, { catch: true });
79
- if (result.code !== this.RESTART_EXIT_CODE) {
80
- return result;
81
- } else {
82
- if (relayInterrupt) {
83
- process.off('SIGINT', interrupt);
84
- }
85
- process.off('message', toMessage);
86
- restarts.unshift(Date.now());
87
- if (restarts.length === maxRetries) {
88
- if ((restarts[0] - restarts[maxRetries - 1]) <= MINUTE) {
89
- console.error(`Bailing, due to ${maxRetries} restarts in under a minute`);
90
- return result;
91
- }
92
- restarts.pop(); // Keep list short
93
- }
94
- console.error('Restarting...', { pid: process.pid });
95
- }
96
- }
45
+ static spawnTrv(cmd: string, args: string[], options: SpawnOptions): ChildProcess {
46
+ const entry = (globalThis as Any).__entry_point__ ?? process.argv.at(1);
47
+ return spawn(process.argv0, [entry, cmd, ...args], options);
97
48
  }
98
49
 
99
50
  /**
@@ -101,15 +52,15 @@ export class ExecUtil {
101
52
  * represents the entire execution. On successful completion the promise will resolve, and
102
53
  * on failed completion the promise will reject.
103
54
  *
104
- * @param proc The process to enhance
55
+ * @param subProcess The process to enhance
105
56
  * @param options The options to use to enhance the process
106
57
  */
107
- static getResult(proc: ChildProcess): Promise<ExecutionResult<string>>;
108
- static getResult(proc: ChildProcess, options: { catch?: boolean, binary?: false }): Promise<ExecutionResult<string>>;
109
- static getResult(proc: ChildProcess, options: { catch?: boolean, binary: true }): Promise<ExecutionResult<Buffer>>;
110
- static getResult<T extends string | Buffer>(proc: ChildProcess, options: { catch?: boolean, binary?: boolean } = {}): Promise<ExecutionResult<T>> {
111
- const _proc: ChildProcess & { [ResultSymbol]?: Promise<ExecutionResult> } = proc;
112
- const result = _proc[ResultSymbol] ??= new Promise<ExecutionResult>(resolve => {
58
+ static getResult(subProcess: ChildProcess): Promise<ExecutionResult<string>>;
59
+ static getResult(subProcess: ChildProcess, options: { catch?: boolean, binary?: false }): Promise<ExecutionResult<string>>;
60
+ static getResult(subProcess: ChildProcess, options: { catch?: boolean, binary: true }): Promise<ExecutionResult<Buffer>>;
61
+ static getResult<T extends string | Buffer>(subProcess: ChildProcess, options: { catch?: boolean, binary?: boolean } = {}): Promise<ExecutionResult<T>> {
62
+ const typed: ChildProcess & { [ResultSymbol]?: Promise<ExecutionResult> } = subProcess;
63
+ const result = typed[ResultSymbol] ??= new Promise<ExecutionResult>(resolve => {
113
64
  const stdout: Buffer[] = [];
114
65
  const stderr: Buffer[] = [];
115
66
  let done = false;
@@ -136,25 +87,25 @@ export class ExecUtil {
136
87
  );
137
88
  };
138
89
 
139
- proc.stdout?.on('data', (d: string | Buffer) => stdout.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
140
- proc.stderr?.on('data', (d: string | Buffer) => stderr.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
90
+ subProcess.stdout?.on('data', (data: string | Buffer) => stdout.push(Buffer.isBuffer(data) ? data : Buffer.from(data)));
91
+ subProcess.stderr?.on('data', (data: string | Buffer) => stderr.push(Buffer.isBuffer(data) ? data : Buffer.from(data)));
141
92
 
142
- proc.on('error', (err: Error) =>
143
- finish({ code: 1, message: err.message, valid: false }));
93
+ subProcess.on('error', (error: Error) =>
94
+ finish({ code: 1, message: error.message, valid: false }));
144
95
 
145
- proc.on('close', (code: number) =>
96
+ subProcess.on('close', (code: number) =>
146
97
  finish({ code, valid: code === null || code === 0 }));
147
98
 
148
- if (proc.exitCode !== null) { // We are already done
149
- finish({ code: proc.exitCode, valid: proc.exitCode === 0 });
99
+ if (subProcess.exitCode !== null) { // We are already done
100
+ finish({ code: subProcess.exitCode, valid: subProcess.exitCode === 0 });
150
101
  }
151
102
  });
152
103
 
153
- return castTo(options.catch ? result : result.then(v => {
154
- if (v.valid) {
155
- return v;
104
+ return castTo(options.catch ? result : result.then(executionResult => {
105
+ if (executionResult.valid) {
106
+ return executionResult;
156
107
  } else {
157
- throw new Error(v.message);
108
+ throw new Error(executionResult.message);
158
109
  }
159
110
  }));
160
111
  }
@@ -4,6 +4,7 @@ import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
 
6
6
  import { AppError } from './error.ts';
7
+ import { JSONUtil } from './json.ts';
7
8
 
8
9
  /**
9
10
  * File loader that will search for files across the provided search paths
@@ -64,4 +65,12 @@ export class FileLoader {
64
65
  async readFile(relativePath: string): Promise<File> {
65
66
  return new File([await this.read(relativePath, true)], path.basename(relativePath));
66
67
  }
68
+
69
+ /**
70
+ * Read relative file as JSON
71
+ */
72
+ async readJSON<T>(relativePath: string): Promise<T> {
73
+ const location = await this.resolve(relativePath);
74
+ return JSONUtil.readFile<T>(location);
75
+ }
67
76
  }
package/src/json.ts ADDED
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs/promises';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+
4
+ import type { Any } from './types';
5
+
6
+ /**
7
+ * JSON Utility functions
8
+ */
9
+ export class JSONUtil {
10
+
11
+ /**
12
+ * Parse JSON safely
13
+ */
14
+ static parseSafe<T>(input: string | Buffer, reviver?: (this: unknown, key: string, value: Any) => unknown): T {
15
+ if (typeof input !== 'string') {
16
+ input = input.toString('utf8');
17
+ }
18
+ // TODO: Ensure we aren't vulnerable to prototype pollution
19
+ return JSON.parse(input, reviver);
20
+ }
21
+
22
+ /**
23
+ * Encode JSON value as base64 encoded string
24
+ */
25
+ static stringifyBase64<T>(value: T | undefined): string | undefined {
26
+ if (value === undefined) {
27
+ return;
28
+ }
29
+ const text = JSON.stringify(value);
30
+ return Buffer.from(text, 'utf8').toString('base64');
31
+ }
32
+
33
+ /**
34
+ * Decode JSON value from base64 encoded string
35
+ */
36
+ static parseBase64<T>(input: string): T;
37
+ static parseBase64<T>(input?: string | undefined): T | undefined;
38
+ static parseBase64<T>(input?: string | undefined): T | undefined {
39
+ if (!input) {
40
+ return undefined;
41
+ }
42
+
43
+ let decoded = Buffer.from(input, 'base64').toString('utf8');
44
+
45
+ // Read from encoded if it happens
46
+ if (decoded.startsWith('%')) {
47
+ decoded = decodeURIComponent(decoded);
48
+ }
49
+
50
+ return this.parseSafe(decoded);
51
+ }
52
+
53
+ /**
54
+ * Read JSON file asynchronously
55
+ * @param file
56
+ * @returns
57
+ */
58
+ static async readFile<T>(file: string): Promise<T> {
59
+ const content = await fs.readFile(file, 'utf8');
60
+ return this.parseSafe(content);
61
+ }
62
+
63
+ /**
64
+ * Read JSON file synchronously
65
+ * @param file
66
+ */
67
+ static readFileSync<T>(file: string, onMissing?: T): T {
68
+ if (!existsSync(file) && onMissing !== undefined) {
69
+ return onMissing;
70
+ }
71
+ const content = readFileSync(file, 'utf8');
72
+ return this.parseSafe(content);
73
+ }
74
+ }
package/src/queue.ts CHANGED
@@ -54,9 +54,9 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
54
54
  /**
55
55
  * Throw an error from the queue, rejecting and terminating immediately
56
56
  */
57
- async throw(e?: Error): Promise<IteratorResult<X>> {
57
+ async throw(error?: Error): Promise<IteratorResult<X>> {
58
58
  this.#done = true;
59
- this.#ready.reject(e);
59
+ this.#ready.reject(error);
60
60
  return { value: undefined, done: this.#done };
61
61
  }
62
62
  }
package/src/resources.ts CHANGED
@@ -15,9 +15,9 @@ class $RuntimeResources extends FileLoader {
15
15
  }
16
16
 
17
17
  override get searchPaths(): readonly string[] {
18
- if (!this.#computed || this.#env !== Env.TRV_RESOURCES.val || this.#mod !== Env.TRV_MODULE.val) {
19
- this.#env = Env.TRV_RESOURCES.val!;
20
- this.#mod = Env.TRV_MODULE.val!;
18
+ if (!this.#computed || this.#env !== Env.TRV_RESOURCES.value || this.#mod !== Env.TRV_MODULE.value) {
19
+ this.#env = Env.TRV_RESOURCES.value!;
20
+ this.#mod = Env.TRV_MODULE.value!;
21
21
  this.#computed = Runtime.resourcePaths();
22
22
  }
23
23
  return this.#computed;
package/src/shutdown.ts CHANGED
@@ -40,7 +40,7 @@ export class ShutdownManager {
40
40
  this.#ensureExitListeners();
41
41
  this.#handlers.push({ handler, scope });
42
42
  return () => {
43
- const idx = this.#handlers.findIndex(x => x.handler === handler);
43
+ const idx = this.#handlers.findIndex(item => item.handler === handler);
44
44
  if (idx >= 0) {
45
45
  this.#handlers.splice(idx, 1);
46
46
  }
@@ -61,7 +61,7 @@ export class ShutdownManager {
61
61
 
62
62
  await Util.queueMacroTask(); // Force the event loop to wait one cycle
63
63
 
64
- const timeout = TimeUtil.fromValue(Env.TRV_SHUTDOWN_WAIT.val) ?? 2000;
64
+ const timeout = TimeUtil.fromValue(Env.TRV_SHUTDOWN_WAIT.value) ?? 2000;
65
65
  const items = this.#handlers.splice(0, this.#handlers.length);
66
66
  console.debug('Graceful shutdown: started', { source, timeout, count: items.length });
67
67
  const handlers = Promise.all(items.map(async ({ scope, handler }) => {
@@ -73,8 +73,8 @@ export class ShutdownManager {
73
73
  if (scope) {
74
74
  console.debug('Stopped', { scope });
75
75
  }
76
- } catch (err) {
77
- console.error('Error stopping', { err, scope });
76
+ } catch (error) {
77
+ console.error('Error stopping', { error, scope });
78
78
  }
79
79
  }));
80
80
 
package/src/time.ts CHANGED
@@ -22,8 +22,8 @@ export class TimeUtil {
22
22
  * Test to see if a string is valid for relative time
23
23
  * @param val
24
24
  */
25
- static isTimeSpan(val: string): val is TimeSpan {
26
- return TIME_PATTERN.test(val);
25
+ static isTimeSpan(value: string): value is TimeSpan {
26
+ return TIME_PATTERN.test(value);
27
27
  }
28
28
 
29
29
  /**
@@ -36,12 +36,12 @@ export class TimeUtil {
36
36
  return amount.getTime();
37
37
  } else if (typeof amount === 'string') {
38
38
  const groups: { amount?: string, unit?: TimeUnit } = amount.match(TIME_PATTERN)?.groups ?? {};
39
- const amountStr = groups.amount ?? `${amount}`;
39
+ const amountString = groups.amount ?? `${amount}`;
40
40
  unit = groups.unit ?? unit ?? 'ms';
41
41
  if (!TIME_UNITS[unit]) {
42
42
  return NaN;
43
43
  }
44
- amount = amountStr.includes('.') ? parseFloat(amountStr) : parseInt(amountStr, 10);
44
+ amount = amountString.includes('.') ? parseFloat(amountString) : parseInt(amountString, 10);
45
45
  }
46
46
  return amount * TIME_UNITS[unit ?? 'ms'];
47
47
  }
@@ -69,11 +69,11 @@ export class TimeUtil {
69
69
  if (value === undefined) {
70
70
  return value;
71
71
  }
72
- const val = (typeof value === 'string' && /\d{1,30}[a-z]$/i.test(value)) ?
72
+ const result = (typeof value === 'string' && /\d{1,30}[a-z]$/i.test(value)) ?
73
73
  (this.isTimeSpan(value) ? this.asMillis(value) : undefined) :
74
74
  (typeof value === 'string' ? parseInt(value, 10) :
75
75
  (value instanceof Date ? value.getTime() : value));
76
- return Number.isNaN(val) ? undefined : val;
76
+ return Number.isNaN(result) ? undefined : result;
77
77
  }
78
78
 
79
79
  /**
@@ -90,11 +90,11 @@ export class TimeUtil {
90
90
  * @param time Time in milliseconds
91
91
  */
92
92
  static asClock(time: number): string {
93
- const s = Math.trunc(time / 1000);
93
+ const seconds = Math.trunc(time / 1000);
94
94
  return [
95
- s > 3600 ? `${Math.trunc(s / 3600).toString().padStart(2, '0')}h` : '',
96
- s > 60 ? `${Math.trunc((s % 3600) / 60).toString().padStart(2, '0')}m` : '',
97
- `${(s % 60).toString().padStart(2, '0')}s`
98
- ].filter(x => !!x).slice(0, 2).join(' ');
95
+ seconds > 3600 ? `${Math.trunc(seconds / 3600).toString().padStart(2, '0')}h` : '',
96
+ seconds > 60 ? `${Math.trunc((seconds % 3600) / 60).toString().padStart(2, '0')}m` : '',
97
+ `${(seconds % 60).toString().padStart(2, '0')}s`
98
+ ].filter(part => !!part).slice(0, 2).join(' ');
99
99
  }
100
100
  }
package/src/trv.d.ts CHANGED
@@ -21,10 +21,6 @@ declare module "@travetto/runtime" {
21
21
  * Special role to run as, used to access additional files from the manifest during runtime.
22
22
  */
23
23
  TRV_ROLE: Role;
24
- /**
25
- * Whether or not to run the program in dynamic mode, allowing for real-time updates
26
- */
27
- TRV_DYNAMIC: boolean;
28
24
  /**
29
25
  * The folders to use for resource lookup
30
26
  */
package/src/types.ts CHANGED
@@ -14,7 +14,7 @@ export type TypedFunction<R = Any, V = unknown> = (this: V, ...args: Any[]) => R
14
14
 
15
15
  export type MethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<R, V>>;
16
16
  export type AsyncMethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<Promise<R>, V>>;
17
- export type AsyncItrMethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<AsyncIterable<R>, V>>;
17
+ export type AsyncIterableMethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<AsyncIterable<R>, V>>;
18
18
  export type ClassTDecorator<T extends Class = Class> = (target: T) => T | void;
19
19
 
20
20
  export type Primitive = number | bigint | boolean | string | Date;
@@ -34,9 +34,10 @@ type ValidPrimitiveFields<T, Z = undefined> = {
34
34
  export type RetainPrimitiveFields<T, Z = undefined> = Pick<T, ValidPrimitiveFields<T, Z>>;
35
35
 
36
36
  export const TypedObject: {
37
- keys<T = unknown, K extends keyof T = keyof T & string>(o: T): K[];
37
+ keys<T = unknown, K extends keyof T = keyof T & string>(value: T): K[];
38
38
  fromEntries<K extends string | symbol, V>(items: ([K, V] | readonly [K, V])[]): Record<K, V>;
39
39
  entries<K extends Record<symbol | string, unknown>>(record: K): [keyof K, K[keyof K]][];
40
+ assign<T extends {}, U extends T>(target: T, ...sources: U[]): U;
40
41
  } & ObjectConstructor = Object;
41
42
 
42
43
  export const safeAssign = <T extends {}, U extends {}>(target: T, ...sources: U[]): T & U =>
@@ -47,6 +48,7 @@ export function castTo<T>(input: unknown): T {
47
48
  return input as T;
48
49
  }
49
50
 
51
+ export const isClass = (input: unknown): input is Class => typeof input === 'function' && 'Ⲑid' in input;
50
52
  export const castKey = <T>(input: string | number | symbol): keyof T => castTo(input);
51
53
  export const asFull = <T>(input: Partial<T>): T => castTo(input);
52
54
  export const asConstructable = <Z = unknown>(input: Class | unknown): { constructor: Class<Z> } => castTo(input);
@@ -56,22 +58,20 @@ export function classConstruct<T>(cls: Class<T>, args: unknown[] = []): ClassIns
56
58
  return castTo(new cons(...args));
57
59
  }
58
60
 
59
- export const hasFunction = <T>(key: keyof T) => (o: unknown): o is T =>
60
- typeof o === 'object' && o !== null && typeof o[castKey(key)] === 'function';
61
+ export const hasFunction = <T>(key: keyof T) => (value: unknown): value is T =>
62
+ typeof value === 'object' && value !== null && typeof value[castKey(key)] === 'function';
61
63
 
62
64
  export const hasToJSON = hasFunction<{ toJSON(): object }>('toJSON');
63
65
 
66
+ /**
67
+ * A type representing unknown type
68
+ */
69
+ export class UnknownType { }
70
+
64
71
  export function toConcrete<T extends unknown>(): Class<T> {
65
72
  return arguments[0];
66
73
  }
67
74
 
68
- export function getAllEntries<V>(obj: Record<string | symbol, V>): [string | symbol, V][] {
69
- return [
70
- ...Object.keys(obj),
71
- ...Object.getOwnPropertySymbols(obj)
72
- ].map(k => [k, obj[k]] as const);
73
- }
74
-
75
75
  /**
76
76
  * Find parent class for a given class object
77
77
  */
@@ -83,7 +83,8 @@ export function getParentClass(cls: Class): Class | undefined {
83
83
  /**
84
84
  * Get the class from an instance or class
85
85
  */
86
- export const getClass = <T = unknown>(x: ClassInstance | Class): Class<T> => 'Ⲑid' in x ? castTo(x) : asConstructable<T>(x).constructor;
86
+ export const getClass = <T = unknown>(value: ClassInstance | Class): Class<T> =>
87
+ 'Ⲑid' in value ? castTo(value) : asConstructable<T>(value).constructor;
87
88
 
88
89
  /**
89
90
  * Range of bytes, inclusive
package/src/util.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import crypto from 'node:crypto';
2
2
  import timers from 'node:timers/promises';
3
3
 
4
- import { castTo, hasToJSON } from './types.ts';
4
+ import { castTo } from './types.ts';
5
5
  import { AppError } from './error.ts';
6
6
 
7
- type MapFn<T, U> = (val: T, i: number) => U | Promise<U>;
7
+ type MapFn<T, U> = (value: T, i: number) => U | Promise<U>;
8
8
 
9
9
  /**
10
10
  * Grab bag of common utilities
@@ -38,35 +38,35 @@ export class Util {
38
38
 
39
39
  /**
40
40
  * Generate a random UUID
41
- * @param len The length of the uuid to generate
41
+ * @param length The length of the uuid to generate
42
42
  */
43
- static uuid(len: number = 32): string {
44
- const bytes = crypto.randomBytes(Math.ceil(len / 2));
45
- if (len === 32) { // Make valid uuid-v4
43
+ static uuid(length: number = 32): string {
44
+ const bytes = crypto.randomBytes(Math.ceil(length / 2));
45
+ if (length === 32) { // Make valid uuid-v4
46
46
  // eslint-disable-next-line no-bitwise
47
47
  bytes[6] = (bytes[6] & 0x0f) | 0x40;
48
48
  // eslint-disable-next-line no-bitwise
49
49
  bytes[8] = (bytes[8] & 0x3f) | 0x80;
50
50
  }
51
- return bytes.toString('hex').substring(0, len);
51
+ return bytes.toString('hex').substring(0, length);
52
52
  }
53
53
 
54
54
  /**
55
55
  * Map an async iterable with various mapping functions
56
56
  */
57
- static mapAsyncItr<T, U, V, W>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>, fn3: MapFn<V, W>): AsyncIterable<W>;
58
- static mapAsyncItr<T, U, V>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>): AsyncIterable<V>;
59
- static mapAsyncItr<T, U>(source: AsyncIterable<T>, fn: MapFn<T, U>): AsyncIterable<U>;
60
- static async * mapAsyncItr<T>(source: AsyncIterable<T>, ...fns: MapFn<unknown, unknown>[]): AsyncIterable<unknown> {
57
+ static mapAsyncIterable<T, U, V, W>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>, fn3: MapFn<V, W>): AsyncIterable<W>;
58
+ static mapAsyncIterable<T, U, V>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>): AsyncIterable<V>;
59
+ static mapAsyncIterable<T, U>(source: AsyncIterable<T>, fn: MapFn<T, U>): AsyncIterable<U>;
60
+ static async * mapAsyncIterable<T>(input: AsyncIterable<T>, ...fns: MapFn<unknown, unknown>[]): AsyncIterable<unknown> {
61
61
  let idx = -1;
62
- for await (const el of source) {
63
- if (el !== undefined) {
62
+ for await (const item of input) {
63
+ if (item !== undefined) {
64
64
  idx += 1;
65
- let m = el;
65
+ let result = item;
66
66
  for (const fn of fns) {
67
- m = castTo(await fn(m, idx));
67
+ result = castTo(await fn(result, idx));
68
68
  }
69
- yield m;
69
+ yield result;
70
70
  }
71
71
  }
72
72
  }
@@ -103,9 +103,9 @@ export class Util {
103
103
  cacheKey?: (...keyInput: K) => string
104
104
  ): (...input: K) => boolean {
105
105
 
106
- const rawRules = (Array.isArray(rules) ? rules : rules.split(/,/g).map(x => x.trim()));
106
+ const rawRules = (Array.isArray(rules) ? rules : rules.split(/,/g).map(rule => rule.trim()));
107
107
  const convertedRules = rawRules.map(rule => this.#allowDenyRuleInput(rule, convert));
108
- const unmatchedValue = !convertedRules.some(r => r.positive);
108
+ const unmatchedValue = !convertedRules.some(rule => rule.positive);
109
109
 
110
110
  if (convertedRules.length) {
111
111
  if (cacheKey) {
@@ -120,99 +120,23 @@ export class Util {
120
120
  }
121
121
  }
122
122
 
123
- /**
124
- * Encode JSON value as base64 encoded string
125
- */
126
- static encodeSafeJSON<T>(value: T | undefined): string | undefined {
127
- if (value === undefined) {
128
- return;
129
- }
130
- const res = JSON.stringify(value);
131
- return Buffer.from(res, 'utf8').toString('base64');
132
- }
133
-
134
- /**
135
- * Decode JSON value from base64 encoded string
136
- */
137
- static decodeSafeJSON<T>(input: string): T;
138
- static decodeSafeJSON<T>(input?: string | undefined): T | undefined;
139
- static decodeSafeJSON<T>(input?: string | undefined): T | undefined {
140
- if (!input) {
141
- return undefined;
142
- }
143
-
144
- let decoded = Buffer.from(input, 'base64').toString('utf8');
145
-
146
- // Read from encoded if it happens
147
- if (decoded.startsWith('%')) {
148
- decoded = decodeURIComponent(decoded);
149
- }
150
-
151
- return JSON.parse(decoded, undefined);
152
- }
153
-
154
- /**
155
- * Serialize to JSON
156
- */
157
- static serializeToJSON<T>(out: T): string {
158
- return JSON.stringify(out, function (k, v) {
159
- const ov = this[k];
160
- if (ov && ov instanceof Error) {
161
- return {
162
- $: true,
163
- ...hasToJSON(ov) ? ov.toJSON() : ov,
164
- name: ov.name,
165
- message: ov.message,
166
- stack: ov.stack,
167
- };
168
- } else if (typeof v === 'bigint') {
169
- return `${v.toString()}$n`;
170
- } else {
171
- return v;
172
- }
173
- });
174
- }
175
-
176
- /**
177
- * Deserialize from JSON
178
- */
179
- static deserializeFromJson<T = unknown>(input: string): T {
180
- return JSON.parse(input, function (k, v) {
181
- if (v && typeof v === 'object' && '$' in v) {
182
- const err = AppError.fromJSON(v) ?? new Error();
183
- if (!(err instanceof AppError)) {
184
- const { $: _, ...rest } = v;
185
- Object.assign(err, rest);
186
- }
187
- err.message = v.message;
188
- err.stack = v.stack;
189
- err.name = v.name;
190
- return err;
191
- } else if (typeof v === 'string' && /^\d+[$]n$/.test(v)) {
192
- return BigInt(v.split('$')[0]);
193
- } else {
194
- return v;
195
- }
196
- });
197
- }
198
-
199
123
  /**
200
124
  * Retry an operation, with a custom conflict handler
201
- * @param op The operation to retry
125
+ * @param operation The operation to retry
202
126
  * @param isHandledConflict Function to determine if the error is a handled conflict
203
127
  * @param maxTries Maximum number of retries
204
128
  */
205
129
  static async acquireWithRetry<T>(
206
- op: () => T | Promise<T>,
207
- prepareRetry: (err: unknown, count: number) => (void | undefined | boolean | Promise<(void | undefined | boolean)>),
130
+ operation: () => T | Promise<T>,
131
+ prepareRetry: (error: unknown, count: number) => (void | undefined | boolean | Promise<(void | undefined | boolean)>),
208
132
  maxTries = 5,
209
133
  ): Promise<T> {
210
134
  for (let i = 0; i < maxTries; i++) {
211
135
  try {
212
- return await op();
213
- } catch (err) {
214
- if (i === maxTries - 1 || await prepareRetry(err, i) === false) {
215
- throw err; // Stop retrying if we reached max tries or prepareRetry returns false
136
+ return await operation();
137
+ } catch (error) {
138
+ if (i === maxTries - 1 || await prepareRetry(error, i) === false) {
139
+ throw error; // Stop retrying if we reached max tries or prepareRetry returns false
216
140
  }
217
141
  }
218
142
  }