@travetto/runtime 8.0.0-alpha.9 → 8.0.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 +31 -28
- package/__index__.ts +6 -5
- package/package.json +18 -15
- package/src/binary-metadata.ts +33 -19
- package/src/binary.ts +18 -10
- package/src/codec.ts +25 -7
- package/src/console.ts +20 -12
- package/src/context.ts +18 -15
- package/src/debug.ts +1 -2
- package/src/env.ts +40 -20
- package/src/error.ts +4 -15
- package/src/exec.ts +41 -27
- package/src/file-loader.ts +2 -3
- package/src/function.ts +14 -6
- package/src/global.d.ts +22 -14
- package/src/json.ts +42 -22
- package/src/manifest-index.ts +1 -1
- package/src/queue.ts +1 -2
- package/src/resources.ts +1 -1
- package/src/shutdown.ts +21 -14
- package/src/time.ts +54 -19
- package/src/trv.d.ts +10 -9
- package/src/types.ts +32 -15
- package/src/util.ts +20 -22
- package/src/watch.ts +21 -17
- package/support/patch.js +15 -20
- package/support/transformer/metadata.ts +15 -28
- package/support/transformer.concrete-type.ts +19 -21
- package/support/transformer.console-log.ts +13 -19
- package/support/transformer.debug-method.ts +13 -9
- package/support/transformer.dynamic-import.ts +1 -2
- package/support/transformer.function-metadata.ts +6 -4
- package/support/transformer.rewrite-path-import.ts +9 -12
package/src/queue.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* An asynchronous queue
|
|
3
3
|
*/
|
|
4
4
|
export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
5
|
-
|
|
6
5
|
#buffer: X[] = [];
|
|
7
6
|
#done = false;
|
|
8
7
|
#ready = Promise.withResolvers<void>();
|
|
@@ -59,4 +58,4 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
59
58
|
this.#ready.reject(error);
|
|
60
59
|
return { value: undefined, done: this.#done };
|
|
61
60
|
}
|
|
62
|
-
}
|
|
61
|
+
}
|
package/src/resources.ts
CHANGED
package/src/shutdown.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type { ChildProcess } from 'node:child_process';
|
|
2
2
|
|
|
3
3
|
import { Env } from './env.ts';
|
|
4
|
-
import { Util } from './util.ts';
|
|
5
4
|
import { TimeUtil } from './time.ts';
|
|
5
|
+
import { Util } from './util.ts';
|
|
6
6
|
|
|
7
|
-
const MAPPING = [
|
|
8
|
-
|
|
7
|
+
const MAPPING = [
|
|
8
|
+
['restart', 200],
|
|
9
|
+
['error', 1],
|
|
10
|
+
['quit', 0]
|
|
11
|
+
] as const;
|
|
12
|
+
export type ShutdownReason = (typeof MAPPING)[number][0];
|
|
9
13
|
|
|
10
14
|
const REASON_TO_CODE = new Map<ShutdownReason, number>(MAPPING);
|
|
11
15
|
const CODE_TO_REASON = new Map<number, ShutdownReason>(MAPPING.map(([k, v]) => [v, k]));
|
|
12
16
|
|
|
13
17
|
type Handler = (event: Event) => unknown;
|
|
14
|
-
type ShutdownEvent = { reason?: ShutdownReason
|
|
18
|
+
type ShutdownEvent = { reason?: ShutdownReason; mode?: 'exit' | 'interrupt' };
|
|
15
19
|
|
|
16
20
|
const isShutdownEvent = (event: unknown): event is ShutdownEvent =>
|
|
17
21
|
typeof event === 'object' && event !== null && 'type' in event && event.type === 'shutdown';
|
|
@@ -33,14 +37,20 @@ export class ShutdownManager {
|
|
|
33
37
|
static #controller = new AbortController();
|
|
34
38
|
|
|
35
39
|
static {
|
|
36
|
-
this.#controller.signal.addEventListener = (_: 'abort', listener: Handler): void => {
|
|
37
|
-
|
|
40
|
+
this.#controller.signal.addEventListener = (_: 'abort', listener: Handler): void => {
|
|
41
|
+
this.#registered.add(listener);
|
|
42
|
+
};
|
|
43
|
+
this.#controller.signal.removeEventListener = (_: 'abort', listener: Handler): void => {
|
|
44
|
+
this.#registered.delete(listener);
|
|
45
|
+
};
|
|
38
46
|
try {
|
|
39
47
|
process
|
|
40
|
-
.on('message', event => {
|
|
48
|
+
.on('message', event => {
|
|
49
|
+
isShutdownEvent(event) && this.shutdown(event);
|
|
50
|
+
})
|
|
41
51
|
.on('SIGINT', () => this.shutdown({ mode: 'interrupt' }))
|
|
42
52
|
.on('SIGTERM', () => this.shutdown());
|
|
43
|
-
} catch {
|
|
53
|
+
} catch {}
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
static get signal(): AbortSignal {
|
|
@@ -60,7 +70,7 @@ export class ShutdownManager {
|
|
|
60
70
|
|
|
61
71
|
/** Trigger a watch signal signal to a subprocess */
|
|
62
72
|
static async shutdownChild(subprocess: ChildProcess, config?: ShutdownEvent): Promise<void> {
|
|
63
|
-
subprocess?.send
|
|
73
|
+
subprocess?.send?.({ type: 'shutdown', ...config });
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
/**
|
|
@@ -87,10 +97,7 @@ export class ShutdownManager {
|
|
|
87
97
|
this.#controller.abort('Shutdown started');
|
|
88
98
|
console.debug('Shutdown started', context);
|
|
89
99
|
|
|
90
|
-
const winner = await Promise.race([
|
|
91
|
-
Util.nonBlockingTimeout(timeout).then(() => this),
|
|
92
|
-
Promise.all([...this.#registered].map(wrapped))
|
|
93
|
-
]);
|
|
100
|
+
const winner = await Promise.race([Util.nonBlockingTimeout(timeout).then(() => this), Promise.all([...this.#registered].map(wrapped))]);
|
|
94
101
|
|
|
95
102
|
if (winner !== this) {
|
|
96
103
|
console.debug('Shutdown completed', context);
|
|
@@ -102,4 +109,4 @@ export class ShutdownManager {
|
|
|
102
109
|
process.exit();
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
|
-
}
|
|
112
|
+
}
|
package/src/time.ts
CHANGED
|
@@ -2,16 +2,32 @@ import { RuntimeError } from './error.ts';
|
|
|
2
2
|
import { castTo } from './types.ts';
|
|
3
3
|
|
|
4
4
|
const TIME_UNIT_TO_TEMPORAL_UNIT = {
|
|
5
|
-
y: 'years',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
y: 'years',
|
|
6
|
+
year: 'years',
|
|
7
|
+
years: 'years',
|
|
8
|
+
M: 'months',
|
|
9
|
+
month: 'months',
|
|
10
|
+
months: 'months',
|
|
11
|
+
w: 'weeks',
|
|
12
|
+
week: 'weeks',
|
|
13
|
+
weeks: 'weeks',
|
|
14
|
+
d: 'days',
|
|
15
|
+
day: 'days',
|
|
16
|
+
days: 'days',
|
|
17
|
+
h: 'hours',
|
|
18
|
+
hour: 'hours',
|
|
19
|
+
hours: 'hours',
|
|
20
|
+
m: 'minutes',
|
|
21
|
+
minute: 'minutes',
|
|
22
|
+
minutes: 'minutes',
|
|
23
|
+
s: 'seconds',
|
|
24
|
+
second: 'seconds',
|
|
25
|
+
seconds: 'seconds',
|
|
26
|
+
ms: 'milliseconds',
|
|
27
|
+
millisecond: 'milliseconds',
|
|
28
|
+
milliseconds: 'milliseconds'
|
|
13
29
|
} as const;
|
|
14
|
-
type TemporalUnit = typeof TIME_UNIT_TO_TEMPORAL_UNIT[keyof typeof TIME_UNIT_TO_TEMPORAL_UNIT];
|
|
30
|
+
type TemporalUnit = (typeof TIME_UNIT_TO_TEMPORAL_UNIT)[keyof typeof TIME_UNIT_TO_TEMPORAL_UNIT];
|
|
15
31
|
|
|
16
32
|
export type TimeSpan = `${number}${keyof typeof TIME_UNIT_TO_TEMPORAL_UNIT}`;
|
|
17
33
|
export type TimeUnit = keyof typeof TIME_UNIT_TO_TEMPORAL_UNIT;
|
|
@@ -19,7 +35,6 @@ export type TimeUnit = keyof typeof TIME_UNIT_TO_TEMPORAL_UNIT;
|
|
|
19
35
|
const TIME_PATTERN = /^(?<amount>-?[0-9]+)(?<unit>(?:year|month|week|day|hour|minute|second|millisecond)s?|y|M|w|d|h|m|s|ms)$/;
|
|
20
36
|
|
|
21
37
|
export class TimeUtil {
|
|
22
|
-
|
|
23
38
|
/**
|
|
24
39
|
* Test to see if a string is valid for relative time
|
|
25
40
|
*/
|
|
@@ -50,20 +65,40 @@ export class TimeUtil {
|
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
switch (unit) {
|
|
53
|
-
case 'years': {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
case 'years': {
|
|
69
|
+
unit = 'hours';
|
|
70
|
+
value = value * 365 * 24;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'months': {
|
|
74
|
+
value = value * 30 * 24;
|
|
75
|
+
unit = 'hours';
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case 'weeks': {
|
|
79
|
+
value = value * 7 * 24;
|
|
80
|
+
unit = 'hours';
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'days': {
|
|
84
|
+
value = value * 24;
|
|
85
|
+
unit = 'hours';
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
57
88
|
}
|
|
58
89
|
|
|
59
90
|
const duration = Temporal.Duration.from({ [unit]: value });
|
|
60
91
|
if (outputUnit) {
|
|
61
92
|
const resolved = TIME_UNIT_TO_TEMPORAL_UNIT[outputUnit];
|
|
62
93
|
switch (resolved) {
|
|
63
|
-
case 'years':
|
|
64
|
-
|
|
65
|
-
case '
|
|
66
|
-
|
|
94
|
+
case 'years':
|
|
95
|
+
return Math.trunc(duration.total('hours') / (365 * 24));
|
|
96
|
+
case 'months':
|
|
97
|
+
return Math.trunc(duration.total('hours') / (30 * 24));
|
|
98
|
+
case 'weeks':
|
|
99
|
+
return Math.trunc(duration.total('hours') / (7 * 24));
|
|
100
|
+
default:
|
|
101
|
+
return Math.trunc(duration.total(resolved));
|
|
67
102
|
}
|
|
68
103
|
} else {
|
|
69
104
|
return duration;
|
|
@@ -94,4 +129,4 @@ export class TimeUtil {
|
|
|
94
129
|
return `${toFixed(seconds)}s`;
|
|
95
130
|
}
|
|
96
131
|
}
|
|
97
|
-
}
|
|
132
|
+
}
|
package/src/trv.d.ts
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
import { type ManifestModuleRole } from '@travetto/manifest';
|
|
2
|
+
|
|
2
3
|
import { type TimeSpan } from './time.ts';
|
|
3
4
|
type Role = Exclude<ManifestModuleRole, 'compile'>;
|
|
4
5
|
|
|
5
6
|
declare module '@travetto/runtime' {
|
|
6
7
|
interface EnvData {
|
|
7
|
-
/**
|
|
8
|
+
/**
|
|
8
9
|
* The node environment we are running in
|
|
9
10
|
* @default development
|
|
10
11
|
*/
|
|
11
12
|
NODE_ENV: 'development' | 'production';
|
|
12
|
-
/**
|
|
13
|
+
/**
|
|
13
14
|
* Outputs all console.debug messages, defaults to off
|
|
14
15
|
*/
|
|
15
16
|
DEBUG: boolean | string;
|
|
16
|
-
/**
|
|
17
|
+
/**
|
|
17
18
|
* The role we are running as, allows access to additional files from the manifest during runtime.
|
|
18
19
|
*/
|
|
19
20
|
TRV_ROLE: Role;
|
|
20
|
-
/**
|
|
21
|
+
/**
|
|
21
22
|
* The folders to use for resource lookup
|
|
22
23
|
*/
|
|
23
24
|
TRV_RESOURCES: string[];
|
|
24
|
-
/**
|
|
25
|
+
/**
|
|
25
26
|
* Resource path overrides
|
|
26
27
|
* @private
|
|
27
28
|
*/
|
|
28
29
|
TRV_RESOURCE_OVERRIDES: Record<string, string>;
|
|
29
|
-
/**
|
|
30
|
-
* The max time to wait for shutdown to finish after initial SIGINT,
|
|
30
|
+
/**
|
|
31
|
+
* The max time to wait for shutdown to finish after initial SIGINT,
|
|
31
32
|
* @default 2s
|
|
32
33
|
*/
|
|
33
34
|
TRV_SHUTDOWN_WAIT: TimeSpan | number;
|
|
34
35
|
/**
|
|
35
|
-
* The desired runtime module
|
|
36
|
+
* The desired runtime module
|
|
36
37
|
*/
|
|
37
38
|
TRV_MODULE: string;
|
|
38
39
|
/**
|
|
@@ -50,4 +51,4 @@ declare module '@travetto/runtime' {
|
|
|
50
51
|
*/
|
|
51
52
|
TRV_DEBUG_BREAK: boolean;
|
|
52
53
|
}
|
|
53
|
-
}
|
|
54
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// biome-ignore lint/suspicious/noExplicitAny: This is the any reference we use explicitly
|
|
4
4
|
export type Any = any;
|
|
5
5
|
|
|
6
6
|
export type AnyMap = { [key: string]: Any };
|
|
@@ -12,7 +12,6 @@ export type TypedFunction<R = Any, V = unknown> = (this: V, ...args: Any[]) => R
|
|
|
12
12
|
export type MethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<R, V>>;
|
|
13
13
|
export type AsyncMethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<Promise<R>, V>>;
|
|
14
14
|
export type AsyncIterableMethodDescriptor<V = Any, R = Any> = TypedPropertyDescriptor<TypedFunction<AsyncIterable<R>, V>>;
|
|
15
|
-
export type ClassTDecorator<T extends Class = Class> = (target: T) => T | void;
|
|
16
15
|
|
|
17
16
|
export type NumericPrimitive = number | bigint;
|
|
18
17
|
export type Primitive = NumericPrimitive | boolean | string;
|
|
@@ -21,19 +20,35 @@ export type NumericLikeIntrinsic = Date | NumericPrimitive;
|
|
|
21
20
|
export type IntrinsicType = Primitive | Date | ArrayBuffer | Uint8Array | Uint16Array | Uint32Array | Readable | Buffer | Blob | File;
|
|
22
21
|
|
|
23
22
|
export type DeepPartial<T> = {
|
|
24
|
-
[P in keyof T]?:
|
|
25
|
-
|
|
23
|
+
[P in keyof T]?: T[P] extends IntrinsicType | undefined
|
|
24
|
+
? T[P] | undefined
|
|
25
|
+
: T[P] extends Any[]
|
|
26
|
+
? (DeepPartial<T[P][number]> | null | undefined)[]
|
|
27
|
+
: DeepPartial<T[P]>;
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
export type ValidFields<T, I> = {
|
|
29
|
-
[K in keyof T]:
|
|
30
|
-
(T[K] extends (Primitive | I | undefined) ? K :
|
|
31
|
-
(T[K] extends (Function | undefined) ? never :
|
|
32
|
-
K))
|
|
31
|
+
[K in keyof T]: T[K] extends Primitive | I | undefined ? K : T[K] extends Function | undefined ? never : K;
|
|
33
32
|
}[keyof T];
|
|
34
33
|
|
|
35
34
|
export type RetainIntrinsicFields<T> = Pick<T, ValidFields<T, IntrinsicType>>;
|
|
36
35
|
|
|
36
|
+
type KeyPathsInner<T, PrimitiveType = IntrinsicType | IntrinsicType[], PREFIX extends string = '', SEP extends string = '.'> = {
|
|
37
|
+
[K in keyof T]: K extends string
|
|
38
|
+
? T[K] extends IntrinsicType[] | IntrinsicType | undefined
|
|
39
|
+
? T[K] extends PrimitiveType
|
|
40
|
+
? `${PREFIX}${K}`
|
|
41
|
+
: never
|
|
42
|
+
: T[K] extends Any[]
|
|
43
|
+
? KeyPathsInner<T[K][number], PrimitiveType, `${K}${SEP}`, SEP>
|
|
44
|
+
: T[K] extends object
|
|
45
|
+
? KeyPathsInner<T[K], PrimitiveType, `${K}${SEP}`, SEP>
|
|
46
|
+
: never
|
|
47
|
+
: never;
|
|
48
|
+
}[keyof T];
|
|
49
|
+
|
|
50
|
+
export type KeyPaths<T, PrimitiveType = IntrinsicType | IntrinsicType[]> = KeyPathsInner<Required<T>, PrimitiveType>;
|
|
51
|
+
|
|
37
52
|
export const TypedObject: {
|
|
38
53
|
keys<T = unknown, K extends keyof T = keyof T & string>(value: T): K[];
|
|
39
54
|
fromEntries<K extends string | symbol, V>(items: ([K, V] | readonly [K, V])[]): Record<K, V>;
|
|
@@ -41,11 +56,9 @@ export const TypedObject: {
|
|
|
41
56
|
assign<T extends {}, U extends T>(target: T, ...sources: U[]): U;
|
|
42
57
|
} & ObjectConstructor = Object;
|
|
43
58
|
|
|
44
|
-
export const safeAssign = <T extends {}, U extends {}>(target: T, ...sources: U[]): T & U =>
|
|
45
|
-
Object.assign(target, ...sources);
|
|
59
|
+
export const safeAssign = <T extends {}, U extends {}>(target: T, ...sources: U[]): T & U => Object.assign(target, ...sources);
|
|
46
60
|
|
|
47
61
|
export function castTo<T>(input: unknown): T {
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
49
62
|
return input as T;
|
|
50
63
|
}
|
|
51
64
|
|
|
@@ -55,16 +68,20 @@ export const asFull = <T>(input: Partial<T>): T => castTo(input);
|
|
|
55
68
|
export const asConstructable = <Z = unknown>(input: Class | unknown): { constructor: Class<Z> } => castTo(input);
|
|
56
69
|
|
|
57
70
|
export function classConstruct<T>(cls: Class<T>, args: unknown[] = []): ClassInstance<T> {
|
|
58
|
-
const cons: { new(..._args: Any[]): T } = castTo(cls);
|
|
71
|
+
const cons: { new (..._args: Any[]): T } = castTo(cls);
|
|
59
72
|
return castTo(new cons(...args));
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
export const hasFunction =
|
|
63
|
-
|
|
75
|
+
export const hasFunction =
|
|
76
|
+
<T>(key: keyof T) =>
|
|
77
|
+
(value: unknown): value is T =>
|
|
78
|
+
typeof value === 'object' && value !== null && typeof value[castKey(key)] === 'function';
|
|
64
79
|
|
|
65
80
|
export const hasToJSON = hasFunction<{ toJSON(): object }>('toJSON');
|
|
66
81
|
|
|
82
|
+
// biome-ignore lint/complexity/noUselessTypeConstraint: Unknown behaves slightly differently when being used for an inferred type
|
|
67
83
|
export function toConcrete<T extends unknown>(): Class<T> {
|
|
84
|
+
// biome-ignore lint/complexity/noArguments: We want to use arguments here
|
|
68
85
|
return arguments[0];
|
|
69
86
|
}
|
|
70
87
|
|
|
@@ -80,4 +97,4 @@ export function getParentClass(cls: Class): Class | undefined {
|
|
|
80
97
|
* Get the class from an instance or class
|
|
81
98
|
*/
|
|
82
99
|
export const getClass = <T = unknown>(value: ClassInstance | Class): Class<T> =>
|
|
83
|
-
'Ⲑid' in value ? castTo(value) : asConstructable<T>(value).constructor;
|
|
100
|
+
'Ⲑid' in value ? castTo(value) : asConstructable<T>(value).constructor;
|
package/src/util.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import timers from 'node:timers/promises';
|
|
2
|
+
|
|
2
3
|
import { path } from '@travetto/manifest';
|
|
3
4
|
|
|
4
5
|
import { castTo } from './types.ts';
|
|
5
6
|
|
|
6
7
|
type MapFn<T, U> = (value: T, i: number) => U | Promise<U>;
|
|
7
|
-
type StackFrame = { message: string
|
|
8
|
+
type StackFrame = { message: string; filename: string; line: number; column: number };
|
|
8
9
|
|
|
9
|
-
const STACK_POSITION_PATTERN =
|
|
10
|
+
const STACK_POSITION_PATTERN =
|
|
11
|
+
/[(]\s{0,5}(?<filename>[^()]{1,1000}[.]([cm]?[jt]s))([:](?<line>\d{1,10}))?([:](?<column>\d{1,10}))?\s{0,5}[)]/;
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Grab bag of common utilities
|
|
13
15
|
*/
|
|
14
16
|
export class Util {
|
|
15
|
-
|
|
16
17
|
static #match<T, K extends unknown[]>(
|
|
17
|
-
rules: { value: T
|
|
18
|
+
rules: { value: T; positive: boolean }[],
|
|
18
19
|
compare: (rule: T, ...compareInput: K) => boolean,
|
|
19
20
|
unmatchedValue: boolean,
|
|
20
21
|
...input: K
|
|
@@ -28,14 +29,14 @@ export class Util {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
static #allowDenyRuleInput<T>(
|
|
31
|
-
rule:
|
|
32
|
+
rule: string | T | [value: T, positive: boolean] | [value: T],
|
|
32
33
|
convert: (inputRule: string) => T
|
|
33
|
-
): { value: T
|
|
34
|
-
return typeof rule === 'string'
|
|
35
|
-
{ value: convert(rule.replace(/^!/, '')), positive: !rule.startsWith('!') }
|
|
36
|
-
Array.isArray(rule)
|
|
37
|
-
{ value: rule[0], positive: rule[1] ?? true }
|
|
38
|
-
{ value: rule, positive: true };
|
|
34
|
+
): { value: T; positive: boolean } {
|
|
35
|
+
return typeof rule === 'string'
|
|
36
|
+
? { value: convert(rule.replace(/^!/, '')), positive: !rule.startsWith('!') }
|
|
37
|
+
: Array.isArray(rule)
|
|
38
|
+
? { value: rule[0], positive: rule[1] ?? true }
|
|
39
|
+
: { value: rule, positive: true };
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -44,10 +45,9 @@ export class Util {
|
|
|
44
45
|
*/
|
|
45
46
|
static uuid(length: number = 32): string {
|
|
46
47
|
const bytes = crypto.getRandomValues(new Uint8Array(Math.ceil(length / 2)));
|
|
47
|
-
if (length === 32) {
|
|
48
|
-
//
|
|
48
|
+
if (length === 32) {
|
|
49
|
+
// Make valid uuid-v4
|
|
49
50
|
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
50
|
-
// eslint-disable-next-line no-bitwise
|
|
51
51
|
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
52
52
|
}
|
|
53
53
|
return bytes.toHex().substring(0, length);
|
|
@@ -59,7 +59,7 @@ export class Util {
|
|
|
59
59
|
static mapAsyncIterable<T, U, V, W>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>, fn3: MapFn<V, W>): AsyncIterable<W>;
|
|
60
60
|
static mapAsyncIterable<T, U, V>(source: AsyncIterable<T>, fn1: MapFn<T, U>, fn2: MapFn<U, V>): AsyncIterable<V>;
|
|
61
61
|
static mapAsyncIterable<T, U>(source: AsyncIterable<T>, fn: MapFn<T, U>): AsyncIterable<U>;
|
|
62
|
-
static async *
|
|
62
|
+
static async *mapAsyncIterable<T>(input: AsyncIterable<T>, ...fns: MapFn<unknown, unknown>[]): AsyncIterable<unknown> {
|
|
63
63
|
let idx = -1;
|
|
64
64
|
for await (const item of input) {
|
|
65
65
|
if (item !== undefined) {
|
|
@@ -77,14 +77,14 @@ export class Util {
|
|
|
77
77
|
* Non-blocking timeout
|
|
78
78
|
*/
|
|
79
79
|
static nonBlockingTimeout(time: number): Promise<void> {
|
|
80
|
-
return timers.setTimeout(time, undefined, { ref: false }).catch(() => {
|
|
80
|
+
return timers.setTimeout(time, undefined, { ref: false }).catch(() => {});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Blocking timeout
|
|
85
85
|
*/
|
|
86
86
|
static blockingTimeout(time: number): Promise<void> {
|
|
87
|
-
return timers.setTimeout(time, undefined, { ref: true }).catch(() => {
|
|
87
|
+
return timers.setTimeout(time, undefined, { ref: true }).catch(() => {});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -104,16 +104,14 @@ export class Util {
|
|
|
104
104
|
compare: (rule: T, ...compareInput: K) => boolean,
|
|
105
105
|
cacheKey?: (...keyInput: K) => string
|
|
106
106
|
): (...input: K) => boolean {
|
|
107
|
-
|
|
108
|
-
const rawRules = (Array.isArray(rules) ? rules : rules.split(/,/g).map(rule => rule.trim()));
|
|
107
|
+
const rawRules = Array.isArray(rules) ? rules : rules.split(/,/g).map(rule => rule.trim());
|
|
109
108
|
const convertedRules = rawRules.map(rule => this.#allowDenyRuleInput(rule, convert));
|
|
110
109
|
const unmatchedValue = !convertedRules.some(rule => rule.positive);
|
|
111
110
|
|
|
112
111
|
if (convertedRules.length) {
|
|
113
112
|
if (cacheKey) {
|
|
114
113
|
const cache: Record<string, boolean> = {};
|
|
115
|
-
return (...input: K) =>
|
|
116
|
-
cache[cacheKey(...input)] ??= this.#match(convertedRules, compare, unmatchedValue, ...input);
|
|
114
|
+
return (...input: K) => (cache[cacheKey(...input)] ??= this.#match(convertedRules, compare, unmatchedValue, ...input));
|
|
117
115
|
} else {
|
|
118
116
|
return (...input: K) => this.#match(convertedRules, compare, unmatchedValue, ...input);
|
|
119
117
|
}
|
|
@@ -133,7 +131,7 @@ export class Util {
|
|
|
133
131
|
.split('\n')
|
|
134
132
|
.map(frameText => {
|
|
135
133
|
const match = STACK_POSITION_PATTERN.exec(frameText);
|
|
136
|
-
if (!match
|
|
134
|
+
if (!match?.groups) {
|
|
137
135
|
return undefined;
|
|
138
136
|
}
|
|
139
137
|
const { filename, line, column } = match.groups;
|
package/src/watch.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CompilerEventPayload, CompilerEventType } from '@travetto/compiler';
|
|
2
2
|
|
|
3
3
|
import { RuntimeError } from './error.ts';
|
|
4
|
-
import { Util } from './util.ts';
|
|
5
4
|
import { RuntimeIndex } from './manifest-index.ts';
|
|
6
5
|
import { ShutdownManager, type ShutdownReason } from './shutdown.ts';
|
|
7
6
|
import { castTo } from './types.ts';
|
|
7
|
+
import { Util } from './util.ts';
|
|
8
8
|
|
|
9
9
|
type RetryRunState = {
|
|
10
10
|
iteration: number;
|
|
@@ -17,25 +17,25 @@ type RetryRunConfig = {
|
|
|
17
17
|
maxRetries: number;
|
|
18
18
|
maxRetryWindow: number;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
|
-
onRetry: (state: RetryRunState, config: RetryRunConfig) =>
|
|
20
|
+
onRetry: (state: RetryRunState, config: RetryRunConfig) => unknown | Promise<unknown>;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Utilities for watching resources
|
|
25
25
|
*/
|
|
26
26
|
export class WatchUtil {
|
|
27
|
-
|
|
28
27
|
/** Compute the delay before restarting */
|
|
29
28
|
static computeRestartDelay(state: RetryRunState, config: RetryRunConfig): number {
|
|
30
|
-
return state.result === 'error'
|
|
31
|
-
? config.maxRetryWindow / (config.maxRetries + 1)
|
|
32
|
-
: 10;
|
|
29
|
+
return state.result === 'error' ? config.maxRetryWindow / (config.maxRetries + 1) : 10;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
/**
|
|
36
33
|
* Run with restart capability
|
|
37
34
|
*/
|
|
38
|
-
static async runWithRetry(
|
|
35
|
+
static async runWithRetry(
|
|
36
|
+
run: (state: RetryRunState & { signal: AbortSignal }) => Promise<ShutdownReason>,
|
|
37
|
+
options?: Partial<RetryRunConfig>
|
|
38
|
+
): Promise<void> {
|
|
39
39
|
let retryExhausted = false;
|
|
40
40
|
|
|
41
41
|
const state: RetryRunState = {
|
|
@@ -48,10 +48,9 @@ export class WatchUtil {
|
|
|
48
48
|
maxRetryWindow: 10 * 1000,
|
|
49
49
|
maxRetries: 10,
|
|
50
50
|
onRetry: () => Util.nonBlockingTimeout(this.computeRestartDelay(state, config)),
|
|
51
|
-
...options
|
|
51
|
+
...options
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
|
|
55
54
|
outer: while (!ShutdownManager.signal.aborted && !retryExhausted) {
|
|
56
55
|
if (state.iteration > 0) {
|
|
57
56
|
await config.onRetry(state, config);
|
|
@@ -59,15 +58,18 @@ export class WatchUtil {
|
|
|
59
58
|
|
|
60
59
|
state.result = await run({ ...state, signal: ShutdownManager.signal }).catch(() => 'error' as const);
|
|
61
60
|
switch (state.result) {
|
|
62
|
-
case 'quit':
|
|
63
|
-
|
|
61
|
+
case 'quit':
|
|
62
|
+
break outer;
|
|
63
|
+
case 'error':
|
|
64
|
+
state.errorIterations += 1;
|
|
65
|
+
break;
|
|
64
66
|
case 'restart': {
|
|
65
67
|
state.startTime = Date.now();
|
|
66
68
|
state.errorIterations = 0;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
retryExhausted =
|
|
72
|
+
retryExhausted = state.errorIterations >= config.maxRetries || Date.now() - state.startTime >= config.maxRetryWindow;
|
|
71
73
|
state.iteration += 1;
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -81,25 +83,27 @@ export class WatchUtil {
|
|
|
81
83
|
type: K,
|
|
82
84
|
onChange: (input: T) => unknown,
|
|
83
85
|
filter?: (input: T) => boolean,
|
|
84
|
-
options?: Partial<RetryRunConfig
|
|
86
|
+
options?: Partial<RetryRunConfig>
|
|
85
87
|
): Promise<void> {
|
|
86
88
|
const { CompilerClient } = await import('@travetto/compiler/src/server/client.ts');
|
|
87
89
|
const client = new CompilerClient(RuntimeIndex.manifest, {
|
|
88
90
|
debug: (...args: unknown[]): void => console.debug(...args),
|
|
89
91
|
info: (...args: unknown[]): void => console.info(...args),
|
|
90
92
|
warn: (...args: unknown[]): void => console.warn(...args),
|
|
91
|
-
error: (...args: unknown[]): void => console.error(...args)
|
|
93
|
+
error: (...args: unknown[]): void => console.error(...args)
|
|
92
94
|
});
|
|
93
95
|
|
|
94
96
|
// pre-check
|
|
95
|
-
if (!await client.isWatching()) {
|
|
97
|
+
if (!(await client.isWatching())) {
|
|
98
|
+
// If we get here, without a watch
|
|
96
99
|
throw new RuntimeError('Compile Server is not running');
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
void this.runWithRetry(async ({ signal }) => {
|
|
100
103
|
await client.waitForState(['watch-start'], undefined, signal);
|
|
101
104
|
|
|
102
|
-
if (!await client.isWatching()) {
|
|
105
|
+
if (!(await client.isWatching())) {
|
|
106
|
+
// If we get here, without a watch
|
|
103
107
|
return 'error';
|
|
104
108
|
} else {
|
|
105
109
|
for await (const event of client.fetchEvents(type, { signal, enforceIteration: true })) {
|
|
@@ -111,4 +115,4 @@ export class WatchUtil {
|
|
|
111
115
|
}
|
|
112
116
|
}, options);
|
|
113
117
|
}
|
|
114
|
-
}
|
|
118
|
+
}
|
package/support/patch.js
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
|
-
import 'temporal-polyfill-lite/global';
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
|
|
4
3
|
if (process.env.NODE_ENV !== 'production') {
|
|
5
4
|
process.setSourceMapsEnabled(true); // Ensure source map during compilation/development
|
|
6
5
|
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ''} --enable-source-maps`; // Ensure it passes to children
|
|
7
6
|
Error.stackTraceLimit = 50;
|
|
8
|
-
|
|
9
|
-
const ogEmitWarning = process.emitWarning;
|
|
10
|
-
const exclusions = global.devProcessWarningExclusions = [];
|
|
11
|
-
process.emitWarning = (message, category, ...other) => {
|
|
12
|
-
if (exclusions.length === 0 || !exclusions.some(filter => filter(message, category))) {
|
|
13
|
-
return ogEmitWarning(message, category, ...other);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
7
|
}
|
|
17
8
|
|
|
18
|
-
const isError = Error.isError.bind(Error);
|
|
19
|
-
Object.defineProperty(Error, 'isError', {
|
|
20
|
-
value: (input) => isError(input) || (input instanceof Error)
|
|
21
|
-
});
|
|
22
|
-
|
|
23
9
|
// polyfills
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
if (!globalThis.Temporal) {
|
|
11
|
+
// For anyone that doesn't have it
|
|
12
|
+
void import('temporal-polyfill-lite/global');
|
|
13
|
+
}
|
|
26
14
|
|
|
27
15
|
Map.prototype.getOrInsert ??= function (key, value) {
|
|
28
|
-
|
|
16
|
+
if (!this.has(key)) {
|
|
17
|
+
this.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
return this.get(key);
|
|
29
20
|
};
|
|
30
21
|
|
|
31
22
|
Map.prototype.getOrInsertComputed ??= function (key, compute) {
|
|
32
|
-
|
|
23
|
+
if (!this.has(key)) {
|
|
24
|
+
this.set(key, compute());
|
|
25
|
+
}
|
|
26
|
+
return this.get(key);
|
|
33
27
|
};
|
|
34
28
|
|
|
35
29
|
// Allow for the throwIfNoEntry if on a version of node that is less than 25.7
|
|
30
|
+
const [majorVersion, minorVersion] = process.version.match(/\d+/g).map(text => parseInt(text, 10));
|
|
36
31
|
if (majorVersion < 25 || (majorVersion === 25 && minorVersion < 7)) {
|
|
37
32
|
const og = fs.stat;
|
|
38
33
|
Object.defineProperty(fs, 'stat', {
|
|
39
34
|
value: (...args) => {
|
|
40
35
|
const out = og.call(fs, ...args);
|
|
41
36
|
if (typeof args[1] === 'object' && args[1].throwIfNoEntry === false) {
|
|
42
|
-
return out.catch(() => {
|
|
37
|
+
return out.catch(() => {});
|
|
43
38
|
}
|
|
44
39
|
return out;
|
|
45
40
|
}
|
|
46
41
|
});
|
|
47
|
-
}
|
|
42
|
+
}
|