@cloudnux/local-cloud-provider 0.17.0 → 0.19.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 +1023 -2031
- package/dist/dev-console-plugin/index.js.map +1 -1
- package/dist/index.d.ts +19 -4
- package/dist/index.js +161 -195
- package/dist/index.js.map +1 -1
- package/dist/queue-plugin/index.d.ts +36 -9
- package/dist/queue-plugin/index.js +363 -1422
- 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 +21 -1031
- package/dist/websocket-plugin/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,1019 +1,15 @@
|
|
|
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";
|
|
1010
4
|
import fsPlugin4 from "fastify-plugin";
|
|
1011
5
|
import { createRequire } from "module";
|
|
6
|
+
import { WebSocketConnectionGoneError as WebSocketConnectionGoneError2 } from "@cloudnux/core-cloud-provider";
|
|
1012
7
|
|
|
1013
8
|
// src/queue-plugin/plugin.ts
|
|
1014
9
|
import fsPlugin from "fastify-plugin";
|
|
1015
10
|
|
|
1016
11
|
// src/queue-plugin/core.ts
|
|
12
|
+
var MAX_MESSAGE_HISTORY = 500;
|
|
1017
13
|
var DEFAULT_CONFIG = {
|
|
1018
14
|
batchSize: 10,
|
|
1019
15
|
batchWindowMs: 500,
|
|
@@ -1021,12 +17,12 @@ var DEFAULT_CONFIG = {
|
|
|
1021
17
|
parallel: true,
|
|
1022
18
|
maxConcurrent: 5,
|
|
1023
19
|
retryBackoff: true,
|
|
20
|
+
tickIntervalMs: 50,
|
|
1024
21
|
persistence: {
|
|
1025
22
|
enabled: true,
|
|
1026
23
|
directory: "./.develop/queue-data",
|
|
1027
24
|
saveInterval: 6e4,
|
|
1028
|
-
saveOnShutdown: false
|
|
1029
|
-
loadOnStartup: true
|
|
25
|
+
saveOnShutdown: false
|
|
1030
26
|
}
|
|
1031
27
|
};
|
|
1032
28
|
var mergeConfig = (defaultConfig, userConfig) => ({
|
|
@@ -1042,22 +38,46 @@ var createQueueService = (handler, module) => ({
|
|
|
1042
38
|
incoming: [],
|
|
1043
39
|
processing: [],
|
|
1044
40
|
dlq: [],
|
|
1045
|
-
timeoutId: null,
|
|
1046
41
|
processingBatch: false,
|
|
1047
42
|
module
|
|
1048
43
|
});
|
|
1049
44
|
var createQueueMessage = (body, headers) => {
|
|
1050
45
|
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
1051
|
-
|
|
46
|
+
const message = {
|
|
1052
47
|
id,
|
|
1053
48
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1054
49
|
attempts: 0,
|
|
1055
50
|
payload: body,
|
|
1056
51
|
attributes: headers
|
|
1057
52
|
};
|
|
53
|
+
const delaySeconds = Number(headers?.["x-delay-seconds"]);
|
|
54
|
+
if (delaySeconds > 0) {
|
|
55
|
+
message.nextAttempt = new Date(Date.now() + delaySeconds * 1e3);
|
|
56
|
+
}
|
|
57
|
+
return message;
|
|
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
|
+
};
|
|
65
|
+
var isMessageReady = (message, now = Date.now()) => {
|
|
66
|
+
return !message.nextAttempt || message.nextAttempt.getTime() <= now;
|
|
1058
67
|
};
|
|
1059
68
|
var moveToProcessing = (queueService, batchSize) => {
|
|
1060
|
-
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
const ready = [];
|
|
71
|
+
const remaining = [];
|
|
72
|
+
for (const message of queueService.incoming) {
|
|
73
|
+
if (ready.length < batchSize && isMessageReady(message, now)) {
|
|
74
|
+
ready.push(message);
|
|
75
|
+
} else {
|
|
76
|
+
remaining.push(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
queueService.incoming = remaining;
|
|
80
|
+
return ready;
|
|
1061
81
|
};
|
|
1062
82
|
var removeFromProcessing = (queueService, messageId) => {
|
|
1063
83
|
queueService.processing = queueService.processing.filter((msg) => msg.id !== messageId);
|
|
@@ -1070,21 +90,9 @@ var moveToDLQ = (queueService, message, error) => {
|
|
|
1070
90
|
});
|
|
1071
91
|
removeFromProcessing(queueService, message.id);
|
|
1072
92
|
};
|
|
1073
|
-
var incrementAttempts = (queueService, messageId) => {
|
|
1074
|
-
const index = queueService.processing.findIndex((m) => m.id === messageId);
|
|
1075
|
-
if (index >= 0) {
|
|
1076
|
-
queueService.processing[index].attempts += 1;
|
|
1077
|
-
return queueService.processing[index];
|
|
1078
|
-
}
|
|
1079
|
-
return null;
|
|
1080
|
-
};
|
|
1081
93
|
var calculateBackoffDelay = (attempts) => {
|
|
1082
94
|
return Math.pow(2, attempts) * 100;
|
|
1083
95
|
};
|
|
1084
|
-
var setNextAttemptTime = (message, delayMs) => ({
|
|
1085
|
-
...message,
|
|
1086
|
-
nextAttempt: new Date(Date.now() + delayMs)
|
|
1087
|
-
});
|
|
1088
96
|
var createQueueSummary = (queueService, config) => ({
|
|
1089
97
|
incoming: queueService.incoming.length,
|
|
1090
98
|
processing: queueService.processing.length,
|
|
@@ -1124,90 +132,21 @@ var purgeDLQ = (queueService) => {
|
|
|
1124
132
|
queueService.dlq = [];
|
|
1125
133
|
return dlqCount;
|
|
1126
134
|
};
|
|
1127
|
-
var
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
try {
|
|
1143
|
-
const result = await queueService.handler(message);
|
|
1144
|
-
if (result?.failureId) {
|
|
1145
|
-
await handleProcessingError(queueName, message, queueService, config);
|
|
1146
|
-
} else {
|
|
1147
|
-
removeFromProcessing(queueService, message.id);
|
|
1148
|
-
logSuccess("Successfully processed message", message.id, queueName);
|
|
1149
|
-
}
|
|
1150
|
-
} catch {
|
|
1151
|
-
await handleProcessingError(queueName, message, queueService, config);
|
|
1152
|
-
}
|
|
1153
|
-
};
|
|
1154
|
-
var handleProcessingError = async (queueName, message, queueService, config) => {
|
|
1155
|
-
logError("Error processing message", message.id, queueName, "handler returned failure");
|
|
1156
|
-
if (message.attempts >= config.maxRetries) {
|
|
1157
|
-
moveToDLQ(queueService, message, "max retries exceeded");
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
const updatedMessage = incrementAttempts(queueService, message.id);
|
|
1161
|
-
if (!updatedMessage) return;
|
|
1162
|
-
if (config.retryBackoff) {
|
|
1163
|
-
const delayMs = calculateBackoffDelay(updatedMessage.attempts);
|
|
1164
|
-
const messageWithNextAttempt = setNextAttemptTime(updatedMessage, delayMs);
|
|
1165
|
-
logRetryScheduled(message.id, delayMs);
|
|
1166
|
-
setTimeout(() => {
|
|
1167
|
-
createProcessMessageHandler(config)(queueName, messageWithNextAttempt, queueService);
|
|
1168
|
-
}, delayMs);
|
|
1169
|
-
}
|
|
1170
|
-
};
|
|
1171
|
-
var createBatchProcessor = (processMessage, config, dirtyQueues) => async (queueName, queueService) => {
|
|
1172
|
-
if (queueService.timeoutId) {
|
|
1173
|
-
clearTimeout(queueService.timeoutId);
|
|
1174
|
-
queueService.timeoutId = null;
|
|
1175
|
-
}
|
|
1176
|
-
if (queueService.processingBatch) {
|
|
1177
|
-
return;
|
|
1178
|
-
}
|
|
1179
|
-
try {
|
|
1180
|
-
queueService.processingBatch = true;
|
|
1181
|
-
const messagesToProcess = moveToProcessing(queueService, config.batchSize);
|
|
1182
|
-
if (messagesToProcess.length === 0) {
|
|
1183
|
-
return;
|
|
1184
|
-
}
|
|
1185
|
-
queueService.processing.push(...messagesToProcess);
|
|
1186
|
-
await Promise.all(messagesToProcess.map((message) => {
|
|
1187
|
-
return processMessage(queueName, message, queueService);
|
|
1188
|
-
}));
|
|
1189
|
-
if (config.persistence.enabled && dirtyQueues) {
|
|
1190
|
-
dirtyQueues.add(queueName);
|
|
1191
|
-
}
|
|
1192
|
-
} finally {
|
|
1193
|
-
queueService.processingBatch = false;
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
var createProcessingScheduler = (processBatch, config) => (queueName, queueService, overrideDelay = null) => {
|
|
1197
|
-
if (!queueService.timeoutId) {
|
|
1198
|
-
const delay = overrideDelay ?? config.batchWindowMs;
|
|
1199
|
-
queueService.timeoutId = setTimeout(() => {
|
|
1200
|
-
processBatch(queueName, queueService);
|
|
1201
|
-
}, delay);
|
|
1202
|
-
}
|
|
1203
|
-
};
|
|
1204
|
-
var handleImmediateProcessing = (queueService, config, processBatch, queueName) => {
|
|
1205
|
-
if (queueService.incoming.length >= config.batchSize) {
|
|
1206
|
-
if (queueService.timeoutId) {
|
|
1207
|
-
clearTimeout(queueService.timeoutId);
|
|
1208
|
-
queueService.timeoutId = null;
|
|
1209
|
-
}
|
|
1210
|
-
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);
|
|
1211
150
|
}
|
|
1212
151
|
};
|
|
1213
152
|
|
|
@@ -1227,123 +166,358 @@ var restoreDates = (messages) => {
|
|
|
1227
166
|
if (msg.failedAt) msg.failedAt = new Date(msg.failedAt);
|
|
1228
167
|
});
|
|
1229
168
|
};
|
|
1230
|
-
var
|
|
169
|
+
var saveQueueState = async (runtime, queueName, queueService) => {
|
|
1231
170
|
try {
|
|
1232
|
-
await fs.mkdir(config.persistence.directory, { recursive: true });
|
|
1233
|
-
if (config.persistence.loadOnStartup) {
|
|
1234
|
-
await loadAllQueueStates();
|
|
1235
|
-
}
|
|
1236
|
-
if (config.persistence.saveInterval && config.persistence.saveInterval > 0) {
|
|
1237
|
-
intervalIdHolder.id = setInterval(saveDirtyQueueStates, config.persistence.saveInterval);
|
|
1238
|
-
}
|
|
1239
|
-
if (config.persistence.saveOnShutdown) {
|
|
1240
|
-
const shutdownHandler = async () => {
|
|
1241
|
-
logger.debug("Saving queue state before shutdown...");
|
|
1242
|
-
if (intervalIdHolder.id) {
|
|
1243
|
-
clearInterval(intervalIdHolder.id);
|
|
1244
|
-
}
|
|
1245
|
-
await saveAllQueueStates();
|
|
1246
|
-
process.exit(0);
|
|
1247
|
-
};
|
|
1248
|
-
process.on("SIGINT", shutdownHandler);
|
|
1249
|
-
process.on("SIGTERM", shutdownHandler);
|
|
1250
|
-
}
|
|
1251
|
-
logger.debug(`Queue persistence initialized: ${config.persistence.directory}`);
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
logger.error(`Failed to initialize queue persistence: ${error?.message}`);
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
var createQueueStateSaver = (config) => async (queueName, queueService) => {
|
|
1257
|
-
try {
|
|
1258
|
-
if (!queueService) return;
|
|
1259
171
|
const queueData = createQueueData(queueService);
|
|
1260
172
|
const now = Date.now();
|
|
1261
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
1262
|
-
const
|
|
1263
|
-
await fs.writeFile(
|
|
1264
|
-
await fs.rename(
|
|
1265
|
-
|
|
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}`);
|
|
1266
178
|
} catch (error) {
|
|
1267
|
-
|
|
179
|
+
runtime.logging.error(
|
|
180
|
+
{ module: queueService.module, error: error?.message },
|
|
181
|
+
`Failed to save queue state for ${queueName}`
|
|
182
|
+
);
|
|
1268
183
|
}
|
|
1269
184
|
};
|
|
1270
|
-
var
|
|
185
|
+
var saveAllQueueStates = async (runtime) => {
|
|
1271
186
|
try {
|
|
1272
|
-
for (const [queueName, queueService] of Object.entries(queues)) {
|
|
1273
|
-
await saveQueueState(queueName, queueService);
|
|
187
|
+
for (const [queueName, queueService] of Object.entries(runtime.queues)) {
|
|
188
|
+
await saveQueueState(runtime, queueName, queueService);
|
|
1274
189
|
}
|
|
1275
|
-
|
|
190
|
+
runtime.logging.debug(`All queue states saved to ${runtime.config.persistence.directory}`);
|
|
1276
191
|
} catch (error) {
|
|
1277
|
-
|
|
192
|
+
runtime.logging.error({ error: error?.message }, `Failed to save all queue states`);
|
|
1278
193
|
}
|
|
1279
194
|
};
|
|
1280
|
-
var
|
|
1281
|
-
if (dirtyQueues.size === 0) return;
|
|
195
|
+
var saveDirtyQueueStates = async (runtime) => {
|
|
196
|
+
if (runtime.dirtyQueues.size === 0) return;
|
|
1282
197
|
try {
|
|
1283
|
-
const queueNames = [...dirtyQueues];
|
|
1284
|
-
dirtyQueues.clear();
|
|
198
|
+
const queueNames = [...runtime.dirtyQueues];
|
|
199
|
+
runtime.dirtyQueues.clear();
|
|
1285
200
|
for (const queueName of queueNames) {
|
|
1286
|
-
if (queues[queueName]) {
|
|
1287
|
-
await saveQueueState(queueName, queues[queueName]);
|
|
201
|
+
if (runtime.queues[queueName]) {
|
|
202
|
+
await saveQueueState(runtime, queueName, runtime.queues[queueName]);
|
|
1288
203
|
}
|
|
1289
204
|
}
|
|
1290
205
|
} catch (error) {
|
|
1291
|
-
|
|
206
|
+
runtime.logging.error({ error: error?.message }, `Failed to save dirty queue states`);
|
|
1292
207
|
}
|
|
1293
208
|
};
|
|
1294
|
-
var
|
|
209
|
+
var loadQueueState = async (runtime, queueName) => {
|
|
1295
210
|
try {
|
|
1296
|
-
|
|
1297
|
-
|
|
211
|
+
const queueService = runtime.queues[queueName];
|
|
212
|
+
if (!queueService) {
|
|
213
|
+
runtime.logging.warn(`Skipping load for non-existent queue: ${queueName}`);
|
|
1298
214
|
return;
|
|
1299
215
|
}
|
|
1300
|
-
const queueFilePath = path.join(config.persistence.directory, `${queueName}.json`);
|
|
216
|
+
const queueFilePath = path.join(runtime.config.persistence.directory, `${queueName}.json`);
|
|
1301
217
|
try {
|
|
1302
218
|
const data = await fs.readFile(queueFilePath, "utf8");
|
|
1303
219
|
const queueData = JSON.parse(data);
|
|
1304
220
|
restoreDates(queueData.incoming);
|
|
1305
221
|
restoreDates(queueData.processing);
|
|
1306
222
|
restoreDates(queueData.dlq);
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
if (message.attempts >= config.maxRetries) continue;
|
|
1317
|
-
setTimeout(() => {
|
|
1318
|
-
processMessage(queueName, message, queues[queueName]);
|
|
1319
|
-
}, 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);
|
|
1320
232
|
}
|
|
1321
233
|
}
|
|
234
|
+
runtime.logging.debug(
|
|
235
|
+
{ module: queueService.module },
|
|
236
|
+
`Queue state loaded: ${queueName} (saved at ${queueData.savedAt})`
|
|
237
|
+
);
|
|
1322
238
|
} catch (error) {
|
|
1323
239
|
if (error.code === "ENOENT") {
|
|
1324
|
-
|
|
240
|
+
runtime.logging.debug({ module: queueService.module }, `No saved state found for queue: ${queueName}`);
|
|
1325
241
|
} else {
|
|
1326
242
|
throw error;
|
|
1327
243
|
}
|
|
1328
244
|
}
|
|
1329
245
|
} catch (error) {
|
|
1330
|
-
|
|
246
|
+
runtime.logging.error({ error: error?.message }, `Failed to load queue state for ${queueName}`);
|
|
1331
247
|
}
|
|
1332
248
|
};
|
|
1333
|
-
var
|
|
249
|
+
var initializePersistence = async (runtime) => {
|
|
1334
250
|
try {
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
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);
|
|
1340
260
|
}
|
|
1341
|
-
|
|
261
|
+
runtime.logging.debug(`Queue persistence initialized: ${runtime.config.persistence.directory}`);
|
|
1342
262
|
} catch (error) {
|
|
1343
|
-
|
|
263
|
+
runtime.logging.error({ error: error?.message }, `Failed to initialize queue persistence`);
|
|
1344
264
|
}
|
|
1345
265
|
};
|
|
1346
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
|
+
|
|
1347
521
|
// src/queue-plugin/routes.ts
|
|
1348
522
|
var registerQueueRoutes = (app, prefix = "") => {
|
|
1349
523
|
app.get(
|
|
@@ -1380,267 +554,45 @@ var registerQueueRoutes = (app, prefix = "") => {
|
|
|
1380
554
|
async (request, reply) => {
|
|
1381
555
|
const queueName = request.params.queue;
|
|
1382
556
|
const result = await app.queues.processDlq(queueName);
|
|
1383
|
-
if (result === null) {
|
|
1384
|
-
return reply.status(404).send(result);
|
|
1385
|
-
}
|
|
1386
|
-
return reply.status(200).send(result);
|
|
1387
|
-
}
|
|
1388
|
-
);
|
|
1389
|
-
app.post(
|
|
1390
|
-
`/${prefix}/:queue/purge-dlq`,
|
|
1391
|
-
async (request, reply) => {
|
|
1392
|
-
const queueName = request.params.queue;
|
|
1393
|
-
const result = await app.queues.purgeDlq(queueName);
|
|
1394
|
-
if (result === null) {
|
|
1395
|
-
return reply.status(404).send({ error: "Queue not found" });
|
|
1396
|
-
}
|
|
1397
|
-
return reply.status(200).send(result);
|
|
1398
|
-
}
|
|
1399
|
-
);
|
|
1400
|
-
};
|
|
1401
|
-
|
|
1402
|
-
// src/queue-plugin/decorator.ts
|
|
1403
|
-
var isValidQueueName = (queueName) => {
|
|
1404
|
-
return typeof queueName === "string" && queueName.length > 0 && /^[a-zA-Z0-9_-]+$/.test(queueName);
|
|
1405
|
-
};
|
|
1406
|
-
var createQueueManager = ({
|
|
1407
|
-
config,
|
|
1408
|
-
queues,
|
|
1409
|
-
dirtyQueues,
|
|
1410
|
-
saveQueueState,
|
|
1411
|
-
loadQueueState,
|
|
1412
|
-
scheduleProcessing,
|
|
1413
|
-
processBatch
|
|
1414
|
-
}) => {
|
|
1415
|
-
const validateQueueName = (queueName) => {
|
|
1416
|
-
if (!queueName || typeof queueName !== "string") {
|
|
1417
|
-
throw new Error("Queue name must be a non-empty string");
|
|
1418
|
-
}
|
|
1419
|
-
if (!isValidQueueName(queueName)) {
|
|
1420
|
-
throw new Error("Queue name must contain only alphanumeric characters, hyphens, and underscores");
|
|
1421
|
-
}
|
|
1422
|
-
};
|
|
1423
|
-
const validateHandler = (handler) => {
|
|
1424
|
-
if (typeof handler !== "function") {
|
|
1425
|
-
throw new Error("Handler must be a function");
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1428
|
-
const addQueue = async (queueName, handler, module) => {
|
|
1429
|
-
try {
|
|
1430
|
-
validateQueueName(queueName);
|
|
1431
|
-
validateHandler(handler);
|
|
1432
|
-
if (queues[queueName]) {
|
|
1433
|
-
logger.warn(`Queue already exists: ${queueName}.`);
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
queues[queueName] = createQueueService(handler, module);
|
|
1437
|
-
if (config.persistence.enabled && loadQueueState) {
|
|
1438
|
-
await loadQueueState(queueName);
|
|
1439
|
-
}
|
|
1440
|
-
logger.info(`Queue added: ${queueName}`);
|
|
1441
|
-
} catch (error) {
|
|
1442
|
-
logger.error(`Failed to add queue ${queueName}: ${error.message}`);
|
|
1443
|
-
throw error;
|
|
1444
|
-
}
|
|
1445
|
-
};
|
|
1446
|
-
const removeQueue = async (queueName) => {
|
|
1447
|
-
try {
|
|
1448
|
-
validateQueueName(queueName);
|
|
1449
|
-
if (!queues[queueName]) {
|
|
1450
|
-
throw new Error(`Queue '${queueName}' does not exist`);
|
|
1451
|
-
}
|
|
1452
|
-
const queueService = queues[queueName];
|
|
1453
|
-
const totalMessages = queueService.incoming.length + queueService.processing.length;
|
|
1454
|
-
if (totalMessages > 0) {
|
|
1455
|
-
logger.warn(`Removing queue with ${totalMessages} pending messages: ${queueName}`);
|
|
1456
|
-
}
|
|
1457
|
-
if (queueService.timeoutId) {
|
|
1458
|
-
clearTimeout(queueService.timeoutId);
|
|
1459
|
-
}
|
|
1460
|
-
if (config.persistence.enabled && saveQueueState) {
|
|
1461
|
-
await saveQueueState(queueName, queueService);
|
|
1462
|
-
}
|
|
1463
|
-
delete queues[queueName];
|
|
1464
|
-
logger.info(`Queue removed: ${queueName}`);
|
|
1465
|
-
} catch (error) {
|
|
1466
|
-
logger.error(`Failed to remove queue ${queueName}: ${error.message}`);
|
|
1467
|
-
throw error;
|
|
1468
|
-
}
|
|
1469
|
-
};
|
|
1470
|
-
const hasQueue = (queueName) => {
|
|
1471
|
-
try {
|
|
1472
|
-
validateQueueName(queueName);
|
|
1473
|
-
return queueName in queues;
|
|
1474
|
-
} catch {
|
|
1475
|
-
return false;
|
|
1476
|
-
}
|
|
1477
|
-
};
|
|
1478
|
-
const listQueues = (module) => {
|
|
1479
|
-
return Object.keys(queues).filter((queueName) => {
|
|
1480
|
-
const queueService = queues[queueName];
|
|
1481
|
-
return !module || queueService.module === module;
|
|
1482
|
-
}).sort();
|
|
1483
|
-
};
|
|
1484
|
-
const getQueueStats = (queueName) => {
|
|
1485
|
-
try {
|
|
1486
|
-
validateQueueName(queueName);
|
|
1487
|
-
return queues[queueName] || null;
|
|
1488
|
-
} catch {
|
|
1489
|
-
return null;
|
|
1490
|
-
}
|
|
1491
|
-
};
|
|
1492
|
-
const getQueuesMap = () => {
|
|
1493
|
-
return { ...queues };
|
|
1494
|
-
};
|
|
1495
|
-
const getConfig = () => config;
|
|
1496
|
-
const getDashboardSummary = () => createDashboardSummary(queues, config);
|
|
1497
|
-
const getQueueSummary = (queueName) => {
|
|
1498
|
-
if (!queues[queueName]) {
|
|
1499
|
-
return null;
|
|
1500
|
-
}
|
|
1501
|
-
const queue = queues[queueName];
|
|
1502
|
-
const stats = createQueueSummary(queue, config);
|
|
1503
|
-
return {
|
|
1504
|
-
stats,
|
|
1505
|
-
messages: {
|
|
1506
|
-
incoming: queue.incoming,
|
|
1507
|
-
processing: queue.processing,
|
|
1508
|
-
dlq: queue.dlq
|
|
1509
|
-
}
|
|
1510
|
-
};
|
|
1511
|
-
};
|
|
1512
|
-
const enqueueMessage = async (queueName, body, attributes) => {
|
|
1513
|
-
if (!queues[queueName]) {
|
|
1514
|
-
return null;
|
|
1515
|
-
}
|
|
1516
|
-
const message = createQueueMessage(body, attributes);
|
|
1517
|
-
queues[queueName].incoming.push(message);
|
|
1518
|
-
scheduleProcessing(queueName, queues[queueName]);
|
|
1519
|
-
handleImmediateProcessing(queues[queueName], config, processBatch, queueName);
|
|
1520
|
-
if (config.persistence.enabled) {
|
|
1521
|
-
dirtyQueues.add(queueName);
|
|
1522
|
-
}
|
|
1523
|
-
return {
|
|
1524
|
-
id: message.id,
|
|
1525
|
-
queueName
|
|
1526
|
-
};
|
|
1527
|
-
};
|
|
1528
|
-
const processDlq = async (queueName) => {
|
|
1529
|
-
if (!queues[queueName]) {
|
|
1530
|
-
return null;
|
|
1531
|
-
}
|
|
1532
|
-
const processedCount = moveDLQToIncoming(queues[queueName]);
|
|
1533
|
-
if (processedCount === 0) {
|
|
1534
|
-
return {
|
|
1535
|
-
status: "success",
|
|
1536
|
-
message: "No messages in DLQ to process",
|
|
1537
|
-
processed: 0
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
logDLQOperation("Moving", processedCount, queueName);
|
|
1541
|
-
scheduleProcessing(queueName, queues[queueName]);
|
|
1542
|
-
if (config.persistence.enabled) {
|
|
1543
|
-
dirtyQueues.add(queueName);
|
|
1544
|
-
}
|
|
1545
|
-
return {
|
|
1546
|
-
status: "success",
|
|
1547
|
-
message: `Moved ${processedCount} messages from DLQ to processing queue`,
|
|
1548
|
-
processed: processedCount
|
|
1549
|
-
};
|
|
1550
|
-
};
|
|
1551
|
-
const purgeDlq = async (queueName) => {
|
|
1552
|
-
if (!queues[queueName]) {
|
|
1553
|
-
return null;
|
|
1554
|
-
}
|
|
1555
|
-
const purgedCount = purgeDLQ(queues[queueName]);
|
|
1556
|
-
if (purgedCount === 0) {
|
|
1557
|
-
return {
|
|
1558
|
-
status: "success",
|
|
1559
|
-
message: "No messages in DLQ to purge",
|
|
1560
|
-
purged: 0
|
|
1561
|
-
};
|
|
557
|
+
if (result === null) {
|
|
558
|
+
return reply.status(404).send(result);
|
|
559
|
+
}
|
|
560
|
+
return reply.status(200).send(result);
|
|
1562
561
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
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);
|
|
1566
572
|
}
|
|
1567
|
-
|
|
1568
|
-
status: "success",
|
|
1569
|
-
message: `Purged ${purgedCount} messages from DLQ`,
|
|
1570
|
-
purged: purgedCount
|
|
1571
|
-
};
|
|
1572
|
-
};
|
|
1573
|
-
return {
|
|
1574
|
-
addQueue,
|
|
1575
|
-
removeQueue,
|
|
1576
|
-
hasQueue,
|
|
1577
|
-
listQueues,
|
|
1578
|
-
getQueueStats,
|
|
1579
|
-
getQueuesMap,
|
|
1580
|
-
getConfig,
|
|
1581
|
-
getDashboardSummary,
|
|
1582
|
-
getQueueSummary,
|
|
1583
|
-
enqueueMessage,
|
|
1584
|
-
processDlq,
|
|
1585
|
-
purgeDlq
|
|
1586
|
-
};
|
|
1587
|
-
};
|
|
1588
|
-
var createQueueDecorator = (queueManager) => (app) => {
|
|
1589
|
-
app.decorate("queues", queueManager);
|
|
573
|
+
);
|
|
1590
574
|
};
|
|
1591
575
|
|
|
1592
576
|
// src/queue-plugin/plugin.ts
|
|
1593
577
|
var queuesPlugin = fsPlugin(async (app, options) => {
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
};
|
|
1602
|
-
const saveDirtyQueueStates = config.persistence.enabled && saveQueueState ? createDirtyQueuesStateSaver(saveQueueState, dirtyQueues)(queues) : async () => {
|
|
1603
|
-
};
|
|
1604
|
-
const processBatch = createBatchProcessor(
|
|
1605
|
-
processMessage,
|
|
1606
|
-
config,
|
|
1607
|
-
config.persistence.enabled ? dirtyQueues : void 0
|
|
1608
|
-
);
|
|
1609
|
-
const scheduleProcessing = createProcessingScheduler(processBatch, config);
|
|
1610
|
-
const loadQueueState = config.persistence.enabled ? createQueueStateLoader(
|
|
1611
|
-
config,
|
|
1612
|
-
scheduleProcessing,
|
|
1613
|
-
processMessage
|
|
1614
|
-
)(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
|
|
1615
585
|
};
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
config,
|
|
1622
|
-
queues,
|
|
1623
|
-
dirtyQueues,
|
|
1624
|
-
saveQueueState: saveQueueState ? (queueName) => saveQueueState(queueName, queues[queueName]) : void 0,
|
|
1625
|
-
loadQueueState,
|
|
1626
|
-
scheduleProcessing,
|
|
1627
|
-
processBatch
|
|
1628
|
-
});
|
|
1629
|
-
const decorateQueues = createQueueDecorator(queueManager);
|
|
1630
|
-
decorateQueues(app);
|
|
1631
|
-
registerQueueRoutes(
|
|
1632
|
-
app,
|
|
1633
|
-
options.prefix
|
|
1634
|
-
);
|
|
1635
|
-
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);
|
|
1636
591
|
});
|
|
1637
592
|
|
|
1638
593
|
// src/schedule-plugin/plugin.ts
|
|
1639
594
|
import fsPlugin2 from "fastify-plugin";
|
|
1640
595
|
|
|
1641
|
-
// src/schedule-plugin/core.ts
|
|
1642
|
-
import * as fs3 from "fs/promises";
|
|
1643
|
-
|
|
1644
596
|
// src/schedule-plugin/cron-utils.ts
|
|
1645
597
|
import { parseExpression } from "cron-parser";
|
|
1646
598
|
function parseCronExpression(cronExpression, lastRun, options = {}) {
|
|
@@ -1673,7 +625,6 @@ function parseCronExpression(cronExpression, lastRun, options = {}) {
|
|
|
1673
625
|
description: describeCronExpression(normalizedCron)
|
|
1674
626
|
};
|
|
1675
627
|
} catch (error) {
|
|
1676
|
-
logger.error(`Invalid cron expression: ${cronExpression} - ${error.message}`);
|
|
1677
628
|
const fallbackTime = /* @__PURE__ */ new Date();
|
|
1678
629
|
fallbackTime.setHours(fallbackTime.getHours() + 1);
|
|
1679
630
|
fallbackTime.setMinutes(0);
|
|
@@ -1701,8 +652,7 @@ function getNextExecutions(cronExpression, count = 5, options = {}) {
|
|
|
1701
652
|
executions.push(interval.next().toDate());
|
|
1702
653
|
}
|
|
1703
654
|
return executions;
|
|
1704
|
-
} catch
|
|
1705
|
-
logger.error(`Error getting next executions: ${error.message}`);
|
|
655
|
+
} catch {
|
|
1706
656
|
return [];
|
|
1707
657
|
}
|
|
1708
658
|
}
|
|
@@ -1975,9 +925,111 @@ var shouldSkipJob = (job) => {
|
|
|
1975
925
|
if (job.maxRuns && job.runCount >= job.maxRuns) return true;
|
|
1976
926
|
return false;
|
|
1977
927
|
};
|
|
928
|
+
var isJobReadyToRun = (job) => {
|
|
929
|
+
const now = /* @__PURE__ */ new Date();
|
|
930
|
+
return job.nextRun.getTime() <= now.getTime();
|
|
931
|
+
};
|
|
1978
932
|
var getTimeUntilNextRun = (job) => Math.max(0, job.nextRun.getTime() - Date.now());
|
|
1979
933
|
|
|
1980
|
-
// 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
|
+
});
|
|
1981
1033
|
var createJobExecution = (jobId) => ({
|
|
1982
1034
|
id: generateExecutionId(),
|
|
1983
1035
|
jobId,
|
|
@@ -1991,661 +1043,523 @@ var completeExecution = (execution, result, error) => ({
|
|
|
1991
1043
|
result,
|
|
1992
1044
|
error
|
|
1993
1045
|
});
|
|
1994
|
-
var canExecuteJob = (scheduler, state) => {
|
|
1995
|
-
if (state.isShuttingDown) {
|
|
1996
|
-
return { canExecute: false, reason: "System is shutting down" };
|
|
1997
|
-
}
|
|
1998
|
-
if (scheduler.isRunning) {
|
|
1999
|
-
return { canExecute: false, reason: "Job is already running" };
|
|
2000
|
-
}
|
|
2001
|
-
if (shouldSkipJob(scheduler.job)) {
|
|
2002
|
-
const reason = !scheduler.job.enabled ? "Job is disabled" : "Job reached maximum runs";
|
|
2003
|
-
return { canExecute: false, reason };
|
|
2004
|
-
}
|
|
2005
|
-
if (state.runningExecutions >= state.config.execution.maxConcurrent) {
|
|
2006
|
-
return { canExecute: false, reason: "Maximum concurrent executions reached" };
|
|
2007
|
-
}
|
|
2008
|
-
return { canExecute: true };
|
|
2009
|
-
};
|
|
2010
|
-
var scheduleJob = (scheduler, state, executeJobFn) => {
|
|
2011
|
-
if (state.isShuttingDown) return scheduler;
|
|
2012
|
-
const timeUntilNextRun = getTimeUntilNextRun(scheduler.job);
|
|
2013
|
-
const updatedScheduler = { ...scheduler };
|
|
2014
|
-
if (timeUntilNextRun <= 0) {
|
|
2015
|
-
setImmediate(() => executeJobFn(updatedScheduler));
|
|
2016
|
-
} else {
|
|
2017
|
-
updatedScheduler.timerId = setTimeout(() => {
|
|
2018
|
-
executeJobFn(updatedScheduler);
|
|
2019
|
-
}, timeUntilNextRun);
|
|
2020
|
-
logger.debug(`Scheduled job ${scheduler.job.name} to run in ${Math.round(timeUntilNextRun / 1e3)}s`);
|
|
2021
|
-
}
|
|
2022
|
-
return updatedScheduler;
|
|
2023
|
-
};
|
|
2024
|
-
var executeJobWithTimeout = async (handler, job, execution, timeoutMs) => {
|
|
2025
|
-
let timerId;
|
|
2026
|
-
try {
|
|
2027
|
-
return await Promise.race([
|
|
2028
|
-
handler(job, execution),
|
|
2029
|
-
new Promise((_, reject) => {
|
|
2030
|
-
timerId = setTimeout(() => reject(new Error("Job execution timeout")), timeoutMs);
|
|
2031
|
-
})
|
|
2032
|
-
]);
|
|
2033
|
-
} finally {
|
|
2034
|
-
clearTimeout(timerId);
|
|
2035
|
-
}
|
|
2036
|
-
};
|
|
2037
1046
|
var updateJobAfterExecution = (job, execution, config) => {
|
|
2038
1047
|
const updatedJob = {
|
|
2039
1048
|
...job,
|
|
2040
1049
|
lastRun: execution.startTime,
|
|
2041
1050
|
runCount: job.runCount + 1
|
|
2042
1051
|
};
|
|
2043
|
-
if (
|
|
1052
|
+
if (updatedJob.enabled) {
|
|
2044
1053
|
updatedJob.nextRun = calculateNextRunFromLastRun(updatedJob, updatedJob.lastRun, config);
|
|
2045
1054
|
}
|
|
2046
1055
|
return updatedJob;
|
|
2047
1056
|
};
|
|
2048
|
-
var
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
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
|
+
});
|
|
2057
1069
|
|
|
2058
1070
|
// src/schedule-plugin/persistence.ts
|
|
2059
1071
|
import * as fs2 from "fs/promises";
|
|
2060
1072
|
import * as path2 from "path";
|
|
2061
|
-
var
|
|
2062
|
-
var
|
|
2063
|
-
var
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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) => {
|
|
2070
1085
|
try {
|
|
2071
|
-
const
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
try {
|
|
2083
|
-
const stateFile = createStateFilePath(directory);
|
|
2084
|
-
const data = await fs2.readFile(stateFile, "utf8");
|
|
2085
|
-
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");
|
|
2086
1097
|
} catch (error) {
|
|
2087
|
-
|
|
2088
|
-
logger.debug("No previous scheduler state found - starting fresh");
|
|
2089
|
-
} else {
|
|
2090
|
-
logger.error("Failed to load scheduler state:", error);
|
|
2091
|
-
}
|
|
2092
|
-
return null;
|
|
1098
|
+
runtime.logging.error({ error: error?.message }, "Failed to save scheduler state");
|
|
2093
1099
|
}
|
|
2094
1100
|
};
|
|
2095
|
-
var validateAndAdjustNextRun = (job, savedNextRun,
|
|
1101
|
+
var validateAndAdjustNextRun = (job, savedNextRun, runtime) => {
|
|
1102
|
+
const { config, lastRestartTime, logging } = runtime;
|
|
2096
1103
|
const now = /* @__PURE__ */ new Date();
|
|
2097
|
-
if (!savedNextRun) {
|
|
2098
|
-
|
|
2099
|
-
}
|
|
2100
|
-
if (savedNextRun <= now) {
|
|
2101
|
-
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`);
|
|
2102
1106
|
return calculateNextRunFromLastRun(job, job.lastRun, config);
|
|
2103
1107
|
}
|
|
2104
|
-
const
|
|
2105
|
-
const isRapidRestart = timeSinceRestart < config.restartBehavior.rapidRestartThreshold;
|
|
1108
|
+
const isRapidRestart = now.getTime() - lastRestartTime.getTime() < config.restartBehavior.rapidRestartThreshold;
|
|
2106
1109
|
if (isRapidRestart) {
|
|
2107
|
-
|
|
1110
|
+
logging.debug({ module: job.module }, `Rapid restart detected for ${job.name} - preserving saved timing`);
|
|
2108
1111
|
return savedNextRun;
|
|
2109
1112
|
}
|
|
2110
1113
|
if (job.cronExpression || job.intervalMs) {
|
|
2111
1114
|
const expectedNextRun = calculateNextRunFromLastRun(job, job.lastRun, config);
|
|
2112
|
-
const
|
|
2113
|
-
if (
|
|
2114
|
-
|
|
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`);
|
|
2115
1118
|
return expectedNextRun;
|
|
2116
1119
|
}
|
|
2117
1120
|
}
|
|
2118
1121
|
return savedNextRun;
|
|
2119
1122
|
};
|
|
2120
|
-
var
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
const
|
|
2132
|
-
updatedJob
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
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;
|
|
2139
1163
|
}
|
|
2140
|
-
return { ...scheduler, job: updatedJob };
|
|
2141
|
-
};
|
|
2142
|
-
var restoreExecutionHistory = (savedExecutions) => {
|
|
2143
|
-
return savedExecutions.map((exec) => ({
|
|
2144
|
-
...exec,
|
|
2145
|
-
startTime: new Date(exec.startTime),
|
|
2146
|
-
endTime: exec.endTime ? new Date(exec.endTime) : void 0
|
|
2147
|
-
}));
|
|
2148
1164
|
};
|
|
2149
|
-
var
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
const restoredScheduler = restoreJobFromSavedData(
|
|
2159
|
-
existingScheduler,
|
|
2160
|
-
savedJob,
|
|
2161
|
-
config,
|
|
2162
|
-
lastRestartTime
|
|
2163
|
-
);
|
|
2164
|
-
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");
|
|
2165
1174
|
}
|
|
2166
1175
|
}
|
|
2167
|
-
const executionHistory = stateData.executions ? restoreExecutionHistory(stateData.executions) : [];
|
|
2168
|
-
logger.debug(`Enhanced scheduler state loaded (saved at ${stateData.savedAt})`);
|
|
2169
|
-
return { schedulers: restoredSchedulers, executionHistory };
|
|
2170
1176
|
};
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
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");
|
|
2181
1193
|
}
|
|
2182
|
-
return [];
|
|
2183
1194
|
};
|
|
2184
1195
|
|
|
2185
|
-
// src/schedule-plugin/
|
|
2186
|
-
var
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
directory: "./.develop/scheduler-data",
|
|
2190
|
-
saveInterval: 3e4
|
|
2191
|
-
// 30 seconds
|
|
2192
|
-
},
|
|
2193
|
-
execution: {
|
|
2194
|
-
maxConcurrent: 5,
|
|
2195
|
-
defaultTimeout: 3e5,
|
|
2196
|
-
// 5 minutes
|
|
2197
|
-
retryOnError: false,
|
|
2198
|
-
maxRetries: 3
|
|
2199
|
-
},
|
|
2200
|
-
cleanup: {
|
|
2201
|
-
maxExecutionHistory: 1e3,
|
|
2202
|
-
cleanupInterval: 6e5
|
|
2203
|
-
// 10 minutes
|
|
2204
|
-
},
|
|
2205
|
-
restartBehavior: {
|
|
2206
|
-
preserveNaturalTiming: true,
|
|
2207
|
-
skipMissedRuns: true,
|
|
2208
|
-
maxTimingDrift: 6e4,
|
|
2209
|
-
// 1 minute
|
|
2210
|
-
rapidRestartThreshold: 1e4
|
|
2211
|
-
// 10 seconds
|
|
2212
|
-
},
|
|
2213
|
-
cron: {
|
|
2214
|
-
defaultTimezone: void 0,
|
|
2215
|
-
// Use system timezone
|
|
2216
|
-
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" };
|
|
2217
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 };
|
|
2218
1212
|
};
|
|
2219
|
-
var
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
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})`);
|
|
2253
1252
|
}
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
state.executionHistory.push(execution);
|
|
2257
|
-
scheduler.isRunning = true;
|
|
2258
|
-
state.runningExecutions++;
|
|
2259
|
-
logger.info(`Executing job ${scheduler.job.name} (${execution.id})`);
|
|
2260
|
-
try {
|
|
2261
|
-
const result = await executeJobWithTimeout(
|
|
2262
|
-
scheduler.handler,
|
|
2263
|
-
scheduler.job,
|
|
2264
|
-
execution,
|
|
2265
|
-
state.config.execution.defaultTimeout
|
|
2266
|
-
);
|
|
2267
|
-
const completedExecution = handleExecutionSuccess(execution, result);
|
|
2268
|
-
state.executions[execution.id] = completedExecution;
|
|
2269
|
-
} catch (error) {
|
|
2270
|
-
const failedExecution = handleExecutionError(execution, error);
|
|
2271
|
-
state.executions[execution.id] = failedExecution;
|
|
2272
|
-
} finally {
|
|
2273
|
-
scheduler.isRunning = false;
|
|
2274
|
-
state.runningExecutions--;
|
|
2275
|
-
scheduler.job = updateJobAfterExecution(scheduler.job, execution, state.config);
|
|
2276
|
-
if (scheduler.job.enabled && !state.isShuttingDown) {
|
|
2277
|
-
scheduleJobFn(scheduler);
|
|
2278
|
-
}
|
|
2279
|
-
if (state.config.persistence.enabled) {
|
|
2280
|
-
state.isDirty = true;
|
|
2281
|
-
}
|
|
1253
|
+
if (runtime.config.persistence.enabled) {
|
|
1254
|
+
runtime.isDirty = true;
|
|
2282
1255
|
}
|
|
2283
|
-
}
|
|
2284
|
-
const scheduleJobFn = (scheduler) => {
|
|
2285
|
-
const updatedScheduler = scheduleJob(scheduler, state, executeJob);
|
|
2286
|
-
state.schedulers[scheduler.job.id] = updatedScheduler;
|
|
2287
|
-
return updatedScheduler;
|
|
2288
|
-
};
|
|
2289
|
-
return { executeJob, scheduleJobFn };
|
|
1256
|
+
}
|
|
2290
1257
|
};
|
|
2291
|
-
var
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
);
|
|
2300
|
-
state.schedulers = schedulers;
|
|
2301
|
-
state.executionHistory = executionHistory;
|
|
2302
|
-
}
|
|
2303
|
-
const tickInterval = (state.config.persistence.saveInterval || 0) > 0 ? state.config.persistence.saveInterval : state.config.cleanup.cleanupInterval;
|
|
2304
|
-
state.tickInterval = setInterval(async () => {
|
|
2305
|
-
if (state.isDirty && state.config.persistence.enabled) {
|
|
2306
|
-
state.isDirty = false;
|
|
2307
|
-
await saveSchedulerState(state);
|
|
2308
|
-
}
|
|
2309
|
-
if (Date.now() - state.lastCleanupTime >= state.config.cleanup.cleanupInterval) {
|
|
2310
|
-
state.lastCleanupTime = Date.now();
|
|
2311
|
-
cleanupExecutionHistory(state);
|
|
2312
|
-
}
|
|
2313
|
-
}, tickInterval);
|
|
2314
|
-
for (const scheduler of Object.values(state.schedulers)) {
|
|
2315
|
-
if (scheduler.job.enabled) {
|
|
2316
|
-
scheduleJobFn(scheduler);
|
|
2317
|
-
}
|
|
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}`);
|
|
2318
1266
|
}
|
|
2319
|
-
|
|
2320
|
-
} catch (error) {
|
|
2321
|
-
logger.error(`Failed to initialize scheduler: ${error.message}`);
|
|
2322
|
-
throw error;
|
|
1267
|
+
return;
|
|
2323
1268
|
}
|
|
1269
|
+
runtime.logging.runWithLogContext(
|
|
1270
|
+
{ module: scheduler.job.module, reqId: scheduler.job.id },
|
|
1271
|
+
() => executeJob(runtime, scheduler)
|
|
1272
|
+
);
|
|
2324
1273
|
};
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
if (expressionType === "unknown") {
|
|
2330
|
-
logger.error(`Invalid cron expression for job: ${jobDef.name}`);
|
|
2331
|
-
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;
|
|
2332
1278
|
}
|
|
1279
|
+
const removed = runtime.executionHistory.splice(0, runtime.executionHistory.length - maxExecutionHistory);
|
|
1280
|
+
runtime.logging.debug(`Cleaned up ${removed.length} old execution records`);
|
|
2333
1281
|
};
|
|
2334
|
-
var
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
if (config.cron.logCronDetails && jobDef.cronExpression) {
|
|
2338
|
-
const cronExpression = expressionType === "rate" ? convertRateToCron(jobDef.cronExpression).cronExpression : jobDef.cronExpression;
|
|
2339
|
-
const result = parseCronExpression(cronExpression, void 0, {
|
|
2340
|
-
timezone: jobDef.timezone ?? config.cron.defaultTimezone
|
|
2341
|
-
});
|
|
2342
|
-
logger.debug(`Job ${jobDef.name}: ${result.description} - Next: ${result.nextRun.toLocaleString()}`);
|
|
2343
|
-
const upcoming = getNextExecutions(cronExpression, 3, {
|
|
2344
|
-
timezone: jobDef.timezone ?? config.cron.defaultTimezone
|
|
2345
|
-
});
|
|
2346
|
-
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;
|
|
2347
1285
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
name: jobDef.name,
|
|
2351
|
-
cronExpression: expressionType === "rate" ? convertRateToCron(jobDef.cronExpression).cronExpression : jobDef.cronExpression,
|
|
2352
|
-
intervalMs: jobDef.intervalMs,
|
|
2353
|
-
delayMs: jobDef.delayMs,
|
|
2354
|
-
nextRun: /* @__PURE__ */ new Date(),
|
|
2355
|
-
enabled: jobDef.enabled ?? true,
|
|
2356
|
-
runCount: 0,
|
|
2357
|
-
maxRuns: jobDef.maxRuns,
|
|
2358
|
-
metadata: jobDef.metadata,
|
|
2359
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2360
|
-
timezone: jobDef.timezone,
|
|
2361
|
-
module: jobDef.module
|
|
2362
|
-
};
|
|
2363
|
-
job.nextRun = calculateNextRun(job);
|
|
2364
|
-
return job;
|
|
1286
|
+
runtime.lastCleanupTime = now;
|
|
1287
|
+
cleanupExecutionHistory(runtime);
|
|
2365
1288
|
};
|
|
2366
|
-
var
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
isRunning: false
|
|
2370
|
-
});
|
|
2371
|
-
var enableJob = (scheduler) => ({
|
|
2372
|
-
...scheduler,
|
|
2373
|
-
job: {
|
|
2374
|
-
...scheduler.job,
|
|
2375
|
-
enabled: true,
|
|
2376
|
-
nextRun: calculateNextRun(scheduler.job)
|
|
1289
|
+
var maybeSavePersistence2 = (runtime, now) => {
|
|
1290
|
+
if (!runtime.config.persistence.enabled || !runtime.isDirty) {
|
|
1291
|
+
return;
|
|
2377
1292
|
}
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
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);
|
|
2382
1298
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
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);
|
|
2388
1307
|
};
|
|
2389
1308
|
|
|
2390
|
-
// src/schedule-plugin/
|
|
2391
|
-
var
|
|
2392
|
-
|
|
2393
|
-
const nextExecutions = s.job.cronExpression ? getNextExecutions(s.job.cronExpression, 3, { timezone: s.job.timezone }) : [];
|
|
2394
|
-
return {
|
|
2395
|
-
...s.job,
|
|
2396
|
-
isRunning: s.isRunning,
|
|
2397
|
-
timeUntilNextRun: getTimeUntilNextRun(s.job),
|
|
2398
|
-
upcomingExecutions: nextExecutions,
|
|
2399
|
-
cronDescription: s.job.cronExpression ? parseCronExpression(s.job.cronExpression, void 0, { timezone: s.job.timezone }).description : void 0
|
|
2400
|
-
};
|
|
2401
|
-
});
|
|
2402
|
-
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);
|
|
2403
1312
|
};
|
|
2404
|
-
var
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
const dashboardData = createDashboardData(state);
|
|
2412
|
-
return reply.status(200).send(dashboardData);
|
|
2413
|
-
});
|
|
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
|
+
}
|
|
2414
1320
|
};
|
|
2415
|
-
var
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
});
|
|
1321
|
+
var validateHandler2 = (handler) => {
|
|
1322
|
+
if (typeof handler !== "function") {
|
|
1323
|
+
throw new Error("Handler must be a function");
|
|
1324
|
+
}
|
|
2420
1325
|
};
|
|
2421
|
-
var
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
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
|
+
);
|
|
2426
1349
|
}
|
|
2427
|
-
|
|
2428
|
-
|
|
1350
|
+
let scheduler = createSchedulerService(job, jobDefinition.handler);
|
|
1351
|
+
if (runtime.config.persistence.enabled) {
|
|
1352
|
+
scheduler = await loadJobState(runtime, scheduler);
|
|
2429
1353
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
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
|
+
}
|
|
2433
1361
|
};
|
|
2434
|
-
var
|
|
2435
|
-
|
|
2436
|
-
|
|
1362
|
+
var removeJob = async (runtime, jobName) => {
|
|
1363
|
+
try {
|
|
1364
|
+
validateJobName(jobName);
|
|
1365
|
+
const scheduler = findSchedulerByName(runtime, jobName);
|
|
2437
1366
|
if (!scheduler) {
|
|
2438
|
-
|
|
1367
|
+
throw new Error(`Job '${jobName}' does not exist`);
|
|
2439
1368
|
}
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
scheduleJobFn(enabledScheduler);
|
|
2443
|
-
return reply.status(200).send({ message: "Job enabled successfully" });
|
|
2444
|
-
});
|
|
2445
|
-
};
|
|
2446
|
-
var registerDisableJobRoute = (app, state) => {
|
|
2447
|
-
app.put("/schedule/jobs/:jobId/disable", async function(request, reply) {
|
|
2448
|
-
const scheduler = state.schedulers[request.params.jobId];
|
|
2449
|
-
if (!scheduler) {
|
|
2450
|
-
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}`);
|
|
2451
1371
|
}
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
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
|
+
}
|
|
2456
1386
|
};
|
|
2457
|
-
var
|
|
2458
|
-
|
|
2459
|
-
registerExecutionsRoute(app, state);
|
|
2460
|
-
registerTriggerJobRoute(app, state, executeJobFn);
|
|
2461
|
-
registerEnableJobRoute(app, state, scheduleJobFn);
|
|
2462
|
-
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();
|
|
2463
1389
|
};
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
1390
|
+
var getJobStats = (runtime, jobName) => {
|
|
1391
|
+
try {
|
|
1392
|
+
validateJobName(jobName);
|
|
1393
|
+
return findSchedulerByName(runtime, jobName) ?? null;
|
|
1394
|
+
} catch {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
2468
1397
|
};
|
|
2469
|
-
var
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
if (!
|
|
2477
|
-
throw new Error(
|
|
1398
|
+
var getJobsMap = (runtime) => {
|
|
1399
|
+
return { ...runtime.schedulers };
|
|
1400
|
+
};
|
|
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`);
|
|
2478
1407
|
}
|
|
2479
|
-
if (
|
|
2480
|
-
|
|
1408
|
+
if (scheduler.job.enabled) {
|
|
1409
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job is already enabled: ${jobName}`);
|
|
1410
|
+
return;
|
|
2481
1411
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
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`);
|
|
2486
1425
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
validateJobName(jobDefinition.name);
|
|
2491
|
-
validateHandler(jobDefinition.handler);
|
|
2492
|
-
validateJobDefinition(jobDefinition);
|
|
2493
|
-
const existingJob = Object.values(state.schedulers).find((s) => s.job.name === jobDefinition.name);
|
|
2494
|
-
if (existingJob) {
|
|
2495
|
-
logger.warn(`Job already exists: ${jobDefinition.name}.`);
|
|
2496
|
-
return existingJob.job.id;
|
|
2497
|
-
}
|
|
2498
|
-
const job = createJobFromDefinition(jobDefinition, config);
|
|
2499
|
-
const scheduler = createSchedulerService(job, jobDefinition.handler);
|
|
2500
|
-
state.schedulers[job.id] = scheduler;
|
|
2501
|
-
if (job.enabled) {
|
|
2502
|
-
scheduleJobFn(scheduler);
|
|
2503
|
-
}
|
|
2504
|
-
logger.info(`Job added: ${jobDefinition.name} (${job.id})`);
|
|
2505
|
-
return job.id;
|
|
2506
|
-
} catch (error) {
|
|
2507
|
-
logger.error(`Failed to add job ${jobDefinition.name}: ${error.message}`);
|
|
2508
|
-
throw error;
|
|
1426
|
+
if (!scheduler.job.enabled) {
|
|
1427
|
+
runtime.logging.debug({ module: scheduler.job.module }, `Job is already disabled: ${jobName}`);
|
|
1428
|
+
return;
|
|
2509
1429
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
}
|
|
2524
|
-
delete state.schedulers[scheduler.job.id];
|
|
2525
|
-
logger.info(`Job removed: ${jobName}`);
|
|
2526
|
-
} catch (error) {
|
|
2527
|
-
logger.error(`Failed to remove job ${jobName}: ${error.message}`);
|
|
2528
|
-
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`);
|
|
2529
1443
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
try {
|
|
2533
|
-
validateJobName(jobName);
|
|
2534
|
-
return Object.values(state.schedulers).some((s) => s.job.name === jobName);
|
|
2535
|
-
} catch {
|
|
2536
|
-
return false;
|
|
1444
|
+
if (scheduler.isRunning) {
|
|
1445
|
+
throw new Error(`Job '${jobName}' is already running`);
|
|
2537
1446
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
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());
|
|
2549
1488
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
if (!scheduler) {
|
|
2556
|
-
throw new Error(`Job '${jobName}' does not exist`);
|
|
2557
|
-
}
|
|
2558
|
-
if (scheduler.job.enabled) {
|
|
2559
|
-
logger.debug(`Job is already enabled: ${jobName}`);
|
|
2560
|
-
return;
|
|
2561
|
-
}
|
|
2562
|
-
scheduler.job.enabled = true;
|
|
2563
|
-
scheduleJobFn(scheduler);
|
|
2564
|
-
logger.info(`Job enabled: ${jobName}`);
|
|
2565
|
-
} catch (error) {
|
|
2566
|
-
logger.error(`Failed to enable job ${jobName}: ${error.message}`);
|
|
2567
|
-
throw error;
|
|
1489
|
+
);
|
|
1490
|
+
app.get(
|
|
1491
|
+
`/${prefix}/executions`,
|
|
1492
|
+
async (_, reply) => {
|
|
1493
|
+
return reply.status(200).send(app.scheduler.getExecutionsSummary());
|
|
2568
1494
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
}
|
|
2577
|
-
|
|
2578
|
-
logger.debug(`Job is already disabled: ${jobName}`);
|
|
2579
|
-
return;
|
|
2580
|
-
}
|
|
2581
|
-
scheduler.job.enabled = false;
|
|
2582
|
-
if (scheduler.timerId) {
|
|
2583
|
-
clearTimeout(scheduler.timerId);
|
|
2584
|
-
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 });
|
|
2585
1504
|
}
|
|
2586
|
-
logger.info(`Job disabled: ${jobName}`);
|
|
2587
|
-
} catch (error) {
|
|
2588
|
-
logger.error(`Failed to disable job ${jobName}: ${error.message}`);
|
|
2589
|
-
throw error;
|
|
2590
1505
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
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 });
|
|
2598
1515
|
}
|
|
2599
|
-
|
|
2600
|
-
|
|
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 });
|
|
2601
1526
|
}
|
|
2602
|
-
setImmediate(() => executeJob(scheduler));
|
|
2603
|
-
logger.info(`Job triggered: ${jobName}`);
|
|
2604
|
-
} catch (error) {
|
|
2605
|
-
logger.error(`Failed to trigger job ${jobName}: ${error.message}`);
|
|
2606
|
-
throw error;
|
|
2607
1527
|
}
|
|
2608
|
-
|
|
2609
|
-
const getJobsMap = () => {
|
|
2610
|
-
return { ...state.schedulers };
|
|
2611
|
-
};
|
|
2612
|
-
return {
|
|
2613
|
-
addJob,
|
|
2614
|
-
removeJob,
|
|
2615
|
-
hasJob,
|
|
2616
|
-
listJobs,
|
|
2617
|
-
getJobStats,
|
|
2618
|
-
enableJob: enableJob2,
|
|
2619
|
-
disableJob: disableJob2,
|
|
2620
|
-
triggerJob,
|
|
2621
|
-
getJobsMap
|
|
2622
|
-
};
|
|
2623
|
-
};
|
|
2624
|
-
var createSchedulerDecorator = (schedulerManager) => (app) => {
|
|
2625
|
-
app.decorate("scheduler", schedulerManager);
|
|
1528
|
+
);
|
|
2626
1529
|
};
|
|
2627
1530
|
|
|
2628
1531
|
// src/schedule-plugin/plugin.ts
|
|
2629
1532
|
var schedulerPlugin = fsPlugin2(async (app, options) => {
|
|
2630
|
-
const
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
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);
|
|
2639
1550
|
});
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
1551
|
+
app.decorate("scheduler", createSchedulerManager(runtime));
|
|
1552
|
+
registerSchedulerRoutes(app, options.prefix);
|
|
1553
|
+
if (runtime.config.persistence.enabled) {
|
|
1554
|
+
await initializePersistence2(runtime);
|
|
1555
|
+
}
|
|
2643
1556
|
});
|
|
2644
1557
|
|
|
2645
1558
|
// src/websocket-plugin/plugin.ts
|
|
2646
1559
|
import crypto from "crypto";
|
|
2647
1560
|
import fsPlugin3 from "fastify-plugin";
|
|
2648
1561
|
import websocketPlugin from "@fastify/websocket";
|
|
1562
|
+
import { WebSocketConnectionGoneError } from "@cloudnux/core-cloud-provider";
|
|
2649
1563
|
var DEFAULT_CONFIG3 = {
|
|
2650
1564
|
routeKeyField: "action"
|
|
2651
1565
|
};
|
|
@@ -2667,15 +1581,20 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2667
1581
|
preHandler: async (request, reply) => {
|
|
2668
1582
|
const connectionId = crypto.randomUUID();
|
|
2669
1583
|
request._wsConnectionId = connectionId;
|
|
1584
|
+
const reqId = `${request.id}:${connectionId}`;
|
|
2670
1585
|
const connectHandlers = handlers.filter((h) => h.path === path4 && h.event === "connect");
|
|
2671
1586
|
for (const h of connectHandlers) {
|
|
2672
|
-
await
|
|
1587
|
+
await app.logging.runWithLogContext(
|
|
1588
|
+
{ module: h.module, reqId },
|
|
1589
|
+
() => h.handler(connectionId, "connect", null, request, reply)
|
|
1590
|
+
);
|
|
2673
1591
|
}
|
|
2674
1592
|
if (reply.statusCode !== 200)
|
|
2675
1593
|
return reply;
|
|
2676
1594
|
}
|
|
2677
1595
|
}, (socket, request) => {
|
|
2678
1596
|
const connectionId = request._wsConnectionId ?? crypto.randomUUID();
|
|
1597
|
+
const reqId = `${request.id}:${connectionId}`;
|
|
2679
1598
|
const connection = {
|
|
2680
1599
|
connectionId,
|
|
2681
1600
|
socket,
|
|
@@ -2700,9 +1619,10 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2700
1619
|
);
|
|
2701
1620
|
for (const h of routeHandlers) {
|
|
2702
1621
|
matched = true;
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
1622
|
+
app.logging.runWithLogContext(
|
|
1623
|
+
{ module: h.module, reqId },
|
|
1624
|
+
() => h.handler(connectionId, "message", data, request).catch((e) => app.logging.error(e))
|
|
1625
|
+
);
|
|
2706
1626
|
}
|
|
2707
1627
|
}
|
|
2708
1628
|
if (!matched) {
|
|
@@ -2710,18 +1630,20 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2710
1630
|
(h) => h.event === "message" && !h.route
|
|
2711
1631
|
);
|
|
2712
1632
|
for (const h of defaultHandlers) {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
1633
|
+
app.logging.runWithLogContext(
|
|
1634
|
+
{ module: h.module, reqId },
|
|
1635
|
+
() => h.handler(connectionId, "message", data, request).catch((e) => app.logging.error(e))
|
|
1636
|
+
);
|
|
2716
1637
|
}
|
|
2717
1638
|
}
|
|
2718
1639
|
});
|
|
2719
1640
|
socket.on("close", () => {
|
|
2720
1641
|
const disconnectHandlers = pathHandlers().filter((h) => h.event === "disconnect");
|
|
2721
1642
|
for (const h of disconnectHandlers) {
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
1643
|
+
app.logging.runWithLogContext(
|
|
1644
|
+
{ module: h.module, reqId },
|
|
1645
|
+
() => h.handler(connectionId, "disconnect", null, request).catch((e) => app.logging.error(e))
|
|
1646
|
+
);
|
|
2725
1647
|
}
|
|
2726
1648
|
connections.delete(connectionId);
|
|
2727
1649
|
});
|
|
@@ -2752,7 +1674,7 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2752
1674
|
async sendToClient(connectionId, data) {
|
|
2753
1675
|
const connection = connections.get(connectionId);
|
|
2754
1676
|
if (!connection) {
|
|
2755
|
-
throw new
|
|
1677
|
+
throw new WebSocketConnectionGoneError(connectionId);
|
|
2756
1678
|
}
|
|
2757
1679
|
const payload = typeof data === "string" ? data : JSON.stringify(data);
|
|
2758
1680
|
connection.socket.send(payload);
|
|
@@ -2760,7 +1682,7 @@ var websocketsPlugin = fsPlugin3(async (app, options) => {
|
|
|
2760
1682
|
async disconnect(connectionId) {
|
|
2761
1683
|
const connection = connections.get(connectionId);
|
|
2762
1684
|
if (!connection) {
|
|
2763
|
-
throw new
|
|
1685
|
+
throw new WebSocketConnectionGoneError(connectionId);
|
|
2764
1686
|
}
|
|
2765
1687
|
connection.socket.close();
|
|
2766
1688
|
}
|
|
@@ -2782,7 +1704,7 @@ var RouteRegistry = class _RouteRegistry {
|
|
|
2782
1704
|
url: routeOptions.url,
|
|
2783
1705
|
handler: routeOptions.handler?.name || "anonymous",
|
|
2784
1706
|
registeredAt: /* @__PURE__ */ new Date(),
|
|
2785
|
-
module: routeOptions.module
|
|
1707
|
+
module: routeOptions.config?.module
|
|
2786
1708
|
};
|
|
2787
1709
|
this.routes.push(routeInfo);
|
|
2788
1710
|
}
|
|
@@ -2833,10 +1755,29 @@ var LogStore = class {
|
|
|
2833
1755
|
}
|
|
2834
1756
|
};
|
|
2835
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();
|
|
2836
1778
|
async function devConsolePluginFunction(fastify, options = {}) {
|
|
2837
1779
|
const { prefix = "console", enableUI = true } = options;
|
|
2838
|
-
const
|
|
2839
|
-
setWriter((entry) => {
|
|
1780
|
+
const unsubscribe = fastify.logging.subscribeToLogs((entry) => {
|
|
2840
1781
|
logStore.addLog({
|
|
2841
1782
|
id: Date.now().toString() + Math.random().toString(36).slice(2, 9),
|
|
2842
1783
|
timestamp: new Date(entry.time),
|
|
@@ -2846,11 +1787,11 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2846
1787
|
module: entry.module,
|
|
2847
1788
|
reqId: entry.reqId
|
|
2848
1789
|
});
|
|
2849
|
-
upstream(entry);
|
|
2850
1790
|
});
|
|
2851
1791
|
fastify.addHook("onClose", (_instance, done) => {
|
|
2852
|
-
|
|
1792
|
+
unsubscribe();
|
|
2853
1793
|
logStore.clear();
|
|
1794
|
+
httpHistoryStore.clear();
|
|
2854
1795
|
done();
|
|
2855
1796
|
});
|
|
2856
1797
|
fastify.addHook("onRoute", (routeOptions) => {
|
|
@@ -2858,6 +1799,29 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2858
1799
|
routeRegistry.register(routeOptions);
|
|
2859
1800
|
}
|
|
2860
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
|
+
});
|
|
2861
1825
|
fastify.get(`/${prefix}/routes`, async () => {
|
|
2862
1826
|
return { routes: routeRegistry.getAll() };
|
|
2863
1827
|
});
|
|
@@ -2865,6 +1829,13 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2865
1829
|
const { method } = request.params;
|
|
2866
1830
|
return { routes: routeRegistry.getByMethod(method) };
|
|
2867
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
|
+
});
|
|
2868
1839
|
fastify.get(`/${prefix}/registry/stats`, async () => {
|
|
2869
1840
|
return {
|
|
2870
1841
|
totalRoutes: routeRegistry.count(),
|
|
@@ -2981,6 +1952,17 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
2981
1952
|
}
|
|
2982
1953
|
};
|
|
2983
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
|
+
});
|
|
2984
1966
|
fastify.get(`/${prefix}/queues/:queueName/process-dlq`, async (request, reply) => {
|
|
2985
1967
|
const { queueName } = request.params;
|
|
2986
1968
|
const queueManager = fastify.queues;
|
|
@@ -3110,6 +2092,17 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
3110
2092
|
timerId: jobStats.timerId ? "scheduled" : "not_scheduled"
|
|
3111
2093
|
};
|
|
3112
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
|
+
});
|
|
3113
2106
|
fastify.post(`/${prefix}/schedules/:jobName/trigger`, async (request, reply) => {
|
|
3114
2107
|
const { jobName } = request.params;
|
|
3115
2108
|
const schedulerManager = fastify.scheduler;
|
|
@@ -3222,25 +2215,37 @@ async function devConsolePluginFunction(fastify, options = {}) {
|
|
|
3222
2215
|
await wsManager.sendToClient(connectionId, request.body);
|
|
3223
2216
|
return reply.status(200).send({ status: "success", message: `Message sent to ${connectionId}` });
|
|
3224
2217
|
} catch (error) {
|
|
2218
|
+
if (error instanceof WebSocketConnectionGoneError2) {
|
|
2219
|
+
return reply.status(410).send({ error: error.message });
|
|
2220
|
+
}
|
|
3225
2221
|
return reply.status(404).send({ error: error.message });
|
|
3226
2222
|
}
|
|
3227
2223
|
});
|
|
3228
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
|
+
};
|
|
3229
2232
|
reply.type("text/event-stream");
|
|
3230
2233
|
reply.header("Cache-Control", "no-cache");
|
|
3231
2234
|
reply.header("Connection", "keep-alive");
|
|
3232
2235
|
reply.header("Access-Control-Allow-Origin", "*");
|
|
3233
|
-
const initialLogs = logStore.getLogs(50);
|
|
2236
|
+
const initialLogs = logStore.getLogs(50).filter(matchesFilters);
|
|
3234
2237
|
reply.raw.write(`data: ${JSON.stringify({ type: "initial", logs: initialLogs })}
|
|
3235
2238
|
|
|
3236
2239
|
`);
|
|
3237
|
-
const
|
|
3238
|
-
|
|
2240
|
+
const unsubscribe2 = logStore.subscribe((log) => {
|
|
2241
|
+
if (matchesFilters(log)) {
|
|
2242
|
+
reply.raw.write(`data: ${JSON.stringify({ type: "log", log })}
|
|
3239
2243
|
|
|
3240
2244
|
`);
|
|
2245
|
+
}
|
|
3241
2246
|
});
|
|
3242
2247
|
request.raw.on("close", () => {
|
|
3243
|
-
|
|
2248
|
+
unsubscribe2();
|
|
3244
2249
|
});
|
|
3245
2250
|
});
|
|
3246
2251
|
if (enableUI) {
|
|
@@ -3264,17 +2269,4 @@ export {
|
|
|
3264
2269
|
devConsolePlugin,
|
|
3265
2270
|
routeRegistry
|
|
3266
2271
|
};
|
|
3267
|
-
/*! Bundled license information:
|
|
3268
|
-
|
|
3269
|
-
lodash-es/lodash.js:
|
|
3270
|
-
(**
|
|
3271
|
-
* @license
|
|
3272
|
-
* Lodash (Custom Build) <https://lodash.com/>
|
|
3273
|
-
* Build: `lodash modularize exports="es" -o ./`
|
|
3274
|
-
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
|
3275
|
-
* Released under MIT license <https://lodash.com/license>
|
|
3276
|
-
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
|
3277
|
-
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
3278
|
-
*)
|
|
3279
|
-
*/
|
|
3280
2272
|
//# sourceMappingURL=index.js.map
|