lifecycleion 0.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/LICENSE +22 -0
- package/README.md +125 -0
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/arrays.cjs +95 -0
- package/dist/lib/arrays.cjs.map +1 -0
- package/dist/lib/arrays.d.cts +15 -0
- package/dist/lib/arrays.d.ts +15 -0
- package/dist/lib/arrays.js +63 -0
- package/dist/lib/arrays.js.map +1 -0
- package/dist/lib/ascii-tables/index.cjs +642 -0
- package/dist/lib/ascii-tables/index.cjs.map +1 -0
- package/dist/lib/ascii-tables/index.d.cts +66 -0
- package/dist/lib/ascii-tables/index.d.ts +66 -0
- package/dist/lib/ascii-tables/index.js +603 -0
- package/dist/lib/ascii-tables/index.js.map +1 -0
- package/dist/lib/clamp.cjs +41 -0
- package/dist/lib/clamp.cjs.map +1 -0
- package/dist/lib/clamp.d.cts +26 -0
- package/dist/lib/clamp.d.ts +26 -0
- package/dist/lib/clamp.js +15 -0
- package/dist/lib/clamp.js.map +1 -0
- package/dist/lib/constants.cjs +73 -0
- package/dist/lib/constants.cjs.map +1 -0
- package/dist/lib/constants.d.cts +17 -0
- package/dist/lib/constants.d.ts +17 -0
- package/dist/lib/constants.js +34 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/curly-brackets.cjs +77 -0
- package/dist/lib/curly-brackets.cjs.map +1 -0
- package/dist/lib/curly-brackets.d.cts +17 -0
- package/dist/lib/curly-brackets.d.ts +17 -0
- package/dist/lib/curly-brackets.js +52 -0
- package/dist/lib/curly-brackets.js.map +1 -0
- package/dist/lib/deep-clone.cjs +87 -0
- package/dist/lib/deep-clone.cjs.map +1 -0
- package/dist/lib/deep-clone.d.cts +19 -0
- package/dist/lib/deep-clone.d.ts +19 -0
- package/dist/lib/deep-clone.js +62 -0
- package/dist/lib/deep-clone.js.map +1 -0
- package/dist/lib/error-to-string.cjs +743 -0
- package/dist/lib/error-to-string.cjs.map +1 -0
- package/dist/lib/error-to-string.d.cts +3 -0
- package/dist/lib/error-to-string.d.ts +3 -0
- package/dist/lib/error-to-string.js +706 -0
- package/dist/lib/error-to-string.js.map +1 -0
- package/dist/lib/event-emitter.cjs +899 -0
- package/dist/lib/event-emitter.cjs.map +1 -0
- package/dist/lib/event-emitter.d.cts +78 -0
- package/dist/lib/event-emitter.d.ts +78 -0
- package/dist/lib/event-emitter.js +861 -0
- package/dist/lib/event-emitter.js.map +1 -0
- package/dist/lib/id-helpers.cjs +205 -0
- package/dist/lib/id-helpers.cjs.map +1 -0
- package/dist/lib/id-helpers.d.cts +198 -0
- package/dist/lib/id-helpers.d.ts +198 -0
- package/dist/lib/id-helpers.js +170 -0
- package/dist/lib/id-helpers.js.map +1 -0
- package/dist/lib/is-boolean.cjs +33 -0
- package/dist/lib/is-boolean.cjs.map +1 -0
- package/dist/lib/is-boolean.d.cts +19 -0
- package/dist/lib/is-boolean.d.ts +19 -0
- package/dist/lib/is-boolean.js +8 -0
- package/dist/lib/is-boolean.js.map +1 -0
- package/dist/lib/is-function.cjs +33 -0
- package/dist/lib/is-function.cjs.map +1 -0
- package/dist/lib/is-function.d.cts +3 -0
- package/dist/lib/is-function.d.ts +3 -0
- package/dist/lib/is-function.js +8 -0
- package/dist/lib/is-function.js.map +1 -0
- package/dist/lib/is-number.cjs +38 -0
- package/dist/lib/is-number.cjs.map +1 -0
- package/dist/lib/is-number.d.cts +38 -0
- package/dist/lib/is-number.d.ts +38 -0
- package/dist/lib/is-number.js +12 -0
- package/dist/lib/is-number.js.map +1 -0
- package/dist/lib/is-plain-object.cjs +33 -0
- package/dist/lib/is-plain-object.cjs.map +1 -0
- package/dist/lib/is-plain-object.d.cts +20 -0
- package/dist/lib/is-plain-object.d.ts +20 -0
- package/dist/lib/is-plain-object.js +8 -0
- package/dist/lib/is-plain-object.js.map +1 -0
- package/dist/lib/is-promise.cjs +34 -0
- package/dist/lib/is-promise.cjs.map +1 -0
- package/dist/lib/is-promise.d.cts +3 -0
- package/dist/lib/is-promise.d.ts +3 -0
- package/dist/lib/is-promise.js +9 -0
- package/dist/lib/is-promise.js.map +1 -0
- package/dist/lib/json-helpers.cjs +49 -0
- package/dist/lib/json-helpers.cjs.map +1 -0
- package/dist/lib/json-helpers.d.cts +10 -0
- package/dist/lib/json-helpers.d.ts +10 -0
- package/dist/lib/json-helpers.js +22 -0
- package/dist/lib/json-helpers.js.map +1 -0
- package/dist/lib/lifecycle-manager/index.cjs +5594 -0
- package/dist/lib/lifecycle-manager/index.cjs.map +1 -0
- package/dist/lib/lifecycle-manager/index.d.cts +2044 -0
- package/dist/lib/lifecycle-manager/index.d.ts +2044 -0
- package/dist/lib/lifecycle-manager/index.js +5543 -0
- package/dist/lib/lifecycle-manager/index.js.map +1 -0
- package/dist/lib/logger/index.cjs +2514 -0
- package/dist/lib/logger/index.cjs.map +1 -0
- package/dist/lib/logger/index.d.cts +630 -0
- package/dist/lib/logger/index.d.ts +630 -0
- package/dist/lib/logger/index.js +2470 -0
- package/dist/lib/logger/index.js.map +1 -0
- package/dist/lib/padding-utils.cjs +77 -0
- package/dist/lib/padding-utils.cjs.map +1 -0
- package/dist/lib/padding-utils.d.cts +44 -0
- package/dist/lib/padding-utils.d.ts +44 -0
- package/dist/lib/padding-utils.js +46 -0
- package/dist/lib/padding-utils.js.map +1 -0
- package/dist/lib/process-signal-manager.cjs +1306 -0
- package/dist/lib/process-signal-manager.cjs.map +1 -0
- package/dist/lib/process-signal-manager.d.cts +305 -0
- package/dist/lib/process-signal-manager.d.ts +305 -0
- package/dist/lib/process-signal-manager.js +1269 -0
- package/dist/lib/process-signal-manager.js.map +1 -0
- package/dist/lib/promise-protected-resolver.cjs +828 -0
- package/dist/lib/promise-protected-resolver.cjs.map +1 -0
- package/dist/lib/promise-protected-resolver.d.cts +17 -0
- package/dist/lib/promise-protected-resolver.d.ts +17 -0
- package/dist/lib/promise-protected-resolver.js +791 -0
- package/dist/lib/promise-protected-resolver.js.map +1 -0
- package/dist/lib/retry-utils/index.cjs +2183 -0
- package/dist/lib/retry-utils/index.cjs.map +1 -0
- package/dist/lib/retry-utils/index.d.cts +321 -0
- package/dist/lib/retry-utils/index.d.ts +321 -0
- package/dist/lib/retry-utils/index.js +2133 -0
- package/dist/lib/retry-utils/index.js.map +1 -0
- package/dist/lib/safe-handle-callback.cjs +818 -0
- package/dist/lib/safe-handle-callback.cjs.map +1 -0
- package/dist/lib/safe-handle-callback.d.cts +43 -0
- package/dist/lib/safe-handle-callback.d.ts +43 -0
- package/dist/lib/safe-handle-callback.js +780 -0
- package/dist/lib/safe-handle-callback.js.map +1 -0
- package/dist/lib/serialize-error/index.cjs +93 -0
- package/dist/lib/serialize-error/index.cjs.map +1 -0
- package/dist/lib/serialize-error/index.d.cts +26 -0
- package/dist/lib/serialize-error/index.d.ts +26 -0
- package/dist/lib/serialize-error/index.js +64 -0
- package/dist/lib/serialize-error/index.js.map +1 -0
- package/dist/lib/single-event-observer.cjs +841 -0
- package/dist/lib/single-event-observer.cjs.map +1 -0
- package/dist/lib/single-event-observer.d.cts +54 -0
- package/dist/lib/single-event-observer.d.ts +54 -0
- package/dist/lib/single-event-observer.js +803 -0
- package/dist/lib/single-event-observer.js.map +1 -0
- package/dist/lib/sleep.cjs +37 -0
- package/dist/lib/sleep.cjs.map +1 -0
- package/dist/lib/sleep.d.cts +11 -0
- package/dist/lib/sleep.d.ts +11 -0
- package/dist/lib/sleep.js +12 -0
- package/dist/lib/sleep.js.map +1 -0
- package/dist/lib/strings.cjs +186 -0
- package/dist/lib/strings.cjs.map +1 -0
- package/dist/lib/strings.d.cts +107 -0
- package/dist/lib/strings.d.ts +107 -0
- package/dist/lib/strings.js +149 -0
- package/dist/lib/strings.js.map +1 -0
- package/dist/lib/tmp-dir.cjs +254 -0
- package/dist/lib/tmp-dir.cjs.map +1 -0
- package/dist/lib/tmp-dir.d.cts +63 -0
- package/dist/lib/tmp-dir.d.ts +63 -0
- package/dist/lib/tmp-dir.js +211 -0
- package/dist/lib/tmp-dir.js.map +1 -0
- package/dist/lib/unix-time-helpers.cjs +53 -0
- package/dist/lib/unix-time-helpers.cjs.map +1 -0
- package/dist/lib/unix-time-helpers.d.cts +56 -0
- package/dist/lib/unix-time-helpers.d.ts +56 -0
- package/dist/lib/unix-time-helpers.js +24 -0
- package/dist/lib/unix-time-helpers.js.map +1 -0
- package/package.json +220 -0
|
@@ -0,0 +1,2133 @@
|
|
|
1
|
+
// src/lib/strings.ts
|
|
2
|
+
function isString(value) {
|
|
3
|
+
return typeof value === "string";
|
|
4
|
+
}
|
|
5
|
+
function splitGraphemes(text) {
|
|
6
|
+
const graphemes = [];
|
|
7
|
+
let grapheme = "";
|
|
8
|
+
let zwjSequence = "";
|
|
9
|
+
for (let i = 0; i < text.length; i++) {
|
|
10
|
+
const char = text[i];
|
|
11
|
+
const nextChar = text[i + 1] || "";
|
|
12
|
+
const code = char.charCodeAt(0);
|
|
13
|
+
if (code >= 768 && code <= 879 || // Combining Diacritical Marks
|
|
14
|
+
code >= 6832 && code <= 6911 || // Combining Diacritical Marks Extended
|
|
15
|
+
code >= 7616 && code <= 7679 || // Combining Diacritical Marks Supplement
|
|
16
|
+
code >= 65056 && code <= 65071 || // Combining Half Marks
|
|
17
|
+
code >= 3633 && code <= 3642 || // Thai combining marks
|
|
18
|
+
code >= 3655 && code <= 3662) {
|
|
19
|
+
grapheme += char;
|
|
20
|
+
} else if (char === "\u200D") {
|
|
21
|
+
zwjSequence += grapheme + char;
|
|
22
|
+
grapheme = "";
|
|
23
|
+
} else {
|
|
24
|
+
if (grapheme) {
|
|
25
|
+
if (zwjSequence) {
|
|
26
|
+
graphemes.push(zwjSequence + grapheme);
|
|
27
|
+
zwjSequence = "";
|
|
28
|
+
} else {
|
|
29
|
+
graphemes.push(grapheme);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
grapheme = char;
|
|
33
|
+
if (char >= "\uD800" && char <= "\uDBFF" && nextChar >= "\uDC00" && nextChar <= "\uDFFF") {
|
|
34
|
+
grapheme += nextChar;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (grapheme) {
|
|
40
|
+
if (zwjSequence) {
|
|
41
|
+
graphemes.push(zwjSequence + grapheme);
|
|
42
|
+
} else {
|
|
43
|
+
graphemes.push(grapheme);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return graphemes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/lib/retry-utils/lib/retry-utils-errors.ts
|
|
50
|
+
var RetryUtilsErrPolicyConfigInvalidStrategy = class extends Error {
|
|
51
|
+
constructor(strategyProvided, validStrategies) {
|
|
52
|
+
super("Invalid strategy provided.");
|
|
53
|
+
this.strategyProvided = strategyProvided;
|
|
54
|
+
this.validStrategies = validStrategies;
|
|
55
|
+
this.name = "RetryUtilsErrPolicyConfigInvalidStrategy";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var RetryUtilsErrRunnerAlreadyCompleted = class extends Error {
|
|
59
|
+
constructor(invokedMethod) {
|
|
60
|
+
super(
|
|
61
|
+
"The runner has already completed running the operation. Use the .reset() method, and .run() to run the operation again."
|
|
62
|
+
);
|
|
63
|
+
this.invokedMethod = invokedMethod;
|
|
64
|
+
this.name = "RetryUtilsErrRunnerAlreadyCompleted";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var RetryUtilsErrRunnerAlreadyRunning = class extends Error {
|
|
68
|
+
constructor(invokedMethod) {
|
|
69
|
+
super("The operation is already running and cannot be started again.");
|
|
70
|
+
this.invokedMethod = invokedMethod;
|
|
71
|
+
this.name = "RetryUtilsErrRunnerAlreadyRunning";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var RetryUtilsErrRunnerForceTryRetryInProgress = class extends Error {
|
|
75
|
+
constructor(invokedMethod) {
|
|
76
|
+
super("Force try retry is already in progress.");
|
|
77
|
+
this.invokedMethod = invokedMethod;
|
|
78
|
+
this.name = "RetryUtilsErrRunnerForceTryRetryInProgress";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var RetryUtilsErrRunnerNotPaused = class extends Error {
|
|
82
|
+
constructor(invokedMethod) {
|
|
83
|
+
super(
|
|
84
|
+
"The runner is not in a paused state. resume() can only be called when the runner state is stopped."
|
|
85
|
+
);
|
|
86
|
+
this.invokedMethod = invokedMethod;
|
|
87
|
+
this.name = "RetryUtilsErrRunnerNotPaused";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var RetryUtilsErrRunnerCancelPending = class extends Error {
|
|
91
|
+
constructor(invokedMethod) {
|
|
92
|
+
super(
|
|
93
|
+
"A cancel operation is pending. The operation cannot be started again."
|
|
94
|
+
);
|
|
95
|
+
this.invokedMethod = invokedMethod;
|
|
96
|
+
this.name = "RetryUtilsErrRunnerCancelPending";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var RetryUtilsErrRunnerRetryCanceled = class extends Error {
|
|
100
|
+
constructor(invokedMethod) {
|
|
101
|
+
super(
|
|
102
|
+
"The operation was already canceled. Use either .resume(), .forceTry() or .reset() and .run() to run the operation again."
|
|
103
|
+
);
|
|
104
|
+
this.invokedMethod = invokedMethod;
|
|
105
|
+
this.name = "RetryUtilsErrRunnerRetryCanceled";
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var RetryUtilsErrRunnerLastRetryFatallyFailed = class extends Error {
|
|
109
|
+
constructor(invokedMethod) {
|
|
110
|
+
super(
|
|
111
|
+
"The last retry attempt failed fatally. The operation cannot be retried. Use either .reset() then .run() or .forceTry() to run the operation again."
|
|
112
|
+
);
|
|
113
|
+
this.invokedMethod = invokedMethod;
|
|
114
|
+
this.name = "RetryUtilsErrRunnerLastRetryFatallyFailed";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var RetryUtilsErrRunnerAttemptsExhausted = class extends Error {
|
|
118
|
+
constructor(invokedMethod) {
|
|
119
|
+
super(
|
|
120
|
+
"All attempts were exhausted. The operation cannot be retried. Use either .reset() then .run() or .forceTry() to run the operation again."
|
|
121
|
+
);
|
|
122
|
+
this.invokedMethod = invokedMethod;
|
|
123
|
+
this.name = "RetryUtilsErrRunnerAttemptsExhausted";
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var RetryUtilsErrRunnerLockAcquisitionError = class extends Error {
|
|
127
|
+
constructor(invokedMethod) {
|
|
128
|
+
super(
|
|
129
|
+
"Failed to acquire operation lock. Cannot attempt to run the operation."
|
|
130
|
+
);
|
|
131
|
+
this.invokedMethod = invokedMethod;
|
|
132
|
+
this.name = "RetryUtilsErrRunnerLockAcquisitionError";
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var RetryUtilsErrRunnerUnexpectedError = class extends Error {
|
|
136
|
+
constructor(invokedMethod, originalError) {
|
|
137
|
+
super("An unexpected error occurred.");
|
|
138
|
+
this.invokedMethod = invokedMethod;
|
|
139
|
+
this.originalError = originalError;
|
|
140
|
+
this.name = "RetryUtilsErrRunnerUnexpectedError";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var RetryUtilsErrRunnerUnknownState = class extends Error {
|
|
144
|
+
constructor(invokedMethod, runnerState) {
|
|
145
|
+
super("An unknown runner state was encountered.");
|
|
146
|
+
this.invokedMethod = invokedMethod;
|
|
147
|
+
this.runnerState = runnerState;
|
|
148
|
+
this.name = "RetryUtilsErrRunnerUnknownState";
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var RetryUtilsErrRunnerNotRunning = class extends Error {
|
|
152
|
+
constructor(invokedMethod) {
|
|
153
|
+
super("The operation is not currently running.");
|
|
154
|
+
this.invokedMethod = invokedMethod;
|
|
155
|
+
this.name = "RetryUtilsErrRunnerNotRunning";
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// src/lib/clamp.ts
|
|
160
|
+
function clamp(value, min, max) {
|
|
161
|
+
return Math.max(min, Math.min(value, max));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/lib/retry-utils/lib/utils.ts
|
|
165
|
+
function calculateExponentialDelay({
|
|
166
|
+
retryCount,
|
|
167
|
+
minTimeoutMS,
|
|
168
|
+
maxTimeoutMS,
|
|
169
|
+
factor,
|
|
170
|
+
dispersion,
|
|
171
|
+
randomFn
|
|
172
|
+
}) {
|
|
173
|
+
let delay = minTimeoutMS * Math.pow(factor, retryCount);
|
|
174
|
+
if (dispersion > 0) {
|
|
175
|
+
const dispersionAmount = delay * dispersion;
|
|
176
|
+
delay += randomFn() * (dispersionAmount * 2) - dispersionAmount;
|
|
177
|
+
}
|
|
178
|
+
return clamp(delay, minTimeoutMS, maxTimeoutMS);
|
|
179
|
+
}
|
|
180
|
+
function extractErrorMessage(error) {
|
|
181
|
+
if (error instanceof Error) {
|
|
182
|
+
return error.message;
|
|
183
|
+
}
|
|
184
|
+
if (error !== null && error !== void 0 && typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
185
|
+
return error.message;
|
|
186
|
+
}
|
|
187
|
+
if (error !== null && error !== void 0 && typeof error === "object" && "error" in error) {
|
|
188
|
+
const nested = error.error;
|
|
189
|
+
if (nested instanceof Error) {
|
|
190
|
+
return nested.message;
|
|
191
|
+
} else if (nested !== null && nested !== void 0 && typeof nested === "object" && "message" in nested && typeof nested.message === "string") {
|
|
192
|
+
return nested.message;
|
|
193
|
+
} else {
|
|
194
|
+
return String(nested);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return String(error);
|
|
198
|
+
}
|
|
199
|
+
function getMostCommonError(errors) {
|
|
200
|
+
if (errors.length === 0) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const refCounts = /* @__PURE__ */ new Map();
|
|
204
|
+
for (const error of errors) {
|
|
205
|
+
refCounts.set(error, (refCounts.get(error) ?? 0) + 1);
|
|
206
|
+
}
|
|
207
|
+
const messageCounts = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const error of errors) {
|
|
209
|
+
const message = extractErrorMessage(error);
|
|
210
|
+
const existing = messageCounts.get(message);
|
|
211
|
+
if (existing) {
|
|
212
|
+
existing.count += 1;
|
|
213
|
+
} else {
|
|
214
|
+
messageCounts.set(message, { count: 1, error });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
let mostCommon = null;
|
|
218
|
+
let maxCount = 0;
|
|
219
|
+
for (const [error, count] of refCounts) {
|
|
220
|
+
if (count > maxCount) {
|
|
221
|
+
maxCount = count;
|
|
222
|
+
mostCommon = error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (const { count, error } of messageCounts.values()) {
|
|
226
|
+
if (count > maxCount) {
|
|
227
|
+
maxCount = count;
|
|
228
|
+
mostCommon = error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return mostCommon;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/lib/retry-utils/lib/retry-policy.ts
|
|
235
|
+
var RetryPolicy = class {
|
|
236
|
+
policy;
|
|
237
|
+
currentState = this.getEmptyCurrentState();
|
|
238
|
+
/**
|
|
239
|
+
* Gets the validated policy information for this retry policy instance.
|
|
240
|
+
* @returns {RetryPolicyValidated} The current retry policy settings.
|
|
241
|
+
*/
|
|
242
|
+
get policyInfo() {
|
|
243
|
+
return this.policy;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Gets the total number of attempts made, including the initial attempt and any retries.
|
|
247
|
+
* @returns {number} The total number of attempts.
|
|
248
|
+
*/
|
|
249
|
+
get attempts() {
|
|
250
|
+
if (this.currentState.wasSuccessful) {
|
|
251
|
+
return this.currentState.errors.length + 1;
|
|
252
|
+
} else {
|
|
253
|
+
return clamp(
|
|
254
|
+
this.currentState.errors.length,
|
|
255
|
+
this.wasInitialAttemptTaken ? 1 : 0,
|
|
256
|
+
Infinity
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Checks if the initial attempt has been taken.
|
|
262
|
+
*/
|
|
263
|
+
get wasInitialAttemptTaken() {
|
|
264
|
+
return this.currentState.wasInitialAttemptTaken;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Checks if the last operation attempt was successful. (used to calculate the number of attempts made)
|
|
268
|
+
*/
|
|
269
|
+
get wasSuccessful() {
|
|
270
|
+
return this.currentState.wasSuccessful;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Gets the maximum number of retry attempts allowed by the current policy.
|
|
274
|
+
* @returns {number} The maximum number of retry attempts.
|
|
275
|
+
*/
|
|
276
|
+
get maxRetryAttempts() {
|
|
277
|
+
return this.policy.maxRetryAttempts;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Gets the number of retry attempts made, excluding the initial attempt.
|
|
281
|
+
* @returns {number} The number of retries.
|
|
282
|
+
*/
|
|
283
|
+
get retryCount() {
|
|
284
|
+
if (!this.wasInitialAttemptTaken) {
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
return Math.max(this.attempts - 1, 0);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Checks if the retry attempts have been exhausted.
|
|
291
|
+
* @returns {boolean} True if the number of retries has reached the maximum allowed attempts; otherwise, false.
|
|
292
|
+
*/
|
|
293
|
+
get areAttemptsExhausted() {
|
|
294
|
+
return this.retryCount >= this.maxRetryAttempts;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Gets a list of errors recorded from each retry attempt.
|
|
298
|
+
* @returns {unknown[]} An array of errors encountered during retry attempts.
|
|
299
|
+
*/
|
|
300
|
+
get errors() {
|
|
301
|
+
return [...this.currentState.errors];
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Gets the most common error encountered across all retry attempts.
|
|
305
|
+
*
|
|
306
|
+
* This method caches the most common error for performance. If the cache is invalidated due to a new error,
|
|
307
|
+
* it recalculates the most common error.
|
|
308
|
+
*
|
|
309
|
+
* @returns {unknown} The most common error, or null if no errors have been encountered.
|
|
310
|
+
*/
|
|
311
|
+
get mostCommonError() {
|
|
312
|
+
if (this.currentState.mostCommonErrorCached.has) {
|
|
313
|
+
return this.currentState.mostCommonErrorCached.value;
|
|
314
|
+
} else {
|
|
315
|
+
const mostCommon = getMostCommonError(this.currentState.errors);
|
|
316
|
+
this.currentState.mostCommonErrorCached.has = true;
|
|
317
|
+
this.currentState.mostCommonErrorCached.value = mostCommon;
|
|
318
|
+
return mostCommon;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Gets the last error encountered during the retry attempts.
|
|
323
|
+
* @returns {unknown} The last error encountered, or null if no errors have been recorded.
|
|
324
|
+
*/
|
|
325
|
+
get lastError() {
|
|
326
|
+
if (this.currentState.errors.length === 0) {
|
|
327
|
+
return null;
|
|
328
|
+
} else {
|
|
329
|
+
return this.currentState.errors[this.currentState.errors.length - 1];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Constructs a RetryPolicy instance with specified options.
|
|
334
|
+
*
|
|
335
|
+
* @param {RetryPolicyOptions} policy The retry policy options, including strategy, maximum retry attempts,
|
|
336
|
+
* and other parameters specific to the fixed or exponential strategy.
|
|
337
|
+
*
|
|
338
|
+
* Throws an error if an invalid retry strategy is provided.
|
|
339
|
+
*/
|
|
340
|
+
constructor(policy) {
|
|
341
|
+
const DEFAULT_MAX_RETRY_ATTEMPTS = 10;
|
|
342
|
+
const DEFAULT_FACTOR = 1.5;
|
|
343
|
+
const DEFAULT_MIN_TIMEOUT_MS = 1e3;
|
|
344
|
+
const DEFAULT_MAX_TIMEOUT_MS = 3e4;
|
|
345
|
+
const DEFAULT_DISPERSION = 0.1;
|
|
346
|
+
if (policy.strategy === "fixed") {
|
|
347
|
+
this.policy = {
|
|
348
|
+
strategy: "fixed",
|
|
349
|
+
maxRetryAttempts: Math.floor(
|
|
350
|
+
clamp(
|
|
351
|
+
policy.maxRetryAttempts ?? DEFAULT_MAX_RETRY_ATTEMPTS,
|
|
352
|
+
1,
|
|
353
|
+
Infinity
|
|
354
|
+
)
|
|
355
|
+
),
|
|
356
|
+
delayMS: clamp(policy.delayMS ?? DEFAULT_MIN_TIMEOUT_MS, 1, Infinity)
|
|
357
|
+
};
|
|
358
|
+
} else if (policy.strategy === "exponential") {
|
|
359
|
+
const minTimeoutMS = clamp(
|
|
360
|
+
policy.minTimeoutMS ?? DEFAULT_MIN_TIMEOUT_MS,
|
|
361
|
+
1,
|
|
362
|
+
Infinity
|
|
363
|
+
);
|
|
364
|
+
const maxTimeoutMS = clamp(
|
|
365
|
+
policy.maxTimeoutMS ?? DEFAULT_MAX_TIMEOUT_MS,
|
|
366
|
+
1,
|
|
367
|
+
Infinity
|
|
368
|
+
);
|
|
369
|
+
const finalMin = Math.min(minTimeoutMS, maxTimeoutMS);
|
|
370
|
+
const finalMax = Math.max(minTimeoutMS, maxTimeoutMS);
|
|
371
|
+
this.policy = {
|
|
372
|
+
strategy: "exponential",
|
|
373
|
+
maxRetryAttempts: Math.floor(
|
|
374
|
+
clamp(
|
|
375
|
+
policy.maxRetryAttempts ?? DEFAULT_MAX_RETRY_ATTEMPTS,
|
|
376
|
+
1,
|
|
377
|
+
Infinity
|
|
378
|
+
)
|
|
379
|
+
),
|
|
380
|
+
factor: clamp(policy.factor ?? DEFAULT_FACTOR, 1, Infinity),
|
|
381
|
+
minTimeoutMS: finalMin,
|
|
382
|
+
maxTimeoutMS: finalMax,
|
|
383
|
+
dispersion: clamp(policy.dispersion ?? DEFAULT_DISPERSION, 0, 1)
|
|
384
|
+
};
|
|
385
|
+
} else {
|
|
386
|
+
throw new RetryUtilsErrPolicyConfigInvalidStrategy(
|
|
387
|
+
isString(policy["strategy"]) ? policy["strategy"] : "unknown",
|
|
388
|
+
["fixed", "exponential"]
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Resets the retry policy to its initial state.
|
|
394
|
+
*
|
|
395
|
+
* This method clears all recorded errors and marks the initial attempt as not taken,
|
|
396
|
+
* effectively resetting the state of the retry policy for a new operation.
|
|
397
|
+
*/
|
|
398
|
+
reset() {
|
|
399
|
+
this.currentState = this.getEmptyCurrentState();
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Determines if the initial operation attempt should proceed.
|
|
403
|
+
*
|
|
404
|
+
* This method checks if the initial attempt has already been taken and updates the state to reflect that
|
|
405
|
+
* the initial attempt is now being made. This method is used to ensure that the retry logic only kicks in after
|
|
406
|
+
* the first attempt has failed.
|
|
407
|
+
*
|
|
408
|
+
* Note that even upon success, you should call the reset method to clear the state for the next operation.
|
|
409
|
+
*
|
|
410
|
+
* @returns {boolean} True if the initial attempt has not been made yet; otherwise, false.
|
|
411
|
+
*/
|
|
412
|
+
shouldDoFirstTry() {
|
|
413
|
+
if (this.currentState.wasInitialAttemptTaken) {
|
|
414
|
+
return false;
|
|
415
|
+
} else {
|
|
416
|
+
this.currentState.wasInitialAttemptTaken = true;
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Marks the last operation attempt as successful.
|
|
422
|
+
*/
|
|
423
|
+
markAsSuccessful() {
|
|
424
|
+
this.currentState.wasSuccessful = true;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Records an error that occurred during the last operation attempt.
|
|
428
|
+
*
|
|
429
|
+
* Does not check if was successful or not, nor checks if should retry or not.
|
|
430
|
+
*/
|
|
431
|
+
reportError(error) {
|
|
432
|
+
this.currentState.errors.push(error);
|
|
433
|
+
this.currentState.mostCommonErrorCached.has = false;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Determines if a retry should be made based on the current state and the provided error.
|
|
437
|
+
*
|
|
438
|
+
* When called, this method stores the provided error, checks if further retries are allowed based on the
|
|
439
|
+
* maximum retry attempts, and calculates the delay for the next retry if applicable.
|
|
440
|
+
*
|
|
441
|
+
* It also invalidates the cached most common error since the error state has changed.
|
|
442
|
+
*
|
|
443
|
+
* @param {unknown} error The error that resulted from the last operation attempt. Can be omitted when `isQueryOnly` is `true`.
|
|
444
|
+
* @param {boolean} isQueryOnly If true, the method only queries if a retry should be made without storing the error.
|
|
445
|
+
* @returns {RetryQueryResult} An object indicating whether a retry should be attempted and the delay before the next attempt.
|
|
446
|
+
*/
|
|
447
|
+
shouldRetry(error, isQueryOnly = false) {
|
|
448
|
+
if (this.currentState.wasSuccessful) {
|
|
449
|
+
if (!isQueryOnly) {
|
|
450
|
+
this.reportError(error);
|
|
451
|
+
}
|
|
452
|
+
return { shouldRetry: false, delayMS: 0 };
|
|
453
|
+
}
|
|
454
|
+
if (!isQueryOnly) {
|
|
455
|
+
this.reportError(error);
|
|
456
|
+
}
|
|
457
|
+
if (this.areAttemptsExhausted) {
|
|
458
|
+
return { shouldRetry: false, delayMS: 0 };
|
|
459
|
+
} else {
|
|
460
|
+
const delayMS = this.calculateNextDelay();
|
|
461
|
+
return { shouldRetry: true, delayMS };
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Returning a fresh copy of the current state
|
|
466
|
+
* to be immutable and not changed by the caller
|
|
467
|
+
*/
|
|
468
|
+
getEmptyCurrentState() {
|
|
469
|
+
return {
|
|
470
|
+
wasInitialAttemptTaken: false,
|
|
471
|
+
wasSuccessful: false,
|
|
472
|
+
errors: [],
|
|
473
|
+
mostCommonErrorCached: {
|
|
474
|
+
has: false,
|
|
475
|
+
value: null
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Calculates the delay before the next retry attempt based on the current retry policy.
|
|
481
|
+
*
|
|
482
|
+
* For a fixed strategy, it returns the specified delay. For an exponential strategy, it calculates the delay
|
|
483
|
+
* based on the exponential backoff formula, considering the number of retry attempts, the base delay,
|
|
484
|
+
* the exponential factor, and any specified dispersion to introduce randomness.
|
|
485
|
+
*
|
|
486
|
+
* @returns {number} The calculated delay in milliseconds before the next retry attempt.
|
|
487
|
+
*/
|
|
488
|
+
calculateNextDelay() {
|
|
489
|
+
if (this.policy.strategy === "fixed") {
|
|
490
|
+
return this.policy.delayMS;
|
|
491
|
+
} else if (this.policy.strategy === "exponential") {
|
|
492
|
+
return calculateExponentialDelay({
|
|
493
|
+
retryCount: this.retryCount,
|
|
494
|
+
minTimeoutMS: this.policy.minTimeoutMS,
|
|
495
|
+
maxTimeoutMS: this.policy.maxTimeoutMS,
|
|
496
|
+
factor: this.policy.factor,
|
|
497
|
+
dispersion: this.policy.dispersion,
|
|
498
|
+
randomFn: Math.random
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return 1;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// src/lib/constants.ts
|
|
506
|
+
var BLANK_SPACE = " ";
|
|
507
|
+
var EOL = "\n";
|
|
508
|
+
var DOUBLE_EOL = EOL + EOL;
|
|
509
|
+
var INDENT = " ".repeat(4);
|
|
510
|
+
var DOUBLE_INDENT = INDENT + INDENT;
|
|
511
|
+
var ASCII_LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
|
|
512
|
+
var ASCII_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
513
|
+
var ASCII_LETTERS = ASCII_LOWERCASE + ASCII_UPPERCASE;
|
|
514
|
+
var DIGITS = "0123456789";
|
|
515
|
+
var HEX_DIGITS = DIGITS + "abcdefABCDEF";
|
|
516
|
+
var PUNCTUATION = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
517
|
+
var WHITESPACE = " \n\r\v\f";
|
|
518
|
+
var PRINTABLE = DIGITS + ASCII_LETTERS + PUNCTUATION + WHITESPACE;
|
|
519
|
+
|
|
520
|
+
// src/lib/padding-utils.ts
|
|
521
|
+
function padLeft(str, length, padStr = BLANK_SPACE) {
|
|
522
|
+
return str.padStart(length, padStr);
|
|
523
|
+
}
|
|
524
|
+
function padRight(str, length, padStr = BLANK_SPACE) {
|
|
525
|
+
return str.padEnd(length, padStr);
|
|
526
|
+
}
|
|
527
|
+
function padCenter(str, length, prefer = "left", padStr = BLANK_SPACE) {
|
|
528
|
+
const midStrLength = length - str.length;
|
|
529
|
+
if (midStrLength > 0) {
|
|
530
|
+
const padLeftAmount = prefer === "left" ? Math.ceil(midStrLength / 2) : Math.floor(midStrLength / 2);
|
|
531
|
+
const padRightAmount = prefer === "left" ? Math.floor(midStrLength / 2) : Math.ceil(midStrLength / 2);
|
|
532
|
+
return padLeft("", padLeftAmount, padStr) + str + padRight("", padRightAmount, padStr);
|
|
533
|
+
} else {
|
|
534
|
+
return str;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function padCenterPreferRight(str, length, padStr = BLANK_SPACE) {
|
|
538
|
+
return padCenter(str, length, "right", padStr);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/lib/ascii-tables/ascii-table-utils.ts
|
|
542
|
+
import stringWidth from "string-width";
|
|
543
|
+
var ASCIITableUtils = class _ASCIITableUtils {
|
|
544
|
+
static centerText(text, width) {
|
|
545
|
+
return padCenterPreferRight(text, width, " ");
|
|
546
|
+
}
|
|
547
|
+
static createSeparator(columnWidths, character = "=") {
|
|
548
|
+
const totalWidth = columnWidths.reduce((sum, width) => sum + width + 3, 0) - 1;
|
|
549
|
+
return `+${padRight("", totalWidth, character)}+`;
|
|
550
|
+
}
|
|
551
|
+
static wrapText(text, maxLength) {
|
|
552
|
+
const words = text.split(" ");
|
|
553
|
+
const lines = [];
|
|
554
|
+
let currentLine = "";
|
|
555
|
+
for (const word of words) {
|
|
556
|
+
if (stringWidth(currentLine) + stringWidth(word) + 1 <= maxLength) {
|
|
557
|
+
currentLine += (currentLine ? " " : "") + word;
|
|
558
|
+
} else {
|
|
559
|
+
if (currentLine) {
|
|
560
|
+
lines.push(currentLine);
|
|
561
|
+
}
|
|
562
|
+
if (stringWidth(word) <= maxLength) {
|
|
563
|
+
currentLine = word;
|
|
564
|
+
} else {
|
|
565
|
+
const subWords = _ASCIITableUtils.splitWord(word, maxLength);
|
|
566
|
+
lines.push(...subWords.slice(0, -1));
|
|
567
|
+
currentLine = subWords[subWords.length - 1];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (currentLine) {
|
|
572
|
+
lines.push(currentLine);
|
|
573
|
+
}
|
|
574
|
+
return lines;
|
|
575
|
+
}
|
|
576
|
+
static splitWord(word, maxLength) {
|
|
577
|
+
const graphemes = splitGraphemes(word);
|
|
578
|
+
const subWords = [];
|
|
579
|
+
let currentSubWord = "";
|
|
580
|
+
for (const grapheme of graphemes) {
|
|
581
|
+
if (stringWidth(currentSubWord + grapheme) <= maxLength) {
|
|
582
|
+
currentSubWord += grapheme;
|
|
583
|
+
} else {
|
|
584
|
+
subWords.push(currentSubWord);
|
|
585
|
+
currentSubWord = grapheme;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (currentSubWord) {
|
|
589
|
+
subWords.push(currentSubWord);
|
|
590
|
+
}
|
|
591
|
+
return subWords;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/lib/ascii-tables/multi-column-ascii-table.ts
|
|
596
|
+
import stringWidth2 from "string-width";
|
|
597
|
+
var MultiColumnASCIITable = class {
|
|
598
|
+
headers;
|
|
599
|
+
rows;
|
|
600
|
+
tableWidth;
|
|
601
|
+
emptyMessage;
|
|
602
|
+
widthMode;
|
|
603
|
+
constructor(headers, options = {}) {
|
|
604
|
+
this.headers = headers;
|
|
605
|
+
this.rows = [];
|
|
606
|
+
this.tableWidth = options.tableWidth || 80;
|
|
607
|
+
this.emptyMessage = options.emptyMessage || "";
|
|
608
|
+
this.widthMode = options.widthMode || "flex";
|
|
609
|
+
const minTableWidth = this.getMinimumWidth();
|
|
610
|
+
if (this.tableWidth < minTableWidth) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
`Table width must be at least ${minTableWidth} to accommodate the headers.`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
getMinimumWidth() {
|
|
617
|
+
return this.headers.length * 4 + 1;
|
|
618
|
+
}
|
|
619
|
+
addRow(row) {
|
|
620
|
+
if (row.length !== this.headers.length) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
`Number of values in the row (${row.length}) must match the number of headers (${this.headers.length}).`
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
this.rows.push(row);
|
|
626
|
+
}
|
|
627
|
+
toString(options = {}) {
|
|
628
|
+
const tableWidth = options.tableWidth || this.tableWidth;
|
|
629
|
+
const emptyMessage = options.emptyMessage || this.emptyMessage;
|
|
630
|
+
if (this.rows.length === 0) {
|
|
631
|
+
const emptyTableWidth = Math.min(tableWidth, 40);
|
|
632
|
+
const separator = "+" + "-".repeat(emptyTableWidth - 2) + "+";
|
|
633
|
+
const emptyMessageLines = ASCIITableUtils.wrapText(
|
|
634
|
+
emptyMessage,
|
|
635
|
+
emptyTableWidth - 4
|
|
636
|
+
);
|
|
637
|
+
const emptyRows = emptyMessageLines.map((line) => {
|
|
638
|
+
const paddingLeft = " ".repeat(
|
|
639
|
+
Math.floor((emptyTableWidth - stringWidth2(line) - 4) / 2)
|
|
640
|
+
);
|
|
641
|
+
const paddingRight = " ".repeat(
|
|
642
|
+
Math.ceil((emptyTableWidth - stringWidth2(line) - 4) / 2)
|
|
643
|
+
);
|
|
644
|
+
return `| ${paddingLeft}${line}${paddingRight} |`;
|
|
645
|
+
});
|
|
646
|
+
if (emptyRows.length === 0) {
|
|
647
|
+
emptyRows.push(`| ${" ".repeat(emptyTableWidth - 4)} |`);
|
|
648
|
+
}
|
|
649
|
+
return [separator, ...emptyRows, separator].join("\n");
|
|
650
|
+
}
|
|
651
|
+
const columnWidths = this.calculateColumnWidths(options);
|
|
652
|
+
const headerSeparator = ASCIITableUtils.createSeparator(columnWidths);
|
|
653
|
+
const rowSeparator = ASCIITableUtils.createSeparator(columnWidths, "-");
|
|
654
|
+
let tableString = headerSeparator + "\n";
|
|
655
|
+
const header = this.renderRow(this.headers, columnWidths);
|
|
656
|
+
tableString += header + "\n" + rowSeparator + "\n";
|
|
657
|
+
const rows = this.rows.map((row) => {
|
|
658
|
+
return this.renderRow(row, columnWidths);
|
|
659
|
+
});
|
|
660
|
+
tableString += rows.join("\n" + rowSeparator + "\n");
|
|
661
|
+
tableString += "\n" + headerSeparator;
|
|
662
|
+
return tableString;
|
|
663
|
+
}
|
|
664
|
+
calculateColumnWidths(options = {}) {
|
|
665
|
+
const tableWidth = options.tableWidth || this.tableWidth;
|
|
666
|
+
const widthMode = options.widthMode || this.widthMode;
|
|
667
|
+
const numColumns = this.headers.length;
|
|
668
|
+
if (widthMode === "fixed") {
|
|
669
|
+
const availableWidth2 = tableWidth - (numColumns + 1) * 3 + 1;
|
|
670
|
+
const columnWidth = Math.floor(availableWidth2 / numColumns);
|
|
671
|
+
const extraWidth = availableWidth2 % numColumns;
|
|
672
|
+
const columnWidths = new Array(numColumns).fill(columnWidth);
|
|
673
|
+
for (let i = 0; i < extraWidth; i++) {
|
|
674
|
+
columnWidths[i] += 1;
|
|
675
|
+
}
|
|
676
|
+
return columnWidths;
|
|
677
|
+
}
|
|
678
|
+
const availableWidth = tableWidth - (numColumns + 1) * 3 + 1;
|
|
679
|
+
const maxColumnWidth = Math.floor(availableWidth / numColumns);
|
|
680
|
+
const totalContentWidth = this.headers.reduce(
|
|
681
|
+
(sum, header) => sum + stringWidth2(header),
|
|
682
|
+
0
|
|
683
|
+
);
|
|
684
|
+
if (availableWidth >= totalContentWidth) {
|
|
685
|
+
const remainingWidth = availableWidth - totalContentWidth;
|
|
686
|
+
const extraCharWidth = Math.floor(remainingWidth / numColumns);
|
|
687
|
+
const extraCharRemainder = remainingWidth % numColumns;
|
|
688
|
+
const columnWidths = this.headers.map((header, index) => {
|
|
689
|
+
const extraWidth = index < extraCharRemainder ? 1 : 0;
|
|
690
|
+
return stringWidth2(header) + extraCharWidth + extraWidth;
|
|
691
|
+
});
|
|
692
|
+
return columnWidths;
|
|
693
|
+
} else {
|
|
694
|
+
const columnWidths = this.headers.map(() => maxColumnWidth);
|
|
695
|
+
return columnWidths;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
renderRow(row, columnWidths) {
|
|
699
|
+
const wrappedCells = row.map((value, index) => {
|
|
700
|
+
const wrappedLines = ASCIITableUtils.wrapText(value, columnWidths[index]);
|
|
701
|
+
return wrappedLines.map((line) => line.padEnd(columnWidths[index])).join("\n");
|
|
702
|
+
});
|
|
703
|
+
const maxLines = Math.max(
|
|
704
|
+
...wrappedCells.map((cell) => cell.split("\n").length)
|
|
705
|
+
);
|
|
706
|
+
const paddedRows = [];
|
|
707
|
+
for (let i = 0; i < maxLines; i++) {
|
|
708
|
+
const rowLine = wrappedCells.map((cell, index) => {
|
|
709
|
+
const cellLines = cell.split("\n");
|
|
710
|
+
const cellLine = cellLines[i] || "";
|
|
711
|
+
const padding = " ".repeat(columnWidths[index] - stringWidth2(cellLine));
|
|
712
|
+
return " " + cellLine + padding + " ";
|
|
713
|
+
});
|
|
714
|
+
paddedRows.push("|" + rowLine.join("|") + "|");
|
|
715
|
+
}
|
|
716
|
+
return paddedRows.join("\n");
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/lib/ascii-tables/key-value-ascii-table.ts
|
|
721
|
+
import stringWidth3 from "string-width";
|
|
722
|
+
var KeyValueASCIITable = class _KeyValueASCIITable {
|
|
723
|
+
tableWidth;
|
|
724
|
+
emptyMessage;
|
|
725
|
+
autoAdjustWidthWhenPossible = true;
|
|
726
|
+
rows = [];
|
|
727
|
+
constructor(options = {}) {
|
|
728
|
+
const minTableWidth = this.getMinimumWidth();
|
|
729
|
+
if (options.tableWidth && options.tableWidth < minTableWidth) {
|
|
730
|
+
throw new Error(
|
|
731
|
+
`Table width must be at least ${minTableWidth} to accommodate the table structure.`
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
this.tableWidth = options.tableWidth || 80;
|
|
735
|
+
this.autoAdjustWidthWhenPossible = options.autoAdjustWidthWhenPossible ?? true;
|
|
736
|
+
this.emptyMessage = options.emptyMessage || "";
|
|
737
|
+
}
|
|
738
|
+
getMinimumWidth() {
|
|
739
|
+
return 9;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Adds key and value to the table but placing the value on its own row.
|
|
743
|
+
*
|
|
744
|
+
* @param key
|
|
745
|
+
* @param value
|
|
746
|
+
*/
|
|
747
|
+
addValueOnSeparateRow(key, value) {
|
|
748
|
+
const row = { kind: "own", key, value };
|
|
749
|
+
this.rows.push(row);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Adds key and value to the table.
|
|
753
|
+
*
|
|
754
|
+
* If provided value is an instance of ASCIITable or MultiColumnASCIITable, it will be rendered as a nested table on its own row for readability.
|
|
755
|
+
*
|
|
756
|
+
* @param key
|
|
757
|
+
* @param value
|
|
758
|
+
*/
|
|
759
|
+
addRow(key, value) {
|
|
760
|
+
const row = { kind: "regular", key, value };
|
|
761
|
+
this.rows.push(row);
|
|
762
|
+
}
|
|
763
|
+
toString(options = {}) {
|
|
764
|
+
const tableWidth = options.tableWidth || this.tableWidth;
|
|
765
|
+
const canAutoAdjustWidthWhenPossible = options.autoAdjustWidthWhenPossible ?? this.autoAdjustWidthWhenPossible;
|
|
766
|
+
const emptyMessage = options.emptyMessage || this.emptyMessage;
|
|
767
|
+
if (this.rows.length === 0) {
|
|
768
|
+
const emptyTableWidth = Math.min(tableWidth, 40);
|
|
769
|
+
const separator = "+" + "-".repeat(emptyTableWidth - 2) + "+";
|
|
770
|
+
const emptyMessageLines = ASCIITableUtils.wrapText(
|
|
771
|
+
emptyMessage,
|
|
772
|
+
emptyTableWidth - 4
|
|
773
|
+
);
|
|
774
|
+
const emptyRows = emptyMessageLines.map((line) => {
|
|
775
|
+
const paddingLeft = padRight(
|
|
776
|
+
"",
|
|
777
|
+
Math.floor((emptyTableWidth - stringWidth3(line) - 4) / 2),
|
|
778
|
+
" "
|
|
779
|
+
);
|
|
780
|
+
const paddingRight = padRight(
|
|
781
|
+
"",
|
|
782
|
+
Math.ceil((emptyTableWidth - stringWidth3(line) - 4) / 2),
|
|
783
|
+
" "
|
|
784
|
+
);
|
|
785
|
+
return `| ${paddingLeft}${line}${paddingRight} |`;
|
|
786
|
+
});
|
|
787
|
+
if (emptyRows.length === 0) {
|
|
788
|
+
emptyRows.push(`| ${" ".repeat(emptyTableWidth - 4)} |`);
|
|
789
|
+
}
|
|
790
|
+
return [separator, ...emptyRows, separator].join("\n");
|
|
791
|
+
}
|
|
792
|
+
const columnWidths = this.calculateColumnWidths(options);
|
|
793
|
+
const headerSeparator = ASCIITableUtils.createSeparator(columnWidths);
|
|
794
|
+
const rowSeparator = ASCIITableUtils.createSeparator(columnWidths, "-");
|
|
795
|
+
let tableString = headerSeparator + "\n";
|
|
796
|
+
for (const [rowIndex, row] of this.rows.entries()) {
|
|
797
|
+
const { kind, key, value } = row;
|
|
798
|
+
if (kind === "own" || value instanceof _KeyValueASCIITable || value instanceof MultiColumnASCIITable || Array.isArray(value)) {
|
|
799
|
+
const keyString = ASCIITableUtils.centerText(
|
|
800
|
+
key,
|
|
801
|
+
columnWidths[0] + columnWidths[1] + 3
|
|
802
|
+
);
|
|
803
|
+
tableString += `| ${keyString} |
|
|
804
|
+
`;
|
|
805
|
+
tableString += rowSeparator + "\n";
|
|
806
|
+
let valueString = "";
|
|
807
|
+
if (value instanceof _KeyValueASCIITable) {
|
|
808
|
+
valueString = this.formatValue(
|
|
809
|
+
value,
|
|
810
|
+
tableWidth - 4,
|
|
811
|
+
canAutoAdjustWidthWhenPossible,
|
|
812
|
+
""
|
|
813
|
+
);
|
|
814
|
+
} else if (value instanceof MultiColumnASCIITable) {
|
|
815
|
+
valueString = this.formatValue(
|
|
816
|
+
value,
|
|
817
|
+
tableWidth - 4,
|
|
818
|
+
canAutoAdjustWidthWhenPossible,
|
|
819
|
+
""
|
|
820
|
+
);
|
|
821
|
+
} else if (Array.isArray(value)) {
|
|
822
|
+
valueString = this.formatValue(
|
|
823
|
+
value,
|
|
824
|
+
tableWidth - 4,
|
|
825
|
+
canAutoAdjustWidthWhenPossible,
|
|
826
|
+
""
|
|
827
|
+
);
|
|
828
|
+
} else if (row.kind === "own") {
|
|
829
|
+
valueString = this.formatTableRowOnOwnRow(
|
|
830
|
+
value,
|
|
831
|
+
tableWidth - 4,
|
|
832
|
+
tableWidth
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
const valueLines = valueString.split("\n");
|
|
836
|
+
const paddedValueLines = valueLines.map((line) => {
|
|
837
|
+
const padding = padRight("", tableWidth - stringWidth3(line) - 4, " ");
|
|
838
|
+
return `| ${line}${padding} |`;
|
|
839
|
+
});
|
|
840
|
+
tableString += paddedValueLines.join("\n") + "\n";
|
|
841
|
+
tableString += headerSeparator + "\n";
|
|
842
|
+
} else {
|
|
843
|
+
const keyLines = ASCIITableUtils.wrapText(key, columnWidths[0]);
|
|
844
|
+
const valueLines = ASCIITableUtils.wrapText(
|
|
845
|
+
this.formatValue(
|
|
846
|
+
value,
|
|
847
|
+
columnWidths[1],
|
|
848
|
+
canAutoAdjustWidthWhenPossible,
|
|
849
|
+
""
|
|
850
|
+
),
|
|
851
|
+
columnWidths[1]
|
|
852
|
+
);
|
|
853
|
+
const maxLines = Math.max(keyLines.length, valueLines.length);
|
|
854
|
+
for (let i = 0; i < maxLines; i++) {
|
|
855
|
+
const keyLine = keyLines[i] || "";
|
|
856
|
+
const valueLine = valueLines[i] || "";
|
|
857
|
+
const keyPadding = " ".repeat(columnWidths[0] - stringWidth3(keyLine));
|
|
858
|
+
const valuePadding = " ".repeat(
|
|
859
|
+
columnWidths[1] - stringWidth3(valueLine)
|
|
860
|
+
);
|
|
861
|
+
tableString += `| ${keyLine}${keyPadding} | ${valueLine}${valuePadding} |
|
|
862
|
+
`;
|
|
863
|
+
if (i === maxLines - 1) {
|
|
864
|
+
if (rowIndex === this.rows.length - 1) {
|
|
865
|
+
tableString += headerSeparator + "\n";
|
|
866
|
+
} else {
|
|
867
|
+
tableString += rowSeparator + "\n";
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return tableString.trim();
|
|
874
|
+
}
|
|
875
|
+
calculateColumnWidths(options = {}) {
|
|
876
|
+
const tableWidth = options.tableWidth || this.tableWidth;
|
|
877
|
+
const canAutoAdjustWidthWhenPossible = options.autoAdjustWidthWhenPossible ?? this.autoAdjustWidthWhenPossible;
|
|
878
|
+
const columnWidths = [0, 0];
|
|
879
|
+
for (const row of this.rows) {
|
|
880
|
+
const { key, value } = row;
|
|
881
|
+
const keyWidth = stringWidth3(key);
|
|
882
|
+
const maxKeyWidth = Math.floor((tableWidth - 7) / 2);
|
|
883
|
+
if (keyWidth > columnWidths[0]) {
|
|
884
|
+
columnWidths[0] = Math.min(keyWidth, maxKeyWidth);
|
|
885
|
+
columnWidths[1] = Math.max(0, tableWidth - columnWidths[0] - 7);
|
|
886
|
+
}
|
|
887
|
+
if (typeof value === "string") {
|
|
888
|
+
const valueWidth = Math.max(
|
|
889
|
+
...value.split("\n").map((line) => stringWidth3(line))
|
|
890
|
+
);
|
|
891
|
+
if (valueWidth > columnWidths[1]) {
|
|
892
|
+
columnWidths[1] = Math.min(
|
|
893
|
+
valueWidth,
|
|
894
|
+
tableWidth - columnWidths[0] - 7
|
|
895
|
+
);
|
|
896
|
+
columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);
|
|
897
|
+
}
|
|
898
|
+
} else if (row.kind === "own") {
|
|
899
|
+
let valueWidth = 0;
|
|
900
|
+
if (isString(value)) {
|
|
901
|
+
valueWidth = Math.max(
|
|
902
|
+
...value.split("\n").map((line) => stringWidth3(line))
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (valueWidth > columnWidths[1]) {
|
|
906
|
+
columnWidths[1] = Math.min(
|
|
907
|
+
valueWidth,
|
|
908
|
+
tableWidth - columnWidths[0] - 7
|
|
909
|
+
);
|
|
910
|
+
columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);
|
|
911
|
+
}
|
|
912
|
+
} else if (value instanceof _KeyValueASCIITable) {
|
|
913
|
+
let nestedTableColumnWidths = [];
|
|
914
|
+
if (canAutoAdjustWidthWhenPossible) {
|
|
915
|
+
const minWidth = value.getMinimumWidth();
|
|
916
|
+
const availableWidth2 = tableWidth - columnWidths[0] - 7;
|
|
917
|
+
const adjustedWidth = clamp(availableWidth2, minWidth, availableWidth2);
|
|
918
|
+
nestedTableColumnWidths = value.calculateColumnWidths({
|
|
919
|
+
tableWidth: adjustedWidth
|
|
920
|
+
});
|
|
921
|
+
} else {
|
|
922
|
+
nestedTableColumnWidths = value.calculateColumnWidths();
|
|
923
|
+
}
|
|
924
|
+
const nestedTableWidth = nestedTableColumnWidths.reduce((sum, width) => sum + width, 0) + nestedTableColumnWidths.length * 3 - 1;
|
|
925
|
+
const availableWidth = tableWidth - columnWidths[0] - 7;
|
|
926
|
+
if (nestedTableWidth > availableWidth) {
|
|
927
|
+
columnWidths[1] = availableWidth;
|
|
928
|
+
} else {
|
|
929
|
+
columnWidths[1] = Math.max(columnWidths[1], nestedTableWidth);
|
|
930
|
+
}
|
|
931
|
+
} else if (value instanceof MultiColumnASCIITable) {
|
|
932
|
+
let nestedTableColumnWidths = [];
|
|
933
|
+
if (canAutoAdjustWidthWhenPossible) {
|
|
934
|
+
const minWidth = value.getMinimumWidth();
|
|
935
|
+
const availableWidth2 = tableWidth - columnWidths[0] - 7;
|
|
936
|
+
const adjustedWidth = clamp(availableWidth2, minWidth, availableWidth2);
|
|
937
|
+
nestedTableColumnWidths = value.calculateColumnWidths({
|
|
938
|
+
tableWidth: adjustedWidth
|
|
939
|
+
});
|
|
940
|
+
} else {
|
|
941
|
+
nestedTableColumnWidths = value.calculateColumnWidths();
|
|
942
|
+
}
|
|
943
|
+
const nestedTableWidth = nestedTableColumnWidths.reduce((sum, width) => sum + width, 0) + nestedTableColumnWidths.length * 3 - 1;
|
|
944
|
+
const availableWidth = tableWidth - columnWidths[0] - 7;
|
|
945
|
+
if (nestedTableWidth > availableWidth) {
|
|
946
|
+
columnWidths[1] = availableWidth;
|
|
947
|
+
} else {
|
|
948
|
+
columnWidths[1] = Math.max(columnWidths[1], nestedTableWidth);
|
|
949
|
+
}
|
|
950
|
+
} else if (Array.isArray(value)) {
|
|
951
|
+
for (const nestedCell of value) {
|
|
952
|
+
const nestedKeyWidth = stringWidth3(nestedCell.key);
|
|
953
|
+
const maxNestedKeyWidth = Math.floor((tableWidth - 7) / 2);
|
|
954
|
+
if (nestedKeyWidth > columnWidths[0]) {
|
|
955
|
+
columnWidths[0] = Math.min(nestedKeyWidth, maxNestedKeyWidth);
|
|
956
|
+
columnWidths[1] = Math.max(0, tableWidth - columnWidths[0] - 7);
|
|
957
|
+
}
|
|
958
|
+
if (typeof nestedCell.value === "string") {
|
|
959
|
+
const nestedValueWidth = Math.max(
|
|
960
|
+
...nestedCell.value.split("\n").map((line) => stringWidth3(line))
|
|
961
|
+
);
|
|
962
|
+
if (nestedValueWidth > columnWidths[1]) {
|
|
963
|
+
columnWidths[1] = Math.min(
|
|
964
|
+
nestedValueWidth,
|
|
965
|
+
tableWidth - columnWidths[0] - 7
|
|
966
|
+
);
|
|
967
|
+
columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return columnWidths;
|
|
974
|
+
}
|
|
975
|
+
formatValue(value, cellWidth, canAutoAdjustWidthWhenPossible, indent = "") {
|
|
976
|
+
if (typeof value === "string") {
|
|
977
|
+
return value;
|
|
978
|
+
} else if (typeof value === "number") {
|
|
979
|
+
return String(value);
|
|
980
|
+
} else if (typeof value === "boolean") {
|
|
981
|
+
return String(value);
|
|
982
|
+
} else if (value === null) {
|
|
983
|
+
return "null";
|
|
984
|
+
} else if (value === void 0) {
|
|
985
|
+
return "undefined";
|
|
986
|
+
} else if (value instanceof _KeyValueASCIITable) {
|
|
987
|
+
let nestedTableLines;
|
|
988
|
+
if (canAutoAdjustWidthWhenPossible) {
|
|
989
|
+
const minWidth = value.getMinimumWidth();
|
|
990
|
+
const adjustedWidth = clamp(cellWidth, minWidth, cellWidth);
|
|
991
|
+
nestedTableLines = value.toString({ tableWidth: adjustedWidth }).split("\n");
|
|
992
|
+
} else {
|
|
993
|
+
nestedTableLines = value.toString().split("\n");
|
|
994
|
+
}
|
|
995
|
+
const indentedLines = nestedTableLines.map((line) => `${indent}${line}`);
|
|
996
|
+
return indentedLines.join("\n");
|
|
997
|
+
} else if (value instanceof MultiColumnASCIITable) {
|
|
998
|
+
let nestedTableLines;
|
|
999
|
+
if (canAutoAdjustWidthWhenPossible) {
|
|
1000
|
+
const minWidth = value.getMinimumWidth();
|
|
1001
|
+
const adjustedWidth = clamp(cellWidth, minWidth, cellWidth);
|
|
1002
|
+
nestedTableLines = value.toString({ tableWidth: adjustedWidth }).split("\n");
|
|
1003
|
+
} else {
|
|
1004
|
+
nestedTableLines = value.toString().split("\n");
|
|
1005
|
+
}
|
|
1006
|
+
const indentedLines = nestedTableLines.map((line) => `${indent}${line}`);
|
|
1007
|
+
return indentedLines.join("\n");
|
|
1008
|
+
} else if (Array.isArray(value)) {
|
|
1009
|
+
const nestedValueLines = [];
|
|
1010
|
+
for (const { key, value: nestedValue } of value) {
|
|
1011
|
+
const formattedKey = `${indent}${key}:`;
|
|
1012
|
+
const formattedValue = this.formatValue(
|
|
1013
|
+
nestedValue,
|
|
1014
|
+
cellWidth - indent.length - stringWidth3(key) - 2,
|
|
1015
|
+
canAutoAdjustWidthWhenPossible,
|
|
1016
|
+
`${indent}`
|
|
1017
|
+
);
|
|
1018
|
+
const wrappedSpacer = padRight("", 4, " ");
|
|
1019
|
+
const wrappedValue = formattedValue.split("\n").map((line) => `${indent}${wrappedSpacer}${line}`);
|
|
1020
|
+
nestedValueLines.push(formattedKey);
|
|
1021
|
+
nestedValueLines.push(...wrappedValue);
|
|
1022
|
+
nestedValueLines.push("");
|
|
1023
|
+
}
|
|
1024
|
+
return nestedValueLines.slice(0, -1).join("\n");
|
|
1025
|
+
} else {
|
|
1026
|
+
throw new TypeError("Invalid value type provided");
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
formatTableRowOnOwnRow(value, width, maxRowLength) {
|
|
1030
|
+
const lines = value.split("\n");
|
|
1031
|
+
const paddedLines = lines.map((line) => {
|
|
1032
|
+
const wrappedLines = ASCIITableUtils.wrapText(line, maxRowLength - 4);
|
|
1033
|
+
return wrappedLines.map((wrappedLine) => {
|
|
1034
|
+
const padding = padRight(
|
|
1035
|
+
"",
|
|
1036
|
+
width - stringWidth3(wrappedLine) - 2,
|
|
1037
|
+
" "
|
|
1038
|
+
);
|
|
1039
|
+
return `${wrappedLine}${padding}`;
|
|
1040
|
+
}).join("\n");
|
|
1041
|
+
});
|
|
1042
|
+
return paddedLines.join("\n");
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
// src/lib/error-to-string.ts
|
|
1047
|
+
function safeStringify(value) {
|
|
1048
|
+
if (value === null || value === void 0) {
|
|
1049
|
+
return String(value);
|
|
1050
|
+
}
|
|
1051
|
+
switch (typeof value) {
|
|
1052
|
+
case "string":
|
|
1053
|
+
return value;
|
|
1054
|
+
case "number":
|
|
1055
|
+
case "boolean":
|
|
1056
|
+
case "bigint":
|
|
1057
|
+
return String(value);
|
|
1058
|
+
case "object":
|
|
1059
|
+
return JSON.stringify(value);
|
|
1060
|
+
case "function":
|
|
1061
|
+
return "[Function]";
|
|
1062
|
+
case "symbol":
|
|
1063
|
+
return value.toString();
|
|
1064
|
+
default:
|
|
1065
|
+
return String(value);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function errorToString(error, maxRowLength = 80) {
|
|
1069
|
+
const table = errorToASCIITable(error, maxRowLength);
|
|
1070
|
+
return table.toString();
|
|
1071
|
+
}
|
|
1072
|
+
function errorToASCIITable(error, maxRowLength) {
|
|
1073
|
+
const table = new KeyValueASCIITable({
|
|
1074
|
+
tableWidth: maxRowLength,
|
|
1075
|
+
autoAdjustWidthWhenPossible: true
|
|
1076
|
+
});
|
|
1077
|
+
if (error && typeof error === "object") {
|
|
1078
|
+
const err = error;
|
|
1079
|
+
table.addRow("Key", "Value");
|
|
1080
|
+
if (err["message"]) {
|
|
1081
|
+
table.addRow("Message", safeStringify(err["message"]));
|
|
1082
|
+
}
|
|
1083
|
+
if (err["name"]) {
|
|
1084
|
+
table.addRow("Name", safeStringify(err["name"]));
|
|
1085
|
+
}
|
|
1086
|
+
if (err["code"]) {
|
|
1087
|
+
table.addRow("Code", safeStringify(err["code"]));
|
|
1088
|
+
}
|
|
1089
|
+
if (err["errno"]) {
|
|
1090
|
+
table.addRow("Errno", safeStringify(err["errno"]));
|
|
1091
|
+
}
|
|
1092
|
+
if (err["errPrefix"]) {
|
|
1093
|
+
table.addRow("Prefix", safeStringify(err["errPrefix"]));
|
|
1094
|
+
}
|
|
1095
|
+
if (err["errType"]) {
|
|
1096
|
+
table.addRow("errType", safeStringify(err["errType"]));
|
|
1097
|
+
}
|
|
1098
|
+
if (err["errCode"]) {
|
|
1099
|
+
table.addRow("errCode", safeStringify(err["errCode"]));
|
|
1100
|
+
}
|
|
1101
|
+
if (err["additionalInfo"]) {
|
|
1102
|
+
const additionalInfo = err["additionalInfo"];
|
|
1103
|
+
const sensitiveFieldNames = err["sensitiveFieldNames"] || [];
|
|
1104
|
+
for (const key in additionalInfo) {
|
|
1105
|
+
if (sensitiveFieldNames.includes(key)) {
|
|
1106
|
+
table.addRow(`AdditionalInfo.${key}`, "***");
|
|
1107
|
+
} else {
|
|
1108
|
+
const value = additionalInfo[key];
|
|
1109
|
+
table.addRow(
|
|
1110
|
+
`AdditionalInfo.${key}`,
|
|
1111
|
+
stringifyValue(value, table, maxRowLength)
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (err["stack"]) {
|
|
1117
|
+
table.addValueOnSeparateRow("Stack", safeStringify(err["stack"]));
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return table;
|
|
1121
|
+
}
|
|
1122
|
+
function stringifyValue(value, table, maxRowLength) {
|
|
1123
|
+
if (typeof value === "string") {
|
|
1124
|
+
return value;
|
|
1125
|
+
} else if (Array.isArray(value)) {
|
|
1126
|
+
return value.map((item) => {
|
|
1127
|
+
const result = stringifyValue(item, table, maxRowLength);
|
|
1128
|
+
if (typeof result === "string") {
|
|
1129
|
+
return result;
|
|
1130
|
+
} else if (result instanceof KeyValueASCIITable) {
|
|
1131
|
+
return result.toString();
|
|
1132
|
+
} else {
|
|
1133
|
+
return JSON.stringify(result);
|
|
1134
|
+
}
|
|
1135
|
+
}).join(", ");
|
|
1136
|
+
} else if (typeof value === "object" && value !== null) {
|
|
1137
|
+
if (value instanceof Error) {
|
|
1138
|
+
return errorToASCIITable(value, maxRowLength - 4);
|
|
1139
|
+
} else {
|
|
1140
|
+
const entries = Object.entries(value).map(
|
|
1141
|
+
([key, val]) => ({
|
|
1142
|
+
key,
|
|
1143
|
+
value: stringifyValue(val, table, maxRowLength - 4)
|
|
1144
|
+
})
|
|
1145
|
+
);
|
|
1146
|
+
return entries;
|
|
1147
|
+
}
|
|
1148
|
+
} else {
|
|
1149
|
+
return String(value);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// src/lib/is-promise.ts
|
|
1154
|
+
function isPromise(obj) {
|
|
1155
|
+
return !!obj && (typeof obj === "object" || typeof obj === "function") && // @ts-expect-error - obj is checked to be object/function, then property access works at runtime
|
|
1156
|
+
typeof obj["then"] === "function";
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// src/lib/is-function.ts
|
|
1160
|
+
function isFunction(value) {
|
|
1161
|
+
return typeof value === "function" || value instanceof Function;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/lib/safe-handle-callback.ts
|
|
1165
|
+
function safeHandleCallback(callbackName, callback, ...args) {
|
|
1166
|
+
const handleError = (error) => {
|
|
1167
|
+
if (typeof globalThis.dispatchEvent === "function") {
|
|
1168
|
+
globalThis.dispatchEvent(
|
|
1169
|
+
new ErrorEvent("reportError", {
|
|
1170
|
+
error: new Error(
|
|
1171
|
+
`Error in a callback ${callbackName}: ${DOUBLE_EOL}${errorToString(error)}`
|
|
1172
|
+
)
|
|
1173
|
+
})
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
if (isFunction(callback)) {
|
|
1178
|
+
try {
|
|
1179
|
+
const result = callback(...args);
|
|
1180
|
+
if (isPromise(result)) {
|
|
1181
|
+
result.catch((error) => {
|
|
1182
|
+
handleError(error);
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
handleError(error);
|
|
1187
|
+
}
|
|
1188
|
+
} else {
|
|
1189
|
+
handleError(
|
|
1190
|
+
new Error(`Callback provided for ${callbackName} is not a function`)
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// src/lib/promise-protected-resolver.ts
|
|
1196
|
+
var PromiseProtectedResolver = class {
|
|
1197
|
+
promise;
|
|
1198
|
+
get hasResolved() {
|
|
1199
|
+
return this._hasResolved;
|
|
1200
|
+
}
|
|
1201
|
+
_hasResolved = false;
|
|
1202
|
+
resolveHandler;
|
|
1203
|
+
rejectHandler;
|
|
1204
|
+
options;
|
|
1205
|
+
constructor(options = {}) {
|
|
1206
|
+
this.options = options;
|
|
1207
|
+
this.promise = new Promise((resolve, reject) => {
|
|
1208
|
+
this.resolveHandler = resolve;
|
|
1209
|
+
this.rejectHandler = reject;
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
resolveOnce(value) {
|
|
1213
|
+
if (!this._hasResolved && this.resolveHandler) {
|
|
1214
|
+
this.executeBeforeCallback("resolve", value);
|
|
1215
|
+
this._hasResolved = true;
|
|
1216
|
+
this.resolveHandler(value);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
rejectOnce(reason) {
|
|
1220
|
+
if (!this._hasResolved && this.rejectHandler) {
|
|
1221
|
+
this.executeBeforeCallback("reject", reason);
|
|
1222
|
+
this._hasResolved = true;
|
|
1223
|
+
this.rejectHandler(reason);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
executeBeforeCallback(action, valueOrReason) {
|
|
1227
|
+
if (this.options.beforeResolveOrReject) {
|
|
1228
|
+
safeHandleCallback(
|
|
1229
|
+
"beforeResolveOrReject",
|
|
1230
|
+
this.options.beforeResolveOrReject,
|
|
1231
|
+
action,
|
|
1232
|
+
valueOrReason
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
// src/lib/id-helpers.ts
|
|
1239
|
+
import ObjectID from "bson-objectid";
|
|
1240
|
+
import {
|
|
1241
|
+
v4 as UUIDv4,
|
|
1242
|
+
v7 as UUIDv7,
|
|
1243
|
+
validate as uuidValidate,
|
|
1244
|
+
version as uuidVersion
|
|
1245
|
+
} from "uuid";
|
|
1246
|
+
import { ulid } from "ulid";
|
|
1247
|
+
|
|
1248
|
+
// src/lib/unix-time-helpers.ts
|
|
1249
|
+
function convertMSToUnix(value) {
|
|
1250
|
+
return Math.floor(value / 1e3);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/lib/id-helpers.ts
|
|
1254
|
+
var IDENTIFIER_TYPES = ["objectID", "uuid4", "uuid7", "ulid"];
|
|
1255
|
+
function assertIdentifierType(type) {
|
|
1256
|
+
if (!IDENTIFIER_TYPES.includes(type)) {
|
|
1257
|
+
throw new TypeError(
|
|
1258
|
+
`Invalid ID type given: "${type}". Expected one of: ${IDENTIFIER_TYPES.join(", ")}`
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function generateID(type, seedTime) {
|
|
1263
|
+
assertIdentifierType(type);
|
|
1264
|
+
if (seedTime !== void 0 && (!Number.isFinite(seedTime) || seedTime < 0)) {
|
|
1265
|
+
throw new TypeError(
|
|
1266
|
+
`seedTime must be a non-negative finite number (milliseconds), got: ${seedTime}`
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
if (type === "objectID") {
|
|
1270
|
+
if (seedTime !== void 0) {
|
|
1271
|
+
const unixTime = convertMSToUnix(seedTime);
|
|
1272
|
+
return new ObjectID(unixTime).toHexString();
|
|
1273
|
+
} else {
|
|
1274
|
+
return new ObjectID().toHexString();
|
|
1275
|
+
}
|
|
1276
|
+
} else if (type === "uuid4") {
|
|
1277
|
+
return UUIDv4();
|
|
1278
|
+
} else if (type === "uuid7") {
|
|
1279
|
+
if (seedTime !== void 0) {
|
|
1280
|
+
return UUIDv7({ msecs: seedTime });
|
|
1281
|
+
} else {
|
|
1282
|
+
return UUIDv7();
|
|
1283
|
+
}
|
|
1284
|
+
} else if (type === "ulid") {
|
|
1285
|
+
if (seedTime !== void 0) {
|
|
1286
|
+
return ulid(seedTime);
|
|
1287
|
+
} else {
|
|
1288
|
+
return ulid();
|
|
1289
|
+
}
|
|
1290
|
+
} else {
|
|
1291
|
+
throw new TypeError(`Unhandled identifier type: "${type}"`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/lib/is-plain-object.ts
|
|
1296
|
+
function isPlainObject(value) {
|
|
1297
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// src/lib/event-emitter.ts
|
|
1301
|
+
var EventEmitterProtected = class {
|
|
1302
|
+
events;
|
|
1303
|
+
constructor() {
|
|
1304
|
+
this.events = /* @__PURE__ */ new Map();
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Subscribe to an event
|
|
1308
|
+
* @param event The event name to subscribe to
|
|
1309
|
+
* @param callback The callback function to be called when the event is emitted
|
|
1310
|
+
* @returns A function to unsubscribe from the event
|
|
1311
|
+
*/
|
|
1312
|
+
on(event, callback) {
|
|
1313
|
+
if (!this.events.has(event)) {
|
|
1314
|
+
this.events.set(event, /* @__PURE__ */ new Set());
|
|
1315
|
+
}
|
|
1316
|
+
const callbacks = this.events.get(event);
|
|
1317
|
+
if (callbacks) {
|
|
1318
|
+
callbacks.add(callback);
|
|
1319
|
+
}
|
|
1320
|
+
return () => {
|
|
1321
|
+
const callbacks2 = this.events.get(event);
|
|
1322
|
+
if (callbacks2) {
|
|
1323
|
+
callbacks2.delete(callback);
|
|
1324
|
+
if (callbacks2.size === 0) {
|
|
1325
|
+
this.events.delete(event);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Subscribe to an event once - automatically unsubscribes after first emission
|
|
1332
|
+
* @param event The event name to subscribe to
|
|
1333
|
+
* @param callback The callback function to be called when the event is emitted
|
|
1334
|
+
* @returns A function to unsubscribe from the event before it's called
|
|
1335
|
+
*/
|
|
1336
|
+
once(event, callback) {
|
|
1337
|
+
const unsubscribe = this.on(event, (data) => {
|
|
1338
|
+
unsubscribe();
|
|
1339
|
+
return callback(data);
|
|
1340
|
+
});
|
|
1341
|
+
return unsubscribe;
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Check if a specific callback is registered for an event.
|
|
1345
|
+
* Note: For 'once' handlers, this will return true for the wrapper function, not the original callback.
|
|
1346
|
+
* This means hasListener will return false when checking for the original callback of a 'once' subscription.
|
|
1347
|
+
*
|
|
1348
|
+
* @param event The event name to check
|
|
1349
|
+
* @param callback The callback function to look for
|
|
1350
|
+
* @returns true if the exact callback is registered, false otherwise
|
|
1351
|
+
*/
|
|
1352
|
+
hasListener(event, callback) {
|
|
1353
|
+
const callbacks = this.events.get(event);
|
|
1354
|
+
return callbacks?.has(callback) ?? false;
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Check if an event has any subscribers
|
|
1358
|
+
* @param event The event name to check
|
|
1359
|
+
* @returns true if the event has subscribers, false otherwise
|
|
1360
|
+
*/
|
|
1361
|
+
hasListeners(event) {
|
|
1362
|
+
const callbacks = this.events.get(event);
|
|
1363
|
+
return callbacks !== void 0 && callbacks.size > 0;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Get the number of subscribers for an event
|
|
1367
|
+
* @param event The event name to check
|
|
1368
|
+
* @returns The number of subscribers
|
|
1369
|
+
*/
|
|
1370
|
+
listenerCount(event) {
|
|
1371
|
+
const callbacks = this.events.get(event);
|
|
1372
|
+
return callbacks ? callbacks.size : 0;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Remove all event listeners
|
|
1376
|
+
* @param event Optional event name. If not provided, removes all listeners for all events
|
|
1377
|
+
*/
|
|
1378
|
+
clear(event) {
|
|
1379
|
+
if (event) {
|
|
1380
|
+
this.events.delete(event);
|
|
1381
|
+
} else {
|
|
1382
|
+
this.events.clear();
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Emit an event with optional data
|
|
1387
|
+
* This method is protected to allow only derived classes to trigger events.
|
|
1388
|
+
* @param event The event name to emit
|
|
1389
|
+
* @param data Optional data to pass to the event handlers
|
|
1390
|
+
*/
|
|
1391
|
+
emit(event, data) {
|
|
1392
|
+
const callbacks = this.events.get(event);
|
|
1393
|
+
if (callbacks) {
|
|
1394
|
+
for (const callback of callbacks) {
|
|
1395
|
+
safeHandleCallback(`event handler for ${event}`, callback, data);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
// src/lib/retry-utils/lib/retry-runner.ts
|
|
1402
|
+
var AttemptContext = class {
|
|
1403
|
+
handled = false;
|
|
1404
|
+
id;
|
|
1405
|
+
abortController;
|
|
1406
|
+
startTime;
|
|
1407
|
+
constructor() {
|
|
1408
|
+
this.id = generateID("ulid");
|
|
1409
|
+
this.abortController = new AbortController();
|
|
1410
|
+
this.startTime = Date.now();
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
var OPERATION_STARTED = "operation-started";
|
|
1414
|
+
var OPERATION_ENDED = "operation-ended";
|
|
1415
|
+
var ATTEMPT_STARTED = "attempt-started";
|
|
1416
|
+
var ATTEMPT_HANDLED = "attempt-handled";
|
|
1417
|
+
var RetryRunner = class extends EventEmitterProtected {
|
|
1418
|
+
// Human-readable label for debugging/logging purposes.
|
|
1419
|
+
_operationLabel = "Unnamed Operation";
|
|
1420
|
+
// Retry policy that determines retry behavior and tracks state.
|
|
1421
|
+
policy;
|
|
1422
|
+
// Prevents concurrent run/resume/forceTry calls.
|
|
1423
|
+
_isOperationLocked = false;
|
|
1424
|
+
// Mutable runtime state for the current operation.
|
|
1425
|
+
currentState = this.getEmptyCurrentState();
|
|
1426
|
+
// Grace period for cancellation before we force-complete.
|
|
1427
|
+
_gracePeriodMS = 1e3;
|
|
1428
|
+
// User-provided operation to retry on failure.
|
|
1429
|
+
operation;
|
|
1430
|
+
// Resolver for the current run/resume/forceTry operation.
|
|
1431
|
+
currentOperationResolver;
|
|
1432
|
+
// Resolvers for pending cancel() calls.
|
|
1433
|
+
cancelResolvers = /* @__PURE__ */ new Set();
|
|
1434
|
+
get operationLabel() {
|
|
1435
|
+
return this._operationLabel;
|
|
1436
|
+
}
|
|
1437
|
+
get runnerState() {
|
|
1438
|
+
return this.currentState.runnerState;
|
|
1439
|
+
}
|
|
1440
|
+
get timeTakenMS() {
|
|
1441
|
+
if (this.currentState.finalTimeTakenMS !== null) {
|
|
1442
|
+
return this.currentState.finalTimeTakenMS;
|
|
1443
|
+
} else if (this.currentState.operationStartTime !== null) {
|
|
1444
|
+
return Date.now() - this.currentState.operationStartTime;
|
|
1445
|
+
}
|
|
1446
|
+
return -1;
|
|
1447
|
+
}
|
|
1448
|
+
get attemptTimeTakenMS() {
|
|
1449
|
+
if (this.currentState.currentAttemptContext instanceof AttemptContext && !this.currentState.currentAttemptContext.handled) {
|
|
1450
|
+
return Date.now() - this.currentState.currentAttemptContext.startTime;
|
|
1451
|
+
}
|
|
1452
|
+
return this.currentState.lastAttemptTimeTakenMS;
|
|
1453
|
+
}
|
|
1454
|
+
get retryTimeRemaining() {
|
|
1455
|
+
if (this.currentState.retryTimeoutHandle !== null && this.currentState.retryTimeoutStartTime !== null && this.currentState.retryTimeoutDelayMS !== null) {
|
|
1456
|
+
const elapsed = Date.now() - this.currentState.retryTimeoutStartTime;
|
|
1457
|
+
const remaining = this.currentState.retryTimeoutDelayMS - elapsed;
|
|
1458
|
+
return Math.max(0, remaining);
|
|
1459
|
+
} else {
|
|
1460
|
+
return -1;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
get canForceTry() {
|
|
1464
|
+
return this.currentState.runnerState === "running" || this.currentState.runnerState === "exhausted" || this.currentState.runnerState === "fatal-error" || this.currentState.runnerState === "not-started" || this.currentState.runnerState === "stopping" || this.currentState.runnerState === "stopped";
|
|
1465
|
+
}
|
|
1466
|
+
get wasLastAttemptForced() {
|
|
1467
|
+
return this.currentState.lastAttemptWasForceTry;
|
|
1468
|
+
}
|
|
1469
|
+
get errors() {
|
|
1470
|
+
return this.policy.errors;
|
|
1471
|
+
}
|
|
1472
|
+
get wasInitialAttemptTaken() {
|
|
1473
|
+
return this.policy.wasInitialAttemptTaken;
|
|
1474
|
+
}
|
|
1475
|
+
get areAttemptsExhausted() {
|
|
1476
|
+
return this.policy.areAttemptsExhausted;
|
|
1477
|
+
}
|
|
1478
|
+
get attempts() {
|
|
1479
|
+
return this.policy.attempts;
|
|
1480
|
+
}
|
|
1481
|
+
get mostCommonError() {
|
|
1482
|
+
return this.policy.mostCommonError;
|
|
1483
|
+
}
|
|
1484
|
+
get lastError() {
|
|
1485
|
+
return this.policy.lastError;
|
|
1486
|
+
}
|
|
1487
|
+
get maxRetryAttempts() {
|
|
1488
|
+
return this.policy.maxRetryAttempts;
|
|
1489
|
+
}
|
|
1490
|
+
get policyInfo() {
|
|
1491
|
+
return this.policy.policyInfo;
|
|
1492
|
+
}
|
|
1493
|
+
get retryCount() {
|
|
1494
|
+
return this.policy.retryCount;
|
|
1495
|
+
}
|
|
1496
|
+
get wasSuccessful() {
|
|
1497
|
+
return this.policy.wasSuccessful;
|
|
1498
|
+
}
|
|
1499
|
+
get isRetryPending() {
|
|
1500
|
+
return this.currentState.retryTimeoutHandle !== null;
|
|
1501
|
+
}
|
|
1502
|
+
get isOperationRunning() {
|
|
1503
|
+
return this.currentState.runnerState === "running" || this.currentState.runnerState === "stopping";
|
|
1504
|
+
}
|
|
1505
|
+
get isAttemptRunning() {
|
|
1506
|
+
if (this.currentState.currentAttemptContext instanceof AttemptContext) {
|
|
1507
|
+
return !this.currentState.currentAttemptContext.handled;
|
|
1508
|
+
} else {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
get graceCancelPeriodMS() {
|
|
1513
|
+
return this._gracePeriodMS;
|
|
1514
|
+
}
|
|
1515
|
+
constructor(policy, operation, options) {
|
|
1516
|
+
super();
|
|
1517
|
+
this.policy = new RetryPolicy(policy);
|
|
1518
|
+
this.operation = operation;
|
|
1519
|
+
if (isPlainObject(options)) {
|
|
1520
|
+
if (isString(options.operationLabel)) {
|
|
1521
|
+
this._operationLabel = options.operationLabel;
|
|
1522
|
+
}
|
|
1523
|
+
if (isFunction(options.onOperationStarted)) {
|
|
1524
|
+
this.on(
|
|
1525
|
+
OPERATION_STARTED,
|
|
1526
|
+
options.onOperationStarted
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
if (isFunction(options.onOperationEnded)) {
|
|
1530
|
+
this.on(
|
|
1531
|
+
OPERATION_ENDED,
|
|
1532
|
+
options.onOperationEnded
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
if (isFunction(options.onAttemptStarted)) {
|
|
1536
|
+
this.on(
|
|
1537
|
+
ATTEMPT_STARTED,
|
|
1538
|
+
options.onAttemptStarted
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
if (isFunction(options.onAttemptHandled)) {
|
|
1542
|
+
this.on(
|
|
1543
|
+
ATTEMPT_HANDLED,
|
|
1544
|
+
options.onAttemptHandled
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Set the grace period for cancellation in milliseconds
|
|
1551
|
+
*
|
|
1552
|
+
* Overrides the default grace period of 1000ms
|
|
1553
|
+
* Non-finite or negative values default to 1000ms. Use 0 for immediate force-cancel.
|
|
1554
|
+
*/
|
|
1555
|
+
overrideGraceCancelPeriodMS(value) {
|
|
1556
|
+
if (!isFinite(value) || value < 0) {
|
|
1557
|
+
this._gracePeriodMS = 1e3;
|
|
1558
|
+
} else {
|
|
1559
|
+
this._gracePeriodMS = value;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
async waitForCompletion() {
|
|
1563
|
+
switch (this.currentState.runnerState) {
|
|
1564
|
+
case "completed":
|
|
1565
|
+
case "exhausted":
|
|
1566
|
+
case "fatal-error":
|
|
1567
|
+
case "stopped":
|
|
1568
|
+
return this.currentOperationResolver.promise;
|
|
1569
|
+
case "running":
|
|
1570
|
+
case "stopping":
|
|
1571
|
+
return this.currentOperationResolver.promise;
|
|
1572
|
+
case "not-started":
|
|
1573
|
+
return {
|
|
1574
|
+
status: "not_started",
|
|
1575
|
+
code: "not_running",
|
|
1576
|
+
error: new RetryUtilsErrRunnerNotRunning("waitForCompletion")
|
|
1577
|
+
};
|
|
1578
|
+
default:
|
|
1579
|
+
throw new RetryUtilsErrRunnerUnknownState(
|
|
1580
|
+
"waitForCompletion",
|
|
1581
|
+
this.currentState.runnerState
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
async run(shouldWaitForCompletion = false) {
|
|
1586
|
+
if (this._isOperationLocked) {
|
|
1587
|
+
return {
|
|
1588
|
+
status: "pre_operation_error",
|
|
1589
|
+
code: "lock_error",
|
|
1590
|
+
error: new RetryUtilsErrRunnerLockAcquisitionError("run")
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
this._isOperationLocked = true;
|
|
1594
|
+
try {
|
|
1595
|
+
const checkDisallowedStates = this.checkForDisallowedPerOperationStates(
|
|
1596
|
+
"run",
|
|
1597
|
+
[
|
|
1598
|
+
"completed",
|
|
1599
|
+
"running",
|
|
1600
|
+
"stopping",
|
|
1601
|
+
"stopped",
|
|
1602
|
+
"fatal-error",
|
|
1603
|
+
"exhausted"
|
|
1604
|
+
]
|
|
1605
|
+
);
|
|
1606
|
+
if (checkDisallowedStates.wasDisallowed && checkDisallowedStates.runResult) {
|
|
1607
|
+
return checkDisallowedStates.runResult;
|
|
1608
|
+
} else if (this.policy.shouldDoFirstTry()) {
|
|
1609
|
+
this.currentState.runnerState = "running";
|
|
1610
|
+
this.currentState.operationStartTime = Date.now();
|
|
1611
|
+
this.currentState.finalTimeTakenMS = null;
|
|
1612
|
+
this.emit(OPERATION_STARTED, { operationType: "initial" });
|
|
1613
|
+
this.currentOperationResolver = new PromiseProtectedResolver();
|
|
1614
|
+
void this.attemptOperation(false);
|
|
1615
|
+
if (shouldWaitForCompletion) {
|
|
1616
|
+
return this.currentOperationResolver.promise;
|
|
1617
|
+
} else {
|
|
1618
|
+
return { status: "running" };
|
|
1619
|
+
}
|
|
1620
|
+
} else {
|
|
1621
|
+
return {
|
|
1622
|
+
status: "pre_operation_error",
|
|
1623
|
+
code: "unexpected_error",
|
|
1624
|
+
error: new RetryUtilsErrRunnerUnexpectedError(
|
|
1625
|
+
"run",
|
|
1626
|
+
new Error(
|
|
1627
|
+
"Not first try, but this should had been caught before this point"
|
|
1628
|
+
)
|
|
1629
|
+
)
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
return {
|
|
1634
|
+
status: "pre_operation_error",
|
|
1635
|
+
code: "unexpected_error",
|
|
1636
|
+
error: new RetryUtilsErrRunnerUnexpectedError("run", error)
|
|
1637
|
+
};
|
|
1638
|
+
} finally {
|
|
1639
|
+
this._isOperationLocked = false;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
async cancel() {
|
|
1643
|
+
if (this.currentState.runnerState === "running" || this.currentState.runnerState === "stopping") {
|
|
1644
|
+
const cancellationPromiseProtectedResolver = new PromiseProtectedResolver();
|
|
1645
|
+
this.cancelResolvers.add(cancellationPromiseProtectedResolver);
|
|
1646
|
+
if (this.currentState.runnerState !== "stopping") {
|
|
1647
|
+
this.currentState.runnerState = "stopping";
|
|
1648
|
+
this.cleanupTimers();
|
|
1649
|
+
if (!this.currentState.currentAttemptContext || this.currentState.currentAttemptContext.handled) {
|
|
1650
|
+
this.confirmCancellation("stopped", {
|
|
1651
|
+
status: "canceled"
|
|
1652
|
+
});
|
|
1653
|
+
} else {
|
|
1654
|
+
this.currentState.currentAttemptContext.abortController.abort();
|
|
1655
|
+
this.currentState.cancellationTimeoutHandle = setTimeout(() => {
|
|
1656
|
+
if (this.currentState.currentAttemptContext instanceof AttemptContext && !this.currentState.currentAttemptContext.handled) {
|
|
1657
|
+
if (this.currentState.runnerState === "stopping") {
|
|
1658
|
+
const context = this.currentState.currentAttemptContext;
|
|
1659
|
+
context.handled = true;
|
|
1660
|
+
this.currentState.currentAttemptContext = null;
|
|
1661
|
+
this.cleanupTimers();
|
|
1662
|
+
const attemptTimeElapsedMS = Date.now() - context.startTime;
|
|
1663
|
+
this.currentState.lastAttemptTimeTakenMS = attemptTimeElapsedMS;
|
|
1664
|
+
this.emit(ATTEMPT_HANDLED, {
|
|
1665
|
+
attemptID: context.id,
|
|
1666
|
+
status: "skip",
|
|
1667
|
+
data: void 0,
|
|
1668
|
+
error: void 0,
|
|
1669
|
+
operationTimeElapsedMS: this.timeTakenMS,
|
|
1670
|
+
attemptTimeElapsedMS,
|
|
1671
|
+
wasCanceled: true
|
|
1672
|
+
});
|
|
1673
|
+
this.confirmCancellation(
|
|
1674
|
+
"stopped",
|
|
1675
|
+
{
|
|
1676
|
+
status: "canceled"
|
|
1677
|
+
},
|
|
1678
|
+
true
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}, this._gracePeriodMS);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return cancellationPromiseProtectedResolver.promise;
|
|
1686
|
+
} else {
|
|
1687
|
+
return "not-running";
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
async reset() {
|
|
1691
|
+
if (this.currentState.runnerState === "running" || this.currentState.runnerState === "stopping") {
|
|
1692
|
+
await this.cancel();
|
|
1693
|
+
}
|
|
1694
|
+
this.currentState = this.getEmptyCurrentState();
|
|
1695
|
+
this.policy.reset();
|
|
1696
|
+
}
|
|
1697
|
+
async resume(shouldWaitForCompletion = false) {
|
|
1698
|
+
if (this._isOperationLocked) {
|
|
1699
|
+
return {
|
|
1700
|
+
status: "pre_operation_error",
|
|
1701
|
+
code: "lock_error",
|
|
1702
|
+
error: new RetryUtilsErrRunnerLockAcquisitionError("resume")
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
this._isOperationLocked = true;
|
|
1706
|
+
try {
|
|
1707
|
+
const checkDisallowedStates = this.checkForDisallowedPerOperationStates(
|
|
1708
|
+
"resume",
|
|
1709
|
+
["completed", "running", "stopping", "fatal-error", "exhausted"]
|
|
1710
|
+
);
|
|
1711
|
+
if (checkDisallowedStates.wasDisallowed && checkDisallowedStates.runResult) {
|
|
1712
|
+
return checkDisallowedStates.runResult;
|
|
1713
|
+
} else {
|
|
1714
|
+
if (this.currentState.runnerState !== "stopped") {
|
|
1715
|
+
return {
|
|
1716
|
+
status: "pre_operation_error",
|
|
1717
|
+
code: "not_paused",
|
|
1718
|
+
error: new RetryUtilsErrRunnerNotPaused("resume")
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
this.currentState.runnerState = "running";
|
|
1722
|
+
this.currentState.operationStartTime = Date.now();
|
|
1723
|
+
this.currentState.finalTimeTakenMS = null;
|
|
1724
|
+
this.emit(OPERATION_STARTED, { operationType: "resume" });
|
|
1725
|
+
this.currentOperationResolver = new PromiseProtectedResolver();
|
|
1726
|
+
void this.attemptOperation(false);
|
|
1727
|
+
if (shouldWaitForCompletion) {
|
|
1728
|
+
return this.currentOperationResolver.promise;
|
|
1729
|
+
} else {
|
|
1730
|
+
return { status: "running" };
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
return {
|
|
1735
|
+
status: "pre_operation_error",
|
|
1736
|
+
code: "unexpected_error",
|
|
1737
|
+
error: new RetryUtilsErrRunnerUnexpectedError("resume", error)
|
|
1738
|
+
};
|
|
1739
|
+
} finally {
|
|
1740
|
+
this._isOperationLocked = false;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
async forceTry(options) {
|
|
1744
|
+
const shouldWaitForCompletion = options?.shouldWaitForCompletion ?? false;
|
|
1745
|
+
const shouldAbortRunning = options?.shouldAbortRunning ?? false;
|
|
1746
|
+
if (this._isOperationLocked) {
|
|
1747
|
+
return {
|
|
1748
|
+
status: "pre_operation_error",
|
|
1749
|
+
code: "lock_error",
|
|
1750
|
+
error: new RetryUtilsErrRunnerLockAcquisitionError("forceTry")
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
this._isOperationLocked = true;
|
|
1754
|
+
try {
|
|
1755
|
+
const checkDisallowedStates = this.checkForDisallowedPerOperationStates(
|
|
1756
|
+
"forceTry",
|
|
1757
|
+
["completed"]
|
|
1758
|
+
);
|
|
1759
|
+
if (checkDisallowedStates.wasDisallowed && checkDisallowedStates.runResult) {
|
|
1760
|
+
return checkDisallowedStates.runResult;
|
|
1761
|
+
}
|
|
1762
|
+
if (this.isAttemptRunning && !shouldAbortRunning) {
|
|
1763
|
+
if (shouldWaitForCompletion) {
|
|
1764
|
+
return this.currentOperationResolver.promise;
|
|
1765
|
+
} else {
|
|
1766
|
+
return { status: "running", reattached: true };
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
if (this.isAttemptRunning && this.currentState.lastAttemptWasForceTry) {
|
|
1770
|
+
return {
|
|
1771
|
+
status: "pre_operation_error",
|
|
1772
|
+
code: "force_try_in_progress",
|
|
1773
|
+
error: new RetryUtilsErrRunnerForceTryRetryInProgress("forceTry")
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
if (this.isAttemptRunning && this.currentState.currentAttemptContext) {
|
|
1777
|
+
this.currentState.currentAttemptContext.abortController.abort();
|
|
1778
|
+
}
|
|
1779
|
+
if (this.currentState.retryTimeoutHandle !== null) {
|
|
1780
|
+
clearTimeout(this.currentState.retryTimeoutHandle);
|
|
1781
|
+
this.currentState.retryTimeoutHandle = null;
|
|
1782
|
+
this.currentState.retryTimeoutStartTime = null;
|
|
1783
|
+
this.currentState.retryTimeoutDelayMS = null;
|
|
1784
|
+
this.currentState.lastAttemptWasForceTry = true;
|
|
1785
|
+
void this.attemptOperation(true);
|
|
1786
|
+
if (shouldWaitForCompletion) {
|
|
1787
|
+
return this.currentOperationResolver.promise;
|
|
1788
|
+
} else {
|
|
1789
|
+
return { status: "running", reattached: false };
|
|
1790
|
+
}
|
|
1791
|
+
} else {
|
|
1792
|
+
if (this.currentState.runnerState === "not-started") {
|
|
1793
|
+
this.policy.shouldDoFirstTry();
|
|
1794
|
+
} else if (this.currentState.runnerState === "stopping" || this.currentState.runnerState === "stopped") {
|
|
1795
|
+
this.cleanupTimers();
|
|
1796
|
+
this.confirmCancellation("running", {
|
|
1797
|
+
status: null
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
this.currentState.runnerState = "running";
|
|
1801
|
+
this.currentState.operationStartTime = Date.now();
|
|
1802
|
+
this.currentState.finalTimeTakenMS = null;
|
|
1803
|
+
this.emit(OPERATION_STARTED, { operationType: "force" });
|
|
1804
|
+
if (!this.currentOperationResolver || this.currentOperationResolver.hasResolved) {
|
|
1805
|
+
this.currentOperationResolver = new PromiseProtectedResolver();
|
|
1806
|
+
}
|
|
1807
|
+
void this.attemptOperation(true);
|
|
1808
|
+
if (shouldWaitForCompletion) {
|
|
1809
|
+
return this.currentOperationResolver.promise;
|
|
1810
|
+
} else {
|
|
1811
|
+
return { status: "running", reattached: false };
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
} catch (error) {
|
|
1815
|
+
return {
|
|
1816
|
+
status: "pre_operation_error",
|
|
1817
|
+
code: "unexpected_error",
|
|
1818
|
+
error: new RetryUtilsErrRunnerUnexpectedError(
|
|
1819
|
+
"forceTry",
|
|
1820
|
+
error
|
|
1821
|
+
)
|
|
1822
|
+
};
|
|
1823
|
+
} finally {
|
|
1824
|
+
this._isOperationLocked = false;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Returning a fresh copy of the current state
|
|
1829
|
+
* to be immutable and not changed by the caller
|
|
1830
|
+
*/
|
|
1831
|
+
getEmptyCurrentState() {
|
|
1832
|
+
return {
|
|
1833
|
+
runnerState: "not-started",
|
|
1834
|
+
lastAttemptWasForceTry: false,
|
|
1835
|
+
currentAttemptContext: null,
|
|
1836
|
+
retryTimeoutHandle: null,
|
|
1837
|
+
retryTimeoutStartTime: null,
|
|
1838
|
+
retryTimeoutDelayMS: null,
|
|
1839
|
+
cancellationTimeoutHandle: null,
|
|
1840
|
+
operationStartTime: null,
|
|
1841
|
+
finalTimeTakenMS: null,
|
|
1842
|
+
lastAttemptTimeTakenMS: -1
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
checkForDisallowedPerOperationStates(methodName, disallowedStates) {
|
|
1846
|
+
for (const checkForState of disallowedStates) {
|
|
1847
|
+
if (checkForState === "completed" && this.currentState.runnerState === "completed") {
|
|
1848
|
+
return {
|
|
1849
|
+
wasDisallowed: true,
|
|
1850
|
+
runResult: {
|
|
1851
|
+
status: "pre_operation_error",
|
|
1852
|
+
code: "already_completed",
|
|
1853
|
+
error: new RetryUtilsErrRunnerAlreadyCompleted(methodName)
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
} else if (checkForState === "running" && this.currentState.runnerState === "running") {
|
|
1857
|
+
return {
|
|
1858
|
+
wasDisallowed: true,
|
|
1859
|
+
runResult: {
|
|
1860
|
+
status: "pre_operation_error",
|
|
1861
|
+
code: "already_running",
|
|
1862
|
+
error: new RetryUtilsErrRunnerAlreadyRunning(
|
|
1863
|
+
methodName
|
|
1864
|
+
)
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
} else if (checkForState === "stopping" && this.currentState.runnerState === "stopping") {
|
|
1868
|
+
return {
|
|
1869
|
+
wasDisallowed: true,
|
|
1870
|
+
runResult: {
|
|
1871
|
+
status: "pre_operation_error",
|
|
1872
|
+
code: "cancel_pending",
|
|
1873
|
+
error: new RetryUtilsErrRunnerCancelPending(
|
|
1874
|
+
methodName
|
|
1875
|
+
)
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
} else if (checkForState === "stopped" && this.currentState.runnerState === "stopped") {
|
|
1879
|
+
return {
|
|
1880
|
+
wasDisallowed: true,
|
|
1881
|
+
runResult: {
|
|
1882
|
+
status: "pre_operation_error",
|
|
1883
|
+
code: "retry_canceled",
|
|
1884
|
+
error: new RetryUtilsErrRunnerRetryCanceled("run")
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
} else if (checkForState === "fatal-error" && this.currentState.runnerState === "fatal-error") {
|
|
1888
|
+
return {
|
|
1889
|
+
wasDisallowed: true,
|
|
1890
|
+
runResult: {
|
|
1891
|
+
status: "pre_operation_error",
|
|
1892
|
+
code: "fatally_failed",
|
|
1893
|
+
error: new RetryUtilsErrRunnerLastRetryFatallyFailed(
|
|
1894
|
+
methodName
|
|
1895
|
+
)
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
} else if (checkForState === "exhausted" && (this.currentState.runnerState === "exhausted" || this.policy.areAttemptsExhausted)) {
|
|
1899
|
+
return {
|
|
1900
|
+
wasDisallowed: true,
|
|
1901
|
+
runResult: {
|
|
1902
|
+
status: "pre_operation_error",
|
|
1903
|
+
code: "attempts_exhausted",
|
|
1904
|
+
error: new RetryUtilsErrRunnerAttemptsExhausted(
|
|
1905
|
+
methodName
|
|
1906
|
+
)
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
return { wasDisallowed: false };
|
|
1912
|
+
}
|
|
1913
|
+
cleanupTimers() {
|
|
1914
|
+
if (this.currentState.retryTimeoutHandle) {
|
|
1915
|
+
clearTimeout(this.currentState.retryTimeoutHandle);
|
|
1916
|
+
this.currentState.retryTimeoutHandle = null;
|
|
1917
|
+
this.currentState.retryTimeoutStartTime = null;
|
|
1918
|
+
this.currentState.retryTimeoutDelayMS = null;
|
|
1919
|
+
}
|
|
1920
|
+
if (this.currentState.cancellationTimeoutHandle) {
|
|
1921
|
+
clearTimeout(this.currentState.cancellationTimeoutHandle);
|
|
1922
|
+
this.currentState.cancellationTimeoutHandle = null;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
confirmCancellation(runnerState, resolveInfo, wasForced = false) {
|
|
1926
|
+
if (this.currentState.runnerState === "stopping") {
|
|
1927
|
+
this.cleanupTimers();
|
|
1928
|
+
this.currentState.runnerState = runnerState;
|
|
1929
|
+
for (const resolver of this.cancelResolvers) {
|
|
1930
|
+
resolver.resolveOnce(wasForced ? "forced" : "canceled");
|
|
1931
|
+
this.cancelResolvers.delete(resolver);
|
|
1932
|
+
}
|
|
1933
|
+
} else {
|
|
1934
|
+
this.currentState.runnerState = runnerState;
|
|
1935
|
+
}
|
|
1936
|
+
if (runnerState !== "running") {
|
|
1937
|
+
this.currentState.finalTimeTakenMS = this.currentState.operationStartTime !== null ? Date.now() - this.currentState.operationStartTime : -1;
|
|
1938
|
+
this.emit(OPERATION_ENDED, {
|
|
1939
|
+
runnerState,
|
|
1940
|
+
timeTakenMS: this.timeTakenMS
|
|
1941
|
+
});
|
|
1942
|
+
if (resolveInfo.status !== null) {
|
|
1943
|
+
if (resolveInfo.status === "attempt_success") {
|
|
1944
|
+
this.currentOperationResolver.resolveOnce({
|
|
1945
|
+
status: "attempt_success",
|
|
1946
|
+
...resolveInfo.data !== void 0 ? { data: resolveInfo.data } : {}
|
|
1947
|
+
});
|
|
1948
|
+
} else {
|
|
1949
|
+
const nonSuccessResult = {
|
|
1950
|
+
status: resolveInfo.status
|
|
1951
|
+
};
|
|
1952
|
+
if (resolveInfo.code !== void 0) {
|
|
1953
|
+
nonSuccessResult.code = resolveInfo.code;
|
|
1954
|
+
}
|
|
1955
|
+
if (resolveInfo.error !== void 0) {
|
|
1956
|
+
nonSuccessResult.error = resolveInfo.error;
|
|
1957
|
+
}
|
|
1958
|
+
this.currentOperationResolver.resolveOnce(nonSuccessResult);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
handleReportResult(context, status, valueInfo) {
|
|
1964
|
+
if (
|
|
1965
|
+
// Ensure the context matches the current context
|
|
1966
|
+
this.currentState.currentAttemptContext && context.id !== this.currentState.currentAttemptContext.id || // Ensure the result hasn't already been handled
|
|
1967
|
+
context.handled
|
|
1968
|
+
) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
this.currentState.currentAttemptContext = null;
|
|
1972
|
+
this.cleanupTimers();
|
|
1973
|
+
context.handled = true;
|
|
1974
|
+
let isSkip = false;
|
|
1975
|
+
let shouldQueryForRetry = false;
|
|
1976
|
+
let confirmCancellationInfo = {
|
|
1977
|
+
run: false,
|
|
1978
|
+
runnerState: null,
|
|
1979
|
+
resolveInfo: null
|
|
1980
|
+
};
|
|
1981
|
+
if (status === "success") {
|
|
1982
|
+
this.policy.markAsSuccessful();
|
|
1983
|
+
confirmCancellationInfo = {
|
|
1984
|
+
run: true,
|
|
1985
|
+
runnerState: "completed",
|
|
1986
|
+
resolveInfo: {
|
|
1987
|
+
status: "attempt_success",
|
|
1988
|
+
data: valueInfo.data
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
} else if (status === "error") {
|
|
1992
|
+
shouldQueryForRetry = true;
|
|
1993
|
+
} else if (status === "fatal") {
|
|
1994
|
+
this.policy.shouldRetry(valueInfo.error ?? valueInfo.data, false);
|
|
1995
|
+
confirmCancellationInfo = {
|
|
1996
|
+
run: true,
|
|
1997
|
+
runnerState: "fatal-error",
|
|
1998
|
+
resolveInfo: {
|
|
1999
|
+
status: "attempt_fatal",
|
|
2000
|
+
error: valueInfo.error
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
} else {
|
|
2004
|
+
isSkip = true;
|
|
2005
|
+
shouldQueryForRetry = true;
|
|
2006
|
+
}
|
|
2007
|
+
if (shouldQueryForRetry) {
|
|
2008
|
+
const shouldRetryQuery = this.policy.shouldRetry(
|
|
2009
|
+
valueInfo.error ?? valueInfo.data,
|
|
2010
|
+
isSkip
|
|
2011
|
+
);
|
|
2012
|
+
const isCanceledOrPendingCancel = this.currentState.runnerState === "stopping" || this.currentState.runnerState === "stopped";
|
|
2013
|
+
if (isCanceledOrPendingCancel) {
|
|
2014
|
+
confirmCancellationInfo = {
|
|
2015
|
+
run: true,
|
|
2016
|
+
runnerState: "stopped",
|
|
2017
|
+
resolveInfo: {
|
|
2018
|
+
status: "canceled"
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
2021
|
+
} else {
|
|
2022
|
+
if (shouldRetryQuery.shouldRetry) {
|
|
2023
|
+
if (shouldRetryQuery.delayMS > 0) {
|
|
2024
|
+
this.currentState.retryTimeoutStartTime = Date.now();
|
|
2025
|
+
this.currentState.retryTimeoutDelayMS = shouldRetryQuery.delayMS;
|
|
2026
|
+
this.currentState.retryTimeoutHandle = setTimeout(() => {
|
|
2027
|
+
this.currentState.retryTimeoutHandle = null;
|
|
2028
|
+
this.currentState.retryTimeoutStartTime = null;
|
|
2029
|
+
this.currentState.retryTimeoutDelayMS = null;
|
|
2030
|
+
void this.attemptOperation(false);
|
|
2031
|
+
}, shouldRetryQuery.delayMS);
|
|
2032
|
+
} else {
|
|
2033
|
+
void this.attemptOperation(false);
|
|
2034
|
+
}
|
|
2035
|
+
} else {
|
|
2036
|
+
confirmCancellationInfo = {
|
|
2037
|
+
run: true,
|
|
2038
|
+
runnerState: "exhausted",
|
|
2039
|
+
resolveInfo: {
|
|
2040
|
+
status: "attempts_exhausted",
|
|
2041
|
+
error: valueInfo.error
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
const attemptTimeElapsedMS = Date.now() - context.startTime;
|
|
2048
|
+
this.currentState.lastAttemptTimeTakenMS = attemptTimeElapsedMS;
|
|
2049
|
+
this.emit(ATTEMPT_HANDLED, {
|
|
2050
|
+
attemptID: context.id,
|
|
2051
|
+
status,
|
|
2052
|
+
data: valueInfo.data,
|
|
2053
|
+
error: valueInfo.error,
|
|
2054
|
+
operationTimeElapsedMS: this.timeTakenMS,
|
|
2055
|
+
attemptTimeElapsedMS,
|
|
2056
|
+
wasCanceled: this.currentState.runnerState === "stopping" || this.currentState.runnerState === "stopped"
|
|
2057
|
+
});
|
|
2058
|
+
if (confirmCancellationInfo.run && confirmCancellationInfo.runnerState !== null && confirmCancellationInfo.resolveInfo !== null) {
|
|
2059
|
+
this.confirmCancellation(
|
|
2060
|
+
confirmCancellationInfo.runnerState,
|
|
2061
|
+
confirmCancellationInfo.resolveInfo
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
if (this.currentState.runnerState === "stopping") {
|
|
2065
|
+
this.confirmCancellation("stopped", {
|
|
2066
|
+
status: null
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
async attemptOperation(wasForced) {
|
|
2071
|
+
if (this.currentState.currentAttemptContext instanceof AttemptContext) {
|
|
2072
|
+
if (this.currentState.currentAttemptContext.handled) {
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
if (this.currentState.runnerState === "running") {
|
|
2077
|
+
this.currentState.lastAttemptWasForceTry = wasForced;
|
|
2078
|
+
const context = new AttemptContext();
|
|
2079
|
+
this.currentState.currentAttemptContext = context;
|
|
2080
|
+
this.emit(ATTEMPT_STARTED, {
|
|
2081
|
+
attemptID: context.id,
|
|
2082
|
+
operationTimeElapsedMS: this.timeTakenMS,
|
|
2083
|
+
attemptTimeElapsedMS: 0
|
|
2084
|
+
});
|
|
2085
|
+
const reportResult = (status, value) => {
|
|
2086
|
+
if (status === "success" || status === "skip") {
|
|
2087
|
+
this.handleReportResult(context, status, {
|
|
2088
|
+
data: value
|
|
2089
|
+
});
|
|
2090
|
+
} else {
|
|
2091
|
+
this.handleReportResult(context, status, {
|
|
2092
|
+
error: value
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
try {
|
|
2097
|
+
const result = this.operation(
|
|
2098
|
+
reportResult,
|
|
2099
|
+
context.abortController.signal
|
|
2100
|
+
);
|
|
2101
|
+
if (isPromise(result)) {
|
|
2102
|
+
await result;
|
|
2103
|
+
}
|
|
2104
|
+
} catch (error) {
|
|
2105
|
+
this.handleReportResult(context, "error", {
|
|
2106
|
+
error
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
export {
|
|
2113
|
+
ATTEMPT_HANDLED,
|
|
2114
|
+
ATTEMPT_STARTED,
|
|
2115
|
+
OPERATION_ENDED,
|
|
2116
|
+
OPERATION_STARTED,
|
|
2117
|
+
RetryPolicy,
|
|
2118
|
+
RetryRunner,
|
|
2119
|
+
RetryUtilsErrPolicyConfigInvalidStrategy,
|
|
2120
|
+
RetryUtilsErrRunnerAlreadyCompleted,
|
|
2121
|
+
RetryUtilsErrRunnerAlreadyRunning,
|
|
2122
|
+
RetryUtilsErrRunnerAttemptsExhausted,
|
|
2123
|
+
RetryUtilsErrRunnerCancelPending,
|
|
2124
|
+
RetryUtilsErrRunnerForceTryRetryInProgress,
|
|
2125
|
+
RetryUtilsErrRunnerLastRetryFatallyFailed,
|
|
2126
|
+
RetryUtilsErrRunnerLockAcquisitionError,
|
|
2127
|
+
RetryUtilsErrRunnerNotPaused,
|
|
2128
|
+
RetryUtilsErrRunnerNotRunning,
|
|
2129
|
+
RetryUtilsErrRunnerRetryCanceled,
|
|
2130
|
+
RetryUtilsErrRunnerUnexpectedError,
|
|
2131
|
+
RetryUtilsErrRunnerUnknownState
|
|
2132
|
+
};
|
|
2133
|
+
//# sourceMappingURL=index.js.map
|