@cloudnux/local-cloud-provider 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev-console-plugin/index.d.ts +76 -6
- package/dist/dev-console-plugin/index.js +994 -2037
- package/dist/dev-console-plugin/index.js.map +1 -1
- package/dist/index.d.ts +19 -4
- package/dist/index.js +162 -197
- package/dist/index.js.map +1 -1
- package/dist/queue-plugin/index.d.ts +36 -13
- package/dist/queue-plugin/index.js +342 -1431
- package/dist/queue-plugin/index.js.map +1 -1
- package/dist/schedule-plugin/index.d.ts +49 -21
- package/dist/schedule-plugin/index.js +529 -1590
- package/dist/schedule-plugin/index.js.map +1 -1
- package/dist/websocket-plugin/index.d.ts +18 -0
- package/dist/websocket-plugin/index.js +18 -1029
- package/dist/websocket-plugin/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,1009 +1,3 @@
|
|
|
1
|
-
// src/logging/pretty-writer.ts
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import { EOL as EOL2 } from "os";
|
|
4
|
-
|
|
5
|
-
// ../../../node_modules/lodash-es/_freeGlobal.js
|
|
6
|
-
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
|
|
7
|
-
var freeGlobal_default = freeGlobal;
|
|
8
|
-
|
|
9
|
-
// ../../../node_modules/lodash-es/_root.js
|
|
10
|
-
var freeSelf = typeof self == "object" && self && self.Object === Object && self;
|
|
11
|
-
var root = freeGlobal_default || freeSelf || Function("return this")();
|
|
12
|
-
var root_default = root;
|
|
13
|
-
|
|
14
|
-
// ../../../node_modules/lodash-es/_Symbol.js
|
|
15
|
-
var Symbol = root_default.Symbol;
|
|
16
|
-
var Symbol_default = Symbol;
|
|
17
|
-
|
|
18
|
-
// ../../../node_modules/lodash-es/_getRawTag.js
|
|
19
|
-
var objectProto = Object.prototype;
|
|
20
|
-
var hasOwnProperty = objectProto.hasOwnProperty;
|
|
21
|
-
var nativeObjectToString = objectProto.toString;
|
|
22
|
-
var symToStringTag = Symbol_default ? Symbol_default.toStringTag : void 0;
|
|
23
|
-
function getRawTag(value) {
|
|
24
|
-
var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag];
|
|
25
|
-
try {
|
|
26
|
-
value[symToStringTag] = void 0;
|
|
27
|
-
var unmasked = true;
|
|
28
|
-
} catch (e) {
|
|
29
|
-
}
|
|
30
|
-
var result = nativeObjectToString.call(value);
|
|
31
|
-
if (unmasked) {
|
|
32
|
-
if (isOwn) {
|
|
33
|
-
value[symToStringTag] = tag;
|
|
34
|
-
} else {
|
|
35
|
-
delete value[symToStringTag];
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
var getRawTag_default = getRawTag;
|
|
41
|
-
|
|
42
|
-
// ../../../node_modules/lodash-es/_objectToString.js
|
|
43
|
-
var objectProto2 = Object.prototype;
|
|
44
|
-
var nativeObjectToString2 = objectProto2.toString;
|
|
45
|
-
function objectToString(value) {
|
|
46
|
-
return nativeObjectToString2.call(value);
|
|
47
|
-
}
|
|
48
|
-
var objectToString_default = objectToString;
|
|
49
|
-
|
|
50
|
-
// ../../../node_modules/lodash-es/_baseGetTag.js
|
|
51
|
-
var nullTag = "[object Null]";
|
|
52
|
-
var undefinedTag = "[object Undefined]";
|
|
53
|
-
var symToStringTag2 = Symbol_default ? Symbol_default.toStringTag : void 0;
|
|
54
|
-
function baseGetTag(value) {
|
|
55
|
-
if (value == null) {
|
|
56
|
-
return value === void 0 ? undefinedTag : nullTag;
|
|
57
|
-
}
|
|
58
|
-
return symToStringTag2 && symToStringTag2 in Object(value) ? getRawTag_default(value) : objectToString_default(value);
|
|
59
|
-
}
|
|
60
|
-
var baseGetTag_default = baseGetTag;
|
|
61
|
-
|
|
62
|
-
// ../../../node_modules/lodash-es/isObjectLike.js
|
|
63
|
-
function isObjectLike(value) {
|
|
64
|
-
return value != null && typeof value == "object";
|
|
65
|
-
}
|
|
66
|
-
var isObjectLike_default = isObjectLike;
|
|
67
|
-
|
|
68
|
-
// ../../../node_modules/lodash-es/isSymbol.js
|
|
69
|
-
var symbolTag = "[object Symbol]";
|
|
70
|
-
function isSymbol(value) {
|
|
71
|
-
return typeof value == "symbol" || isObjectLike_default(value) && baseGetTag_default(value) == symbolTag;
|
|
72
|
-
}
|
|
73
|
-
var isSymbol_default = isSymbol;
|
|
74
|
-
|
|
75
|
-
// ../../../node_modules/lodash-es/_arrayMap.js
|
|
76
|
-
function arrayMap(array2, iteratee) {
|
|
77
|
-
var index = -1, length = array2 == null ? 0 : array2.length, result = Array(length);
|
|
78
|
-
while (++index < length) {
|
|
79
|
-
result[index] = iteratee(array2[index], index, array2);
|
|
80
|
-
}
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
var arrayMap_default = arrayMap;
|
|
84
|
-
|
|
85
|
-
// ../../../node_modules/lodash-es/isArray.js
|
|
86
|
-
var isArray = Array.isArray;
|
|
87
|
-
var isArray_default = isArray;
|
|
88
|
-
|
|
89
|
-
// ../../../node_modules/lodash-es/_baseToString.js
|
|
90
|
-
var INFINITY = 1 / 0;
|
|
91
|
-
var symbolProto = Symbol_default ? Symbol_default.prototype : void 0;
|
|
92
|
-
var symbolToString = symbolProto ? symbolProto.toString : void 0;
|
|
93
|
-
function baseToString(value) {
|
|
94
|
-
if (typeof value == "string") {
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
if (isArray_default(value)) {
|
|
98
|
-
return arrayMap_default(value, baseToString) + "";
|
|
99
|
-
}
|
|
100
|
-
if (isSymbol_default(value)) {
|
|
101
|
-
return symbolToString ? symbolToString.call(value) : "";
|
|
102
|
-
}
|
|
103
|
-
var result = value + "";
|
|
104
|
-
return result == "0" && 1 / value == -INFINITY ? "-0" : result;
|
|
105
|
-
}
|
|
106
|
-
var baseToString_default = baseToString;
|
|
107
|
-
|
|
108
|
-
// ../../../node_modules/lodash-es/_trimmedEndIndex.js
|
|
109
|
-
var reWhitespace = /\s/;
|
|
110
|
-
function trimmedEndIndex(string) {
|
|
111
|
-
var index = string.length;
|
|
112
|
-
while (index-- && reWhitespace.test(string.charAt(index))) {
|
|
113
|
-
}
|
|
114
|
-
return index;
|
|
115
|
-
}
|
|
116
|
-
var trimmedEndIndex_default = trimmedEndIndex;
|
|
117
|
-
|
|
118
|
-
// ../../../node_modules/lodash-es/_baseTrim.js
|
|
119
|
-
var reTrimStart = /^\s+/;
|
|
120
|
-
function baseTrim(string) {
|
|
121
|
-
return string ? string.slice(0, trimmedEndIndex_default(string) + 1).replace(reTrimStart, "") : string;
|
|
122
|
-
}
|
|
123
|
-
var baseTrim_default = baseTrim;
|
|
124
|
-
|
|
125
|
-
// ../../../node_modules/lodash-es/isObject.js
|
|
126
|
-
function isObject(value) {
|
|
127
|
-
var type = typeof value;
|
|
128
|
-
return value != null && (type == "object" || type == "function");
|
|
129
|
-
}
|
|
130
|
-
var isObject_default = isObject;
|
|
131
|
-
|
|
132
|
-
// ../../../node_modules/lodash-es/isFunction.js
|
|
133
|
-
var asyncTag = "[object AsyncFunction]";
|
|
134
|
-
var funcTag = "[object Function]";
|
|
135
|
-
var genTag = "[object GeneratorFunction]";
|
|
136
|
-
var proxyTag = "[object Proxy]";
|
|
137
|
-
function isFunction(value) {
|
|
138
|
-
if (!isObject_default(value)) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
var tag = baseGetTag_default(value);
|
|
142
|
-
return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
|
|
143
|
-
}
|
|
144
|
-
var isFunction_default = isFunction;
|
|
145
|
-
|
|
146
|
-
// ../../../node_modules/lodash-es/_coreJsData.js
|
|
147
|
-
var coreJsData = root_default["__core-js_shared__"];
|
|
148
|
-
var coreJsData_default = coreJsData;
|
|
149
|
-
|
|
150
|
-
// ../../../node_modules/lodash-es/_isMasked.js
|
|
151
|
-
var maskSrcKey = function() {
|
|
152
|
-
var uid = /[^.]+$/.exec(coreJsData_default && coreJsData_default.keys && coreJsData_default.keys.IE_PROTO || "");
|
|
153
|
-
return uid ? "Symbol(src)_1." + uid : "";
|
|
154
|
-
}();
|
|
155
|
-
function isMasked(func) {
|
|
156
|
-
return !!maskSrcKey && maskSrcKey in func;
|
|
157
|
-
}
|
|
158
|
-
var isMasked_default = isMasked;
|
|
159
|
-
|
|
160
|
-
// ../../../node_modules/lodash-es/_toSource.js
|
|
161
|
-
var funcProto = Function.prototype;
|
|
162
|
-
var funcToString = funcProto.toString;
|
|
163
|
-
function toSource(func) {
|
|
164
|
-
if (func != null) {
|
|
165
|
-
try {
|
|
166
|
-
return funcToString.call(func);
|
|
167
|
-
} catch (e) {
|
|
168
|
-
}
|
|
169
|
-
try {
|
|
170
|
-
return func + "";
|
|
171
|
-
} catch (e) {
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return "";
|
|
175
|
-
}
|
|
176
|
-
var toSource_default = toSource;
|
|
177
|
-
|
|
178
|
-
// ../../../node_modules/lodash-es/_baseIsNative.js
|
|
179
|
-
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
|
|
180
|
-
var reIsHostCtor = /^\[object .+?Constructor\]$/;
|
|
181
|
-
var funcProto2 = Function.prototype;
|
|
182
|
-
var objectProto3 = Object.prototype;
|
|
183
|
-
var funcToString2 = funcProto2.toString;
|
|
184
|
-
var hasOwnProperty2 = objectProto3.hasOwnProperty;
|
|
185
|
-
var reIsNative = RegExp(
|
|
186
|
-
"^" + funcToString2.call(hasOwnProperty2).replace(reRegExpChar, "\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, "$1.*?") + "$"
|
|
187
|
-
);
|
|
188
|
-
function baseIsNative(value) {
|
|
189
|
-
if (!isObject_default(value) || isMasked_default(value)) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
var pattern = isFunction_default(value) ? reIsNative : reIsHostCtor;
|
|
193
|
-
return pattern.test(toSource_default(value));
|
|
194
|
-
}
|
|
195
|
-
var baseIsNative_default = baseIsNative;
|
|
196
|
-
|
|
197
|
-
// ../../../node_modules/lodash-es/_getValue.js
|
|
198
|
-
function getValue(object, key) {
|
|
199
|
-
return object == null ? void 0 : object[key];
|
|
200
|
-
}
|
|
201
|
-
var getValue_default = getValue;
|
|
202
|
-
|
|
203
|
-
// ../../../node_modules/lodash-es/_getNative.js
|
|
204
|
-
function getNative(object, key) {
|
|
205
|
-
var value = getValue_default(object, key);
|
|
206
|
-
return baseIsNative_default(value) ? value : void 0;
|
|
207
|
-
}
|
|
208
|
-
var getNative_default = getNative;
|
|
209
|
-
|
|
210
|
-
// ../../../node_modules/lodash-es/_baseFindIndex.js
|
|
211
|
-
function baseFindIndex(array2, predicate, fromIndex, fromRight) {
|
|
212
|
-
var length = array2.length, index = fromIndex + (fromRight ? 1 : -1);
|
|
213
|
-
while (fromRight ? index-- : ++index < length) {
|
|
214
|
-
if (predicate(array2[index], index, array2)) {
|
|
215
|
-
return index;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return -1;
|
|
219
|
-
}
|
|
220
|
-
var baseFindIndex_default = baseFindIndex;
|
|
221
|
-
|
|
222
|
-
// ../../../node_modules/lodash-es/_baseIsNaN.js
|
|
223
|
-
function baseIsNaN(value) {
|
|
224
|
-
return value !== value;
|
|
225
|
-
}
|
|
226
|
-
var baseIsNaN_default = baseIsNaN;
|
|
227
|
-
|
|
228
|
-
// ../../../node_modules/lodash-es/_strictIndexOf.js
|
|
229
|
-
function strictIndexOf(array2, value, fromIndex) {
|
|
230
|
-
var index = fromIndex - 1, length = array2.length;
|
|
231
|
-
while (++index < length) {
|
|
232
|
-
if (array2[index] === value) {
|
|
233
|
-
return index;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return -1;
|
|
237
|
-
}
|
|
238
|
-
var strictIndexOf_default = strictIndexOf;
|
|
239
|
-
|
|
240
|
-
// ../../../node_modules/lodash-es/_baseIndexOf.js
|
|
241
|
-
function baseIndexOf(array2, value, fromIndex) {
|
|
242
|
-
return value === value ? strictIndexOf_default(array2, value, fromIndex) : baseFindIndex_default(array2, baseIsNaN_default, fromIndex);
|
|
243
|
-
}
|
|
244
|
-
var baseIndexOf_default = baseIndexOf;
|
|
245
|
-
|
|
246
|
-
// ../../../node_modules/lodash-es/_isIndex.js
|
|
247
|
-
var MAX_SAFE_INTEGER = 9007199254740991;
|
|
248
|
-
var reIsUint = /^(?:0|[1-9]\d*)$/;
|
|
249
|
-
function isIndex(value, length) {
|
|
250
|
-
var type = typeof value;
|
|
251
|
-
length = length == null ? MAX_SAFE_INTEGER : length;
|
|
252
|
-
return !!length && (type == "number" || type != "symbol" && reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
|
|
253
|
-
}
|
|
254
|
-
var isIndex_default = isIndex;
|
|
255
|
-
|
|
256
|
-
// ../../../node_modules/lodash-es/eq.js
|
|
257
|
-
function eq(value, other) {
|
|
258
|
-
return value === other || value !== value && other !== other;
|
|
259
|
-
}
|
|
260
|
-
var eq_default = eq;
|
|
261
|
-
|
|
262
|
-
// ../../../node_modules/lodash-es/isLength.js
|
|
263
|
-
var MAX_SAFE_INTEGER2 = 9007199254740991;
|
|
264
|
-
function isLength(value) {
|
|
265
|
-
return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER2;
|
|
266
|
-
}
|
|
267
|
-
var isLength_default = isLength;
|
|
268
|
-
|
|
269
|
-
// ../../../node_modules/lodash-es/_baseIsArguments.js
|
|
270
|
-
var argsTag = "[object Arguments]";
|
|
271
|
-
function baseIsArguments(value) {
|
|
272
|
-
return isObjectLike_default(value) && baseGetTag_default(value) == argsTag;
|
|
273
|
-
}
|
|
274
|
-
var baseIsArguments_default = baseIsArguments;
|
|
275
|
-
|
|
276
|
-
// ../../../node_modules/lodash-es/isArguments.js
|
|
277
|
-
var objectProto4 = Object.prototype;
|
|
278
|
-
var hasOwnProperty3 = objectProto4.hasOwnProperty;
|
|
279
|
-
var propertyIsEnumerable = objectProto4.propertyIsEnumerable;
|
|
280
|
-
var isArguments = baseIsArguments_default(/* @__PURE__ */ function() {
|
|
281
|
-
return arguments;
|
|
282
|
-
}()) ? baseIsArguments_default : function(value) {
|
|
283
|
-
return isObjectLike_default(value) && hasOwnProperty3.call(value, "callee") && !propertyIsEnumerable.call(value, "callee");
|
|
284
|
-
};
|
|
285
|
-
var isArguments_default = isArguments;
|
|
286
|
-
|
|
287
|
-
// ../../../node_modules/lodash-es/_isKey.js
|
|
288
|
-
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/;
|
|
289
|
-
var reIsPlainProp = /^\w*$/;
|
|
290
|
-
function isKey(value, object) {
|
|
291
|
-
if (isArray_default(value)) {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
var type = typeof value;
|
|
295
|
-
if (type == "number" || type == "symbol" || type == "boolean" || value == null || isSymbol_default(value)) {
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);
|
|
299
|
-
}
|
|
300
|
-
var isKey_default = isKey;
|
|
301
|
-
|
|
302
|
-
// ../../../node_modules/lodash-es/_nativeCreate.js
|
|
303
|
-
var nativeCreate = getNative_default(Object, "create");
|
|
304
|
-
var nativeCreate_default = nativeCreate;
|
|
305
|
-
|
|
306
|
-
// ../../../node_modules/lodash-es/_hashClear.js
|
|
307
|
-
function hashClear() {
|
|
308
|
-
this.__data__ = nativeCreate_default ? nativeCreate_default(null) : {};
|
|
309
|
-
this.size = 0;
|
|
310
|
-
}
|
|
311
|
-
var hashClear_default = hashClear;
|
|
312
|
-
|
|
313
|
-
// ../../../node_modules/lodash-es/_hashDelete.js
|
|
314
|
-
function hashDelete(key) {
|
|
315
|
-
var result = this.has(key) && delete this.__data__[key];
|
|
316
|
-
this.size -= result ? 1 : 0;
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
var hashDelete_default = hashDelete;
|
|
320
|
-
|
|
321
|
-
// ../../../node_modules/lodash-es/_hashGet.js
|
|
322
|
-
var HASH_UNDEFINED = "__lodash_hash_undefined__";
|
|
323
|
-
var objectProto5 = Object.prototype;
|
|
324
|
-
var hasOwnProperty4 = objectProto5.hasOwnProperty;
|
|
325
|
-
function hashGet(key) {
|
|
326
|
-
var data = this.__data__;
|
|
327
|
-
if (nativeCreate_default) {
|
|
328
|
-
var result = data[key];
|
|
329
|
-
return result === HASH_UNDEFINED ? void 0 : result;
|
|
330
|
-
}
|
|
331
|
-
return hasOwnProperty4.call(data, key) ? data[key] : void 0;
|
|
332
|
-
}
|
|
333
|
-
var hashGet_default = hashGet;
|
|
334
|
-
|
|
335
|
-
// ../../../node_modules/lodash-es/_hashHas.js
|
|
336
|
-
var objectProto6 = Object.prototype;
|
|
337
|
-
var hasOwnProperty5 = objectProto6.hasOwnProperty;
|
|
338
|
-
function hashHas(key) {
|
|
339
|
-
var data = this.__data__;
|
|
340
|
-
return nativeCreate_default ? data[key] !== void 0 : hasOwnProperty5.call(data, key);
|
|
341
|
-
}
|
|
342
|
-
var hashHas_default = hashHas;
|
|
343
|
-
|
|
344
|
-
// ../../../node_modules/lodash-es/_hashSet.js
|
|
345
|
-
var HASH_UNDEFINED2 = "__lodash_hash_undefined__";
|
|
346
|
-
function hashSet(key, value) {
|
|
347
|
-
var data = this.__data__;
|
|
348
|
-
this.size += this.has(key) ? 0 : 1;
|
|
349
|
-
data[key] = nativeCreate_default && value === void 0 ? HASH_UNDEFINED2 : value;
|
|
350
|
-
return this;
|
|
351
|
-
}
|
|
352
|
-
var hashSet_default = hashSet;
|
|
353
|
-
|
|
354
|
-
// ../../../node_modules/lodash-es/_Hash.js
|
|
355
|
-
function Hash(entries) {
|
|
356
|
-
var index = -1, length = entries == null ? 0 : entries.length;
|
|
357
|
-
this.clear();
|
|
358
|
-
while (++index < length) {
|
|
359
|
-
var entry = entries[index];
|
|
360
|
-
this.set(entry[0], entry[1]);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
Hash.prototype.clear = hashClear_default;
|
|
364
|
-
Hash.prototype["delete"] = hashDelete_default;
|
|
365
|
-
Hash.prototype.get = hashGet_default;
|
|
366
|
-
Hash.prototype.has = hashHas_default;
|
|
367
|
-
Hash.prototype.set = hashSet_default;
|
|
368
|
-
var Hash_default = Hash;
|
|
369
|
-
|
|
370
|
-
// ../../../node_modules/lodash-es/_listCacheClear.js
|
|
371
|
-
function listCacheClear() {
|
|
372
|
-
this.__data__ = [];
|
|
373
|
-
this.size = 0;
|
|
374
|
-
}
|
|
375
|
-
var listCacheClear_default = listCacheClear;
|
|
376
|
-
|
|
377
|
-
// ../../../node_modules/lodash-es/_assocIndexOf.js
|
|
378
|
-
function assocIndexOf(array2, key) {
|
|
379
|
-
var length = array2.length;
|
|
380
|
-
while (length--) {
|
|
381
|
-
if (eq_default(array2[length][0], key)) {
|
|
382
|
-
return length;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return -1;
|
|
386
|
-
}
|
|
387
|
-
var assocIndexOf_default = assocIndexOf;
|
|
388
|
-
|
|
389
|
-
// ../../../node_modules/lodash-es/_listCacheDelete.js
|
|
390
|
-
var arrayProto = Array.prototype;
|
|
391
|
-
var splice = arrayProto.splice;
|
|
392
|
-
function listCacheDelete(key) {
|
|
393
|
-
var data = this.__data__, index = assocIndexOf_default(data, key);
|
|
394
|
-
if (index < 0) {
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
var lastIndex = data.length - 1;
|
|
398
|
-
if (index == lastIndex) {
|
|
399
|
-
data.pop();
|
|
400
|
-
} else {
|
|
401
|
-
splice.call(data, index, 1);
|
|
402
|
-
}
|
|
403
|
-
--this.size;
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
var listCacheDelete_default = listCacheDelete;
|
|
407
|
-
|
|
408
|
-
// ../../../node_modules/lodash-es/_listCacheGet.js
|
|
409
|
-
function listCacheGet(key) {
|
|
410
|
-
var data = this.__data__, index = assocIndexOf_default(data, key);
|
|
411
|
-
return index < 0 ? void 0 : data[index][1];
|
|
412
|
-
}
|
|
413
|
-
var listCacheGet_default = listCacheGet;
|
|
414
|
-
|
|
415
|
-
// ../../../node_modules/lodash-es/_listCacheHas.js
|
|
416
|
-
function listCacheHas(key) {
|
|
417
|
-
return assocIndexOf_default(this.__data__, key) > -1;
|
|
418
|
-
}
|
|
419
|
-
var listCacheHas_default = listCacheHas;
|
|
420
|
-
|
|
421
|
-
// ../../../node_modules/lodash-es/_listCacheSet.js
|
|
422
|
-
function listCacheSet(key, value) {
|
|
423
|
-
var data = this.__data__, index = assocIndexOf_default(data, key);
|
|
424
|
-
if (index < 0) {
|
|
425
|
-
++this.size;
|
|
426
|
-
data.push([key, value]);
|
|
427
|
-
} else {
|
|
428
|
-
data[index][1] = value;
|
|
429
|
-
}
|
|
430
|
-
return this;
|
|
431
|
-
}
|
|
432
|
-
var listCacheSet_default = listCacheSet;
|
|
433
|
-
|
|
434
|
-
// ../../../node_modules/lodash-es/_ListCache.js
|
|
435
|
-
function ListCache(entries) {
|
|
436
|
-
var index = -1, length = entries == null ? 0 : entries.length;
|
|
437
|
-
this.clear();
|
|
438
|
-
while (++index < length) {
|
|
439
|
-
var entry = entries[index];
|
|
440
|
-
this.set(entry[0], entry[1]);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
ListCache.prototype.clear = listCacheClear_default;
|
|
444
|
-
ListCache.prototype["delete"] = listCacheDelete_default;
|
|
445
|
-
ListCache.prototype.get = listCacheGet_default;
|
|
446
|
-
ListCache.prototype.has = listCacheHas_default;
|
|
447
|
-
ListCache.prototype.set = listCacheSet_default;
|
|
448
|
-
var ListCache_default = ListCache;
|
|
449
|
-
|
|
450
|
-
// ../../../node_modules/lodash-es/_Map.js
|
|
451
|
-
var Map2 = getNative_default(root_default, "Map");
|
|
452
|
-
var Map_default = Map2;
|
|
453
|
-
|
|
454
|
-
// ../../../node_modules/lodash-es/_mapCacheClear.js
|
|
455
|
-
function mapCacheClear() {
|
|
456
|
-
this.size = 0;
|
|
457
|
-
this.__data__ = {
|
|
458
|
-
"hash": new Hash_default(),
|
|
459
|
-
"map": new (Map_default || ListCache_default)(),
|
|
460
|
-
"string": new Hash_default()
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
var mapCacheClear_default = mapCacheClear;
|
|
464
|
-
|
|
465
|
-
// ../../../node_modules/lodash-es/_isKeyable.js
|
|
466
|
-
function isKeyable(value) {
|
|
467
|
-
var type = typeof value;
|
|
468
|
-
return type == "string" || type == "number" || type == "symbol" || type == "boolean" ? value !== "__proto__" : value === null;
|
|
469
|
-
}
|
|
470
|
-
var isKeyable_default = isKeyable;
|
|
471
|
-
|
|
472
|
-
// ../../../node_modules/lodash-es/_getMapData.js
|
|
473
|
-
function getMapData(map, key) {
|
|
474
|
-
var data = map.__data__;
|
|
475
|
-
return isKeyable_default(key) ? data[typeof key == "string" ? "string" : "hash"] : data.map;
|
|
476
|
-
}
|
|
477
|
-
var getMapData_default = getMapData;
|
|
478
|
-
|
|
479
|
-
// ../../../node_modules/lodash-es/_mapCacheDelete.js
|
|
480
|
-
function mapCacheDelete(key) {
|
|
481
|
-
var result = getMapData_default(this, key)["delete"](key);
|
|
482
|
-
this.size -= result ? 1 : 0;
|
|
483
|
-
return result;
|
|
484
|
-
}
|
|
485
|
-
var mapCacheDelete_default = mapCacheDelete;
|
|
486
|
-
|
|
487
|
-
// ../../../node_modules/lodash-es/_mapCacheGet.js
|
|
488
|
-
function mapCacheGet(key) {
|
|
489
|
-
return getMapData_default(this, key).get(key);
|
|
490
|
-
}
|
|
491
|
-
var mapCacheGet_default = mapCacheGet;
|
|
492
|
-
|
|
493
|
-
// ../../../node_modules/lodash-es/_mapCacheHas.js
|
|
494
|
-
function mapCacheHas(key) {
|
|
495
|
-
return getMapData_default(this, key).has(key);
|
|
496
|
-
}
|
|
497
|
-
var mapCacheHas_default = mapCacheHas;
|
|
498
|
-
|
|
499
|
-
// ../../../node_modules/lodash-es/_mapCacheSet.js
|
|
500
|
-
function mapCacheSet(key, value) {
|
|
501
|
-
var data = getMapData_default(this, key), size = data.size;
|
|
502
|
-
data.set(key, value);
|
|
503
|
-
this.size += data.size == size ? 0 : 1;
|
|
504
|
-
return this;
|
|
505
|
-
}
|
|
506
|
-
var mapCacheSet_default = mapCacheSet;
|
|
507
|
-
|
|
508
|
-
// ../../../node_modules/lodash-es/_MapCache.js
|
|
509
|
-
function MapCache(entries) {
|
|
510
|
-
var index = -1, length = entries == null ? 0 : entries.length;
|
|
511
|
-
this.clear();
|
|
512
|
-
while (++index < length) {
|
|
513
|
-
var entry = entries[index];
|
|
514
|
-
this.set(entry[0], entry[1]);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
MapCache.prototype.clear = mapCacheClear_default;
|
|
518
|
-
MapCache.prototype["delete"] = mapCacheDelete_default;
|
|
519
|
-
MapCache.prototype.get = mapCacheGet_default;
|
|
520
|
-
MapCache.prototype.has = mapCacheHas_default;
|
|
521
|
-
MapCache.prototype.set = mapCacheSet_default;
|
|
522
|
-
var MapCache_default = MapCache;
|
|
523
|
-
|
|
524
|
-
// ../../../node_modules/lodash-es/memoize.js
|
|
525
|
-
var FUNC_ERROR_TEXT = "Expected a function";
|
|
526
|
-
function memoize(func, resolver) {
|
|
527
|
-
if (typeof func != "function" || resolver != null && typeof resolver != "function") {
|
|
528
|
-
throw new TypeError(FUNC_ERROR_TEXT);
|
|
529
|
-
}
|
|
530
|
-
var memoized = function() {
|
|
531
|
-
var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
|
|
532
|
-
if (cache.has(key)) {
|
|
533
|
-
return cache.get(key);
|
|
534
|
-
}
|
|
535
|
-
var result = func.apply(this, args);
|
|
536
|
-
memoized.cache = cache.set(key, result) || cache;
|
|
537
|
-
return result;
|
|
538
|
-
};
|
|
539
|
-
memoized.cache = new (memoize.Cache || MapCache_default)();
|
|
540
|
-
return memoized;
|
|
541
|
-
}
|
|
542
|
-
memoize.Cache = MapCache_default;
|
|
543
|
-
var memoize_default = memoize;
|
|
544
|
-
|
|
545
|
-
// ../../../node_modules/lodash-es/_memoizeCapped.js
|
|
546
|
-
var MAX_MEMOIZE_SIZE = 500;
|
|
547
|
-
function memoizeCapped(func) {
|
|
548
|
-
var result = memoize_default(func, function(key) {
|
|
549
|
-
if (cache.size === MAX_MEMOIZE_SIZE) {
|
|
550
|
-
cache.clear();
|
|
551
|
-
}
|
|
552
|
-
return key;
|
|
553
|
-
});
|
|
554
|
-
var cache = result.cache;
|
|
555
|
-
return result;
|
|
556
|
-
}
|
|
557
|
-
var memoizeCapped_default = memoizeCapped;
|
|
558
|
-
|
|
559
|
-
// ../../../node_modules/lodash-es/_stringToPath.js
|
|
560
|
-
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
|
|
561
|
-
var reEscapeChar = /\\(\\)?/g;
|
|
562
|
-
var stringToPath = memoizeCapped_default(function(string) {
|
|
563
|
-
var result = [];
|
|
564
|
-
if (string.charCodeAt(0) === 46) {
|
|
565
|
-
result.push("");
|
|
566
|
-
}
|
|
567
|
-
string.replace(rePropName, function(match, number, quote, subString) {
|
|
568
|
-
result.push(quote ? subString.replace(reEscapeChar, "$1") : number || match);
|
|
569
|
-
});
|
|
570
|
-
return result;
|
|
571
|
-
});
|
|
572
|
-
var stringToPath_default = stringToPath;
|
|
573
|
-
|
|
574
|
-
// ../../../node_modules/lodash-es/toString.js
|
|
575
|
-
function toString(value) {
|
|
576
|
-
return value == null ? "" : baseToString_default(value);
|
|
577
|
-
}
|
|
578
|
-
var toString_default = toString;
|
|
579
|
-
|
|
580
|
-
// ../../../node_modules/lodash-es/_castPath.js
|
|
581
|
-
function castPath(value, object) {
|
|
582
|
-
if (isArray_default(value)) {
|
|
583
|
-
return value;
|
|
584
|
-
}
|
|
585
|
-
return isKey_default(value, object) ? [value] : stringToPath_default(toString_default(value));
|
|
586
|
-
}
|
|
587
|
-
var castPath_default = castPath;
|
|
588
|
-
|
|
589
|
-
// ../../../node_modules/lodash-es/_toKey.js
|
|
590
|
-
var INFINITY2 = 1 / 0;
|
|
591
|
-
function toKey(value) {
|
|
592
|
-
if (typeof value == "string" || isSymbol_default(value)) {
|
|
593
|
-
return value;
|
|
594
|
-
}
|
|
595
|
-
var result = value + "";
|
|
596
|
-
return result == "0" && 1 / value == -INFINITY2 ? "-0" : result;
|
|
597
|
-
}
|
|
598
|
-
var toKey_default = toKey;
|
|
599
|
-
|
|
600
|
-
// ../../../node_modules/lodash-es/_baseSlice.js
|
|
601
|
-
function baseSlice(array2, start, end) {
|
|
602
|
-
var index = -1, length = array2.length;
|
|
603
|
-
if (start < 0) {
|
|
604
|
-
start = -start > length ? 0 : length + start;
|
|
605
|
-
}
|
|
606
|
-
end = end > length ? length : end;
|
|
607
|
-
if (end < 0) {
|
|
608
|
-
end += length;
|
|
609
|
-
}
|
|
610
|
-
length = start > end ? 0 : end - start >>> 0;
|
|
611
|
-
start >>>= 0;
|
|
612
|
-
var result = Array(length);
|
|
613
|
-
while (++index < length) {
|
|
614
|
-
result[index] = array2[index + start];
|
|
615
|
-
}
|
|
616
|
-
return result;
|
|
617
|
-
}
|
|
618
|
-
var baseSlice_default = baseSlice;
|
|
619
|
-
|
|
620
|
-
// ../../../node_modules/lodash-es/_castSlice.js
|
|
621
|
-
function castSlice(array2, start, end) {
|
|
622
|
-
var length = array2.length;
|
|
623
|
-
end = end === void 0 ? length : end;
|
|
624
|
-
return !start && end >= length ? array2 : baseSlice_default(array2, start, end);
|
|
625
|
-
}
|
|
626
|
-
var castSlice_default = castSlice;
|
|
627
|
-
|
|
628
|
-
// ../../../node_modules/lodash-es/_hasUnicode.js
|
|
629
|
-
var rsAstralRange = "\\ud800-\\udfff";
|
|
630
|
-
var rsComboMarksRange = "\\u0300-\\u036f";
|
|
631
|
-
var reComboHalfMarksRange = "\\ufe20-\\ufe2f";
|
|
632
|
-
var rsComboSymbolsRange = "\\u20d0-\\u20ff";
|
|
633
|
-
var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange;
|
|
634
|
-
var rsVarRange = "\\ufe0e\\ufe0f";
|
|
635
|
-
var rsZWJ = "\\u200d";
|
|
636
|
-
var reHasUnicode = RegExp("[" + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + "]");
|
|
637
|
-
function hasUnicode(string) {
|
|
638
|
-
return reHasUnicode.test(string);
|
|
639
|
-
}
|
|
640
|
-
var hasUnicode_default = hasUnicode;
|
|
641
|
-
|
|
642
|
-
// ../../../node_modules/lodash-es/_asciiToArray.js
|
|
643
|
-
function asciiToArray(string) {
|
|
644
|
-
return string.split("");
|
|
645
|
-
}
|
|
646
|
-
var asciiToArray_default = asciiToArray;
|
|
647
|
-
|
|
648
|
-
// ../../../node_modules/lodash-es/_unicodeToArray.js
|
|
649
|
-
var rsAstralRange2 = "\\ud800-\\udfff";
|
|
650
|
-
var rsComboMarksRange2 = "\\u0300-\\u036f";
|
|
651
|
-
var reComboHalfMarksRange2 = "\\ufe20-\\ufe2f";
|
|
652
|
-
var rsComboSymbolsRange2 = "\\u20d0-\\u20ff";
|
|
653
|
-
var rsComboRange2 = rsComboMarksRange2 + reComboHalfMarksRange2 + rsComboSymbolsRange2;
|
|
654
|
-
var rsVarRange2 = "\\ufe0e\\ufe0f";
|
|
655
|
-
var rsAstral = "[" + rsAstralRange2 + "]";
|
|
656
|
-
var rsCombo = "[" + rsComboRange2 + "]";
|
|
657
|
-
var rsFitz = "\\ud83c[\\udffb-\\udfff]";
|
|
658
|
-
var rsModifier = "(?:" + rsCombo + "|" + rsFitz + ")";
|
|
659
|
-
var rsNonAstral = "[^" + rsAstralRange2 + "]";
|
|
660
|
-
var rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}";
|
|
661
|
-
var rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]";
|
|
662
|
-
var rsZWJ2 = "\\u200d";
|
|
663
|
-
var reOptMod = rsModifier + "?";
|
|
664
|
-
var rsOptVar = "[" + rsVarRange2 + "]?";
|
|
665
|
-
var rsOptJoin = "(?:" + rsZWJ2 + "(?:" + [rsNonAstral, rsRegional, rsSurrPair].join("|") + ")" + rsOptVar + reOptMod + ")*";
|
|
666
|
-
var rsSeq = rsOptVar + reOptMod + rsOptJoin;
|
|
667
|
-
var rsSymbol = "(?:" + [rsNonAstral + rsCombo + "?", rsCombo, rsRegional, rsSurrPair, rsAstral].join("|") + ")";
|
|
668
|
-
var reUnicode = RegExp(rsFitz + "(?=" + rsFitz + ")|" + rsSymbol + rsSeq, "g");
|
|
669
|
-
function unicodeToArray(string) {
|
|
670
|
-
return string.match(reUnicode) || [];
|
|
671
|
-
}
|
|
672
|
-
var unicodeToArray_default = unicodeToArray;
|
|
673
|
-
|
|
674
|
-
// ../../../node_modules/lodash-es/_stringToArray.js
|
|
675
|
-
function stringToArray(string) {
|
|
676
|
-
return hasUnicode_default(string) ? unicodeToArray_default(string) : asciiToArray_default(string);
|
|
677
|
-
}
|
|
678
|
-
var stringToArray_default = stringToArray;
|
|
679
|
-
|
|
680
|
-
// ../../../node_modules/lodash-es/_hasPath.js
|
|
681
|
-
function hasPath(object, path4, hasFunc) {
|
|
682
|
-
path4 = castPath_default(path4, object);
|
|
683
|
-
var index = -1, length = path4.length, result = false;
|
|
684
|
-
while (++index < length) {
|
|
685
|
-
var key = toKey_default(path4[index]);
|
|
686
|
-
if (!(result = object != null && hasFunc(object, key))) {
|
|
687
|
-
break;
|
|
688
|
-
}
|
|
689
|
-
object = object[key];
|
|
690
|
-
}
|
|
691
|
-
if (result || ++index != length) {
|
|
692
|
-
return result;
|
|
693
|
-
}
|
|
694
|
-
length = object == null ? 0 : object.length;
|
|
695
|
-
return !!length && isLength_default(length) && isIndex_default(key, length) && (isArray_default(object) || isArguments_default(object));
|
|
696
|
-
}
|
|
697
|
-
var hasPath_default = hasPath;
|
|
698
|
-
|
|
699
|
-
// ../../../node_modules/lodash-es/_baseHas.js
|
|
700
|
-
var objectProto7 = Object.prototype;
|
|
701
|
-
var hasOwnProperty6 = objectProto7.hasOwnProperty;
|
|
702
|
-
function baseHas(object, key) {
|
|
703
|
-
return object != null && hasOwnProperty6.call(object, key);
|
|
704
|
-
}
|
|
705
|
-
var baseHas_default = baseHas;
|
|
706
|
-
|
|
707
|
-
// ../../../node_modules/lodash-es/has.js
|
|
708
|
-
function has(object, path4) {
|
|
709
|
-
return object != null && hasPath_default(object, path4, baseHas_default);
|
|
710
|
-
}
|
|
711
|
-
var has_default = has;
|
|
712
|
-
|
|
713
|
-
// ../../../node_modules/lodash-es/_charsEndIndex.js
|
|
714
|
-
function charsEndIndex(strSymbols, chrSymbols) {
|
|
715
|
-
var index = strSymbols.length;
|
|
716
|
-
while (index-- && baseIndexOf_default(chrSymbols, strSymbols[index], 0) > -1) {
|
|
717
|
-
}
|
|
718
|
-
return index;
|
|
719
|
-
}
|
|
720
|
-
var charsEndIndex_default = charsEndIndex;
|
|
721
|
-
|
|
722
|
-
// ../../../node_modules/lodash-es/_charsStartIndex.js
|
|
723
|
-
function charsStartIndex(strSymbols, chrSymbols) {
|
|
724
|
-
var index = -1, length = strSymbols.length;
|
|
725
|
-
while (++index < length && baseIndexOf_default(chrSymbols, strSymbols[index], 0) > -1) {
|
|
726
|
-
}
|
|
727
|
-
return index;
|
|
728
|
-
}
|
|
729
|
-
var charsStartIndex_default = charsStartIndex;
|
|
730
|
-
|
|
731
|
-
// ../../../node_modules/lodash-es/trim.js
|
|
732
|
-
function trim(string, chars, guard) {
|
|
733
|
-
string = toString_default(string);
|
|
734
|
-
if (string && (guard || chars === void 0)) {
|
|
735
|
-
return baseTrim_default(string);
|
|
736
|
-
}
|
|
737
|
-
if (!string || !(chars = baseToString_default(chars))) {
|
|
738
|
-
return string;
|
|
739
|
-
}
|
|
740
|
-
var strSymbols = stringToArray_default(string), chrSymbols = stringToArray_default(chars), start = charsStartIndex_default(strSymbols, chrSymbols), end = charsEndIndex_default(strSymbols, chrSymbols) + 1;
|
|
741
|
-
return castSlice_default(strSymbols, start, end).join("");
|
|
742
|
-
}
|
|
743
|
-
var trim_default = trim;
|
|
744
|
-
|
|
745
|
-
// ../../utils/src/config/env.ts
|
|
746
|
-
function _env(key, defaultValue) {
|
|
747
|
-
return has_default(process.env, key) ? process.env[key] ?? "" : defaultValue ?? "";
|
|
748
|
-
}
|
|
749
|
-
function int(key, defaultValue) {
|
|
750
|
-
if (!has_default(process.env, key)) {
|
|
751
|
-
return defaultValue ?? 0;
|
|
752
|
-
}
|
|
753
|
-
const value = process.env[key] ?? "0";
|
|
754
|
-
return parseInt(value, 10);
|
|
755
|
-
}
|
|
756
|
-
function float(key, defaultValue) {
|
|
757
|
-
if (!has_default(process.env, key)) {
|
|
758
|
-
return defaultValue ?? 0;
|
|
759
|
-
}
|
|
760
|
-
const value = process.env[key] ?? "0";
|
|
761
|
-
return parseFloat(value);
|
|
762
|
-
}
|
|
763
|
-
function bool(key, defaultValue) {
|
|
764
|
-
if (!has_default(process.env, key)) {
|
|
765
|
-
return defaultValue ?? false;
|
|
766
|
-
}
|
|
767
|
-
const value = process.env[key];
|
|
768
|
-
return value === "true";
|
|
769
|
-
}
|
|
770
|
-
function json(key, defaultValue) {
|
|
771
|
-
if (!has_default(process.env, key)) {
|
|
772
|
-
return defaultValue ?? {};
|
|
773
|
-
}
|
|
774
|
-
const value = process.env[key] ?? "{}";
|
|
775
|
-
try {
|
|
776
|
-
return JSON.parse(value);
|
|
777
|
-
} catch (error) {
|
|
778
|
-
throw new Error(`Invalid json environment variable ${key}: ${error.message}`);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
function array(key, defaultValue) {
|
|
782
|
-
if (!has_default(process.env, key)) {
|
|
783
|
-
return defaultValue ?? [];
|
|
784
|
-
}
|
|
785
|
-
let value = process.env[key] ?? "";
|
|
786
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
787
|
-
value = value.substring(1, value.length - 1);
|
|
788
|
-
}
|
|
789
|
-
return value.split(",").map((v) => {
|
|
790
|
-
return trim_default(trim_default(v, " "), '"');
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
function date(key, defaultValue) {
|
|
794
|
-
if (!has_default(process.env, key)) {
|
|
795
|
-
return defaultValue ?? /* @__PURE__ */ new Date();
|
|
796
|
-
}
|
|
797
|
-
const value = process.env[key] ?? Date();
|
|
798
|
-
return new Date(value);
|
|
799
|
-
}
|
|
800
|
-
var utils = {
|
|
801
|
-
int,
|
|
802
|
-
float,
|
|
803
|
-
bool,
|
|
804
|
-
json,
|
|
805
|
-
array,
|
|
806
|
-
date,
|
|
807
|
-
/**
|
|
808
|
-
* Gets a value from env that matches oneOf provided values
|
|
809
|
-
* @param {string} key
|
|
810
|
-
* @param {string[]} expectedValues
|
|
811
|
-
* @param {string|undefined} defaultValue
|
|
812
|
-
* @returns {string|undefined}
|
|
813
|
-
*/
|
|
814
|
-
oneOf(key, expectedValues, defaultValue) {
|
|
815
|
-
if (!expectedValues) {
|
|
816
|
-
throw new Error(`env.oneOf requires expectedValues`);
|
|
817
|
-
}
|
|
818
|
-
if (defaultValue && !expectedValues.includes(defaultValue)) {
|
|
819
|
-
throw new Error(`env.oneOf requires defaultValue to be included in expectedValues`);
|
|
820
|
-
}
|
|
821
|
-
const rawValue = env(key, defaultValue);
|
|
822
|
-
return expectedValues.includes(rawValue) ? rawValue : defaultValue ?? "";
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
var env = Object.assign(_env, utils);
|
|
826
|
-
|
|
827
|
-
// ../../utils/src/logging/types.ts
|
|
828
|
-
var logLevels = {
|
|
829
|
-
fatal: -1,
|
|
830
|
-
error: 0,
|
|
831
|
-
warn: 1,
|
|
832
|
-
info: 2,
|
|
833
|
-
debug: 3,
|
|
834
|
-
trace: 4
|
|
835
|
-
};
|
|
836
|
-
|
|
837
|
-
// ../../utils/src/logging/error-to-string.ts
|
|
838
|
-
var safeJsonStringify = (value) => {
|
|
839
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
840
|
-
return JSON.stringify(value, (key, currentValue) => {
|
|
841
|
-
if (key === "constructor") return void 0;
|
|
842
|
-
if (typeof currentValue === "object" && currentValue !== null) {
|
|
843
|
-
if (seen.has(currentValue)) return "[Circular]";
|
|
844
|
-
seen.add(currentValue);
|
|
845
|
-
}
|
|
846
|
-
return currentValue;
|
|
847
|
-
});
|
|
848
|
-
};
|
|
849
|
-
var hasOwnConstructorProp = (value) => {
|
|
850
|
-
if (typeof value !== "object" || value === null) return false;
|
|
851
|
-
return Object.prototype.hasOwnProperty.call(value, "constructor");
|
|
852
|
-
};
|
|
853
|
-
var errorToString = (error) => {
|
|
854
|
-
if (error === null) return "Null error";
|
|
855
|
-
if (error === void 0) return "Undefined error";
|
|
856
|
-
if (error instanceof Error) {
|
|
857
|
-
return [
|
|
858
|
-
`Name: ${error.name}`,
|
|
859
|
-
`Message: ${error.message}`,
|
|
860
|
-
`Stack: ${error.stack || "No stack trace available"}`,
|
|
861
|
-
// Handle additional properties that might exist on custom errors
|
|
862
|
-
...Object.entries(error).filter(
|
|
863
|
-
([key, value]) => !["name", "message", "stack"].includes(key) && !hasOwnConstructorProp(value)
|
|
864
|
-
).map(([key, value]) => `${key}: ${safeJsonStringify(value)}`)
|
|
865
|
-
].join("\n");
|
|
866
|
-
}
|
|
867
|
-
if (typeof error === "string") return error;
|
|
868
|
-
if (typeof error === "object") {
|
|
869
|
-
try {
|
|
870
|
-
return JSON.stringify(
|
|
871
|
-
error,
|
|
872
|
-
(key, value) => {
|
|
873
|
-
if (key === "constructor") return void 0;
|
|
874
|
-
return value;
|
|
875
|
-
},
|
|
876
|
-
2
|
|
877
|
-
);
|
|
878
|
-
} catch {
|
|
879
|
-
return `[Object that cannot be stringified: ${Object.prototype.toString.call(error)}]`;
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return String(error);
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
// ../../utils/src/logging/writer.ts
|
|
886
|
-
import { EOL } from "os";
|
|
887
|
-
var jsonWriter = (entry) => {
|
|
888
|
-
process.stdout.write(JSON.stringify(entry) + EOL);
|
|
889
|
-
};
|
|
890
|
-
var _writer = jsonWriter;
|
|
891
|
-
function setWriter(writer) {
|
|
892
|
-
_writer = writer;
|
|
893
|
-
}
|
|
894
|
-
function getWriter() {
|
|
895
|
-
return _writer;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// ../../utils/src/logging/index.ts
|
|
899
|
-
var levelValues = {
|
|
900
|
-
fatal: 70,
|
|
901
|
-
error: 60,
|
|
902
|
-
warn: 50,
|
|
903
|
-
info: 40,
|
|
904
|
-
debug: 30,
|
|
905
|
-
trace: 20
|
|
906
|
-
};
|
|
907
|
-
var currentLogLevel = 2;
|
|
908
|
-
var _module = "default";
|
|
909
|
-
var _requestId = "";
|
|
910
|
-
function writeLine(level, mergeObject, msg, bindings) {
|
|
911
|
-
getWriter()({
|
|
912
|
-
level: levelValues[level],
|
|
913
|
-
levelName: level,
|
|
914
|
-
time: Date.now(),
|
|
915
|
-
module: bindings.module || _module,
|
|
916
|
-
reqId: bindings.reqId || _requestId,
|
|
917
|
-
msg: level === "error" || level === "fatal" ? errorToString(msg) : msg,
|
|
918
|
-
...mergeObject ? { meta: mergeObject } : {}
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
function createLogger(bindings = {}) {
|
|
922
|
-
const currentLogLevelName = env("LOG_LEVEL")?.toLowerCase() ?? "info";
|
|
923
|
-
currentLogLevel = logLevels[currentLogLevelName] ?? logLevels.info;
|
|
924
|
-
return {
|
|
925
|
-
level: currentLogLevelName,
|
|
926
|
-
fatal: (mergeObject, msg) => {
|
|
927
|
-
if (currentLogLevel >= logLevels.fatal) {
|
|
928
|
-
typeof mergeObject === "string" || mergeObject instanceof Error ? writeLine("fatal", null, mergeObject, bindings) : writeLine("fatal", mergeObject, msg ?? "", bindings);
|
|
929
|
-
}
|
|
930
|
-
},
|
|
931
|
-
error: (mergeObject, msg) => {
|
|
932
|
-
if (currentLogLevel >= logLevels.error) {
|
|
933
|
-
typeof mergeObject === "string" || mergeObject instanceof Error ? writeLine("error", null, mergeObject, bindings) : writeLine("error", mergeObject, msg ?? "", bindings);
|
|
934
|
-
}
|
|
935
|
-
},
|
|
936
|
-
warn: (mergeObject, msg) => {
|
|
937
|
-
if (currentLogLevel >= logLevels.warn) {
|
|
938
|
-
typeof mergeObject === "string" ? writeLine("warn", null, mergeObject, bindings) : writeLine("warn", mergeObject, msg ?? "", bindings);
|
|
939
|
-
}
|
|
940
|
-
},
|
|
941
|
-
info: (mergeObject, msg) => {
|
|
942
|
-
if (currentLogLevel >= logLevels.info) {
|
|
943
|
-
typeof mergeObject === "string" ? writeLine("info", null, mergeObject, bindings) : writeLine("info", mergeObject, msg ?? "", bindings);
|
|
944
|
-
}
|
|
945
|
-
},
|
|
946
|
-
debug: (mergeObject, msg) => {
|
|
947
|
-
if (currentLogLevel >= logLevels.debug) {
|
|
948
|
-
typeof mergeObject === "string" ? writeLine("debug", null, mergeObject, bindings) : writeLine("debug", mergeObject, msg ?? "", bindings);
|
|
949
|
-
}
|
|
950
|
-
},
|
|
951
|
-
trace: (mergeObject, msg) => {
|
|
952
|
-
if (currentLogLevel >= logLevels.trace) {
|
|
953
|
-
typeof mergeObject === "string" ? writeLine("trace", null, mergeObject, bindings) : writeLine("trace", mergeObject, msg ?? "", bindings);
|
|
954
|
-
}
|
|
955
|
-
},
|
|
956
|
-
silent: () => {
|
|
957
|
-
},
|
|
958
|
-
child: (childBindings) => createLogger({ ...bindings, ...childBindings })
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
var logger = createLogger();
|
|
962
|
-
|
|
963
|
-
// src/logging/pretty-writer.ts
|
|
964
|
-
var levelColor = {
|
|
965
|
-
fatal: chalk.bgRed.white,
|
|
966
|
-
error: chalk.red,
|
|
967
|
-
warn: chalk.yellow,
|
|
968
|
-
info: chalk.cyan,
|
|
969
|
-
debug: chalk.gray,
|
|
970
|
-
trace: chalk.white
|
|
971
|
-
};
|
|
972
|
-
function formatTime(ms) {
|
|
973
|
-
return new Date(ms).toTimeString().slice(0, 8);
|
|
974
|
-
}
|
|
975
|
-
function formatMeta(meta) {
|
|
976
|
-
const { req, res, ...rest } = meta;
|
|
977
|
-
const lines = [];
|
|
978
|
-
if (req) {
|
|
979
|
-
const reqParts = [`${req.method} ${req.url}`];
|
|
980
|
-
if (req.remoteAddress) reqParts.push(`from ${req.remoteAddress}:${req.remotePort ?? ""}`);
|
|
981
|
-
if (req.headers) reqParts.push(`headers=${JSON.stringify(req.headers, null, 2)}`);
|
|
982
|
-
if (req.body !== void 0) reqParts.push(`body=${typeof req.body === "string" ? req.body : JSON.stringify(req.body, null, 2)}`);
|
|
983
|
-
lines.push(chalk.dim(" req: " + reqParts.join(" ")));
|
|
984
|
-
}
|
|
985
|
-
if (res) {
|
|
986
|
-
const resParts = [`${res.statusCode}`];
|
|
987
|
-
if (res.headers) resParts.push(`headers=${JSON.stringify(res.headers, null, 2)}`);
|
|
988
|
-
if (res.body !== void 0) resParts.push(`body=${typeof res.body === "string" ? res.body : JSON.stringify(res.body, null, 2)}`);
|
|
989
|
-
lines.push(chalk.dim(" res: " + resParts.join(" ")));
|
|
990
|
-
}
|
|
991
|
-
if (Object.keys(rest).length) {
|
|
992
|
-
lines.push(chalk.dim(" " + JSON.stringify(rest, null, 2)));
|
|
993
|
-
}
|
|
994
|
-
return lines.length ? EOL2 + lines.join(EOL2) : "";
|
|
995
|
-
}
|
|
996
|
-
var prettyWriter = (entry) => {
|
|
997
|
-
const color = levelColor[entry.levelName] ?? chalk.white;
|
|
998
|
-
const time = chalk.dim(formatTime(entry.time));
|
|
999
|
-
const level = color(entry.levelName.toUpperCase().padEnd(5));
|
|
1000
|
-
const module = chalk.magenta(entry.module);
|
|
1001
|
-
const reqId = entry.reqId ? chalk.dim(`[${entry.reqId}]`) + " " : "";
|
|
1002
|
-
const meta = entry.meta ? formatMeta(entry.meta) : "";
|
|
1003
|
-
process.stdout.write(`${time} ${level} ${module} ${reqId}- ${entry.msg}${meta}${EOL2}`);
|
|
1004
|
-
};
|
|
1005
|
-
setWriter(prettyWriter);
|
|
1006
|
-
|
|
1007
1
|
// src/dev-console-plugin/plugin.ts
|
|
1008
2
|
import path3 from "path";
|
|
1009
3
|
import fastifyStatic from "@fastify/static";
|
|
@@ -1015,6 +9,7 @@ import { WebSocketConnectionGoneError as WebSocketConnectionGoneError2 } from "@
|
|
|
1015
9
|
import fsPlugin from "fastify-plugin";
|
|
1016
10
|
|
|
1017
11
|
// src/queue-plugin/core.ts
|
|
12
|
+
var MAX_MESSAGE_HISTORY = 500;
|
|
1018
13
|
var DEFAULT_CONFIG = {
|
|
1019
14
|
batchSize: 10,
|
|
1020
15
|
batchWindowMs: 500,
|
|
@@ -1022,12 +17,12 @@ var DEFAULT_CONFIG = {
|
|
|
1022
17
|
parallel: true,
|
|
1023
18
|
maxConcurrent: 5,
|
|
1024
19
|
retryBackoff: true,
|
|
20
|
+
tickIntervalMs: 50,
|
|
1025
21
|
persistence: {
|
|
1026
22
|
enabled: true,
|
|
1027
23
|
directory: "./.develop/queue-data",
|
|
1028
24
|
saveInterval: 6e4,
|
|
1029
|
-
saveOnShutdown: false
|
|
1030
|
-
loadOnStartup: true
|
|
25
|
+
saveOnShutdown: false
|
|
1031
26
|
}
|
|
1032
27
|
};
|
|
1033
28
|
var mergeConfig = (defaultConfig, userConfig) => ({
|
|
@@ -1043,7 +38,6 @@ var createQueueService = (handler, module) => ({
|
|
|
1043
38
|
incoming: [],
|
|
1044
39
|
processing: [],
|
|
1045
40
|
dlq: [],
|
|
1046
|
-
timeoutId: null,
|
|
1047
41
|
processingBatch: false,
|
|
1048
42
|
module
|
|
1049
43
|
});
|
|
@@ -1062,6 +56,12 @@ var createQueueMessage = (body, headers) => {
|
|
|
1062
56
|
}
|
|
1063
57
|
return message;
|
|
1064
58
|
};
|
|
59
|
+
var scheduleRetry = (queueService, message, delayMs) => {
|
|
60
|
+
removeFromProcessing(queueService, message.id);
|
|
61
|
+
message.attempts += 1;
|
|
62
|
+
message.nextAttempt = new Date(Date.now() + delayMs);
|
|
63
|
+
queueService.incoming.push(message);
|
|
64
|
+
};
|
|
1065
65
|
var isMessageReady = (message, now = Date.now()) => {
|
|
1066
66
|
return !message.nextAttempt || message.nextAttempt.getTime() <= now;
|
|
1067
67
|
};
|
|
@@ -1079,11 +79,6 @@ var moveToProcessing = (queueService, batchSize) => {
|
|
|
1079
79
|
queueService.incoming = remaining;
|
|
1080
80
|
return ready;
|
|
1081
81
|
};
|
|
1082
|
-
var getNextReadyDelay = (queueService) => {
|
|
1083
|
-
const nextAttempts = queueService.incoming.filter((message) => message.nextAttempt).map((message) => message.nextAttempt.getTime());
|
|
1084
|
-
if (nextAttempts.length === 0) return null;
|
|
1085
|
-
return Math.max(0, Math.min(...nextAttempts) - Date.now());
|
|
1086
|
-
};
|
|
1087
82
|
var removeFromProcessing = (queueService, messageId) => {
|
|
1088
83
|
queueService.processing = queueService.processing.filter((msg) => msg.id !== messageId);
|
|
1089
84
|
};
|
|
@@ -1095,21 +90,9 @@ var moveToDLQ = (queueService, message, error) => {
|
|
|
1095
90
|
});
|
|
1096
91
|
removeFromProcessing(queueService, message.id);
|
|
1097
92
|
};
|
|
1098
|
-
var incrementAttempts = (queueService, messageId) => {
|
|
1099
|
-
const index = queueService.processing.findIndex((m) => m.id === messageId);
|
|
1100
|
-
if (index >= 0) {
|
|
1101
|
-
queueService.processing[index].attempts += 1;
|
|
1102
|
-
return queueService.processing[index];
|
|
1103
|
-
}
|
|
1104
|
-
return null;
|
|
1105
|
-
};
|
|
1106
93
|
var calculateBackoffDelay = (attempts) => {
|
|
1107
94
|
return Math.pow(2, attempts) * 100;
|
|
1108
95
|
};
|
|
1109
|
-
var setNextAttemptTime = (message, delayMs) => ({
|
|
1110
|
-
...message,
|
|
1111
|
-
nextAttempt: new Date(Date.now() + delayMs)
|
|
1112
|
-
});
|
|
1113
96
|
var createQueueSummary = (queueService, config) => ({
|
|
1114
97
|
incoming: queueService.incoming.length,
|
|
1115
98
|
processing: queueService.processing.length,
|
|
@@ -1149,93 +132,21 @@ var purgeDLQ = (queueService) => {
|
|
|
1149
132
|
queueService.dlq = [];
|
|
1150
133
|
return dlqCount;
|
|
1151
134
|
};
|
|
1152
|
-
var
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
try {
|
|
1168
|
-
const result = await queueService.handler(message);
|
|
1169
|
-
if (result?.failureId) {
|
|
1170
|
-
await handleProcessingError(queueName, message, queueService, config);
|
|
1171
|
-
} else {
|
|
1172
|
-
removeFromProcessing(queueService, message.id);
|
|
1173
|
-
logSuccess("Successfully processed message", message.id, queueName);
|
|
1174
|
-
}
|
|
1175
|
-
} catch {
|
|
1176
|
-
await handleProcessingError(queueName, message, queueService, config);
|
|
1177
|
-
}
|
|
1178
|
-
};
|
|
1179
|
-
var handleProcessingError = async (queueName, message, queueService, config) => {
|
|
1180
|
-
logError("Error processing message", message.id, queueName, "handler returned failure");
|
|
1181
|
-
if (message.attempts >= config.maxRetries) {
|
|
1182
|
-
moveToDLQ(queueService, message, "max retries exceeded");
|
|
1183
|
-
return;
|
|
1184
|
-
}
|
|
1185
|
-
const updatedMessage = incrementAttempts(queueService, message.id);
|
|
1186
|
-
if (!updatedMessage) return;
|
|
1187
|
-
if (config.retryBackoff) {
|
|
1188
|
-
const delayMs = calculateBackoffDelay(updatedMessage.attempts);
|
|
1189
|
-
const messageWithNextAttempt = setNextAttemptTime(updatedMessage, delayMs);
|
|
1190
|
-
logRetryScheduled(message.id, delayMs);
|
|
1191
|
-
setTimeout(() => {
|
|
1192
|
-
createProcessMessageHandler(config)(queueName, messageWithNextAttempt, queueService);
|
|
1193
|
-
}, delayMs);
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
var createBatchProcessor = (processMessage, config, dirtyQueues, schedulerRef) => async (queueName, queueService) => {
|
|
1197
|
-
if (queueService.timeoutId) {
|
|
1198
|
-
clearTimeout(queueService.timeoutId);
|
|
1199
|
-
queueService.timeoutId = null;
|
|
1200
|
-
}
|
|
1201
|
-
if (queueService.processingBatch) {
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
try {
|
|
1205
|
-
queueService.processingBatch = true;
|
|
1206
|
-
const messagesToProcess = moveToProcessing(queueService, config.batchSize);
|
|
1207
|
-
if (messagesToProcess.length > 0) {
|
|
1208
|
-
queueService.processing.push(...messagesToProcess);
|
|
1209
|
-
await Promise.all(messagesToProcess.map((message) => {
|
|
1210
|
-
return processMessage(queueName, message, queueService);
|
|
1211
|
-
}));
|
|
1212
|
-
if (config.persistence.enabled && dirtyQueues) {
|
|
1213
|
-
dirtyQueues.add(queueName);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
} finally {
|
|
1217
|
-
queueService.processingBatch = false;
|
|
1218
|
-
const nextReadyDelay = getNextReadyDelay(queueService);
|
|
1219
|
-
if (nextReadyDelay !== null) {
|
|
1220
|
-
schedulerRef?.current?.(queueName, queueService, nextReadyDelay);
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
};
|
|
1224
|
-
var createProcessingScheduler = (processBatch, config) => (queueName, queueService, overrideDelay = null) => {
|
|
1225
|
-
if (!queueService.timeoutId) {
|
|
1226
|
-
const delay = overrideDelay ?? config.batchWindowMs;
|
|
1227
|
-
queueService.timeoutId = setTimeout(() => {
|
|
1228
|
-
processBatch(queueName, queueService);
|
|
1229
|
-
}, delay);
|
|
1230
|
-
}
|
|
1231
|
-
};
|
|
1232
|
-
var handleImmediateProcessing = (queueService, config, processBatch, queueName) => {
|
|
1233
|
-
if (queueService.incoming.length >= config.batchSize) {
|
|
1234
|
-
if (queueService.timeoutId) {
|
|
1235
|
-
clearTimeout(queueService.timeoutId);
|
|
1236
|
-
queueService.timeoutId = null;
|
|
1237
|
-
}
|
|
1238
|
-
setImmediate(() => processBatch(queueName, queueService));
|
|
135
|
+
var createHistoryEntry = (queueName, message, status, error) => ({
|
|
136
|
+
id: message.id,
|
|
137
|
+
queueName,
|
|
138
|
+
status,
|
|
139
|
+
timestamp: message.timestamp,
|
|
140
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
141
|
+
attempts: message.attempts,
|
|
142
|
+
payload: message.payload,
|
|
143
|
+
attributes: message.attributes,
|
|
144
|
+
error
|
|
145
|
+
});
|
|
146
|
+
var recordMessageHistory = (history, entry) => {
|
|
147
|
+
history.push(entry);
|
|
148
|
+
if (history.length > MAX_MESSAGE_HISTORY) {
|
|
149
|
+
history.splice(0, history.length - MAX_MESSAGE_HISTORY);
|
|
1239
150
|
}
|
|
1240
151
|
};
|
|
1241
152
|
|
|
@@ -1255,123 +166,358 @@ var restoreDates = (messages) => {
|
|
|
1255
166
|
if (msg.failedAt) msg.failedAt = new Date(msg.failedAt);
|
|
1256
167
|
});
|
|
1257
168
|
};
|
|
1258
|
-
var
|
|
1259
|
-
try {
|
|
1260
|
-
await fs.mkdir(config.persistence.directory, { recursive: true });
|
|
1261
|
-
if (config.persistence.loadOnStartup) {
|
|
1262
|
-
await loadAllQueueStates();
|
|
1263
|
-
}
|
|
1264
|
-
if (config.persistence.saveInterval && config.persistence.saveInterval > 0) {
|
|
1265
|
-
intervalIdHolder.id = setInterval(saveDirtyQueueStates, config.persistence.saveInterval);
|
|
1266
|
-
}
|
|
1267
|
-
if (config.persistence.saveOnShutdown) {
|
|
1268
|
-
const shutdownHandler = async () => {
|
|
1269
|
-
logger.debug("Saving queue state before shutdown...");
|
|
1270
|
-
if (intervalIdHolder.id) {
|
|
1271
|
-
clearInterval(intervalIdHolder.id);
|
|
1272
|
-
}
|
|
1273
|
-
await saveAllQueueStates();
|
|
1274
|
-
process.exit(0);
|
|
1275
|
-
};
|
|
1276
|
-
process.on("SIGINT", shutdownHandler);
|
|
1277
|
-
process.on("SIGTERM", shutdownHandler);
|
|
1278
|
-
}
|
|
1279
|
-
logger.debug(`Queue persistence initialized: ${config.persistence.directory}`);
|
|
1280
|
-
} catch (error) {
|
|
1281
|
-
logger.error(`Failed to initialize queue persistence: ${error?.message}`);
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1284
|
-
var createQueueStateSaver = (config) => async (queueName, queueService) => {
|
|
169
|
+
var saveQueueState = async (runtime, queueName, queueService) => {
|
|
1285
170
|
try {
|
|
1286
|
-
if (!queueService) return;
|
|
1287
171
|
const queueData = createQueueData(queueService);
|
|
1288
172
|
const now = Date.now();
|
|
1289
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
1290
|
-
const
|
|
1291
|
-
await fs.writeFile(
|
|
1292
|
-
await fs.rename(
|
|
1293
|
-
|
|
173
|
+
const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);
|
|
174
|
+
const tempFilePath2 = path.join(runtime.config.persistence.directory, `${queueName}.${now}temp.json`);
|
|
175
|
+
await fs.writeFile(tempFilePath2, JSON.stringify(queueData, null, 2), "utf8");
|
|
176
|
+
await fs.rename(tempFilePath2, queueFilePath);
|
|
177
|
+
runtime.logging.debug({ module: queueService.module }, `Queue state saved: ${queueName}`);
|
|
1294
178
|
} catch (error) {
|
|
1295
|
-
|
|
179
|
+
runtime.logging.error(
|
|
180
|
+
{ module: queueService.module, error: error?.message },
|
|
181
|
+
`Failed to save queue state for ${queueName}`
|
|
182
|
+
);
|
|
1296
183
|
}
|
|
1297
184
|
};
|
|
1298
|
-
var
|
|
185
|
+
var saveAllQueueStates = async (runtime) => {
|
|
1299
186
|
try {
|
|
1300
|
-
for (const [queueName, queueService] of Object.entries(queues)) {
|
|
1301
|
-
await saveQueueState(queueName, queueService);
|
|
187
|
+
for (const [queueName, queueService] of Object.entries(runtime.queues)) {
|
|
188
|
+
await saveQueueState(runtime, queueName, queueService);
|
|
1302
189
|
}
|
|
1303
|
-
|
|
190
|
+
runtime.logging.debug(`All queue states saved to ${runtime.config.persistence.directory}`);
|
|
1304
191
|
} catch (error) {
|
|
1305
|
-
|
|
192
|
+
runtime.logging.error({ error: error?.message }, `Failed to save all queue states`);
|
|
1306
193
|
}
|
|
1307
194
|
};
|
|
1308
|
-
var
|
|
1309
|
-
if (dirtyQueues.size === 0) return;
|
|
195
|
+
var saveDirtyQueueStates = async (runtime) => {
|
|
196
|
+
if (runtime.dirtyQueues.size === 0) return;
|
|
1310
197
|
try {
|
|
1311
|
-
const queueNames = [...dirtyQueues];
|
|
1312
|
-
dirtyQueues.clear();
|
|
198
|
+
const queueNames = [...runtime.dirtyQueues];
|
|
199
|
+
runtime.dirtyQueues.clear();
|
|
1313
200
|
for (const queueName of queueNames) {
|
|
1314
|
-
if (queues[queueName]) {
|
|
1315
|
-
await saveQueueState(queueName, queues[queueName]);
|
|
201
|
+
if (runtime.queues[queueName]) {
|
|
202
|
+
await saveQueueState(runtime, queueName, runtime.queues[queueName]);
|
|
1316
203
|
}
|
|
1317
204
|
}
|
|
1318
205
|
} catch (error) {
|
|
1319
|
-
|
|
206
|
+
runtime.logging.error({ error: error?.message }, `Failed to save dirty queue states`);
|
|
1320
207
|
}
|
|
1321
208
|
};
|
|
1322
|
-
var
|
|
209
|
+
var loadQueueState = async (runtime, queueName) => {
|
|
1323
210
|
try {
|
|
1324
|
-
|
|
1325
|
-
|
|
211
|
+
const queueService = runtime.queues[queueName];
|
|
212
|
+
if (!queueService) {
|
|
213
|
+
runtime.logging.warn(`Skipping load for non-existent queue: ${queueName}`);
|
|
1326
214
|
return;
|
|
1327
215
|
}
|
|
1328
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
216
|
+
const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);
|
|
1329
217
|
try {
|
|
1330
218
|
const data = await fs.readFile(queueFilePath, "utf8");
|
|
1331
219
|
const queueData = JSON.parse(data);
|
|
1332
220
|
restoreDates(queueData.incoming);
|
|
1333
221
|
restoreDates(queueData.processing);
|
|
1334
222
|
restoreDates(queueData.dlq);
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if (message.attempts >= config.maxRetries) continue;
|
|
1345
|
-
setTimeout(() => {
|
|
1346
|
-
processMessage(queueName, message, queues[queueName]);
|
|
1347
|
-
}, 100 * (Math.random() * 10));
|
|
223
|
+
queueService.incoming = queueData.incoming;
|
|
224
|
+
queueService.dlq = queueData.dlq;
|
|
225
|
+
queueService.processing = [];
|
|
226
|
+
for (const message of queueData.processing) {
|
|
227
|
+
if (message.attempts >= runtime.config.maxRetries) {
|
|
228
|
+
queueService.dlq.push({ ...message, error: "max retries exceeded before restart", failedAt: /* @__PURE__ */ new Date() });
|
|
229
|
+
} else {
|
|
230
|
+
message.nextAttempt = /* @__PURE__ */ new Date();
|
|
231
|
+
queueService.incoming.push(message);
|
|
1348
232
|
}
|
|
1349
233
|
}
|
|
234
|
+
runtime.logging.debug(
|
|
235
|
+
{ module: queueService.module },
|
|
236
|
+
`Queue state loaded: ${queueName} (saved at ${queueData.savedAt})`
|
|
237
|
+
);
|
|
1350
238
|
} catch (error) {
|
|
1351
239
|
if (error.code === "ENOENT") {
|
|
1352
|
-
|
|
240
|
+
runtime.logging.debug({ module: queueService.module }, `No saved state found for queue: ${queueName}`);
|
|
1353
241
|
} else {
|
|
1354
242
|
throw error;
|
|
1355
243
|
}
|
|
1356
244
|
}
|
|
1357
245
|
} catch (error) {
|
|
1358
|
-
|
|
246
|
+
runtime.logging.error({ error: error?.message }, `Failed to load queue state for ${queueName}`);
|
|
1359
247
|
}
|
|
1360
248
|
};
|
|
1361
|
-
var
|
|
249
|
+
var initializePersistence = async (runtime) => {
|
|
1362
250
|
try {
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
251
|
+
await fs.mkdir(runtime.config.persistence.directory, { recursive: true });
|
|
252
|
+
if (runtime.config.persistence.saveOnShutdown) {
|
|
253
|
+
const shutdownHandler = async () => {
|
|
254
|
+
runtime.logging.debug("Saving queue state before shutdown...");
|
|
255
|
+
await saveAllQueueStates(runtime);
|
|
256
|
+
process.exit(0);
|
|
257
|
+
};
|
|
258
|
+
process.on("SIGINT", shutdownHandler);
|
|
259
|
+
process.on("SIGTERM", shutdownHandler);
|
|
1368
260
|
}
|
|
1369
|
-
|
|
261
|
+
runtime.logging.debug(`Queue persistence initialized: ${runtime.config.persistence.directory}`);
|
|
1370
262
|
} catch (error) {
|
|
1371
|
-
|
|
263
|
+
runtime.logging.error({ error: error?.message }, `Failed to initialize queue persistence`);
|
|
1372
264
|
}
|
|
1373
265
|
};
|
|
1374
266
|
|
|
267
|
+
// src/queue-plugin/processing.ts
|
|
268
|
+
var processMessage = async (runtime, queueName, message, queueService) => {
|
|
269
|
+
try {
|
|
270
|
+
const result = await queueService.handler(message);
|
|
271
|
+
if (result?.failureId) {
|
|
272
|
+
handleFailure(runtime, queueName, message, queueService);
|
|
273
|
+
} else {
|
|
274
|
+
removeFromProcessing(queueService, message.id);
|
|
275
|
+
recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, "completed"));
|
|
276
|
+
runtime.logging.debug(`Successfully processed message ${message.id} in queue ${queueName}`);
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
handleFailure(runtime, queueName, message, queueService);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var handleFailure = (runtime, queueName, message, queueService) => {
|
|
283
|
+
runtime.logging.error(`Error processing message ${message.id} in queue ${queueName}: handler returned failure`);
|
|
284
|
+
if (message.attempts >= runtime.config.maxRetries) {
|
|
285
|
+
moveToDLQ(queueService, message, "max retries exceeded");
|
|
286
|
+
recordMessageHistory(runtime.messageHistory, createHistoryEntry(queueName, message, "failed", "max retries exceeded"));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const delayMs = runtime.config.retryBackoff ? calculateBackoffDelay(message.attempts + 1) : 0;
|
|
290
|
+
scheduleRetry(queueService, message, delayMs);
|
|
291
|
+
runtime.logging.debug(`Scheduling retry for message ${message.id} in ${delayMs}ms`);
|
|
292
|
+
};
|
|
293
|
+
var processBatch = async (runtime, queueName, queueService) => {
|
|
294
|
+
if (queueService.processingBatch) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
queueService.processingBatch = true;
|
|
299
|
+
const messagesToProcess = moveToProcessing(queueService, runtime.config.batchSize);
|
|
300
|
+
if (messagesToProcess.length === 0) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
queueService.processing.push(...messagesToProcess);
|
|
304
|
+
await Promise.all(
|
|
305
|
+
messagesToProcess.map((message) => runtime.logging.runWithLogContext(
|
|
306
|
+
{ module: queueService.module, reqId: message.id },
|
|
307
|
+
() => processMessage(runtime, queueName, message, queueService)
|
|
308
|
+
))
|
|
309
|
+
);
|
|
310
|
+
if (runtime.config.persistence.enabled) {
|
|
311
|
+
runtime.dirtyQueues.add(queueName);
|
|
312
|
+
}
|
|
313
|
+
} finally {
|
|
314
|
+
queueService.processingBatch = false;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var dispatchBatchIfDue = (runtime, queueName, queueService, now) => {
|
|
318
|
+
if (queueService.processingBatch) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const ready = queueService.incoming.filter((message) => isMessageReady(message, now));
|
|
322
|
+
if (ready.length === 0) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const dueBySize = ready.length >= runtime.config.batchSize;
|
|
326
|
+
const oldestReadyAge = now - Math.min(...ready.map((message) => message.timestamp.getTime()));
|
|
327
|
+
const dueByWindow = oldestReadyAge >= runtime.config.batchWindowMs;
|
|
328
|
+
if (dueBySize || dueByWindow) {
|
|
329
|
+
processBatch(runtime, queueName, queueService);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var maybeSavePersistence = (runtime, now) => {
|
|
333
|
+
if (!runtime.config.persistence.enabled) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const saveInterval = runtime.config.persistence.saveInterval ?? 0;
|
|
337
|
+
if (saveInterval > 0 && now - runtime.lastSavedAt >= saveInterval) {
|
|
338
|
+
runtime.lastSavedAt = now;
|
|
339
|
+
saveDirtyQueueStates(runtime);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var tick = (runtime) => {
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
for (const [queueName, queueService] of Object.entries(runtime.queues)) {
|
|
345
|
+
dispatchBatchIfDue(runtime, queueName, queueService, now);
|
|
346
|
+
}
|
|
347
|
+
maybeSavePersistence(runtime, now);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/queue-plugin/decorator.ts
|
|
351
|
+
var isValidQueueName = (queueName) => {
|
|
352
|
+
return typeof queueName === "string" && queueName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(queueName);
|
|
353
|
+
};
|
|
354
|
+
var validateQueueName = (queueName) => {
|
|
355
|
+
if (!queueName || typeof queueName !== "string") {
|
|
356
|
+
throw new Error("Queue name must be a non-empty string");
|
|
357
|
+
}
|
|
358
|
+
if (!isValidQueueName(queueName)) {
|
|
359
|
+
throw new Error("Queue name must contain only alphanumeric characters, hyphens, and underscores");
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var validateHandler = (handler) => {
|
|
363
|
+
if (typeof handler !== "function") {
|
|
364
|
+
throw new Error("Handler must be a function");
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
var addQueue = async (runtime, queueName, handler, module) => {
|
|
368
|
+
try {
|
|
369
|
+
validateQueueName(queueName);
|
|
370
|
+
validateHandler(handler);
|
|
371
|
+
if (runtime.queues[queueName]) {
|
|
372
|
+
runtime.logging.warn({ module }, `Queue already exists: ${queueName}.`);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
runtime.queues[queueName] = createQueueService(handler, module);
|
|
376
|
+
if (runtime.config.persistence.enabled) {
|
|
377
|
+
await loadQueueState(runtime, queueName);
|
|
378
|
+
}
|
|
379
|
+
runtime.logging.info({ module }, `Queue added: ${queueName}`);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
runtime.logging.error({ module, error: error.message }, `Failed to add queue ${queueName}`);
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var removeQueue = async (runtime, queueName) => {
|
|
386
|
+
try {
|
|
387
|
+
validateQueueName(queueName);
|
|
388
|
+
const queueService = runtime.queues[queueName];
|
|
389
|
+
if (!queueService) {
|
|
390
|
+
throw new Error(`Queue '${queueName}' does not exist`);
|
|
391
|
+
}
|
|
392
|
+
const totalMessages = queueService.incoming.length + queueService.processing.length;
|
|
393
|
+
if (totalMessages > 0) {
|
|
394
|
+
runtime.logging.warn(
|
|
395
|
+
{ module: queueService.module },
|
|
396
|
+
`Removing queue with ${totalMessages} pending messages: ${queueName}`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (runtime.config.persistence.enabled) {
|
|
400
|
+
await saveQueueState(runtime, queueName, queueService);
|
|
401
|
+
}
|
|
402
|
+
delete runtime.queues[queueName];
|
|
403
|
+
runtime.logging.info({ module: queueService.module }, `Queue removed: ${queueName}`);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
runtime.logging.error({ error: error.message }, `Failed to remove queue ${queueName}`);
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var hasQueue = (runtime, queueName) => {
|
|
410
|
+
try {
|
|
411
|
+
validateQueueName(queueName);
|
|
412
|
+
return queueName in runtime.queues;
|
|
413
|
+
} catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
var listQueues = (runtime, module) => {
|
|
418
|
+
return Object.keys(runtime.queues).filter((queueName) => !module || runtime.queues[queueName].module === module).sort();
|
|
419
|
+
};
|
|
420
|
+
var getQueueStats = (runtime, queueName) => {
|
|
421
|
+
try {
|
|
422
|
+
validateQueueName(queueName);
|
|
423
|
+
return runtime.queues[queueName] || null;
|
|
424
|
+
} catch {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var getQueuesMap = (runtime) => {
|
|
429
|
+
return { ...runtime.queues };
|
|
430
|
+
};
|
|
431
|
+
var getDashboardSummary = (runtime) => createDashboardSummary(runtime.queues, runtime.config);
|
|
432
|
+
var getQueueSummary = (runtime, queueName) => {
|
|
433
|
+
const queue = runtime.queues[queueName];
|
|
434
|
+
if (!queue) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
stats: createQueueSummary(queue, runtime.config),
|
|
439
|
+
messages: {
|
|
440
|
+
incoming: queue.incoming,
|
|
441
|
+
processing: queue.processing,
|
|
442
|
+
dlq: queue.dlq
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
var enqueueMessage = async (runtime, queueName, body, attributes) => {
|
|
447
|
+
const queueService = runtime.queues[queueName];
|
|
448
|
+
if (!queueService) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
const message = createQueueMessage(body, attributes);
|
|
452
|
+
queueService.incoming.push(message);
|
|
453
|
+
if (runtime.config.persistence.enabled) {
|
|
454
|
+
runtime.dirtyQueues.add(queueName);
|
|
455
|
+
}
|
|
456
|
+
return { id: message.id, queueName };
|
|
457
|
+
};
|
|
458
|
+
var processDlq = async (runtime, queueName) => {
|
|
459
|
+
const queueService = runtime.queues[queueName];
|
|
460
|
+
if (!queueService) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
const processedCount = moveDLQToIncoming(queueService);
|
|
464
|
+
if (processedCount === 0) {
|
|
465
|
+
return { status: "success", message: "No messages in DLQ to process", processed: 0 };
|
|
466
|
+
}
|
|
467
|
+
runtime.logging.warn(
|
|
468
|
+
{ module: queueService.module },
|
|
469
|
+
`Moving ${processedCount} messages from DLQ for ${queueName}`
|
|
470
|
+
);
|
|
471
|
+
if (runtime.config.persistence.enabled) {
|
|
472
|
+
runtime.dirtyQueues.add(queueName);
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
status: "success",
|
|
476
|
+
message: `Moved ${processedCount} messages from DLQ to processing queue`,
|
|
477
|
+
processed: processedCount
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
var purgeDlq = async (runtime, queueName) => {
|
|
481
|
+
const queueService = runtime.queues[queueName];
|
|
482
|
+
if (!queueService) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const purgedCount = purgeDLQ(queueService);
|
|
486
|
+
if (purgedCount === 0) {
|
|
487
|
+
return { status: "success", message: "No messages in DLQ to purge", purged: 0 };
|
|
488
|
+
}
|
|
489
|
+
runtime.logging.warn(
|
|
490
|
+
{ module: queueService.module },
|
|
491
|
+
`Purging ${purgedCount} messages from DLQ for ${queueName}`
|
|
492
|
+
);
|
|
493
|
+
if (runtime.config.persistence.enabled) {
|
|
494
|
+
runtime.dirtyQueues.add(queueName);
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
status: "success",
|
|
498
|
+
message: `Purged ${purgedCount} messages from DLQ`,
|
|
499
|
+
purged: purgedCount
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
var getMessageHistory = (runtime, queueName, limit = 50) => {
|
|
503
|
+
return runtime.messageHistory.filter((entry) => entry.queueName === queueName).slice(-limit).reverse();
|
|
504
|
+
};
|
|
505
|
+
var createQueueManager = (runtime) => ({
|
|
506
|
+
addQueue: (queueName, handler, module) => addQueue(runtime, queueName, handler, module),
|
|
507
|
+
removeQueue: (queueName) => removeQueue(runtime, queueName),
|
|
508
|
+
hasQueue: (queueName) => hasQueue(runtime, queueName),
|
|
509
|
+
listQueues: (module) => listQueues(runtime, module),
|
|
510
|
+
getQueueStats: (queueName) => getQueueStats(runtime, queueName),
|
|
511
|
+
getQueuesMap: () => getQueuesMap(runtime),
|
|
512
|
+
getConfig: () => runtime.config,
|
|
513
|
+
getDashboardSummary: () => getDashboardSummary(runtime),
|
|
514
|
+
getQueueSummary: (queueName) => getQueueSummary(runtime, queueName),
|
|
515
|
+
enqueueMessage: (queueName, body, attributes) => enqueueMessage(runtime, queueName, body, attributes),
|
|
516
|
+
processDlq: (queueName) => processDlq(runtime, queueName),
|
|
517
|
+
purgeDlq: (queueName) => purgeDlq(runtime, queueName),
|
|
518
|
+
getMessageHistory: (queueName, limit) => getMessageHistory(runtime, queueName, limit)
|
|
519
|
+
});
|
|
520
|
+
|
|
1375
521
|
// src/queue-plugin/routes.ts
|
|
1376
522
|
var registerQueueRoutes = (app, prefix = "") => {
|
|
1377
523
|
app.get(
|
|
@@ -1408,270 +554,45 @@ var registerQueueRoutes = (app, prefix = "") => {
|
|
|
1408
554
|
async (request, reply) => {
|
|
1409
555
|
const queueName = request.params.queue;
|
|
1410
556
|
const result = await app.queues.processDlq(queueName);
|
|
1411
|
-
if (result === null) {
|
|
1412
|
-
return reply.status(404).send(result);
|
|
1413
|
-
}
|
|
1414
|
-
return reply.status(200).send(result);
|
|
1415
|
-
}
|
|
1416
|
-
);
|
|
1417
|
-
app.post(
|
|
1418
|
-
`/${prefix}/:queue/purge-dlq`,
|
|
1419
|
-
async (request, reply) => {
|
|
1420
|
-
const queueName = request.params.queue;
|
|
1421
|
-
const result = await app.queues.purgeDlq(queueName);
|
|
1422
|
-
if (result === null) {
|
|
1423
|
-
return reply.status(404).send({ error: "Queue not found" });
|
|
1424
|
-
}
|
|
1425
|
-
return reply.status(200).send(result);
|
|
1426
|
-
}
|
|
1427
|
-
);
|
|
1428
|
-
};
|
|
1429
|
-
|
|
1430
|
-
// src/queue-plugin/decorator.ts
|
|
1431
|
-
var isValidQueueName = (queueName) => {
|
|
1432
|
-
return typeof queueName === "string" && queueName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(queueName);
|
|
1433
|
-
};
|
|
1434
|
-
var createQueueManager = ({
|
|
1435
|
-
config,
|
|
1436
|
-
queues,
|
|
1437
|
-
dirtyQueues,
|
|
1438
|
-
saveQueueState,
|
|
1439
|
-
loadQueueState,
|
|
1440
|
-
scheduleProcessing,
|
|
1441
|
-
processBatch
|
|
1442
|
-
}) => {
|
|
1443
|
-
const validateQueueName = (queueName) => {
|
|
1444
|
-
if (!queueName || typeof queueName !== "string") {
|
|
1445
|
-
throw new Error("Queue name must be a non-empty string");
|
|
1446
|
-
}
|
|
1447
|
-
if (!isValidQueueName(queueName)) {
|
|
1448
|
-
throw new Error("Queue name must contain only alphanumeric characters, hyphens, and underscores");
|
|
1449
|
-
}
|
|
1450
|
-
};
|
|
1451
|
-
const validateHandler = (handler) => {
|
|
1452
|
-
if (typeof handler !== "function") {
|
|
1453
|
-
throw new Error("Handler must be a function");
|
|
1454
|
-
}
|
|
1455
|
-
};
|
|
1456
|
-
const addQueue = async (queueName, handler, module) => {
|
|
1457
|
-
try {
|
|
1458
|
-
validateQueueName(queueName);
|
|
1459
|
-
validateHandler(handler);
|
|
1460
|
-
if (queues[queueName]) {
|
|
1461
|
-
logger.warn(`Queue already exists: ${queueName}.`);
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
queues[queueName] = createQueueService(handler, module);
|
|
1465
|
-
if (config.persistence.enabled && loadQueueState) {
|
|
1466
|
-
await loadQueueState(queueName);
|
|
1467
|
-
}
|
|
1468
|
-
logger.info(`Queue added: ${queueName}`);
|
|
1469
|
-
} catch (error) {
|
|
1470
|
-
logger.error(`Failed to add queue ${queueName}: ${error.message}`);
|
|
1471
|
-
throw error;
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
const removeQueue = async (queueName) => {
|
|
1475
|
-
try {
|
|
1476
|
-
validateQueueName(queueName);
|
|
1477
|
-
if (!queues[queueName]) {
|
|
1478
|
-
throw new Error(`Queue '${queueName}' does not exist`);
|
|
1479
|
-
}
|
|
1480
|
-
const queueService = queues[queueName];
|
|
1481
|
-
const totalMessages = queueService.incoming.length + queueService.processing.length;
|
|
1482
|
-
if (totalMessages > 0) {
|
|
1483
|
-
logger.warn(`Removing queue with ${totalMessages} pending messages: ${queueName}`);
|
|
1484
|
-
}
|
|
1485
|
-
if (queueService.timeoutId) {
|
|
1486
|
-
clearTimeout(queueService.timeoutId);
|
|
1487
|
-
}
|
|
1488
|
-
if (config.persistence.enabled && saveQueueState) {
|
|
1489
|
-
await saveQueueState(queueName, queueService);
|
|
1490
|
-
}
|
|
1491
|
-
delete queues[queueName];
|
|
1492
|
-
logger.info(`Queue removed: ${queueName}`);
|
|
1493
|
-
} catch (error) {
|
|
1494
|
-
logger.error(`Failed to remove queue ${queueName}: ${error.message}`);
|
|
1495
|
-
throw error;
|
|
1496
|
-
}
|
|
1497
|
-
};
|
|
1498
|
-
const hasQueue = (queueName) => {
|
|
1499
|
-
try {
|
|
1500
|
-
validateQueueName(queueName);
|
|
1501
|
-
return queueName in queues;
|
|
1502
|
-
} catch {
|
|
1503
|
-
return false;
|
|
1504
|
-
}
|
|
1505
|
-
};
|
|
1506
|
-
const listQueues = (module) => {
|
|
1507
|
-
return Object.keys(queues).filter((queueName) => {
|
|
1508
|
-
const queueService = queues[queueName];
|
|
1509
|
-
return !module || queueService.module === module;
|
|
1510
|
-
}).sort();
|
|
1511
|
-
};
|
|
1512
|
-
const getQueueStats = (queueName) => {
|
|
1513
|
-
try {
|
|
1514
|
-
validateQueueName(queueName);
|
|
1515
|
-
return queues[queueName] || null;
|
|
1516
|
-
} catch {
|
|
1517
|
-
return null;
|
|
1518
|
-
}
|
|
1519
|
-
};
|
|
1520
|
-
const getQueuesMap = () => {
|
|
1521
|
-
return { ...queues };
|
|
1522
|
-
};
|
|
1523
|
-
const getConfig = () => config;
|
|
1524
|
-
const getDashboardSummary = () => createDashboardSummary(queues, config);
|
|
1525
|
-
const getQueueSummary = (queueName) => {
|
|
1526
|
-
if (!queues[queueName]) {
|
|
1527
|
-
return null;
|
|
1528
|
-
}
|
|
1529
|
-
const queue = queues[queueName];
|
|
1530
|
-
const stats = createQueueSummary(queue, config);
|
|
1531
|
-
return {
|
|
1532
|
-
stats,
|
|
1533
|
-
messages: {
|
|
1534
|
-
incoming: queue.incoming,
|
|
1535
|
-
processing: queue.processing,
|
|
1536
|
-
dlq: queue.dlq
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
};
|
|
1540
|
-
const enqueueMessage = async (queueName, body, attributes) => {
|
|
1541
|
-
if (!queues[queueName]) {
|
|
1542
|
-
return null;
|
|
1543
|
-
}
|
|
1544
|
-
const message = createQueueMessage(body, attributes);
|
|
1545
|
-
queues[queueName].incoming.push(message);
|
|
1546
|
-
scheduleProcessing(queueName, queues[queueName]);
|
|
1547
|
-
handleImmediateProcessing(queues[queueName], config, processBatch, queueName);
|
|
1548
|
-
if (config.persistence.enabled) {
|
|
1549
|
-
dirtyQueues.add(queueName);
|
|
1550
|
-
}
|
|
1551
|
-
return {
|
|
1552
|
-
id: message.id,
|
|
1553
|
-
queueName
|
|
1554
|
-
};
|
|
1555
|
-
};
|
|
1556
|
-
const processDlq = async (queueName) => {
|
|
1557
|
-
if (!queues[queueName]) {
|
|
1558
|
-
return null;
|
|
1559
|
-
}
|
|
1560
|
-
const processedCount = moveDLQToIncoming(queues[queueName]);
|
|
1561
|
-
if (processedCount === 0) {
|
|
1562
|
-
return {
|
|
1563
|
-
status: "success",
|
|
1564
|
-
message: "No messages in DLQ to process",
|
|
1565
|
-
processed: 0
|
|
1566
|
-
};
|
|
1567
|
-
}
|
|
1568
|
-
logDLQOperation("Moving", processedCount, queueName);
|
|
1569
|
-
scheduleProcessing(queueName, queues[queueName]);
|
|
1570
|
-
if (config.persistence.enabled) {
|
|
1571
|
-
dirtyQueues.add(queueName);
|
|
1572
|
-
}
|
|
1573
|
-
return {
|
|
1574
|
-
status: "success",
|
|
1575
|
-
message: `Moved ${processedCount} messages from DLQ to processing queue`,
|
|
1576
|
-
processed: processedCount
|
|
1577
|
-
};
|
|
1578
|
-
};
|
|
1579
|
-
const purgeDlq = async (queueName) => {
|
|
1580
|
-
if (!queues[queueName]) {
|
|
1581
|
-
return null;
|
|
1582
|
-
}
|
|
1583
|
-
const purgedCount = purgeDLQ(queues[queueName]);
|
|
1584
|
-
if (purgedCount === 0) {
|
|
1585
|
-
return {
|
|
1586
|
-
status: "success",
|
|
1587
|
-
message: "No messages in DLQ to purge",
|
|
1588
|
-
purged: 0
|
|
1589
|
-
};
|
|
557
|
+
if (result === null) {
|
|
558
|
+
return reply.status(404).send(result);
|
|
559
|
+
}
|
|
560
|
+
return reply.status(200).send(result);
|
|
1590
561
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
562
|
+
);
|
|
563
|
+
app.post(
|
|
564
|
+
`/${prefix}/:queue/purge-dlq`,
|
|
565
|
+
async (request, reply) => {
|
|
566
|
+
const queueName = request.params.queue;
|
|
567
|
+
const result = await app.queues.purgeDlq(queueName);
|
|
568
|
+
if (result === null) {
|
|
569
|
+
return reply.status(404).send({ error: "Queue not found" });
|
|
570
|
+
}
|
|
571
|
+
return reply.status(200).send(result);
|
|
1594
572
|
}
|
|
1595
|
-
|
|
1596
|
-
status: "success",
|
|
1597
|
-
message: `Purged ${purgedCount} messages from DLQ`,
|
|
1598
|
-
purged: purgedCount
|
|
1599
|
-
};
|
|
1600
|
-
};
|
|
1601
|
-
return {
|
|
1602
|
-
addQueue,
|
|
1603
|
-
removeQueue,
|
|
1604
|
-
hasQueue,
|
|
1605
|
-
listQueues,
|
|
1606
|
-
getQueueStats,
|
|
1607
|
-
getQueuesMap,
|
|
1608
|
-
getConfig,
|
|
1609
|
-
getDashboardSummary,
|
|
1610
|
-
getQueueSummary,
|
|
1611
|
-
enqueueMessage,
|
|
1612
|
-
processDlq,
|
|
1613
|
-
purgeDlq
|
|
1614
|
-
};
|
|
1615
|
-
};
|
|
1616
|
-
var createQueueDecorator = (queueManager) => (app) => {
|
|
1617
|
-
app.decorate("queues", queueManager);
|
|
573
|
+
);
|
|
1618
574
|
};
|
|
1619
575
|
|
|
1620
576
|
// src/queue-plugin/plugin.ts
|
|
1621
577
|
var queuesPlugin = fsPlugin(async (app, options) => {
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
};
|
|
1630
|
-
const saveDirtyQueueStates = config.persistence.enabled && saveQueueState ? createDirtyQueuesStateSaver(saveQueueState, dirtyQueues)(queues) : async () => {
|
|
1631
|
-
};
|
|
1632
|
-
const schedulerRef = {};
|
|
1633
|
-
const processBatch = createBatchProcessor(
|
|
1634
|
-
processMessage,
|
|
1635
|
-
config,
|
|
1636
|
-
config.persistence.enabled ? dirtyQueues : void 0,
|
|
1637
|
-
schedulerRef
|
|
1638
|
-
);
|
|
1639
|
-
const scheduleProcessing = createProcessingScheduler(processBatch, config);
|
|
1640
|
-
schedulerRef.current = scheduleProcessing;
|
|
1641
|
-
const loadQueueState = config.persistence.enabled ? createQueueStateLoader(
|
|
1642
|
-
config,
|
|
1643
|
-
scheduleProcessing,
|
|
1644
|
-
processMessage
|
|
1645
|
-
)(queues) : async () => {
|
|
578
|
+
const runtime = {
|
|
579
|
+
config: mergeConfig(DEFAULT_CONFIG, options.config),
|
|
580
|
+
queues: {},
|
|
581
|
+
dirtyQueues: /* @__PURE__ */ new Set(),
|
|
582
|
+
lastSavedAt: Date.now(),
|
|
583
|
+
messageHistory: [],
|
|
584
|
+
logging: app.logging
|
|
1646
585
|
};
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
config,
|
|
1653
|
-
queues,
|
|
1654
|
-
dirtyQueues,
|
|
1655
|
-
saveQueueState: saveQueueState ? (queueName) => saveQueueState(queueName, queues[queueName]) : void 0,
|
|
1656
|
-
loadQueueState,
|
|
1657
|
-
scheduleProcessing,
|
|
1658
|
-
processBatch
|
|
1659
|
-
});
|
|
1660
|
-
const decorateQueues = createQueueDecorator(queueManager);
|
|
1661
|
-
decorateQueues(app);
|
|
1662
|
-
registerQueueRoutes(
|
|
1663
|
-
app,
|
|
1664
|
-
options.prefix
|
|
1665
|
-
);
|
|
1666
|
-
await initializePersistence();
|
|
586
|
+
const tickIntervalId = setInterval(() => tick(runtime), runtime.config.tickIntervalMs);
|
|
587
|
+
app.addHook("onClose", async () => clearInterval(tickIntervalId));
|
|
588
|
+
app.decorate("queues", createQueueManager(runtime));
|
|
589
|
+
registerQueueRoutes(app, options.prefix);
|
|
590
|
+
await initializePersistence(runtime);
|
|
1667
591
|
});
|
|
1668
592
|
|
|
1669
593
|
// src/schedule-plugin/plugin.ts
|
|
1670
594
|
import fsPlugin2 from "fastify-plugin";
|
|
1671
595
|
|
|
1672
|
-
// src/schedule-plugin/core.ts
|
|
1673
|
-
import * as fs3 from "fs/promises";
|
|
1674
|
-
|
|
1675
596
|
// src/schedule-plugin/cron-utils.ts
|
|
1676
597
|
import { parseExpression } from "cron-parser";
|
|
1677
598
|
function parseCronExpression(cronExpression, lastRun, options = {}) {
|
|
@@ -1704,7 +625,6 @@ function parseCronExpression(cronExpression, lastRun, options = {}) {
|
|
|
1704
625
|
description: describeCronExpression(normalizedCron)
|
|
1705
626
|
};
|
|
1706
627
|
} catch (error) {
|
|
1707
|
-
logger.error(`Invalid cron expression: ${cronExpression} - ${error.message}`);
|
|
1708
628
|
const fallbackTime = /* @__PURE__ */ new Date();
|
|
1709
629
|
fallbackTime.setHours(fallbackTime.getHours() + 1);
|
|
1710
630
|
fallbackTime.setMinutes(0);
|
|
@@ -1732,8 +652,7 @@ function getNextExecutions(cronExpression, count = 5, options = {}) {
|
|
|
1732
652
|
executions.push(interval.next().toDate());
|
|
1733
653
|
}
|
|
1734
654
|
return executions;
|
|
1735
|
-
} catch
|
|
1736
|
-
logger.error(`Error getting next executions: ${error.message}`);
|
|
655
|
+
} catch {
|
|
1737
656
|
return [];
|
|
1738
657
|
}
|
|
1739
658
|
}
|
|
@@ -2006,9 +925,111 @@ var shouldSkipJob = (job) => {
|
|
|
2006
925
|
if (job.maxRuns && job.runCount >= job.maxRuns) return true;
|
|
2007
926
|
return false;
|
|
2008
927
|
};
|
|
928
|
+
var isJobReadyToRun = (job) => {
|
|
929
|
+
const now = /* @__PURE__ */ new Date();
|
|
930
|
+
return job.nextRun.getTime() <= now.getTime();
|
|
931
|
+
};
|
|
2009
932
|
var getTimeUntilNextRun = (job) => Math.max(0, job.nextRun.getTime() - Date.now());
|
|
2010
933
|
|
|
2011
|
-
// src/schedule-plugin/
|
|
934
|
+
// src/schedule-plugin/core.ts
|
|
935
|
+
var DEFAULT_CONFIG2 = {
|
|
936
|
+
tickIntervalMs: 1e3,
|
|
937
|
+
persistence: {
|
|
938
|
+
enabled: true,
|
|
939
|
+
directory: "./.develop/scheduler-data",
|
|
940
|
+
saveInterval: 3e4,
|
|
941
|
+
// 30 seconds
|
|
942
|
+
saveOnShutdown: false
|
|
943
|
+
},
|
|
944
|
+
execution: {
|
|
945
|
+
maxConcurrent: 5,
|
|
946
|
+
defaultTimeout: 3e5
|
|
947
|
+
// 5 minutes
|
|
948
|
+
},
|
|
949
|
+
cleanup: {
|
|
950
|
+
maxExecutionHistory: 1e3,
|
|
951
|
+
cleanupInterval: 6e5
|
|
952
|
+
// 10 minutes
|
|
953
|
+
},
|
|
954
|
+
restartBehavior: {
|
|
955
|
+
preserveNaturalTiming: true,
|
|
956
|
+
skipMissedRuns: true,
|
|
957
|
+
maxTimingDrift: 6e4,
|
|
958
|
+
// 1 minute
|
|
959
|
+
rapidRestartThreshold: 1e4
|
|
960
|
+
// 10 seconds
|
|
961
|
+
},
|
|
962
|
+
cron: {
|
|
963
|
+
defaultTimezone: void 0,
|
|
964
|
+
// Use system timezone
|
|
965
|
+
logCronDetails: true
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
var mergeConfig2 = (defaultConfig, userConfig) => ({
|
|
969
|
+
...defaultConfig,
|
|
970
|
+
...userConfig,
|
|
971
|
+
persistence: { ...defaultConfig.persistence, ...userConfig?.persistence },
|
|
972
|
+
execution: { ...defaultConfig.execution, ...userConfig?.execution },
|
|
973
|
+
cleanup: { ...defaultConfig.cleanup, ...userConfig?.cleanup },
|
|
974
|
+
restartBehavior: { ...defaultConfig.restartBehavior, ...userConfig?.restartBehavior },
|
|
975
|
+
cron: { ...defaultConfig.cron, ...userConfig?.cron }
|
|
976
|
+
});
|
|
977
|
+
var createDashboardSummary2 = (runtime) => {
|
|
978
|
+
const jobs = Object.values(runtime.schedulers).map((scheduler) => {
|
|
979
|
+
const upcomingExecutions = scheduler.job.cronExpression ? getNextExecutions(scheduler.job.cronExpression, 3, { timezone: scheduler.job.timezone }) : [];
|
|
980
|
+
const cronDescription = scheduler.job.cronExpression ? parseCronExpression(scheduler.job.cronExpression, void 0, { timezone: scheduler.job.timezone }).description : void 0;
|
|
981
|
+
return {
|
|
982
|
+
...scheduler.job,
|
|
983
|
+
isRunning: scheduler.isRunning,
|
|
984
|
+
timeUntilNextRun: getTimeUntilNextRun(scheduler.job),
|
|
985
|
+
upcomingExecutions,
|
|
986
|
+
cronDescription
|
|
987
|
+
};
|
|
988
|
+
});
|
|
989
|
+
return { jobs, runningExecutions: runtime.runningExecutions };
|
|
990
|
+
};
|
|
991
|
+
var createExecutionsSummary = (runtime) => ({
|
|
992
|
+
executions: runtime.executionHistory.slice(-50),
|
|
993
|
+
// Last 50 executions
|
|
994
|
+
running: Object.values(runtime.executions).filter((e) => e.status === "running")
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
// src/schedule-plugin/jobs.ts
|
|
998
|
+
var validateJobDefinition = (jobDef) => {
|
|
999
|
+
if (!jobDef.cronExpression && !jobDef.intervalMs && !jobDef.delayMs) {
|
|
1000
|
+
throw new Error(`Job ${jobDef.name} must specify a cronExpression, intervalMs, or delayMs`);
|
|
1001
|
+
}
|
|
1002
|
+
if (jobDef.cronExpression && detectExpressionType(jobDef.cronExpression) === "unknown") {
|
|
1003
|
+
throw new Error(`Invalid cron expression for job: ${jobDef.name}`);
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
var createJobFromDefinition = (jobDef, config) => {
|
|
1007
|
+
validateJobDefinition(jobDef);
|
|
1008
|
+
const expressionType = detectExpressionType(jobDef.cronExpression);
|
|
1009
|
+
const cronExpression = expressionType === "rate" ? convertRateToCron(jobDef.cronExpression).cronExpression : jobDef.cronExpression;
|
|
1010
|
+
const job = {
|
|
1011
|
+
id: generateJobId(),
|
|
1012
|
+
name: jobDef.name,
|
|
1013
|
+
cronExpression,
|
|
1014
|
+
intervalMs: jobDef.intervalMs,
|
|
1015
|
+
delayMs: jobDef.delayMs,
|
|
1016
|
+
nextRun: /* @__PURE__ */ new Date(),
|
|
1017
|
+
enabled: jobDef.enabled ?? true,
|
|
1018
|
+
runCount: 0,
|
|
1019
|
+
maxRuns: jobDef.maxRuns,
|
|
1020
|
+
metadata: jobDef.metadata,
|
|
1021
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1022
|
+
timezone: jobDef.timezone,
|
|
1023
|
+
module: jobDef.module
|
|
1024
|
+
};
|
|
1025
|
+
job.nextRun = calculateNextRun(job, config.restartBehavior.preserveNaturalTiming);
|
|
1026
|
+
return job;
|
|
1027
|
+
};
|
|
1028
|
+
var createSchedulerService = (job, handler) => ({
|
|
1029
|
+
job,
|
|
1030
|
+
handler,
|
|
1031
|
+
isRunning: false
|
|
1032
|
+
});
|
|
2012
1033
|
var createJobExecution = (jobId) => ({
|
|
2013
1034
|
id: generateExecutionId(),
|
|
2014
1035
|
jobId,
|
|
@@ -2022,655 +1043,516 @@ var completeExecution = (execution, result, error) => ({
|
|
|
2022
1043
|
result,
|
|
2023
1044
|
error
|
|
2024
1045
|
});
|
|
2025
|
-
var canExecuteJob = (scheduler, state) => {
|
|
2026
|
-
if (state.isShuttingDown) {
|
|
2027
|
-
return { canExecute: false, reason: "System is shutting down" };
|
|
2028
|
-
}
|
|
2029
|
-
if (scheduler.isRunning) {
|
|
2030
|
-
return { canExecute: false, reason: "Job is already running" };
|
|
2031
|
-
}
|
|
2032
|
-
if (shouldSkipJob(scheduler.job)) {
|
|
2033
|
-
const reason = !scheduler.job.enabled ? "Job is disabled" : "Job reached maximum runs";
|
|
2034
|
-
return { canExecute: false, reason };
|
|
2035
|
-
}
|
|
2036
|
-
if (state.runningExecutions >= state.config.execution.maxConcurrent) {
|
|
2037
|
-
return { canExecute: false, reason: "Maximum concurrent executions reached" };
|
|
2038
|
-
}
|
|
2039
|
-
return { canExecute: true };
|
|
2040
|
-
};
|
|
2041
|
-
var scheduleJob = (scheduler, state, executeJobFn) => {
|
|
2042
|
-
if (state.isShuttingDown) return scheduler;
|
|
2043
|
-
const timeUntilNextRun = getTimeUntilNextRun(scheduler.job);
|
|
2044
|
-
const updatedScheduler = { ...scheduler };
|
|
2045
|
-
if (timeUntilNextRun <= 0) {
|
|
2046
|
-
setImmediate(() => executeJobFn(updatedScheduler));
|
|
2047
|
-
} else {
|
|
2048
|
-
updatedScheduler.timerId = setTimeout(() => {
|
|
2049
|
-
executeJobFn(updatedScheduler);
|
|
2050
|
-
}, timeUntilNextRun);
|
|
2051
|
-
logger.debug(`Scheduled job ${scheduler.job.name} to run in ${Math.round(timeUntilNextRun / 1e3)}s`);
|
|
2052
|
-
}
|
|
2053
|
-
return updatedScheduler;
|
|
2054
|
-
};
|
|
2055
|
-
var executeJobWithTimeout = async (handler, job, execution, timeoutMs) => {
|
|
2056
|
-
let timerId;
|
|
2057
|
-
try {
|
|
2058
|
-
return await Promise.race([
|
|
2059
|
-
handler(job, execution),
|
|
2060
|
-
new Promise((_, reject) => {
|
|
2061
|
-
timerId = setTimeout(() => reject(new Error("Job execution timeout")), timeoutMs);
|
|
2062
|
-
})
|
|
2063
|
-
]);
|
|
2064
|
-
} finally {
|
|
2065
|
-
clearTimeout(timerId);
|
|
2066
|
-
}
|
|
2067
|
-
};
|
|
2068
1046
|
var updateJobAfterExecution = (job, execution, config) => {
|
|
2069
1047
|
const updatedJob = {
|
|
2070
1048
|
...job,
|
|
2071
1049
|
lastRun: execution.startTime,
|
|
2072
1050
|
runCount: job.runCount + 1
|
|
2073
1051
|
};
|
|
2074
|
-
if (
|
|
1052
|
+
if (updatedJob.enabled) {
|
|
2075
1053
|
updatedJob.nextRun = calculateNextRunFromLastRun(updatedJob, updatedJob.lastRun, config);
|
|
2076
1054
|
}
|
|
2077
1055
|
return updatedJob;
|
|
2078
1056
|
};
|
|
2079
|
-
var
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
1057
|
+
var enableJob = (scheduler) => ({
|
|
1058
|
+
...scheduler,
|
|
1059
|
+
job: {
|
|
1060
|
+
...scheduler.job,
|
|
1061
|
+
enabled: true,
|
|
1062
|
+
nextRun: calculateNextRun(scheduler.job)
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
var disableJob = (scheduler) => ({
|
|
1066
|
+
...scheduler,
|
|
1067
|
+
job: { ...scheduler.job, enabled: false }
|
|
1068
|
+
});
|
|
2088
1069
|
|
|
2089
1070
|
// src/schedule-plugin/persistence.ts
|
|
2090
1071
|
import * as fs2 from "fs/promises";
|
|
2091
1072
|
import * as path2 from "path";
|
|
2092
|
-
var
|
|
2093
|
-
var
|
|
2094
|
-
var
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
1073
|
+
var stateFilePath = (directory) => path2.join(directory, "scheduler-state.json");
|
|
1074
|
+
var tempFilePath = (directory) => path2.join(directory, `scheduler-state.${Date.now()}.temp.json`);
|
|
1075
|
+
var restoreExecutionHistory = (savedExecutions) => savedExecutions.map((exec) => ({
|
|
1076
|
+
...exec,
|
|
1077
|
+
startTime: new Date(exec.startTime),
|
|
1078
|
+
endTime: exec.endTime ? new Date(exec.endTime) : void 0
|
|
1079
|
+
}));
|
|
1080
|
+
var readStateData = async (directory) => {
|
|
1081
|
+
const data = await fs2.readFile(stateFilePath(directory), "utf8");
|
|
1082
|
+
return JSON.parse(data);
|
|
1083
|
+
};
|
|
1084
|
+
var saveSchedulerState = async (runtime) => {
|
|
2101
1085
|
try {
|
|
2102
|
-
const
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
try {
|
|
2114
|
-
const stateFile = createStateFilePath(directory);
|
|
2115
|
-
const data = await fs2.readFile(stateFile, "utf8");
|
|
2116
|
-
return JSON.parse(data);
|
|
1086
|
+
const stateData = {
|
|
1087
|
+
jobs: Object.values(runtime.schedulers).map((s) => s.job),
|
|
1088
|
+
executions: runtime.executionHistory.slice(-100),
|
|
1089
|
+
savedAt: /* @__PURE__ */ new Date(),
|
|
1090
|
+
version: "1.0"
|
|
1091
|
+
};
|
|
1092
|
+
const file = stateFilePath(runtime.config.persistence.directory);
|
|
1093
|
+
const temp = tempFilePath(runtime.config.persistence.directory);
|
|
1094
|
+
await fs2.writeFile(temp, JSON.stringify(stateData, null, 2), "utf8");
|
|
1095
|
+
await fs2.rename(temp, file);
|
|
1096
|
+
runtime.logging.debug("Scheduler state saved");
|
|
2117
1097
|
} catch (error) {
|
|
2118
|
-
|
|
2119
|
-
logger.debug("No previous scheduler state found - starting fresh");
|
|
2120
|
-
} else {
|
|
2121
|
-
logger.error("Failed to load scheduler state:", error);
|
|
2122
|
-
}
|
|
2123
|
-
return null;
|
|
1098
|
+
runtime.logging.error({ error: error?.message }, "Failed to save scheduler state");
|
|
2124
1099
|
}
|
|
2125
1100
|
};
|
|
2126
|
-
var validateAndAdjustNextRun = (job, savedNextRun,
|
|
1101
|
+
var validateAndAdjustNextRun = (job, savedNextRun, runtime) => {
|
|
1102
|
+
const { config, lastRestartTime, logging } = runtime;
|
|
2127
1103
|
const now = /* @__PURE__ */ new Date();
|
|
2128
|
-
if (!savedNextRun) {
|
|
2129
|
-
|
|
2130
|
-
}
|
|
2131
|
-
if (savedNextRun <= now) {
|
|
2132
|
-
logger.debug(`Saved next run is in the past for ${job.name} - recalculating`);
|
|
1104
|
+
if (!savedNextRun || savedNextRun <= now) {
|
|
1105
|
+
logging.debug({ module: job.module }, `Saved next run is in the past for ${job.name} - recalculating`);
|
|
2133
1106
|
return calculateNextRunFromLastRun(job, job.lastRun, config);
|
|
2134
1107
|
}
|
|
2135
|
-
const
|
|
2136
|
-
const isRapidRestart = timeSinceRestart < config.restartBehavior.rapidRestartThreshold;
|
|
1108
|
+
const isRapidRestart = now.getTime() - lastRestartTime.getTime() < config.restartBehavior.rapidRestartThreshold;
|
|
2137
1109
|
if (isRapidRestart) {
|
|
2138
|
-
|
|
1110
|
+
logging.debug({ module: job.module }, `Rapid restart detected for ${job.name} - preserving saved timing`);
|
|
2139
1111
|
return savedNextRun;
|
|
2140
1112
|
}
|
|
2141
1113
|
if (job.cronExpression || job.intervalMs) {
|
|
2142
1114
|
const expectedNextRun = calculateNextRunFromLastRun(job, job.lastRun, config);
|
|
2143
|
-
const
|
|
2144
|
-
if (
|
|
2145
|
-
|
|
1115
|
+
const drift = Math.abs(savedNextRun.getTime() - expectedNextRun.getTime());
|
|
1116
|
+
if (drift > config.restartBehavior.maxTimingDrift) {
|
|
1117
|
+
logging.debug({ module: job.module }, `Adjusting timing for ${job.name} - drift of ${Math.round(drift / 1e3)}s detected`);
|
|
2146
1118
|
return expectedNextRun;
|
|
2147
1119
|
}
|
|
2148
1120
|
}
|
|
2149
1121
|
return savedNextRun;
|
|
2150
1122
|
};
|
|
2151
|
-
var
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
const
|
|
2163
|
-
updatedJob
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
1123
|
+
var loadJobState = async (runtime, scheduler) => {
|
|
1124
|
+
try {
|
|
1125
|
+
const stateData = await readStateData(runtime.config.persistence.directory);
|
|
1126
|
+
if (!stateData) {
|
|
1127
|
+
return scheduler;
|
|
1128
|
+
}
|
|
1129
|
+
const savedJob = stateData.jobs.find((job) => job.name === scheduler.job.name);
|
|
1130
|
+
if (!savedJob) {
|
|
1131
|
+
return scheduler;
|
|
1132
|
+
}
|
|
1133
|
+
const definitionChanged = hasJobDefinitionChanged(scheduler.job, savedJob);
|
|
1134
|
+
const lastRun = savedJob.lastRun ? new Date(savedJob.lastRun) : void 0;
|
|
1135
|
+
const updatedJob = {
|
|
1136
|
+
...scheduler.job,
|
|
1137
|
+
runCount: savedJob.runCount,
|
|
1138
|
+
lastRun
|
|
1139
|
+
};
|
|
1140
|
+
if (definitionChanged) {
|
|
1141
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job definition changed: ${savedJob.name} - recalculating schedule`);
|
|
1142
|
+
updatedJob.nextRun = calculateNextRunFromLastRun(updatedJob, lastRun, runtime.config);
|
|
1143
|
+
} else {
|
|
1144
|
+
updatedJob.nextRun = validateAndAdjustNextRun(updatedJob, savedJob.nextRun ? new Date(savedJob.nextRun) : void 0, runtime);
|
|
1145
|
+
}
|
|
1146
|
+
if (runtime.config.cron.logCronDetails && updatedJob.cronExpression) {
|
|
1147
|
+
const result = parseCronExpression(updatedJob.cronExpression, updatedJob.lastRun, { timezone: updatedJob.timezone });
|
|
1148
|
+
runtime.logging.debug(
|
|
1149
|
+
{ module: scheduler.job.module },
|
|
1150
|
+
`Job restored: ${savedJob.name} - ${result.description} - next: ${updatedJob.nextRun.toLocaleString()}`
|
|
1151
|
+
);
|
|
1152
|
+
} else {
|
|
1153
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job state restored: ${savedJob.name}`);
|
|
1154
|
+
}
|
|
1155
|
+
return { ...scheduler, job: updatedJob };
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
if (error.code === "ENOENT") {
|
|
1158
|
+
runtime.logging.debug("No saved scheduler state found - starting fresh");
|
|
1159
|
+
} else {
|
|
1160
|
+
runtime.logging.error({ error: error?.message }, `Failed to load job state for ${scheduler.job.name}`);
|
|
1161
|
+
}
|
|
1162
|
+
return scheduler;
|
|
2170
1163
|
}
|
|
2171
|
-
return { ...scheduler, job: updatedJob };
|
|
2172
|
-
};
|
|
2173
|
-
var restoreExecutionHistory = (savedExecutions) => {
|
|
2174
|
-
return savedExecutions.map((exec) => ({
|
|
2175
|
-
...exec,
|
|
2176
|
-
startTime: new Date(exec.startTime),
|
|
2177
|
-
endTime: exec.endTime ? new Date(exec.endTime) : void 0
|
|
2178
|
-
}));
|
|
2179
1164
|
};
|
|
2180
|
-
var
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
const restoredScheduler = restoreJobFromSavedData(
|
|
2190
|
-
existingScheduler,
|
|
2191
|
-
savedJob,
|
|
2192
|
-
config,
|
|
2193
|
-
lastRestartTime
|
|
2194
|
-
);
|
|
2195
|
-
restoredSchedulers[restoredScheduler.job.id] = restoredScheduler;
|
|
1165
|
+
var loadExecutionHistory = async (runtime) => {
|
|
1166
|
+
try {
|
|
1167
|
+
const stateData = await readStateData(runtime.config.persistence.directory);
|
|
1168
|
+
if (stateData?.executions) {
|
|
1169
|
+
runtime.executionHistory = restoreExecutionHistory(stateData.executions);
|
|
1170
|
+
}
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
if (error.code !== "ENOENT") {
|
|
1173
|
+
runtime.logging.error({ error: error?.message }, "Failed to load execution history");
|
|
2196
1174
|
}
|
|
2197
1175
|
}
|
|
2198
|
-
const executionHistory = stateData.executions ? restoreExecutionHistory(stateData.executions) : [];
|
|
2199
|
-
logger.debug(`Enhanced scheduler state loaded (saved at ${stateData.savedAt})`);
|
|
2200
|
-
return { schedulers: restoredSchedulers, executionHistory };
|
|
2201
1176
|
};
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
1177
|
+
var initializePersistence2 = async (runtime) => {
|
|
1178
|
+
try {
|
|
1179
|
+
await fs2.mkdir(runtime.config.persistence.directory, { recursive: true });
|
|
1180
|
+
await loadExecutionHistory(runtime);
|
|
1181
|
+
if (runtime.config.persistence.saveOnShutdown) {
|
|
1182
|
+
const shutdownHandler = async () => {
|
|
1183
|
+
runtime.logging.debug("Saving scheduler state before shutdown...");
|
|
1184
|
+
await saveSchedulerState(runtime);
|
|
1185
|
+
process.exit(0);
|
|
1186
|
+
};
|
|
1187
|
+
process.on("SIGINT", shutdownHandler);
|
|
1188
|
+
process.on("SIGTERM", shutdownHandler);
|
|
1189
|
+
}
|
|
1190
|
+
runtime.logging.debug(`Scheduler persistence initialized: ${runtime.config.persistence.directory}`);
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
runtime.logging.error({ error: error?.message }, "Failed to initialize scheduler persistence");
|
|
2212
1193
|
}
|
|
2213
|
-
return [];
|
|
2214
1194
|
};
|
|
2215
1195
|
|
|
2216
|
-
// src/schedule-plugin/
|
|
2217
|
-
var
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
directory: "./.develop/scheduler-data",
|
|
2221
|
-
saveInterval: 3e4
|
|
2222
|
-
// 30 seconds
|
|
2223
|
-
},
|
|
2224
|
-
execution: {
|
|
2225
|
-
maxConcurrent: 5,
|
|
2226
|
-
defaultTimeout: 3e5,
|
|
2227
|
-
// 5 minutes
|
|
2228
|
-
retryOnError: false,
|
|
2229
|
-
maxRetries: 3
|
|
2230
|
-
},
|
|
2231
|
-
cleanup: {
|
|
2232
|
-
maxExecutionHistory: 1e3,
|
|
2233
|
-
cleanupInterval: 6e5
|
|
2234
|
-
// 10 minutes
|
|
2235
|
-
},
|
|
2236
|
-
restartBehavior: {
|
|
2237
|
-
preserveNaturalTiming: true,
|
|
2238
|
-
skipMissedRuns: true,
|
|
2239
|
-
maxTimingDrift: 6e4,
|
|
2240
|
-
// 1 minute
|
|
2241
|
-
rapidRestartThreshold: 1e4
|
|
2242
|
-
// 10 seconds
|
|
2243
|
-
},
|
|
2244
|
-
cron: {
|
|
2245
|
-
defaultTimezone: void 0,
|
|
2246
|
-
// Use system timezone
|
|
2247
|
-
logCronDetails: true
|
|
1196
|
+
// src/schedule-plugin/processing.ts
|
|
1197
|
+
var canExecuteJob = (runtime, scheduler) => {
|
|
1198
|
+
if (runtime.isShuttingDown) {
|
|
1199
|
+
return { canExecute: false, reason: "System is shutting down" };
|
|
2248
1200
|
}
|
|
1201
|
+
if (scheduler.isRunning) {
|
|
1202
|
+
return { canExecute: false, reason: "Job is already running" };
|
|
1203
|
+
}
|
|
1204
|
+
if (shouldSkipJob(scheduler.job)) {
|
|
1205
|
+
const reason = !scheduler.job.enabled ? "Job is disabled" : "Job reached maximum runs";
|
|
1206
|
+
return { canExecute: false, reason };
|
|
1207
|
+
}
|
|
1208
|
+
if (runtime.runningExecutions >= runtime.config.execution.maxConcurrent) {
|
|
1209
|
+
return { canExecute: false, reason: "Maximum concurrent executions reached" };
|
|
1210
|
+
}
|
|
1211
|
+
return { canExecute: true };
|
|
2249
1212
|
};
|
|
2250
|
-
var
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
1213
|
+
var executeJobWithTimeout = async (handler, job, execution, timeoutMs) => {
|
|
1214
|
+
let timerId;
|
|
1215
|
+
try {
|
|
1216
|
+
return await Promise.race([
|
|
1217
|
+
handler(job, execution),
|
|
1218
|
+
new Promise((_, reject) => {
|
|
1219
|
+
timerId = setTimeout(() => reject(new Error("Job execution timeout")), timeoutMs);
|
|
1220
|
+
})
|
|
1221
|
+
]);
|
|
1222
|
+
} finally {
|
|
1223
|
+
clearTimeout(timerId);
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
var executeJob = async (runtime, scheduler) => {
|
|
1227
|
+
const execution = createJobExecution(scheduler.job.id);
|
|
1228
|
+
runtime.executions[execution.id] = execution;
|
|
1229
|
+
runtime.executionHistory.push(execution);
|
|
1230
|
+
scheduler.isRunning = true;
|
|
1231
|
+
runtime.runningExecutions++;
|
|
1232
|
+
runtime.logging.info(`Executing job ${scheduler.job.name} (${execution.id})`);
|
|
1233
|
+
try {
|
|
1234
|
+
const result = await executeJobWithTimeout(
|
|
1235
|
+
scheduler.handler,
|
|
1236
|
+
scheduler.job,
|
|
1237
|
+
execution,
|
|
1238
|
+
runtime.config.execution.defaultTimeout
|
|
1239
|
+
);
|
|
1240
|
+
Object.assign(execution, completeExecution(execution, result));
|
|
1241
|
+
const duration = execution.endTime.getTime() - execution.startTime.getTime();
|
|
1242
|
+
runtime.logging.debug(`Job completed in ${duration}ms`);
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
Object.assign(execution, completeExecution(execution, void 0, error.message));
|
|
1245
|
+
runtime.logging.error(`Job failed ${scheduler.job.name}: ${error.message}`);
|
|
1246
|
+
} finally {
|
|
1247
|
+
scheduler.isRunning = false;
|
|
1248
|
+
runtime.runningExecutions--;
|
|
1249
|
+
scheduler.job = updateJobAfterExecution(scheduler.job, execution, runtime.config);
|
|
1250
|
+
if (scheduler.job.maxRuns && scheduler.job.runCount >= scheduler.job.maxRuns) {
|
|
1251
|
+
runtime.logging.info(`Job ${scheduler.job.name} reached max runs (${scheduler.job.maxRuns})`);
|
|
2284
1252
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
state.executionHistory.push(execution);
|
|
2288
|
-
scheduler.isRunning = true;
|
|
2289
|
-
state.runningExecutions++;
|
|
2290
|
-
logger.info(`Executing job ${scheduler.job.name} (${execution.id})`);
|
|
2291
|
-
try {
|
|
2292
|
-
const result = await executeJobWithTimeout(
|
|
2293
|
-
scheduler.handler,
|
|
2294
|
-
scheduler.job,
|
|
2295
|
-
execution,
|
|
2296
|
-
state.config.execution.defaultTimeout
|
|
2297
|
-
);
|
|
2298
|
-
const completedExecution = handleExecutionSuccess(execution, result);
|
|
2299
|
-
state.executions[execution.id] = completedExecution;
|
|
2300
|
-
} catch (error) {
|
|
2301
|
-
const failedExecution = handleExecutionError(execution, error);
|
|
2302
|
-
state.executions[execution.id] = failedExecution;
|
|
2303
|
-
} finally {
|
|
2304
|
-
scheduler.isRunning = false;
|
|
2305
|
-
state.runningExecutions--;
|
|
2306
|
-
scheduler.job = updateJobAfterExecution(scheduler.job, execution, state.config);
|
|
2307
|
-
if (scheduler.job.enabled && !state.isShuttingDown) {
|
|
2308
|
-
scheduleJobFn(scheduler);
|
|
2309
|
-
}
|
|
2310
|
-
if (state.config.persistence.enabled) {
|
|
2311
|
-
state.isDirty = true;
|
|
2312
|
-
}
|
|
1253
|
+
if (runtime.config.persistence.enabled) {
|
|
1254
|
+
runtime.isDirty = true;
|
|
2313
1255
|
}
|
|
2314
|
-
}
|
|
2315
|
-
const scheduleJobFn = (scheduler) => {
|
|
2316
|
-
const updatedScheduler = scheduleJob(scheduler, state, executeJob);
|
|
2317
|
-
state.schedulers[scheduler.job.id] = updatedScheduler;
|
|
2318
|
-
return updatedScheduler;
|
|
2319
|
-
};
|
|
2320
|
-
return { executeJob, scheduleJobFn };
|
|
1256
|
+
}
|
|
2321
1257
|
};
|
|
2322
|
-
var
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
);
|
|
2331
|
-
state.schedulers = schedulers;
|
|
2332
|
-
state.executionHistory = executionHistory;
|
|
2333
|
-
}
|
|
2334
|
-
const tickInterval = (state.config.persistence.saveInterval || 0) > 0 ? state.config.persistence.saveInterval : state.config.cleanup.cleanupInterval;
|
|
2335
|
-
state.tickInterval = setInterval(async () => {
|
|
2336
|
-
if (state.isDirty && state.config.persistence.enabled) {
|
|
2337
|
-
state.isDirty = false;
|
|
2338
|
-
await saveSchedulerState(state);
|
|
2339
|
-
}
|
|
2340
|
-
if (Date.now() - state.lastCleanupTime >= state.config.cleanup.cleanupInterval) {
|
|
2341
|
-
state.lastCleanupTime = Date.now();
|
|
2342
|
-
cleanupExecutionHistory(state);
|
|
2343
|
-
}
|
|
2344
|
-
}, tickInterval);
|
|
2345
|
-
for (const scheduler of Object.values(state.schedulers)) {
|
|
2346
|
-
if (scheduler.job.enabled) {
|
|
2347
|
-
scheduleJobFn(scheduler);
|
|
2348
|
-
}
|
|
1258
|
+
var dispatchJobIfDue = (runtime, scheduler) => {
|
|
1259
|
+
if (!scheduler.job.enabled || !isJobReadyToRun(scheduler.job)) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
const { canExecute, reason } = canExecuteJob(runtime, scheduler);
|
|
1263
|
+
if (!canExecute) {
|
|
1264
|
+
if (reason === "Maximum concurrent executions reached") {
|
|
1265
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Delaying job ${scheduler.job.name} - ${reason}`);
|
|
2349
1266
|
}
|
|
2350
|
-
|
|
2351
|
-
} catch (error) {
|
|
2352
|
-
logger.error(`Failed to initialize scheduler: ${error.message}`);
|
|
2353
|
-
throw error;
|
|
1267
|
+
return;
|
|
2354
1268
|
}
|
|
1269
|
+
runtime.logging.runWithLogContext(
|
|
1270
|
+
{ module: scheduler.job.module, reqId: scheduler.job.id },
|
|
1271
|
+
() => executeJob(runtime, scheduler)
|
|
1272
|
+
);
|
|
2355
1273
|
};
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
if (expressionType === "unknown") {
|
|
2361
|
-
logger.error(`Invalid cron expression for job: ${jobDef.name}`);
|
|
2362
|
-
throw new Error(`Invalid cron expression for job: ${jobDef.name}`);
|
|
1274
|
+
var cleanupExecutionHistory = (runtime) => {
|
|
1275
|
+
const { maxExecutionHistory } = runtime.config.cleanup;
|
|
1276
|
+
if (runtime.executionHistory.length <= maxExecutionHistory) {
|
|
1277
|
+
return;
|
|
2363
1278
|
}
|
|
1279
|
+
const removed = runtime.executionHistory.splice(0, runtime.executionHistory.length - maxExecutionHistory);
|
|
1280
|
+
runtime.logging.debug(`Cleaned up ${removed.length} old execution records`);
|
|
2364
1281
|
};
|
|
2365
|
-
var
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
if (config.cron.logCronDetails && jobDef.cronExpression) {
|
|
2369
|
-
const cronExpression = expressionType === "rate" ? convertRateToCron(jobDef.cronExpression).cronExpression : jobDef.cronExpression;
|
|
2370
|
-
const result = parseCronExpression(cronExpression, void 0, {
|
|
2371
|
-
timezone: jobDef.timezone ?? config.cron.defaultTimezone
|
|
2372
|
-
});
|
|
2373
|
-
logger.debug(`Job ${jobDef.name}: ${result.description} - Next: ${result.nextRun.toLocaleString()}`);
|
|
2374
|
-
const upcoming = getNextExecutions(cronExpression, 3, {
|
|
2375
|
-
timezone: jobDef.timezone ?? config.cron.defaultTimezone
|
|
2376
|
-
});
|
|
2377
|
-
logger.debug(`Upcoming: ${upcoming.map((d) => d.toLocaleTimeString()).join(", ")}`);
|
|
1282
|
+
var maybeCleanup = (runtime, now) => {
|
|
1283
|
+
if (now - runtime.lastCleanupTime < runtime.config.cleanup.cleanupInterval) {
|
|
1284
|
+
return;
|
|
2378
1285
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
name: jobDef.name,
|
|
2382
|
-
cronExpression: expressionType === "rate" ? convertRateToCron(jobDef.cronExpression).cronExpression : jobDef.cronExpression,
|
|
2383
|
-
intervalMs: jobDef.intervalMs,
|
|
2384
|
-
delayMs: jobDef.delayMs,
|
|
2385
|
-
nextRun: /* @__PURE__ */ new Date(),
|
|
2386
|
-
enabled: jobDef.enabled ?? true,
|
|
2387
|
-
runCount: 0,
|
|
2388
|
-
maxRuns: jobDef.maxRuns,
|
|
2389
|
-
metadata: jobDef.metadata,
|
|
2390
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2391
|
-
timezone: jobDef.timezone,
|
|
2392
|
-
module: jobDef.module
|
|
2393
|
-
};
|
|
2394
|
-
job.nextRun = calculateNextRun(job);
|
|
2395
|
-
return job;
|
|
1286
|
+
runtime.lastCleanupTime = now;
|
|
1287
|
+
cleanupExecutionHistory(runtime);
|
|
2396
1288
|
};
|
|
2397
|
-
var
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
isRunning: false
|
|
2401
|
-
});
|
|
2402
|
-
var enableJob = (scheduler) => ({
|
|
2403
|
-
...scheduler,
|
|
2404
|
-
job: {
|
|
2405
|
-
...scheduler.job,
|
|
2406
|
-
enabled: true,
|
|
2407
|
-
nextRun: calculateNextRun(scheduler.job)
|
|
1289
|
+
var maybeSavePersistence2 = (runtime, now) => {
|
|
1290
|
+
if (!runtime.config.persistence.enabled || !runtime.isDirty) {
|
|
1291
|
+
return;
|
|
2408
1292
|
}
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
1293
|
+
const saveInterval = runtime.config.persistence.saveInterval ?? 0;
|
|
1294
|
+
if (saveInterval > 0 && now - runtime.lastSavedAt >= saveInterval) {
|
|
1295
|
+
runtime.lastSavedAt = now;
|
|
1296
|
+
runtime.isDirty = false;
|
|
1297
|
+
saveSchedulerState(runtime);
|
|
2413
1298
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
1299
|
+
};
|
|
1300
|
+
var tick2 = (runtime) => {
|
|
1301
|
+
const now = Date.now();
|
|
1302
|
+
for (const scheduler of Object.values(runtime.schedulers)) {
|
|
1303
|
+
dispatchJobIfDue(runtime, scheduler);
|
|
1304
|
+
}
|
|
1305
|
+
maybeCleanup(runtime, now);
|
|
1306
|
+
maybeSavePersistence2(runtime, now);
|
|
2419
1307
|
};
|
|
2420
1308
|
|
|
2421
|
-
// src/schedule-plugin/
|
|
2422
|
-
var
|
|
2423
|
-
|
|
2424
|
-
const nextExecutions = s.job.cronExpression ? getNextExecutions(s.job.cronExpression, 3, { timezone: s.job.timezone }) : [];
|
|
2425
|
-
return {
|
|
2426
|
-
...s.job,
|
|
2427
|
-
isRunning: s.isRunning,
|
|
2428
|
-
timeUntilNextRun: getTimeUntilNextRun(s.job),
|
|
2429
|
-
upcomingExecutions: nextExecutions,
|
|
2430
|
-
cronDescription: s.job.cronExpression ? parseCronExpression(s.job.cronExpression, void 0, { timezone: s.job.timezone }).description : void 0
|
|
2431
|
-
};
|
|
2432
|
-
});
|
|
2433
|
-
return { jobs, runningExecutions: state.runningExecutions };
|
|
1309
|
+
// src/schedule-plugin/decorator.ts
|
|
1310
|
+
var isValidJobName = (jobName) => {
|
|
1311
|
+
return typeof jobName === "string" && jobName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(jobName);
|
|
2434
1312
|
};
|
|
2435
|
-
var
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
const dashboardData = createDashboardData(state);
|
|
2443
|
-
return reply.status(200).send(dashboardData);
|
|
2444
|
-
});
|
|
1313
|
+
var validateJobName = (jobName) => {
|
|
1314
|
+
if (!jobName || typeof jobName !== "string") {
|
|
1315
|
+
throw new Error("Job name must be a non-empty string");
|
|
1316
|
+
}
|
|
1317
|
+
if (!isValidJobName(jobName)) {
|
|
1318
|
+
throw new Error("Job name must contain only alphanumeric characters, hyphens, and underscores");
|
|
1319
|
+
}
|
|
2445
1320
|
};
|
|
2446
|
-
var
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
});
|
|
1321
|
+
var validateHandler2 = (handler) => {
|
|
1322
|
+
if (typeof handler !== "function") {
|
|
1323
|
+
throw new Error("Handler must be a function");
|
|
1324
|
+
}
|
|
2451
1325
|
};
|
|
2452
|
-
var
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
1326
|
+
var findSchedulerByName = (runtime, jobName) => Object.values(runtime.schedulers).find((scheduler) => scheduler.job.name === jobName);
|
|
1327
|
+
var addJob = async (runtime, jobDefinition) => {
|
|
1328
|
+
try {
|
|
1329
|
+
validateJobName(jobDefinition.name);
|
|
1330
|
+
validateHandler2(jobDefinition.handler);
|
|
1331
|
+
const existing = findSchedulerByName(runtime, jobDefinition.name);
|
|
1332
|
+
if (existing) {
|
|
1333
|
+
runtime.logging.warn({ module: jobDefinition.module }, `Job already exists: ${jobDefinition.name}.`);
|
|
1334
|
+
return existing.job.id;
|
|
1335
|
+
}
|
|
1336
|
+
const job = createJobFromDefinition(jobDefinition, runtime.config);
|
|
1337
|
+
if (runtime.config.cron.logCronDetails && job.cronExpression) {
|
|
1338
|
+
const timezone = job.timezone ?? runtime.config.cron.defaultTimezone;
|
|
1339
|
+
const result = parseCronExpression(job.cronExpression, void 0, { timezone });
|
|
1340
|
+
runtime.logging.debug(
|
|
1341
|
+
{ module: jobDefinition.module },
|
|
1342
|
+
`Job ${jobDefinition.name}: ${result.description} - Next: ${result.nextRun.toLocaleString()}`
|
|
1343
|
+
);
|
|
1344
|
+
const upcoming = getNextExecutions(job.cronExpression, 3, { timezone });
|
|
1345
|
+
runtime.logging.debug(
|
|
1346
|
+
{ module: jobDefinition.module },
|
|
1347
|
+
`Upcoming: ${upcoming.map((d) => d.toLocaleTimeString()).join(", ")}`
|
|
1348
|
+
);
|
|
2457
1349
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
1350
|
+
let scheduler = createSchedulerService(job, jobDefinition.handler);
|
|
1351
|
+
if (runtime.config.persistence.enabled) {
|
|
1352
|
+
scheduler = await loadJobState(runtime, scheduler);
|
|
2460
1353
|
}
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
1354
|
+
runtime.schedulers[scheduler.job.id] = scheduler;
|
|
1355
|
+
runtime.logging.info({ module: jobDefinition.module }, `Job added: ${jobDefinition.name} (${scheduler.job.id})`);
|
|
1356
|
+
return scheduler.job.id;
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
runtime.logging.error({ module: jobDefinition.module, error: error.message }, `Failed to add job ${jobDefinition.name}`);
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
2464
1361
|
};
|
|
2465
|
-
var
|
|
2466
|
-
|
|
2467
|
-
|
|
1362
|
+
var removeJob = async (runtime, jobName) => {
|
|
1363
|
+
try {
|
|
1364
|
+
validateJobName(jobName);
|
|
1365
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
2468
1366
|
if (!scheduler) {
|
|
2469
|
-
|
|
1367
|
+
throw new Error(`Job '${jobName}' does not exist`);
|
|
2470
1368
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
scheduleJobFn(enabledScheduler);
|
|
2474
|
-
return reply.status(200).send({ message: "Job enabled successfully" });
|
|
2475
|
-
});
|
|
2476
|
-
};
|
|
2477
|
-
var registerDisableJobRoute = (app, state) => {
|
|
2478
|
-
app.put("/schedule/jobs/:jobId/disable", async function(request, reply) {
|
|
2479
|
-
const scheduler = state.schedulers[request.params.jobId];
|
|
2480
|
-
if (!scheduler) {
|
|
2481
|
-
return reply.status(404).send({ error: "Job not found" });
|
|
1369
|
+
if (scheduler.isRunning) {
|
|
1370
|
+
runtime.logging.warn({ module: scheduler.job.module }, `Removing job that is currently running: ${jobName}`);
|
|
2482
1371
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
1372
|
+
delete runtime.schedulers[scheduler.job.id];
|
|
1373
|
+
runtime.logging.info({ module: scheduler.job.module }, `Job removed: ${jobName}`);
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
runtime.logging.error({ error: error.message }, `Failed to remove job ${jobName}`);
|
|
1376
|
+
throw error;
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
var hasJob = (runtime, jobName) => {
|
|
1380
|
+
try {
|
|
1381
|
+
validateJobName(jobName);
|
|
1382
|
+
return !!findSchedulerByName(runtime, jobName);
|
|
1383
|
+
} catch {
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
2487
1386
|
};
|
|
2488
|
-
var
|
|
2489
|
-
|
|
2490
|
-
registerExecutionsRoute(app, state);
|
|
2491
|
-
registerTriggerJobRoute(app, state, executeJobFn);
|
|
2492
|
-
registerEnableJobRoute(app, state, scheduleJobFn);
|
|
2493
|
-
registerDisableJobRoute(app, state);
|
|
1387
|
+
var listJobs = (runtime, module) => {
|
|
1388
|
+
return Object.values(runtime.schedulers).filter((scheduler) => !module || scheduler.job.module === module).map((scheduler) => scheduler.job.name).sort();
|
|
2494
1389
|
};
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
1390
|
+
var getJobStats = (runtime, jobName) => {
|
|
1391
|
+
try {
|
|
1392
|
+
validateJobName(jobName);
|
|
1393
|
+
return findSchedulerByName(runtime, jobName) ?? null;
|
|
1394
|
+
} catch {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
var getJobsMap = (runtime) => {
|
|
1399
|
+
return { ...runtime.schedulers };
|
|
2499
1400
|
};
|
|
2500
|
-
var
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
}
|
|
2506
|
-
const validateJobName = (jobName) => {
|
|
2507
|
-
if (!jobName || typeof jobName !== "string") {
|
|
2508
|
-
throw new Error("Job name must be a non-empty string");
|
|
1401
|
+
var enableJob2 = async (runtime, jobName) => {
|
|
1402
|
+
try {
|
|
1403
|
+
validateJobName(jobName);
|
|
1404
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
1405
|
+
if (!scheduler) {
|
|
1406
|
+
throw new Error(`Job '${jobName}' does not exist`);
|
|
2509
1407
|
}
|
|
2510
|
-
if (
|
|
2511
|
-
|
|
1408
|
+
if (scheduler.job.enabled) {
|
|
1409
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job is already enabled: ${jobName}`);
|
|
1410
|
+
return;
|
|
2512
1411
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
1412
|
+
runtime.schedulers[scheduler.job.id] = enableJob(scheduler);
|
|
1413
|
+
runtime.logging.info({ module: scheduler.job.module }, `Job enabled: ${jobName}`);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
runtime.logging.error({ error: error.message }, `Failed to enable job ${jobName}`);
|
|
1416
|
+
throw error;
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
var disableJob2 = async (runtime, jobName) => {
|
|
1420
|
+
try {
|
|
1421
|
+
validateJobName(jobName);
|
|
1422
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
1423
|
+
if (!scheduler) {
|
|
1424
|
+
throw new Error(`Job '${jobName}' does not exist`);
|
|
2517
1425
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
validateJobName(jobDefinition.name);
|
|
2522
|
-
validateHandler(jobDefinition.handler);
|
|
2523
|
-
validateJobDefinition(jobDefinition);
|
|
2524
|
-
const existingJob = Object.values(state.schedulers).find((s) => s.job.name === jobDefinition.name);
|
|
2525
|
-
if (existingJob) {
|
|
2526
|
-
logger.warn(`Job already exists: ${jobDefinition.name}.`);
|
|
2527
|
-
return existingJob.job.id;
|
|
2528
|
-
}
|
|
2529
|
-
const job = createJobFromDefinition(jobDefinition, config);
|
|
2530
|
-
const scheduler = createSchedulerService(job, jobDefinition.handler);
|
|
2531
|
-
state.schedulers[job.id] = scheduler;
|
|
2532
|
-
if (job.enabled) {
|
|
2533
|
-
scheduleJobFn(scheduler);
|
|
2534
|
-
}
|
|
2535
|
-
logger.info(`Job added: ${jobDefinition.name} (${job.id})`);
|
|
2536
|
-
return job.id;
|
|
2537
|
-
} catch (error) {
|
|
2538
|
-
logger.error(`Failed to add job ${jobDefinition.name}: ${error.message}`);
|
|
2539
|
-
throw error;
|
|
1426
|
+
if (!scheduler.job.enabled) {
|
|
1427
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job is already disabled: ${jobName}`);
|
|
1428
|
+
return;
|
|
2540
1429
|
}
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
}
|
|
2555
|
-
delete state.schedulers[scheduler.job.id];
|
|
2556
|
-
logger.info(`Job removed: ${jobName}`);
|
|
2557
|
-
} catch (error) {
|
|
2558
|
-
logger.error(`Failed to remove job ${jobName}: ${error.message}`);
|
|
2559
|
-
throw error;
|
|
1430
|
+
runtime.schedulers[scheduler.job.id] = disableJob(scheduler);
|
|
1431
|
+
runtime.logging.info({ module: scheduler.job.module }, `Job disabled: ${jobName}`);
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
runtime.logging.error({ error: error.message }, `Failed to disable job ${jobName}`);
|
|
1434
|
+
throw error;
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
var triggerJob = async (runtime, jobName) => {
|
|
1438
|
+
try {
|
|
1439
|
+
validateJobName(jobName);
|
|
1440
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
1441
|
+
if (!scheduler) {
|
|
1442
|
+
throw new Error(`Job '${jobName}' does not exist`);
|
|
2560
1443
|
}
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
try {
|
|
2564
|
-
validateJobName(jobName);
|
|
2565
|
-
return Object.values(state.schedulers).some((s) => s.job.name === jobName);
|
|
2566
|
-
} catch {
|
|
2567
|
-
return false;
|
|
1444
|
+
if (scheduler.isRunning) {
|
|
1445
|
+
throw new Error(`Job '${jobName}' is already running`);
|
|
2568
1446
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
1447
|
+
runtime.logging.info({ module: scheduler.job.module }, `Job triggered: ${jobName}`);
|
|
1448
|
+
runtime.logging.runWithLogContext(
|
|
1449
|
+
{ module: scheduler.job.module, reqId: scheduler.job.id },
|
|
1450
|
+
() => executeJob(runtime, scheduler)
|
|
1451
|
+
);
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
runtime.logging.error({ error: error.message }, `Failed to trigger job ${jobName}`);
|
|
1454
|
+
throw error;
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
var getDashboardSummary2 = (runtime) => createDashboardSummary2(runtime);
|
|
1458
|
+
var getExecutionsSummary = (runtime) => createExecutionsSummary(runtime);
|
|
1459
|
+
var getJobExecutionHistory = (runtime, jobName, limit = 50) => {
|
|
1460
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
1461
|
+
if (!scheduler) {
|
|
1462
|
+
return [];
|
|
1463
|
+
}
|
|
1464
|
+
return runtime.executionHistory.filter((execution) => execution.jobId === scheduler.job.id).slice(-limit).reverse();
|
|
1465
|
+
};
|
|
1466
|
+
var createSchedulerManager = (runtime) => ({
|
|
1467
|
+
addJob: (jobDefinition) => addJob(runtime, jobDefinition),
|
|
1468
|
+
removeJob: (jobName) => removeJob(runtime, jobName),
|
|
1469
|
+
hasJob: (jobName) => hasJob(runtime, jobName),
|
|
1470
|
+
listJobs: (module) => listJobs(runtime, module),
|
|
1471
|
+
getJobStats: (jobName) => getJobStats(runtime, jobName),
|
|
1472
|
+
getJobsMap: () => getJobsMap(runtime),
|
|
1473
|
+
getConfig: () => runtime.config,
|
|
1474
|
+
enableJob: (jobName) => enableJob2(runtime, jobName),
|
|
1475
|
+
disableJob: (jobName) => disableJob2(runtime, jobName),
|
|
1476
|
+
triggerJob: (jobName) => triggerJob(runtime, jobName),
|
|
1477
|
+
getDashboardSummary: () => getDashboardSummary2(runtime),
|
|
1478
|
+
getExecutionsSummary: () => getExecutionsSummary(runtime),
|
|
1479
|
+
getJobExecutionHistory: (jobName, limit) => getJobExecutionHistory(runtime, jobName, limit)
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
// src/schedule-plugin/routes.ts
|
|
1483
|
+
var registerSchedulerRoutes = (app, prefix = "schedule") => {
|
|
1484
|
+
app.get(
|
|
1485
|
+
`/${prefix}/dashboard`,
|
|
1486
|
+
async (_, reply) => {
|
|
1487
|
+
return reply.status(200).send(app.scheduler.getDashboardSummary());
|
|
2580
1488
|
}
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
if (!scheduler) {
|
|
2587
|
-
throw new Error(`Job '${jobName}' does not exist`);
|
|
2588
|
-
}
|
|
2589
|
-
if (scheduler.job.enabled) {
|
|
2590
|
-
logger.debug(`Job is already enabled: ${jobName}`);
|
|
2591
|
-
return;
|
|
2592
|
-
}
|
|
2593
|
-
scheduler.job.enabled = true;
|
|
2594
|
-
scheduleJobFn(scheduler);
|
|
2595
|
-
logger.info(`Job enabled: ${jobName}`);
|
|
2596
|
-
} catch (error) {
|
|
2597
|
-
logger.error(`Failed to enable job ${jobName}: ${error.message}`);
|
|
2598
|
-
throw error;
|
|
1489
|
+
);
|
|
1490
|
+
app.get(
|
|
1491
|
+
`/${prefix}/executions`,
|
|
1492
|
+
async (_, reply) => {
|
|
1493
|
+
return reply.status(200).send(app.scheduler.getExecutionsSummary());
|
|
2599
1494
|
}
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
}
|
|
2608
|
-
|
|
2609
|
-
logger.debug(`Job is already disabled: ${jobName}`);
|
|
2610
|
-
return;
|
|
2611
|
-
}
|
|
2612
|
-
scheduler.job.enabled = false;
|
|
2613
|
-
if (scheduler.timerId) {
|
|
2614
|
-
clearTimeout(scheduler.timerId);
|
|
2615
|
-
scheduler.timerId = void 0;
|
|
1495
|
+
);
|
|
1496
|
+
app.post(
|
|
1497
|
+
`/${prefix}/jobs/:jobName/trigger`,
|
|
1498
|
+
async (request, reply) => {
|
|
1499
|
+
try {
|
|
1500
|
+
await app.scheduler.triggerJob(request.params.jobName);
|
|
1501
|
+
return reply.status(200).send({ message: "Job triggered successfully" });
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
return reply.status(404).send({ error: error.message });
|
|
2616
1504
|
}
|
|
2617
|
-
logger.info(`Job disabled: ${jobName}`);
|
|
2618
|
-
} catch (error) {
|
|
2619
|
-
logger.error(`Failed to disable job ${jobName}: ${error.message}`);
|
|
2620
|
-
throw error;
|
|
2621
1505
|
}
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
1506
|
+
);
|
|
1507
|
+
app.put(
|
|
1508
|
+
`/${prefix}/jobs/:jobName/enable`,
|
|
1509
|
+
async (request, reply) => {
|
|
1510
|
+
try {
|
|
1511
|
+
await app.scheduler.enableJob(request.params.jobName);
|
|
1512
|
+
return reply.status(200).send({ message: "Job enabled successfully" });
|
|
1513
|
+
} catch (error) {
|
|
1514
|
+
return reply.status(404).send({ error: error.message });
|
|
2629
1515
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
1516
|
+
}
|
|
1517
|
+
);
|
|
1518
|
+
app.put(
|
|
1519
|
+
`/${prefix}/jobs/:jobName/disable`,
|
|
1520
|
+
async (request, reply) => {
|
|
1521
|
+
try {
|
|
1522
|
+
await app.scheduler.disableJob(request.params.jobName);
|
|
1523
|
+
return reply.status(200).send({ message: "Job disabled successfully" });
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
return reply.status(404).send({ error: error.message });
|
|
2632
1526
|
}
|
|
2633
|
-
setImmediate(() => executeJob(scheduler));
|
|
2634
|
-
logger.info(`Job triggered: ${jobName}`);
|
|
2635
|
-
} catch (error) {
|
|
2636
|
-
logger.error(`Failed to trigger job ${jobName}: ${error.message}`);
|
|
2637
|
-
throw error;
|
|
2638
1527
|
}
|
|
2639
|
-
|
|
2640
|
-
const getJobsMap = () => {
|
|
2641
|
-
return { ...state.schedulers };
|
|
2642
|
-
};
|
|
2643
|
-
return {
|
|
2644
|
-
addJob,
|
|
2645
|
-
removeJob,
|
|
2646
|
-
hasJob,
|
|
2647
|
-
listJobs,
|
|
2648
|
-
getJobStats,
|
|
2649
|
-
enableJob: enableJob2,
|
|
2650
|
-
disableJob: disableJob2,
|
|
2651
|
-
triggerJob,
|
|
2652
|
-
getJobsMap
|
|
2653
|
-
};
|
|
2654
|
-
};
|
|
2655
|
-
var createSchedulerDecorator = (schedulerManager) => (app) => {
|
|
2656
|
-
app.decorate("scheduler", schedulerManager);
|
|
1528
|
+
);
|
|
2657
1529
|
};
|
|
2658
1530
|
|
|
2659
1531
|
// src/schedule-plugin/plugin.ts
|
|
2660
1532
|
var schedulerPlugin = fsPlugin2(async (app, options) => {
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
1533
|
+
const runtime = {
|
|
1534
|
+
config: mergeConfig2(DEFAULT_CONFIG2, options.config),
|
|
1535
|
+
schedulers: {},
|
|
1536
|
+
executions: {},
|
|
1537
|
+
executionHistory: [],
|
|
1538
|
+
isShuttingDown: false,
|
|
1539
|
+
runningExecutions: 0,
|
|
1540
|
+
isDirty: false,
|
|
1541
|
+
lastCleanupTime: Date.now(),
|
|
1542
|
+
lastSavedAt: Date.now(),
|
|
1543
|
+
lastRestartTime: /* @__PURE__ */ new Date(),
|
|
1544
|
+
logging: app.logging
|
|
1545
|
+
};
|
|
1546
|
+
const tickIntervalId = setInterval(() => tick2(runtime), runtime.config.tickIntervalMs);
|
|
1547
|
+
app.addHook("onClose", async () => {
|
|
1548
|
+
runtime.isShuttingDown = true;
|
|
1549
|
+
clearInterval(tickIntervalId);
|
|
2670
1550
|
});
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
1551
|
+
app.decorate("scheduler", createSchedulerManager(runtime));
|
|
1552
|
+
registerSchedulerRoutes(app, options.prefix);
|
|
1553
|
+
if (runtime.config.persistence.enabled) {
|
|
1554
|
+
await initializePersistence2(runtime);
|
|
1555
|
+
}
|
|
2674
1556
|
});
|
|
2675
1557
|
|
|
2676
1558
|
// src/websocket-plugin/plugin.ts
|
|
@@ -2699,15 +1581,20 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2699
1581
|
preHandler: async (request, reply) => {
|
|
2700
1582
|
const connectionId = crypto.randomUUID();
|
|
2701
1583
|
request._wsConnectionId = connectionId;
|
|
1584
|
+
const reqId = `${request.id}:${connectionId}`;
|
|
2702
1585
|
const connectHandlers = handlers.filter((h) => h.path === path4 && h.event === "connect");
|
|
2703
1586
|
for (const h of connectHandlers) {
|
|
2704
|
-
await
|
|
1587
|
+
await app.logging.runWithLogContext(
|
|
1588
|
+
{ module: h.module, reqId },
|
|
1589
|
+
() => h.handler(connectionId, "connect", null, request, reply)
|
|
1590
|
+
);
|
|
2705
1591
|
}
|
|
2706
1592
|
if (reply.statusCode !== 200)
|
|
2707
1593
|
return reply;
|
|
2708
1594
|
}
|
|
2709
1595
|
}, (socket, request) => {
|
|
2710
1596
|
const connectionId = request._wsConnectionId ?? crypto.randomUUID();
|
|
1597
|
+
const reqId = `${request.id}:${connectionId}`;
|
|
2711
1598
|
const connection = {
|
|
2712
1599
|
connectionId,
|
|
2713
1600
|
socket,
|
|
@@ -2732,9 +1619,10 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2732
1619
|
);
|
|
2733
1620
|
for (const h of routeHandlers) {
|
|
2734
1621
|
matched = true;
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
1622
|
+
app.logging.runWithLogContext(
|
|
1623
|
+
{ module: h.module, reqId },
|
|
1624
|
+
() => h.handler(connectionId, "message", data, request).catch((e) => app.logging.error(e))
|
|
1625
|
+
);
|
|
2738
1626
|
}
|
|
2739
1627
|
}
|
|
2740
1628
|
if (!matched) {
|
|
@@ -2742,18 +1630,20 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2742
1630
|
(h) => h.event === "message" && !h.route
|
|
2743
1631
|
);
|
|
2744
1632
|
for (const h of defaultHandlers) {
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
1633
|
+
app.logging.runWithLogContext(
|
|
1634
|
+
{ module: h.module, reqId },
|
|
1635
|
+
() => h.handler(connectionId, "message", data, request).catch((e) => app.logging.error(e))
|
|
1636
|
+
);
|
|
2748
1637
|
}
|
|
2749
1638
|
}
|
|
2750
1639
|
});
|
|
2751
1640
|
socket.on("close", () => {
|
|
2752
1641
|
const disconnectHandlers = pathHandlers().filter((h) => h.event === "disconnect");
|
|
2753
1642
|
for (const h of disconnectHandlers) {
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
1643
|
+
app.logging.runWithLogContext(
|
|
1644
|
+
{ module: h.module, reqId },
|
|
1645
|
+
() => h.handler(connectionId, "disconnect", null, request).catch((e) => app.logging.error(e))
|
|
1646
|
+
);
|
|
2757
1647
|
}
|
|
2758
1648
|
connections.delete(connectionId);
|
|
2759
1649
|
});
|
|
@@ -2865,10 +1755,29 @@ var LogStore = class {
|
|
|
2865
1755
|
}
|
|
2866
1756
|
};
|
|
2867
1757
|
var logStore = new LogStore();
|
|
1758
|
+
var HttpHistoryStore = class {
|
|
1759
|
+
entries = /* @__PURE__ */ new Map();
|
|
1760
|
+
maxPerRoute = 50;
|
|
1761
|
+
add(entry) {
|
|
1762
|
+
const key = `${entry.method} ${entry.routeUrl}`;
|
|
1763
|
+
const existing = this.entries.get(key) ?? [];
|
|
1764
|
+
existing.unshift(entry);
|
|
1765
|
+
if (existing.length > this.maxPerRoute) {
|
|
1766
|
+
existing.length = this.maxPerRoute;
|
|
1767
|
+
}
|
|
1768
|
+
this.entries.set(key, existing);
|
|
1769
|
+
}
|
|
1770
|
+
get(method, routeUrl) {
|
|
1771
|
+
return this.entries.get(`${method.toUpperCase()} ${routeUrl}`) ?? [];
|
|
1772
|
+
}
|
|
1773
|
+
clear() {
|
|
1774
|
+
this.entries.clear();
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
var httpHistoryStore = new HttpHistoryStore();
|
|
2868
1778
|
async function devConsolePluginFunction(fastify, options = {}) {
|
|
2869
1779
|
const { prefix = "console", enableUI = true } = options;
|
|
2870
|
-
const
|
|
2871
|
-
setWriter((entry) => {
|
|
1780
|
+
const unsubscribe = fastify.logging.subscribeToLogs((entry) => {
|
|
2872
1781
|
logStore.addLog({
|
|
2873
1782
|
id: Date.now().toString() + Math.random().toString(36).slice(2, 9),
|
|
2874
1783
|
timestamp: new Date(entry.time),
|
|
@@ -2878,11 +1787,11 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2878
1787
|
module: entry.module,
|
|
2879
1788
|
reqId: entry.reqId
|
|
2880
1789
|
});
|
|
2881
|
-
upstream(entry);
|
|
2882
1790
|
});
|
|
2883
1791
|
fastify.addHook("onClose", (_instance, done) => {
|
|
2884
|
-
|
|
1792
|
+
unsubscribe();
|
|
2885
1793
|
logStore.clear();
|
|
1794
|
+
httpHistoryStore.clear();
|
|
2886
1795
|
done();
|
|
2887
1796
|
});
|
|
2888
1797
|
fastify.addHook("onRoute", (routeOptions) => {
|
|
@@ -2890,6 +1799,29 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2890
1799
|
routeRegistry.register(routeOptions);
|
|
2891
1800
|
}
|
|
2892
1801
|
});
|
|
1802
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
1803
|
+
request._historyStart = Date.now();
|
|
1804
|
+
done();
|
|
1805
|
+
});
|
|
1806
|
+
fastify.addHook("onResponse", (request, reply, done) => {
|
|
1807
|
+
const routeUrl = request.routeOptions.url;
|
|
1808
|
+
if (routeUrl?.startsWith("/api")) {
|
|
1809
|
+
const start = request._historyStart;
|
|
1810
|
+
httpHistoryStore.add({
|
|
1811
|
+
id: Date.now().toString() + Math.random().toString(36).slice(2, 9),
|
|
1812
|
+
method: request.method,
|
|
1813
|
+
routeUrl,
|
|
1814
|
+
url: request.url,
|
|
1815
|
+
statusCode: reply.statusCode,
|
|
1816
|
+
duration: start ? Date.now() - start : 0,
|
|
1817
|
+
reqId: request.id,
|
|
1818
|
+
requestHeaders: request.headers,
|
|
1819
|
+
requestBody: request.body,
|
|
1820
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
done();
|
|
1824
|
+
});
|
|
2893
1825
|
fastify.get(`/${prefix}/routes`, async () => {
|
|
2894
1826
|
return { routes: routeRegistry.getAll() };
|
|
2895
1827
|
});
|
|
@@ -2897,6 +1829,13 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2897
1829
|
const { method } = request.params;
|
|
2898
1830
|
return { routes: routeRegistry.getByMethod(method) };
|
|
2899
1831
|
});
|
|
1832
|
+
fastify.get(`/${prefix}/routes/history`, async (request, reply) => {
|
|
1833
|
+
const { method, url } = request.query;
|
|
1834
|
+
if (!method || !url) {
|
|
1835
|
+
return reply.status(400).send({ error: "method and url query params are required" });
|
|
1836
|
+
}
|
|
1837
|
+
return { history: httpHistoryStore.get(method, url) };
|
|
1838
|
+
});
|
|
2900
1839
|
fastify.get(`/${prefix}/registry/stats`, async () => {
|
|
2901
1840
|
return {
|
|
2902
1841
|
totalRoutes: routeRegistry.count(),
|
|
@@ -3013,6 +1952,17 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
3013
1952
|
}
|
|
3014
1953
|
};
|
|
3015
1954
|
});
|
|
1955
|
+
fastify.get(`/${prefix}/queues/:queueName/history`, async (request, reply) => {
|
|
1956
|
+
const { queueName } = request.params;
|
|
1957
|
+
const queueManager = fastify.queues;
|
|
1958
|
+
if (!queueManager) {
|
|
1959
|
+
return reply.status(503).send({ error: "Queue service not available" });
|
|
1960
|
+
}
|
|
1961
|
+
if (!queueManager.getQueueStats(queueName)) {
|
|
1962
|
+
return reply.status(404).send({ error: "Queue not found" });
|
|
1963
|
+
}
|
|
1964
|
+
return { history: queueManager.getMessageHistory(queueName) };
|
|
1965
|
+
});
|
|
3016
1966
|
fastify.get(`/${prefix}/queues/:queueName/process-dlq`, async (request, reply) => {
|
|
3017
1967
|
const { queueName } = request.params;
|
|
3018
1968
|
const queueManager = fastify.queues;
|
|
@@ -3142,6 +2092,17 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
3142
2092
|
timerId: jobStats.timerId ? "scheduled" : "not_scheduled"
|
|
3143
2093
|
};
|
|
3144
2094
|
});
|
|
2095
|
+
fastify.get(`/${prefix}/schedules/:jobName/executions`, async (request, reply) => {
|
|
2096
|
+
const { jobName } = request.params;
|
|
2097
|
+
const schedulerManager = fastify.scheduler;
|
|
2098
|
+
if (!schedulerManager) {
|
|
2099
|
+
return reply.status(503).send({ error: "Scheduler service not available" });
|
|
2100
|
+
}
|
|
2101
|
+
if (!schedulerManager.getJobStats(jobName)) {
|
|
2102
|
+
return reply.status(404).send({ error: "Job not found" });
|
|
2103
|
+
}
|
|
2104
|
+
return { executions: schedulerManager.getJobExecutionHistory(jobName) };
|
|
2105
|
+
});
|
|
3145
2106
|
fastify.post(`/${prefix}/schedules/:jobName/trigger`, async (request, reply) => {
|
|
3146
2107
|
const { jobName } = request.params;
|
|
3147
2108
|
const schedulerManager = fastify.scheduler;
|
|
@@ -3261,21 +2222,30 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
3261
2222
|
}
|
|
3262
2223
|
});
|
|
3263
2224
|
fastify.get(`/${prefix}/logs/stream`, async (request, reply) => {
|
|
2225
|
+
const { level, module, reqId } = request.query;
|
|
2226
|
+
const matchesFilters = (log) => {
|
|
2227
|
+
if (level && log.levelName !== level) return false;
|
|
2228
|
+
if (module && log.module !== module) return false;
|
|
2229
|
+
if (reqId && log.reqId !== reqId) return false;
|
|
2230
|
+
return true;
|
|
2231
|
+
};
|
|
3264
2232
|
reply.type("text/event-stream");
|
|
3265
2233
|
reply.header("Cache-Control", "no-cache");
|
|
3266
2234
|
reply.header("Connection", "keep-alive");
|
|
3267
2235
|
reply.header("Access-Control-Allow-Origin", "*");
|
|
3268
|
-
const initialLogs = logStore.getLogs(50);
|
|
2236
|
+
const initialLogs = logStore.getLogs(50).filter(matchesFilters);
|
|
3269
2237
|
reply.raw.write(`data: ${JSON.stringify({ type: "initial", logs: initialLogs })}
|
|
3270
2238
|
|
|
3271
2239
|
`);
|
|
3272
|
-
const
|
|
3273
|
-
|
|
2240
|
+
const unsubscribe2 = logStore.subscribe((log) => {
|
|
2241
|
+
if (matchesFilters(log)) {
|
|
2242
|
+
reply.raw.write(`data: ${JSON.stringify({ type: "log", log })}
|
|
3274
2243
|
|
|
3275
2244
|
`);
|
|
2245
|
+
}
|
|
3276
2246
|
});
|
|
3277
2247
|
request.raw.on("close", () => {
|
|
3278
|
-
|
|
2248
|
+
unsubscribe2();
|
|
3279
2249
|
});
|
|
3280
2250
|
});
|
|
3281
2251
|
if (enableUI) {
|
|
@@ -3299,17 +2269,4 @@ export {
|
|
|
3299
2269
|
devConsolePlugin,
|
|
3300
2270
|
routeRegistry
|
|
3301
2271
|
};
|
|
3302
|
-
/*! Bundled license information:
|
|
3303
|
-
|
|
3304
|
-
lodash-es/lodash.js:
|
|
3305
|
-
(**
|
|
3306
|
-
* @license
|
|
3307
|
-
* Lodash (Custom Build) <https://lodash.com/>
|
|
3308
|
-
* Build: `lodash modularize exports="es" -o ./`
|
|
3309
|
-
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
|
3310
|
-
* Released under MIT license <https://lodash.com/license>
|
|
3311
|
-
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
|
3312
|
-
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
3313
|
-
*)
|
|
3314
|
-
*/
|
|
3315
2272
|
//# sourceMappingURL=index.js.map
|