logixia 2.0.10 → 2.1.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/README.md +2 -1
- package/dist/{async-context-Dv0bmPC-.js → async-context-Dgl3ZLyb.js} +4 -4
- package/dist/{async-context-Dv0bmPC-.js.map → async-context-Dgl3ZLyb.js.map} +1 -1
- package/dist/{async-context-COuvmwsS.mjs → async-context-fBqC5lNc.mjs} +3 -3
- package/dist/{async-context-COuvmwsS.mjs.map → async-context-fBqC5lNc.mjs.map} +1 -1
- package/dist/{azure-monitor.transport-CbSHOnNX.d.mts → azure-monitor.transport-C5VjvREZ.d.mts} +2 -2
- package/dist/{azure-monitor.transport-CbSHOnNX.d.mts.map → azure-monitor.transport-C5VjvREZ.d.mts.map} +1 -1
- package/dist/{azure-monitor.transport-Bvf7Xegt.d.ts → azure-monitor.transport-CyHDjHS-.d.ts} +2 -2
- package/dist/{azure-monitor.transport-Bvf7Xegt.d.ts.map → azure-monitor.transport-CyHDjHS-.d.ts.map} +1 -1
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs → azure-monitor.transport-DGiacfvT.mjs} +19 -8
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs.map → azure-monitor.transport-DGiacfvT.mjs.map} +1 -1
- package/dist/{azure-monitor.transport-D6ryi8rA.js → azure-monitor.transport-DX3fuK2t.js} +19 -8
- package/dist/{azure-monitor.transport-D6ryi8rA.js.map → azure-monitor.transport-DX3fuK2t.js.map} +1 -1
- package/dist/azure.d.mts +1 -1
- package/dist/azure.d.ts +1 -1
- package/dist/azure.js +1 -1
- package/dist/azure.mjs +1 -1
- package/dist/{batch-buffer-CFzKnuNZ.js → batch-buffer-BLmVvJAZ.js} +11 -7
- package/dist/{batch-buffer-CFzKnuNZ.js.map → batch-buffer-BLmVvJAZ.js.map} +1 -1
- package/dist/{batch-buffer-CQUWDMYq.mjs → batch-buffer-CM-lK_Qy.mjs} +11 -7
- package/dist/{batch-buffer-CQUWDMYq.mjs.map → batch-buffer-CM-lK_Qy.mjs.map} +1 -1
- package/dist/browser.d.mts +2 -0
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.d.ts +2 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +42 -13
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +42 -13
- package/dist/browser.mjs.map +1 -1
- package/dist/{builder-Bj3mDeBw.mjs → builder-CA_dokb4.mjs} +246 -101
- package/dist/builder-CA_dokb4.mjs.map +1 -0
- package/dist/{builder-BLIStsCK.js → builder-jMlVYaQa.js} +256 -111
- package/dist/builder-jMlVYaQa.js.map +1 -0
- package/dist/cli/index.js +21 -16
- package/dist/cloudwatch.d.mts +1 -1
- package/dist/cloudwatch.d.ts +1 -1
- package/dist/cloudwatch.js +1 -1
- package/dist/cloudwatch.mjs +1 -1
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs → cloudwatch.transport-Cr0fEpko.mjs} +24 -9
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs.map → cloudwatch.transport-Cr0fEpko.mjs.map} +1 -1
- package/dist/{cloudwatch.transport-CQyT6U91.d.ts → cloudwatch.transport-DdSjUWvu.d.ts} +2 -2
- package/dist/{cloudwatch.transport-CQyT6U91.d.ts.map → cloudwatch.transport-DdSjUWvu.d.ts.map} +1 -1
- package/dist/{cloudwatch.transport-D4G5xYfG.js → cloudwatch.transport-Lst1Bbmr.js} +24 -9
- package/dist/{cloudwatch.transport-D4G5xYfG.js.map → cloudwatch.transport-Lst1Bbmr.js.map} +1 -1
- package/dist/{cloudwatch.transport-CjVrI6S4.d.mts → cloudwatch.transport-_hS7Mcc5.d.mts} +2 -2
- package/dist/{cloudwatch.transport-CjVrI6S4.d.mts.map → cloudwatch.transport-_hS7Mcc5.d.mts.map} +1 -1
- package/dist/correlation.js +3 -3
- package/dist/correlation.mjs +3 -3
- package/dist/{coerce.utils-C1C5sIX0.js → csv.utils-CbVHY1pd.js} +24 -1
- package/dist/csv.utils-CbVHY1pd.js.map +1 -0
- package/dist/{coerce.utils-B_rEudCn.mjs → csv.utils-ZdxSod5g.mjs} +19 -2
- package/dist/csv.utils-ZdxSod5g.mjs.map +1 -0
- package/dist/{entry-fields-CVwoQplO.js → entry-fields-BSLkzw18.js} +3 -3
- package/dist/{entry-fields-CVwoQplO.js.map → entry-fields-BSLkzw18.js.map} +1 -1
- package/dist/{entry-fields-DNTKQxoU.mjs → entry-fields-J1OBi24o.mjs} +2 -2
- package/dist/{entry-fields-DNTKQxoU.mjs.map → entry-fields-J1OBi24o.mjs.map} +1 -1
- package/dist/gcp.d.mts +1 -1
- package/dist/gcp.d.ts +1 -1
- package/dist/gcp.js +1 -1
- package/dist/gcp.mjs +1 -1
- package/dist/{gcp.transport-CVuRpchl.js → gcp.transport-5-ZIjN8r.js} +18 -9
- package/dist/{gcp.transport-CVuRpchl.js.map → gcp.transport-5-ZIjN8r.js.map} +1 -1
- package/dist/{gcp.transport-D9SdLPwP.d.mts → gcp.transport-C8VJiEM7.d.mts} +2 -2
- package/dist/{gcp.transport-D9SdLPwP.d.mts.map → gcp.transport-C8VJiEM7.d.mts.map} +1 -1
- package/dist/{gcp.transport-BCIvdFV-.d.ts → gcp.transport-Ct0BtKFo.d.ts} +2 -2
- package/dist/{gcp.transport-BCIvdFV-.d.ts.map → gcp.transport-Ct0BtKFo.d.ts.map} +1 -1
- package/dist/{gcp.transport-LLK2bw2-.mjs → gcp.transport-Cw3Qke85.mjs} +18 -9
- package/dist/{gcp.transport-LLK2bw2-.mjs.map → gcp.transport-Cw3Qke85.mjs.map} +1 -1
- package/dist/{index-BAkPHp5D.d.mts → index-B3ajtXJd.d.mts} +2 -2
- package/dist/{index-BAkPHp5D.d.mts.map → index-B3ajtXJd.d.mts.map} +1 -1
- package/dist/{index-e7LowmAu.d.ts → index-oqD3WNxf.d.ts} +2 -2
- package/dist/{index-e7LowmAu.d.ts.map → index-oqD3WNxf.d.ts.map} +1 -1
- package/dist/index.d.mts +8 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +34 -17
- package/dist/index.mjs.map +1 -1
- package/dist/{logitron-logger-DVUdgmBY.d.ts → logitron-logger-Csn3e6Qu.d.ts} +2 -2
- package/dist/{logitron-logger-DVUdgmBY.d.ts.map → logitron-logger-Csn3e6Qu.d.ts.map} +1 -1
- package/dist/{logitron-logger-DoWmMjYB.d.mts → logitron-logger-fMOkXr8H.d.mts} +2 -2
- package/dist/{logitron-logger-DoWmMjYB.d.mts.map → logitron-logger-fMOkXr8H.d.mts.map} +1 -1
- package/dist/middleware.d.mts +1 -1
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +12 -7
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +11 -6
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +51 -18
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +51 -18
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +207 -65
- package/dist/nest.js.map +1 -1
- package/dist/nest.mjs +202 -60
- package/dist/nest.mjs.map +1 -1
- package/dist/{otel-ChnVYRLA.js → otel-8ypFGG19.js} +452 -120
- package/dist/otel-8ypFGG19.js.map +1 -0
- package/dist/{otel-DOuwGPCd.mjs → otel-b-s2QbY4.mjs} +450 -118
- package/dist/otel-b-s2QbY4.mjs.map +1 -0
- package/dist/own-property-BWkibepN.js +40 -0
- package/dist/own-property-BWkibepN.js.map +1 -0
- package/dist/own-property-CjFw9qwZ.mjs +29 -0
- package/dist/own-property-CjFw9qwZ.mjs.map +1 -0
- package/dist/{request-trace-ByujsUZk.js → request-trace-C7kXMjx5.js} +5 -6
- package/dist/{request-trace-ByujsUZk.js.map → request-trace-C7kXMjx5.js.map} +1 -1
- package/dist/{request-trace-BgNykH9M.mjs → request-trace-Dekcnenb.mjs} +5 -6
- package/dist/{request-trace-BgNykH9M.mjs.map → request-trace-Dekcnenb.mjs.map} +1 -1
- package/dist/{safe-stringify--7gbe95n.mjs → safe-stringify-B5p5ktFe.mjs} +2 -2
- package/dist/{safe-stringify--7gbe95n.mjs.map → safe-stringify-B5p5ktFe.mjs.map} +1 -1
- package/dist/{safe-stringify-CN7icp8S.js → safe-stringify-J3sh-7YM.js} +5 -5
- package/dist/{safe-stringify-CN7icp8S.js.map → safe-stringify-J3sh-7YM.js.map} +1 -1
- package/dist/{search-DPwTKPve.d.ts → search-B-hJe2N-.d.mts} +9 -8
- package/dist/{search-D-8HJADE.d.mts.map → search-B-hJe2N-.d.mts.map} +1 -1
- package/dist/{search-D-8HJADE.d.mts → search-BlKBLm5D.d.ts} +9 -8
- package/dist/{search-DPwTKPve.d.ts.map → search-BlKBLm5D.d.ts.map} +1 -1
- package/dist/{search-DAXKE-P9.mjs → search-CTR4XNHq.mjs} +91 -44
- package/dist/search-CTR4XNHq.mjs.map +1 -0
- package/dist/{search-CRjhHxe9.js → search-_qx4YzRP.js} +94 -47
- package/dist/search-_qx4YzRP.js.map +1 -0
- package/dist/search.d.mts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +1 -1
- package/dist/search.mjs +1 -1
- package/dist/{secret-config-BvFl2dC8.js → secret-config--cVTOWwp.js} +6 -5
- package/dist/{secret-config-BvFl2dC8.js.map → secret-config--cVTOWwp.js.map} +1 -1
- package/dist/{secret-config-ByGq7kJd.mjs → secret-config-GKmb15Me.mjs} +3 -2
- package/dist/{secret-config-ByGq7kJd.mjs.map → secret-config-GKmb15Me.mjs.map} +1 -1
- package/dist/{sensitive-names-o__1PUUR.mjs → sensitive-names-BiBM6R1X.mjs} +2 -28
- package/dist/sensitive-names-BiBM6R1X.mjs.map +1 -0
- package/dist/{sensitive-names-CHMowujD.js → sensitive-names-NiKIRhp5.js} +5 -37
- package/dist/sensitive-names-NiKIRhp5.js.map +1 -0
- package/dist/{slack.transport-BDyENuR0.d.mts → slack.transport-5OPL7H3u.d.mts} +76 -3
- package/dist/slack.transport-5OPL7H3u.d.mts.map +1 -0
- package/dist/{slack.transport-C6E0l4kw.d.ts → slack.transport-BFpBD_Ri.d.ts} +76 -3
- package/dist/slack.transport-BFpBD_Ri.d.ts.map +1 -0
- package/dist/{slack.transport-zLQ9MGxS.js → slack.transport-Cpx5fKga.js} +272 -21
- package/dist/slack.transport-Cpx5fKga.js.map +1 -0
- package/dist/{slack.transport-CSj1j0_X.mjs → slack.transport-Qwj9WkrJ.mjs} +267 -22
- package/dist/slack.transport-Qwj9WkrJ.mjs.map +1 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{transport.types-CzJq4IrF.d.ts → transport.types-B1ejJJP6.d.mts} +6 -1
- package/dist/transport.types-B1ejJJP6.d.mts.map +1 -0
- package/dist/{transport.types-CzJq4IrF.d.mts → transport.types-B1ejJJP6.d.ts} +6 -1
- package/dist/transport.types-B1ejJJP6.d.ts.map +1 -0
- package/dist/transports.d.mts +61 -11
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +61 -11
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +7 -6
- package/dist/transports.mjs +7 -7
- package/dist/worker-entry.js +200 -52
- package/dist/worker.d.mts +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/{worker.transport-B9mCQSM1.d.ts → worker.transport-B7olL1Uh.d.ts} +12 -2
- package/dist/worker.transport-B7olL1Uh.d.ts.map +1 -0
- package/dist/{worker.transport-D78xhNhB.mjs → worker.transport-BHnWDXPH.mjs} +59 -13
- package/dist/worker.transport-BHnWDXPH.mjs.map +1 -0
- package/dist/{worker.transport-DQDv1nAJ.js → worker.transport-C7jNGhev.js} +59 -13
- package/dist/worker.transport-C7jNGhev.js.map +1 -0
- package/dist/{worker.transport-Bl8lFRck.d.mts → worker.transport-DnU0_9Bd.d.mts} +12 -2
- package/dist/worker.transport-DnU0_9Bd.d.mts.map +1 -0
- package/package.json +10 -3
- package/dist/builder-BLIStsCK.js.map +0 -1
- package/dist/builder-Bj3mDeBw.mjs.map +0 -1
- package/dist/coerce.utils-B_rEudCn.mjs.map +0 -1
- package/dist/coerce.utils-C1C5sIX0.js.map +0 -1
- package/dist/otel-ChnVYRLA.js.map +0 -1
- package/dist/otel-DOuwGPCd.mjs.map +0 -1
- package/dist/search-CRjhHxe9.js.map +0 -1
- package/dist/search-DAXKE-P9.mjs.map +0 -1
- package/dist/sensitive-names-CHMowujD.js.map +0 -1
- package/dist/sensitive-names-o__1PUUR.mjs.map +0 -1
- package/dist/slack.transport-BDyENuR0.d.mts.map +0 -1
- package/dist/slack.transport-C6E0l4kw.d.ts.map +0 -1
- package/dist/slack.transport-CSj1j0_X.mjs.map +0 -1
- package/dist/slack.transport-zLQ9MGxS.js.map +0 -1
- package/dist/transport.types-CzJq4IrF.d.mts.map +0 -1
- package/dist/transport.types-CzJq4IrF.d.ts.map +0 -1
- package/dist/worker.transport-B9mCQSM1.d.ts.map +0 -1
- package/dist/worker.transport-Bl8lFRck.d.mts.map +0 -1
- package/dist/worker.transport-D78xhNhB.mjs.map +0 -1
- package/dist/worker.transport-DQDv1nAJ.js.map +0 -1
package/dist/worker-entry.js
CHANGED
|
@@ -243,12 +243,35 @@ function mergeCoreFields(core, data, extraReserved = []) {
|
|
|
243
243
|
* transports: { console: { format: 'json' } }
|
|
244
244
|
*/
|
|
245
245
|
var ConsoleTransport = class ConsoleTransport {
|
|
246
|
+
config;
|
|
247
|
+
name = "console";
|
|
248
|
+
level;
|
|
249
|
+
filter;
|
|
250
|
+
static COLORS = /* @__PURE__ */ new Map([
|
|
251
|
+
["black", "\x1B[30m"],
|
|
252
|
+
["red", "\x1B[31m"],
|
|
253
|
+
["green", "\x1B[32m"],
|
|
254
|
+
["yellow", "\x1B[33m"],
|
|
255
|
+
["blue", "\x1B[34m"],
|
|
256
|
+
["magenta", "\x1B[35m"],
|
|
257
|
+
["cyan", "\x1B[36m"],
|
|
258
|
+
["white", "\x1B[37m"],
|
|
259
|
+
["gray", "\x1B[90m"],
|
|
260
|
+
["grey", "\x1B[90m"],
|
|
261
|
+
["brightred", "\x1B[91m"],
|
|
262
|
+
["brightgreen", "\x1B[92m"],
|
|
263
|
+
["brightyellow", "\x1B[93m"],
|
|
264
|
+
["brightblue", "\x1B[94m"],
|
|
265
|
+
["brightmagenta", "\x1B[95m"],
|
|
266
|
+
["brightcyan", "\x1B[96m"],
|
|
267
|
+
["brightwhite", "\x1B[97m"],
|
|
268
|
+
["reset", "\x1B[0m"]
|
|
269
|
+
]);
|
|
246
270
|
static sanitize(value) {
|
|
247
271
|
return stripControlChars(safeToString(value));
|
|
248
272
|
}
|
|
249
273
|
constructor(config = {}) {
|
|
250
274
|
this.config = config;
|
|
251
|
-
this.name = "console";
|
|
252
275
|
this.level = config.level;
|
|
253
276
|
if (config.filter) this.filter = config.filter;
|
|
254
277
|
}
|
|
@@ -312,26 +335,6 @@ var ConsoleTransport = class ConsoleTransport {
|
|
|
312
335
|
close() {
|
|
313
336
|
return Promise.resolve();
|
|
314
337
|
}
|
|
315
|
-
static #_ = () => this.COLORS = /* @__PURE__ */ new Map([
|
|
316
|
-
["black", "\x1B[30m"],
|
|
317
|
-
["red", "\x1B[31m"],
|
|
318
|
-
["green", "\x1B[32m"],
|
|
319
|
-
["yellow", "\x1B[33m"],
|
|
320
|
-
["blue", "\x1B[34m"],
|
|
321
|
-
["magenta", "\x1B[35m"],
|
|
322
|
-
["cyan", "\x1B[36m"],
|
|
323
|
-
["white", "\x1B[37m"],
|
|
324
|
-
["gray", "\x1B[90m"],
|
|
325
|
-
["grey", "\x1B[90m"],
|
|
326
|
-
["brightred", "\x1B[91m"],
|
|
327
|
-
["brightgreen", "\x1B[92m"],
|
|
328
|
-
["brightyellow", "\x1B[93m"],
|
|
329
|
-
["brightblue", "\x1B[94m"],
|
|
330
|
-
["brightmagenta", "\x1B[95m"],
|
|
331
|
-
["brightcyan", "\x1B[96m"],
|
|
332
|
-
["brightwhite", "\x1B[97m"],
|
|
333
|
-
["reset", "\x1B[0m"]
|
|
334
|
-
]);
|
|
335
338
|
};
|
|
336
339
|
// src/utils/internal-log.ts
|
|
337
340
|
/**
|
|
@@ -396,10 +399,12 @@ const DROP_WARN_INTERVAL_MS$1 = 5e3;
|
|
|
396
399
|
* A cap below `batchSize` is raised to `batchSize` so one batch always fits.
|
|
397
400
|
*/
|
|
398
401
|
var BufferCap = class {
|
|
402
|
+
name;
|
|
403
|
+
max;
|
|
404
|
+
dropped = 0;
|
|
405
|
+
lastWarning = 0;
|
|
399
406
|
constructor(name, requested, batchSize, fallback = DEFAULT_BUFFER_CAP) {
|
|
400
407
|
this.name = name;
|
|
401
|
-
this.dropped = 0;
|
|
402
|
-
this.lastWarning = 0;
|
|
403
408
|
let max = fallback;
|
|
404
409
|
if (requested !== void 0) {
|
|
405
410
|
if (Number.isInteger(requested) && requested >= 1) max = requested;
|
|
@@ -776,7 +781,9 @@ function isDataError(type, error) {
|
|
|
776
781
|
return false;
|
|
777
782
|
}
|
|
778
783
|
/** A write that stored part of a batch: only `remaining` still needs to be written. */
|
|
784
|
+
var DatabaseUnavailableError = class extends Error {};
|
|
779
785
|
var PartialWriteError = class extends Error {
|
|
786
|
+
remaining;
|
|
780
787
|
constructor(message, remaining, cause) {
|
|
781
788
|
super(message, { cause });
|
|
782
789
|
this.remaining = remaining;
|
|
@@ -803,11 +810,30 @@ function validateSqlIdentifier(name, label = "table") {
|
|
|
803
810
|
* }
|
|
804
811
|
*/
|
|
805
812
|
var DatabaseTransport = class {
|
|
813
|
+
config;
|
|
814
|
+
name = "database";
|
|
815
|
+
batchSize;
|
|
816
|
+
flushInterval;
|
|
817
|
+
level;
|
|
818
|
+
filter;
|
|
819
|
+
batch = [];
|
|
820
|
+
flushTimer;
|
|
821
|
+
/**
|
|
822
|
+
* The in-flight drain promise, or undefined when idle. Concurrent flush()
|
|
823
|
+
* calls (the un-awaited threshold flush write() fires, plus the interval
|
|
824
|
+
* timer) all await this single promise instead of starting their own drain,
|
|
825
|
+
* so a batch is never snapshotted and written twice. Mirrors the FileTransport
|
|
826
|
+
* fix for the overlapping-batch-flush duplication bug.
|
|
827
|
+
*/
|
|
828
|
+
flushPromise;
|
|
829
|
+
connection;
|
|
830
|
+
isConnected = false;
|
|
831
|
+
connectionPromise;
|
|
832
|
+
connectFailures = 0;
|
|
833
|
+
nextConnectAt = 0;
|
|
834
|
+
cap;
|
|
806
835
|
constructor(config) {
|
|
807
836
|
this.config = config;
|
|
808
|
-
this.name = "database";
|
|
809
|
-
this.batch = [];
|
|
810
|
-
this.isConnected = false;
|
|
811
837
|
protectSecrets(this, ["config", "connection"]);
|
|
812
838
|
this.batchSize = config.batchSize || 100;
|
|
813
839
|
this.flushInterval = config.flushInterval || 5e3;
|
|
@@ -826,9 +852,12 @@ var DatabaseTransport = class {
|
|
|
826
852
|
];
|
|
827
853
|
}
|
|
828
854
|
async write(entry) {
|
|
829
|
-
if (!this.isConnected) await this.connect();
|
|
830
855
|
this.addToBatch(entry);
|
|
831
|
-
if (this.batch.length >= this.batchSize)
|
|
856
|
+
if (this.batch.length >= this.batchSize) try {
|
|
857
|
+
await this.flush();
|
|
858
|
+
} catch (error) {
|
|
859
|
+
if (!(error instanceof DatabaseUnavailableError)) throw error;
|
|
860
|
+
}
|
|
832
861
|
}
|
|
833
862
|
addToBatch(entry) {
|
|
834
863
|
this.batch.push(entry);
|
|
@@ -853,6 +882,7 @@ var DatabaseTransport = class {
|
|
|
853
882
|
*/
|
|
854
883
|
async drain() {
|
|
855
884
|
while (this.batch.length > 0) {
|
|
885
|
+
await this.ensureConnected();
|
|
856
886
|
const entries = this.batch.splice(0, this.batchSize);
|
|
857
887
|
try {
|
|
858
888
|
await this.writeEntries(entries);
|
|
@@ -950,6 +980,38 @@ var DatabaseTransport = class {
|
|
|
950
980
|
return false;
|
|
951
981
|
}
|
|
952
982
|
}
|
|
983
|
+
async ensureConnected() {
|
|
984
|
+
if (this.isConnected) return;
|
|
985
|
+
const wait = this.nextConnectAt - Date.now();
|
|
986
|
+
if (wait > 0) throw new DatabaseUnavailableError(`Database unavailable; next connect attempt in ${wait}ms`);
|
|
987
|
+
try {
|
|
988
|
+
await this.connect();
|
|
989
|
+
this.connectFailures = 0;
|
|
990
|
+
} catch (error) {
|
|
991
|
+
this.connectFailures += 1;
|
|
992
|
+
this.nextConnectAt = Date.now() + Math.min(3e4, 1e3 * 2 ** (this.connectFailures - 1));
|
|
993
|
+
internalError("Database connection error", error);
|
|
994
|
+
throw new DatabaseUnavailableError("Database unavailable", { cause: error });
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* The driver reported the connection gone (idle timeout, server restart,
|
|
999
|
+
* network drop). Forget it so the next flush reconnects; without an 'error'
|
|
1000
|
+
* listener the driver's emitter would crash the process.
|
|
1001
|
+
*/
|
|
1002
|
+
onConnectionLost(connection, error) {
|
|
1003
|
+
if (this.connection !== connection) return;
|
|
1004
|
+
if (error) internalError("Database connection lost", error);
|
|
1005
|
+
this.isConnected = false;
|
|
1006
|
+
this.connectionPromise = void 0;
|
|
1007
|
+
this.connection = void 0;
|
|
1008
|
+
}
|
|
1009
|
+
watchConnection(connection) {
|
|
1010
|
+
var _connection$on, _connection$on2;
|
|
1011
|
+
const lost = (error) => this.onConnectionLost(connection, error);
|
|
1012
|
+
(_connection$on = connection.on) === null || _connection$on === void 0 || _connection$on.call(connection, "error", lost);
|
|
1013
|
+
(_connection$on2 = connection.on) === null || _connection$on2 === void 0 || _connection$on2.call(connection, "end", () => lost());
|
|
1014
|
+
}
|
|
953
1015
|
async connect() {
|
|
954
1016
|
if (this.connectionPromise) return this.connectionPromise;
|
|
955
1017
|
this.connectionPromise = this.establishConnection().catch((error) => {
|
|
@@ -986,6 +1048,7 @@ var DatabaseTransport = class {
|
|
|
986
1048
|
});
|
|
987
1049
|
const connectionString = this.config.connectionString || `mongodb://${this.config.host}:${this.config.port}/${this.config.database}`;
|
|
988
1050
|
this.connection = new MongoClient(connectionString);
|
|
1051
|
+
this.watchConnection(this.connection);
|
|
989
1052
|
await this.connection.connect();
|
|
990
1053
|
await this.connection.db(this.config.database).admin().ping();
|
|
991
1054
|
} catch (error) {
|
|
@@ -1007,6 +1070,7 @@ var DatabaseTransport = class {
|
|
|
1007
1070
|
if (this.config.password) config.password = this.config.password;
|
|
1008
1071
|
if (this.config.ssl) config.ssl = typeof this.config.ssl === "boolean" ? {} : this.config.ssl;
|
|
1009
1072
|
this.connection = new Client(config);
|
|
1073
|
+
this.watchConnection(this.connection);
|
|
1010
1074
|
await this.connection.connect();
|
|
1011
1075
|
await this.createPostgreSQLTable();
|
|
1012
1076
|
} catch (error) {
|
|
@@ -1027,6 +1091,7 @@ var DatabaseTransport = class {
|
|
|
1027
1091
|
if (this.config.password) config.password = this.config.password;
|
|
1028
1092
|
if (this.config.ssl) config.ssl = typeof this.config.ssl === "boolean" ? {} : this.config.ssl;
|
|
1029
1093
|
this.connection = await mysql.createConnection(config);
|
|
1094
|
+
this.watchConnection(this.connection);
|
|
1030
1095
|
await this.createMySQLTable();
|
|
1031
1096
|
} catch (error) {
|
|
1032
1097
|
throw new Error(`MySQL connection failed: ${error}`, { cause: error });
|
|
@@ -1249,6 +1314,7 @@ var DatabaseTransport = class {
|
|
|
1249
1314
|
this.flushTimer = void 0;
|
|
1250
1315
|
}
|
|
1251
1316
|
const MAX_CLOSE_FLUSH_ATTEMPTS = 3;
|
|
1317
|
+
this.nextConnectAt = 0;
|
|
1252
1318
|
if (this.flushPromise) await this.flushPromise.catch(() => {});
|
|
1253
1319
|
for (let attempt = 0; attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0; attempt += 1) try {
|
|
1254
1320
|
await this.flush();
|
|
@@ -1257,17 +1323,19 @@ var DatabaseTransport = class {
|
|
|
1257
1323
|
}
|
|
1258
1324
|
if (this.batch.length > 0) internalError(`Database transport closing with ${this.batch.length} unflushed log entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
|
|
1259
1325
|
this.cap.report(0);
|
|
1260
|
-
|
|
1326
|
+
const connection = this.connection;
|
|
1327
|
+
this.connection = void 0;
|
|
1328
|
+
if (connection) switch (this.config.type) {
|
|
1261
1329
|
case "mongodb":
|
|
1262
|
-
await
|
|
1330
|
+
await connection.close();
|
|
1263
1331
|
break;
|
|
1264
1332
|
case "postgresql":
|
|
1265
|
-
await
|
|
1333
|
+
await connection.end();
|
|
1266
1334
|
break;
|
|
1267
1335
|
case "mysql":
|
|
1268
|
-
await
|
|
1336
|
+
await connection.end();
|
|
1269
1337
|
break;
|
|
1270
|
-
case "sqlite": await
|
|
1338
|
+
case "sqlite": await connection.close();
|
|
1271
1339
|
}
|
|
1272
1340
|
this.isConnected = false;
|
|
1273
1341
|
}
|
|
@@ -1308,6 +1376,23 @@ var DatabaseTransport = class {
|
|
|
1308
1376
|
}
|
|
1309
1377
|
}
|
|
1310
1378
|
};
|
|
1379
|
+
// src/utils/csv.utils.ts
|
|
1380
|
+
const CSV_FORMULA_PREFIX = /^[=+\-@\t\r]/;
|
|
1381
|
+
/**
|
|
1382
|
+
* Render one CSV field. String values that a spreadsheet would treat as a
|
|
1383
|
+
* formula (leading `=`, `+`, `-`, `@`, tab or CR) are prefixed with `'`.
|
|
1384
|
+
* Objects are JSON-encoded. Fields containing a comma, quote, CR or LF are quoted;
|
|
1385
|
+
* `alwaysQuote` quotes every field. `null` / `undefined` render as empty.
|
|
1386
|
+
*/
|
|
1387
|
+
function csvField$1(value, alwaysQuote = false) {
|
|
1388
|
+
if (value === void 0 || value === null) return alwaysQuote ? "\"\"" : "";
|
|
1389
|
+
let str;
|
|
1390
|
+
if (typeof value === "string") str = CSV_FORMULA_PREFIX.test(value) ? `'${value}` : value;
|
|
1391
|
+
else if (typeof value === "object") str = JSON.stringify(value);
|
|
1392
|
+
else str = String(value);
|
|
1393
|
+
if (alwaysQuote || /[",\r\n]/.test(str)) return `"${str.replace(/"/g, "\"\"")}"`;
|
|
1394
|
+
return str;
|
|
1395
|
+
}
|
|
1311
1396
|
// src/transports/file.transport.ts
|
|
1312
1397
|
const appendFile = (0, node_util.promisify)(node_fs.appendFile);
|
|
1313
1398
|
const rename = (0, node_util.promisify)(node_fs.rename);
|
|
@@ -1319,7 +1404,7 @@ const DEFAULT_MAX_QUEUE = 1e4;
|
|
|
1319
1404
|
const DEFAULT_FILE_MODE = 384;
|
|
1320
1405
|
const DEFAULT_DIR_MODE = 448;
|
|
1321
1406
|
function csvField(value) {
|
|
1322
|
-
return
|
|
1407
|
+
return csvField$1(stripControlChars(safeToString(value)), true);
|
|
1323
1408
|
}
|
|
1324
1409
|
const SIZE_UNITS = {
|
|
1325
1410
|
"": 1,
|
|
@@ -1364,21 +1449,51 @@ function formatDatePattern(date, pattern) {
|
|
|
1364
1449
|
* transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }
|
|
1365
1450
|
*/
|
|
1366
1451
|
var FileTransport = class {
|
|
1452
|
+
name = "file";
|
|
1453
|
+
level;
|
|
1454
|
+
batchSize;
|
|
1455
|
+
flushInterval;
|
|
1456
|
+
filter;
|
|
1457
|
+
config;
|
|
1458
|
+
maxBytes;
|
|
1459
|
+
maxFiles;
|
|
1460
|
+
compress;
|
|
1461
|
+
intervalMs;
|
|
1462
|
+
timestampedActive;
|
|
1463
|
+
dir;
|
|
1464
|
+
base;
|
|
1465
|
+
ext;
|
|
1466
|
+
batch = [];
|
|
1467
|
+
batchTimer;
|
|
1468
|
+
lastRotation = /* @__PURE__ */ new Date();
|
|
1469
|
+
currentFilePath;
|
|
1470
|
+
bytesWritten = 0;
|
|
1471
|
+
period;
|
|
1472
|
+
rotateRequested = false;
|
|
1473
|
+
rotationWaiters = [];
|
|
1474
|
+
/**
|
|
1475
|
+
* The one drain loop, or undefined when idle. Only one ever runs, so the batch
|
|
1476
|
+
* is never snapshotted and written twice, and rotation (which runs inside it)
|
|
1477
|
+
* never races a write.
|
|
1478
|
+
*/
|
|
1479
|
+
draining;
|
|
1480
|
+
/** Entries queued and entries written (or failed) so far; flush() waits on these. */
|
|
1481
|
+
enqueued = 0;
|
|
1482
|
+
settled = 0;
|
|
1483
|
+
flushWaiters = [];
|
|
1484
|
+
maxQueue;
|
|
1485
|
+
fileMode;
|
|
1486
|
+
dirMode;
|
|
1487
|
+
queuePolicy;
|
|
1488
|
+
spaceWaiters = [];
|
|
1489
|
+
inFlight = 0;
|
|
1490
|
+
dropped = 0;
|
|
1491
|
+
lastDropWarning = 0;
|
|
1492
|
+
/** Gzip and pruning of rotated files run off the write path, one at a time. */
|
|
1493
|
+
housekeeping = Promise.resolve();
|
|
1494
|
+
exitListenerAttached = false;
|
|
1495
|
+
onExit = () => this.flushSyncOnExit();
|
|
1367
1496
|
constructor(config) {
|
|
1368
|
-
this.name = "file";
|
|
1369
|
-
this.batch = [];
|
|
1370
|
-
this.lastRotation = /* @__PURE__ */ new Date();
|
|
1371
|
-
this.bytesWritten = 0;
|
|
1372
|
-
this.rotateRequested = false;
|
|
1373
|
-
this.rotationWaiters = [];
|
|
1374
|
-
this.enqueued = 0;
|
|
1375
|
-
this.settled = 0;
|
|
1376
|
-
this.flushWaiters = [];
|
|
1377
|
-
this.spaceWaiters = [];
|
|
1378
|
-
this.inFlight = 0;
|
|
1379
|
-
this.dropped = 0;
|
|
1380
|
-
this.lastDropWarning = 0;
|
|
1381
|
-
this.housekeeping = Promise.resolve();
|
|
1382
1497
|
this.config = {
|
|
1383
1498
|
format: "json",
|
|
1384
1499
|
batchSize: 100,
|
|
@@ -1466,6 +1581,7 @@ var FileTransport = class {
|
|
|
1466
1581
|
await done;
|
|
1467
1582
|
}
|
|
1468
1583
|
enqueue(entry) {
|
|
1584
|
+
this.attachExitListener();
|
|
1469
1585
|
if (this.queueFull()) {
|
|
1470
1586
|
if (this.queuePolicy === "drop-oldest" && this.batch.length > 0) {
|
|
1471
1587
|
this.batch.shift();
|
|
@@ -1477,6 +1593,30 @@ var FileTransport = class {
|
|
|
1477
1593
|
this.batch.push(entry);
|
|
1478
1594
|
this.enqueued += 1;
|
|
1479
1595
|
}
|
|
1596
|
+
attachExitListener() {
|
|
1597
|
+
if (this.exitListenerAttached) return;
|
|
1598
|
+
this.exitListenerAttached = true;
|
|
1599
|
+
process.on("exit", this.onExit);
|
|
1600
|
+
}
|
|
1601
|
+
detachExitListener() {
|
|
1602
|
+
if (!this.exitListenerAttached) return;
|
|
1603
|
+
this.exitListenerAttached = false;
|
|
1604
|
+
process.removeListener("exit", this.onExit);
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Last chance on process exit: async writes cannot finish, so whatever is still
|
|
1608
|
+
* batched is appended synchronously to the current file.
|
|
1609
|
+
*/
|
|
1610
|
+
flushSyncOnExit() {
|
|
1611
|
+
if (this.batch.length === 0) return;
|
|
1612
|
+
const pending = this.batch;
|
|
1613
|
+
this.batch = [];
|
|
1614
|
+
this.enqueued -= pending.length;
|
|
1615
|
+
try {
|
|
1616
|
+
const content = pending.map((entry) => this.formatEntry(entry)).join("\n") + "\n";
|
|
1617
|
+
node_fs.appendFileSync(this.currentFilePath, content, { mode: this.fileMode });
|
|
1618
|
+
} catch {}
|
|
1619
|
+
}
|
|
1480
1620
|
queueFull() {
|
|
1481
1621
|
return this.batch.length + this.inFlight >= this.maxQueue;
|
|
1482
1622
|
}
|
|
@@ -1561,7 +1701,11 @@ var FileTransport = class {
|
|
|
1561
1701
|
case "json": return stringifyForLog(mergeCoreFields({
|
|
1562
1702
|
timestamp: entry.timestamp.toISOString(),
|
|
1563
1703
|
level: entry.level,
|
|
1564
|
-
message: entry.message
|
|
1704
|
+
message: entry.message,
|
|
1705
|
+
context: entry.context,
|
|
1706
|
+
traceId: entry.traceId,
|
|
1707
|
+
appName: entry.appName,
|
|
1708
|
+
environment: entry.environment
|
|
1565
1709
|
}, entry.data));
|
|
1566
1710
|
case "csv": return [
|
|
1567
1711
|
entry.timestamp.toISOString(),
|
|
@@ -1583,9 +1727,12 @@ var FileTransport = class {
|
|
|
1583
1727
|
this.batchTimer = void 0;
|
|
1584
1728
|
}
|
|
1585
1729
|
this.startDrain();
|
|
1586
|
-
} else if (!this.batchTimer && this.config.flushInterval)
|
|
1587
|
-
this.
|
|
1588
|
-
|
|
1730
|
+
} else if (!this.batchTimer && this.config.flushInterval) {
|
|
1731
|
+
this.batchTimer = setTimeout(() => {
|
|
1732
|
+
this.flush().catch((err) => internalError("FileTransport interval flush failed", err));
|
|
1733
|
+
}, this.config.flushInterval);
|
|
1734
|
+
this.batchTimer.unref();
|
|
1735
|
+
}
|
|
1589
1736
|
}
|
|
1590
1737
|
/** Only ever called from drain(), so writes and rotations never overlap. */
|
|
1591
1738
|
async writeBatch(entries) {
|
|
@@ -1751,6 +1898,7 @@ var FileTransport = class {
|
|
|
1751
1898
|
this.batchTimer = void 0;
|
|
1752
1899
|
}
|
|
1753
1900
|
await this.flush();
|
|
1901
|
+
this.detachExitListener();
|
|
1754
1902
|
await this.housekeeping;
|
|
1755
1903
|
this.reportDropped(0);
|
|
1756
1904
|
}
|
package/dist/worker.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as WorkerTransportConfig, t as WorkerTransport } from "./worker.transport-
|
|
1
|
+
import { n as WorkerTransportConfig, t as WorkerTransport } from "./worker.transport-DnU0_9Bd.mjs";
|
|
2
2
|
export { WorkerTransport, WorkerTransportConfig };
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as WorkerTransportConfig, t as WorkerTransport } from "./worker.transport-
|
|
1
|
+
import { n as WorkerTransportConfig, t as WorkerTransport } from "./worker.transport-B7olL1Uh.js";
|
|
2
2
|
export { WorkerTransport, WorkerTransportConfig };
|
package/dist/worker.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_worker_transport = require("./worker.transport-
|
|
2
|
+
const require_worker_transport = require("./worker.transport-C7jNGhev.js");
|
|
3
3
|
exports.WorkerTransport = require_worker_transport.WorkerTransport;
|
package/dist/worker.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as WorkerTransport } from "./worker.transport-
|
|
1
|
+
export { t as WorkerTransport } from "./worker.transport-BHnWDXPH.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as TransportLogEntry, d as ITransport } from "./transport.types-
|
|
1
|
+
import { _ as TransportLogEntry, d as ITransport } from "./transport.types-B1ejJJP6.js";
|
|
2
2
|
interface WorkerTransportConfig {
|
|
3
3
|
/**
|
|
4
4
|
* Which built-in logixia transport to run inside the worker thread.
|
|
@@ -49,11 +49,21 @@ declare class WorkerTransport implements ITransport {
|
|
|
49
49
|
private restartTimer;
|
|
50
50
|
/** Set once close() begins, so a worker 'exit' no longer triggers a restart. */
|
|
51
51
|
private closing;
|
|
52
|
+
/** Writes have been posted since the last flush; keeps the beforeExit flush from looping. */
|
|
53
|
+
private dirty;
|
|
54
|
+
/** Run once the worker reports ready, used when the process wants to exit before that. */
|
|
55
|
+
private onReady;
|
|
52
56
|
/** Max time to wait for the worker's 'flushed' ack before giving up. */
|
|
53
57
|
private readonly FLUSH_TIMEOUT_MS;
|
|
54
58
|
/** Max time to wait for a graceful worker exit before force-terminating. */
|
|
55
59
|
private readonly SHUTDOWN_TIMEOUT_MS;
|
|
56
60
|
constructor(config: WorkerTransportConfig);
|
|
61
|
+
/**
|
|
62
|
+
* The worker is unref'd so it never keeps the process alive on its own; the
|
|
63
|
+
* trade-off is that lines still queued on it would be lost when the event loop
|
|
64
|
+
* drains. On beforeExit, re-ref the worker until it acknowledges a flush.
|
|
65
|
+
*/
|
|
66
|
+
private readonly flushForExit;
|
|
57
67
|
private startWorker;
|
|
58
68
|
/** The worker entry is always built as CommonJS, next to this module's output. */
|
|
59
69
|
protected workerFile(): string;
|
|
@@ -74,4 +84,4 @@ declare class WorkerTransport implements ITransport {
|
|
|
74
84
|
private drainBufferTo;
|
|
75
85
|
}
|
|
76
86
|
export { WorkerTransportConfig as n, WorkerTransport as t };
|
|
77
|
-
//# sourceMappingURL=worker.transport-
|
|
87
|
+
//# sourceMappingURL=worker.transport-B7olL1Uh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.transport-B7olL1Uh.d.ts","names":[],"sources":["../src/transports/worker.transport.ts"],"mappings":";UA0CiB;;;;;EAKf;;EAEA,iBAAiB;;;;;;EAMjB;;;;;EAKA;;;;;EAKA,SAAS;;;;;EAKT;;;;;;cAOW,2BAA2B;WACtB;WACA;WACA;WACA;UAER;UACA;mBACS;mBACA;UACT;mBACS;mBACA;;UAET;;UAEA;;UAEA;;UAEA;;mBAES;;mBAEA;EAEL,YAAA,QAAQ;;;;;;mBAeH;UAgBT;;YA4CE;UAIF;UAuBA;EASR,MAAM,OAAO;EAaP,SAAS;;;;;;;;EAsCT,SAAS;;EAqCT,YAAY;UAIV"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as protectSecrets } from "./secret-config-
|
|
1
|
+
import { n as protectSecrets } from "./secret-config-GKmb15Me.mjs";
|
|
2
2
|
import { r as internalWarn, t as internalError } from "./internal-log-Cy5nF1ZL.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -47,25 +47,56 @@ const __dirname = /* @__PURE__ */ getDirname();
|
|
|
47
47
|
* thread, completely removing JSON serialization and I/O from the main event loop.
|
|
48
48
|
*/
|
|
49
49
|
var WorkerTransport = class {
|
|
50
|
+
name = "worker";
|
|
51
|
+
level;
|
|
52
|
+
batchSize = 1;
|
|
53
|
+
flushInterval = 0;
|
|
54
|
+
worker = null;
|
|
55
|
+
ready = false;
|
|
56
|
+
buffer = [];
|
|
57
|
+
bufferSize;
|
|
58
|
+
restarts = 0;
|
|
59
|
+
maxRestarts;
|
|
60
|
+
config;
|
|
61
|
+
/** Pending restart-backoff timer, so close() can cancel a queued restart. */
|
|
62
|
+
restartTimer = null;
|
|
63
|
+
/** Set once close() begins, so a worker 'exit' no longer triggers a restart. */
|
|
64
|
+
closing = false;
|
|
65
|
+
/** Writes have been posted since the last flush; keeps the beforeExit flush from looping. */
|
|
66
|
+
dirty = false;
|
|
67
|
+
/** Run once the worker reports ready, used when the process wants to exit before that. */
|
|
68
|
+
onReady;
|
|
69
|
+
/** Max time to wait for the worker's 'flushed' ack before giving up. */
|
|
70
|
+
FLUSH_TIMEOUT_MS = 5e3;
|
|
71
|
+
/** Max time to wait for a graceful worker exit before force-terminating. */
|
|
72
|
+
SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
50
73
|
constructor(config) {
|
|
51
|
-
this.name = "worker";
|
|
52
|
-
this.batchSize = 1;
|
|
53
|
-
this.flushInterval = 0;
|
|
54
|
-
this.worker = null;
|
|
55
|
-
this.ready = false;
|
|
56
|
-
this.buffer = [];
|
|
57
|
-
this.restarts = 0;
|
|
58
|
-
this.restartTimer = null;
|
|
59
|
-
this.closing = false;
|
|
60
|
-
this.FLUSH_TIMEOUT_MS = 5e3;
|
|
61
|
-
this.SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
62
74
|
this.config = config;
|
|
63
75
|
this.bufferSize = config.bufferSize ?? 1e3;
|
|
64
76
|
this.maxRestarts = config.maxRestarts ?? 3;
|
|
65
77
|
this.level = config.level;
|
|
66
78
|
protectSecrets(this, ["config"]);
|
|
67
79
|
this.startWorker();
|
|
80
|
+
process.on("beforeExit", this.flushForExit);
|
|
68
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The worker is unref'd so it never keeps the process alive on its own; the
|
|
84
|
+
* trade-off is that lines still queued on it would be lost when the event loop
|
|
85
|
+
* drains. On beforeExit, re-ref the worker until it acknowledges a flush.
|
|
86
|
+
*/
|
|
87
|
+
flushForExit = () => {
|
|
88
|
+
var _worker$ref;
|
|
89
|
+
const worker = this.worker;
|
|
90
|
+
if (!worker || this.closing || !this.dirty) return;
|
|
91
|
+
(_worker$ref = worker.ref) === null || _worker$ref === void 0 || _worker$ref.call(worker);
|
|
92
|
+
const timer = setTimeout(() => worker.unref(), this.FLUSH_TIMEOUT_MS);
|
|
93
|
+
const done = () => {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
worker.unref();
|
|
96
|
+
};
|
|
97
|
+
if (this.ready) this.flush().finally(done);
|
|
98
|
+
else this.onReady = () => void this.flush().finally(done);
|
|
99
|
+
};
|
|
69
100
|
startWorker() {
|
|
70
101
|
try {
|
|
71
102
|
this.worker = new Worker(this.workerFile(), { workerData: {
|
|
@@ -77,6 +108,9 @@ var WorkerTransport = class {
|
|
|
77
108
|
if (msg.type === "ready") {
|
|
78
109
|
this.ready = true;
|
|
79
110
|
this.drainBuffer();
|
|
111
|
+
const onReady = this.onReady;
|
|
112
|
+
this.onReady = void 0;
|
|
113
|
+
onReady === null || onReady === void 0 || onReady();
|
|
80
114
|
} else if (msg.type === "error" || msg.type === "write-error") internalError(`WorkerTransport [${this.config.transportType}]: ${msg.error ?? "unknown"}`);
|
|
81
115
|
});
|
|
82
116
|
this.worker.on("error", (err) => {
|
|
@@ -89,6 +123,7 @@ var WorkerTransport = class {
|
|
|
89
123
|
this.handleWorkerExit();
|
|
90
124
|
}
|
|
91
125
|
});
|
|
126
|
+
this.worker.unref();
|
|
92
127
|
} catch (err) {
|
|
93
128
|
internalError("WorkerTransport failed to start", err);
|
|
94
129
|
}
|
|
@@ -120,6 +155,7 @@ var WorkerTransport = class {
|
|
|
120
155
|
}
|
|
121
156
|
}
|
|
122
157
|
write(entry) {
|
|
158
|
+
this.dirty = true;
|
|
123
159
|
if (this.ready && this.worker) this.worker.postMessage({
|
|
124
160
|
type: "write",
|
|
125
161
|
entry
|
|
@@ -133,18 +169,25 @@ var WorkerTransport = class {
|
|
|
133
169
|
}
|
|
134
170
|
}
|
|
135
171
|
async flush() {
|
|
172
|
+
var _worker$ref2;
|
|
136
173
|
if (!this.ready || !this.worker) return;
|
|
137
174
|
const worker = this.worker;
|
|
175
|
+
this.dirty = false;
|
|
176
|
+
(_worker$ref2 = worker.ref) === null || _worker$ref2 === void 0 || _worker$ref2.call(worker);
|
|
138
177
|
return new Promise((resolve) => {
|
|
139
178
|
const timer = setTimeout(() => {
|
|
179
|
+
var _worker$unref;
|
|
140
180
|
worker.off("message", handler);
|
|
181
|
+
(_worker$unref = worker.unref) === null || _worker$unref === void 0 || _worker$unref.call(worker);
|
|
141
182
|
resolve();
|
|
142
183
|
}, this.FLUSH_TIMEOUT_MS);
|
|
143
184
|
if (timer.unref) timer.unref();
|
|
144
185
|
const handler = (msg) => {
|
|
145
186
|
if (msg.type === "flushed") {
|
|
187
|
+
var _worker$unref2;
|
|
146
188
|
clearTimeout(timer);
|
|
147
189
|
worker.off("message", handler);
|
|
190
|
+
(_worker$unref2 = worker.unref) === null || _worker$unref2 === void 0 || _worker$unref2.call(worker);
|
|
148
191
|
resolve();
|
|
149
192
|
}
|
|
150
193
|
};
|
|
@@ -160,7 +203,9 @@ var WorkerTransport = class {
|
|
|
160
203
|
* graceful exit, leaking the worker thread and dropping its buffered logs.
|
|
161
204
|
*/
|
|
162
205
|
async close() {
|
|
206
|
+
var _worker$ref3;
|
|
163
207
|
this.closing = true;
|
|
208
|
+
process.removeListener("beforeExit", this.flushForExit);
|
|
164
209
|
if (this.restartTimer) {
|
|
165
210
|
clearTimeout(this.restartTimer);
|
|
166
211
|
this.restartTimer = null;
|
|
@@ -170,6 +215,7 @@ var WorkerTransport = class {
|
|
|
170
215
|
this.worker = null;
|
|
171
216
|
this.drainBufferTo(worker);
|
|
172
217
|
worker.postMessage({ type: "shutdown" });
|
|
218
|
+
(_worker$ref3 = worker.ref) === null || _worker$ref3 === void 0 || _worker$ref3.call(worker);
|
|
173
219
|
await new Promise((resolve) => {
|
|
174
220
|
const timer = setTimeout(() => {
|
|
175
221
|
worker.terminate().finally(() => resolve());
|
|
@@ -197,4 +243,4 @@ var WorkerTransport = class {
|
|
|
197
243
|
};
|
|
198
244
|
export { WorkerTransport as t };
|
|
199
245
|
|
|
200
|
-
//# sourceMappingURL=worker.transport-
|
|
246
|
+
//# sourceMappingURL=worker.transport-BHnWDXPH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.transport-BHnWDXPH.mjs","names":[],"sources":["../node_modules/tsdown/esm-shims.js","../src/transports/worker.transport.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * logixia — Worker Thread Transport\n *\n * Offloads JSON serialization and all I/O to a Node.js worker thread,\n * completely eliminating any event-loop impact from logging. The main thread\n * only posts a raw message to the worker — it never calls JSON.stringify or\n * any I/O syscalls on the hot path.\n *\n * Architecture:\n * Main thread → MessageChannel → Worker thread → Wrapped transport\n *\n * The worker runs an ordinary logixia transport (file, database, console…)\n * inside its own event loop. If the worker crashes it is restarted automatically\n * (up to `maxRestarts` times) and buffered messages are replayed.\n *\n * @example\n * ```ts\n * import { WorkerTransport } from 'logixia/worker';\n *\n * const logger = createLogger({\n * transports: {\n * // Run file transport inside a worker thread — zero event-loop impact\n * custom: [\n * new WorkerTransport({\n * transportType: 'file',\n * transportConfig: { filename: 'app.log', dirname: './logs' },\n * bufferSize: 500,\n * maxRestarts: 3,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport path from 'node:path';\nimport { Worker } from 'node:worker_threads';\n\nimport type { ITransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { protectSecrets } from './secret-config';\n\nexport interface WorkerTransportConfig {\n /**\n * Which built-in logixia transport to run inside the worker thread.\n * The worker resolves the correct module automatically.\n */\n transportType: 'file' | 'database' | 'console';\n /** Config forwarded verbatim to the wrapped transport constructor. */\n transportConfig: Record<string, unknown>;\n /**\n * Max log entries to buffer in the main thread while the worker is starting\n * or restarting. Entries beyond this limit are dropped with a warning.\n * @default 1000\n */\n bufferSize?: number;\n /**\n * How many times to restart the worker if it crashes unexpectedly.\n * @default 3\n */\n maxRestarts?: number;\n /**\n * Custom level severities (`levelOptions.levels`) the wrapped transport's own\n * `level` is compared against inside the worker. Built-in levels need no entry.\n */\n levels?: Record<string, number>;\n /**\n * Log level filter — only entries at or above this level are forwarded.\n * Useful for routing only errors to an expensive transport in a worker.\n */\n level?: string;\n}\n\n/**\n * Transport that runs a wrapped transport inside a dedicated Node.js worker\n * thread, completely removing JSON serialization and I/O from the main event loop.\n */\nexport class WorkerTransport implements ITransport {\n public readonly name = 'worker';\n public readonly level: string | undefined;\n public readonly batchSize = 1;\n public readonly flushInterval = 0;\n\n private worker: Worker | null = null;\n private ready = false;\n private readonly buffer: TransportLogEntry[] = [];\n private readonly bufferSize: number;\n private restarts = 0;\n private readonly maxRestarts: number;\n private readonly config: WorkerTransportConfig;\n /** Pending restart-backoff timer, so close() can cancel a queued restart. */\n private restartTimer: NodeJS.Timeout | null = null;\n /** Set once close() begins, so a worker 'exit' no longer triggers a restart. */\n private closing = false;\n /** Writes have been posted since the last flush; keeps the beforeExit flush from looping. */\n private dirty = false;\n /** Run once the worker reports ready, used when the process wants to exit before that. */\n private onReady: (() => void) | undefined;\n /** Max time to wait for the worker's 'flushed' ack before giving up. */\n private readonly FLUSH_TIMEOUT_MS = 5000;\n /** Max time to wait for a graceful worker exit before force-terminating. */\n private readonly SHUTDOWN_TIMEOUT_MS = 5000;\n\n constructor(config: WorkerTransportConfig) {\n this.config = config;\n this.bufferSize = config.bufferSize ?? 1000;\n this.maxRestarts = config.maxRestarts ?? 3;\n this.level = config.level;\n protectSecrets(this, ['config']);\n this.startWorker();\n process.on('beforeExit', this.flushForExit);\n }\n\n /**\n * The worker is unref'd so it never keeps the process alive on its own; the\n * trade-off is that lines still queued on it would be lost when the event loop\n * drains. On beforeExit, re-ref the worker until it acknowledges a flush.\n */\n private readonly flushForExit = (): void => {\n const worker = this.worker;\n if (!worker || this.closing || !this.dirty) return;\n worker.ref?.();\n const timer = setTimeout(() => worker.unref(), this.FLUSH_TIMEOUT_MS);\n const done = (): void => {\n clearTimeout(timer);\n worker.unref();\n };\n if (this.ready) {\n void this.flush().finally(done);\n } else {\n this.onReady = () => void this.flush().finally(done);\n }\n };\n\n private startWorker(): void {\n try {\n this.worker = new Worker(this.workerFile(), {\n workerData: {\n transportType: this.config.transportType,\n transportConfig: this.config.transportConfig,\n levels: this.config.levels,\n },\n });\n\n this.worker.on('message', (msg: { type: string; error?: string }) => {\n if (msg.type === 'ready') {\n this.ready = true;\n this.drainBuffer();\n const onReady = this.onReady;\n this.onReady = undefined;\n onReady?.();\n } else if (msg.type === 'error' || msg.type === 'write-error') {\n internalError(\n `WorkerTransport [${this.config.transportType}]: ${msg.error ?? 'unknown'}`\n );\n }\n });\n\n this.worker.on('error', (err) => {\n internalError('WorkerTransport worker error', err);\n this.handleWorkerExit();\n });\n\n this.worker.on('exit', (code) => {\n if (code !== 0) {\n internalWarn(`WorkerTransport worker exited with code ${code}`);\n this.handleWorkerExit();\n }\n });\n\n // After the listeners: adding a 'message' listener re-refs the worker's port.\n this.worker.unref();\n } catch (err) {\n internalError('WorkerTransport failed to start', err);\n }\n }\n\n /** The worker entry is always built as CommonJS, next to this module's output. */\n protected workerFile(): string {\n return path.join(__dirname, 'worker-entry.js');\n }\n\n private handleWorkerExit(): void {\n this.ready = false;\n // Don't resurrect a worker we're intentionally shutting down — close() sets\n // `closing` and nulls the worker, so a late 'exit'/'error' event must not\n // queue a restart (which would spawn a fresh leaked thread post-shutdown).\n if (this.closing) return;\n if (this.restarts < this.maxRestarts) {\n this.restarts++;\n internalWarn(`WorkerTransport restarting (attempt ${this.restarts}/${this.maxRestarts})`);\n // Small backoff before restart. unref() so a perpetually-failing worker's\n // pending restart never keeps the process alive on its own.\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n this.startWorker();\n }, 500 * this.restarts);\n if (this.restartTimer.unref) this.restartTimer.unref();\n } else {\n internalError(\n `WorkerTransport exhausted ${this.maxRestarts} restart attempts — transport disabled`\n );\n }\n }\n\n private drainBuffer(): void {\n while (this.buffer.length > 0 && this.ready && this.worker) {\n const entry = this.buffer.shift();\n if (entry) {\n this.worker.postMessage({ type: 'write', entry });\n }\n }\n }\n\n write(entry: TransportLogEntry): void {\n this.dirty = true;\n if (this.ready && this.worker) {\n this.worker.postMessage({ type: 'write', entry });\n } else {\n if (this.buffer.length >= this.bufferSize) {\n internalWarn('WorkerTransport buffer full — dropping oldest entry');\n this.buffer.shift();\n }\n this.buffer.push(entry);\n }\n }\n\n async flush(): Promise<void> {\n if (!this.ready || !this.worker) return;\n const worker = this.worker;\n this.dirty = false;\n // Unref'd between flushes; ref'd here so an awaited flush() keeps the process alive.\n worker.ref?.();\n return new Promise<void>((resolve) => {\n // Time-box the flush: if the worker dies or restarts before answering,\n // the 'flushed' message would never arrive and this promise would hang\n // forever, blocking shutdown until the force-exit timer kills the process\n // (losing everything). Resolve on timeout so close() can still terminate.\n const timer = setTimeout(() => {\n worker.off('message', handler);\n worker.unref?.();\n resolve();\n }, this.FLUSH_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n const handler = (msg: { type: string }) => {\n if (msg.type === 'flushed') {\n clearTimeout(timer);\n worker.off('message', handler);\n worker.unref?.();\n resolve();\n }\n };\n worker.on('message', handler);\n worker.postMessage({ type: 'flush' });\n });\n }\n\n /**\n * Flush the worker's buffer and terminate the thread on shutdown.\n *\n * This MUST be named close() — TransportManager.close() invokes\n * transport.close(), so a method named only shutdown() was never called on\n * graceful exit, leaking the worker thread and dropping its buffered logs.\n */\n async close(): Promise<void> {\n // Stop the restart loop first: cancel any queued backoff restart and flag\n // that further worker exits are expected (so handleWorkerExit no-ops).\n this.closing = true;\n process.removeListener('beforeExit', this.flushForExit);\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n if (!this.worker) return;\n const worker = this.worker;\n this.worker = null;\n\n // Push any locally-buffered (pre-ready) entries first: the worker's port\n // queues them until its listener attaches, so they are handled before the\n // shutdown message, which flushes the wrapped transport and exits.\n this.drainBufferTo(worker);\n worker.postMessage({ type: 'shutdown' });\n // The worker is unref'd, so hold the process open until it has drained and exited.\n worker.ref?.();\n\n await new Promise<void>((resolve) => {\n // Don't wait forever for a wedged worker — terminate() it after a timeout.\n const timer = setTimeout(() => {\n worker.terminate().finally(() => resolve());\n }, this.SHUTDOWN_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n worker.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /** Back-compat alias — prefer close(). */\n async shutdown(): Promise<void> {\n await this.close();\n }\n\n private drainBufferTo(worker: Worker): void {\n while (this.buffer.length > 0) {\n const entry = this.buffer.shift();\n if (entry) worker.postMessage({ type: 'write', entry });\n }\n }\n}\n"],"ignoreList":[0],"mappings":";;;;;;AAIA,MAAM,oBAAoB,cAAc,YAAY,GAAG;AACvD,MAAM,mBAAmB,KAAK,QAAQ,YAAY,CAAC;AAEnD,MAAa,YAA4B,2BAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsEpD,IAAa,kBAAb,MAAmD;CACjD,OAAuB;CACvB;CACA,YAA4B;CAC5B,gBAAgC;CAEhC,SAAgC;CAChC,QAAgB;CAChB,SAA+C,CAAC;CAChD;CACA,WAAmB;CACnB;CACA;;CAEA,eAA8C;;CAE9C,UAAkB;;CAElB,QAAgB;;CAEhB;;CAEA,mBAAoC;;CAEpC,sBAAuC;CAEvC,YAAY,QAA+B;EACzC,KAAK,SAAS;EACd,KAAK,aAAa,OAAO,cAAc;EACvC,KAAK,cAAc,OAAO,eAAe;EACzC,KAAK,QAAQ,OAAO;EACpB,eAAe,MAAM,CAAC,QAAQ,CAAC;EAC/B,KAAK,YAAY;EACjB,QAAQ,GAAG,cAAc,KAAK,YAAY;CAC5C;;;;;;CAOA,qBAA4C;;EAC1C,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,UAAU,KAAK,WAAW,CAAC,KAAK,OAAO;EAC5C,CAAA,cAAA,OAAO,SAAA,QAAA,gBAAA,KAAA,KAAA,YAAA,KAAA,MAAM;EACb,MAAM,QAAQ,iBAAiB,OAAO,MAAM,GAAG,KAAK,gBAAgB;EACpE,MAAM,aAAmB;GACvB,aAAa,KAAK;GAClB,OAAO,MAAM;EACf;EACA,IAAI,KAAK,OACP,KAAU,MAAM,CAAC,CAAC,QAAQ,IAAI;OAE9B,KAAK,gBAAgB,KAAK,KAAK,MAAM,CAAC,CAAC,QAAQ,IAAI;CAEvD;CAEA,cAA4B;EAC1B,IAAI;GACF,KAAK,SAAS,IAAI,OAAO,KAAK,WAAW,GAAG,EAC1C,YAAY;IACV,eAAe,KAAK,OAAO;IAC3B,iBAAiB,KAAK,OAAO;IAC7B,QAAQ,KAAK,OAAO;GACtB,EACF,CAAC;GAED,KAAK,OAAO,GAAG,YAAY,QAA0C;IACnE,IAAI,IAAI,SAAS,SAAS;KACxB,KAAK,QAAQ;KACb,KAAK,YAAY;KACjB,MAAM,UAAU,KAAK;KACrB,KAAK,UAAU,KAAA;KACf,YAAA,QAAA,YAAA,KAAA,KAAA,QAAU;IACZ,OAAO,IAAI,IAAI,SAAS,WAAW,IAAI,SAAS,eAC9C,cACE,oBAAoB,KAAK,OAAO,cAAc,KAAK,IAAI,SAAS,WAClE;GAEJ,CAAC;GAED,KAAK,OAAO,GAAG,UAAU,QAAQ;IAC/B,cAAc,gCAAgC,GAAG;IACjD,KAAK,iBAAiB;GACxB,CAAC;GAED,KAAK,OAAO,GAAG,SAAS,SAAS;IAC/B,IAAI,SAAS,GAAG;KACd,aAAa,2CAA2C,MAAM;KAC9D,KAAK,iBAAiB;IACxB;GACF,CAAC;GAGD,KAAK,OAAO,MAAM;EACpB,SAAS,KAAK;GACZ,cAAc,mCAAmC,GAAG;EACtD;CACF;;CAGA,aAA+B;EAC7B,OAAO,KAAK,KAAK,WAAW,iBAAiB;CAC/C;CAEA,mBAAiC;EAC/B,KAAK,QAAQ;EAIb,IAAI,KAAK,SAAS;EAClB,IAAI,KAAK,WAAW,KAAK,aAAa;GACpC,KAAK;GACL,aAAa,uCAAuC,KAAK,SAAS,GAAG,KAAK,YAAY,EAAE;GAGxF,KAAK,eAAe,iBAAiB;IACnC,KAAK,eAAe;IACpB,KAAK,YAAY;GACnB,GAAG,MAAM,KAAK,QAAQ;GACtB,IAAI,KAAK,aAAa,OAAO,KAAK,aAAa,MAAM;EACvD,OACE,cACE,6BAA6B,KAAK,YAAY,uCAChD;CAEJ;CAEA,cAA4B;EAC1B,OAAO,KAAK,OAAO,SAAS,KAAK,KAAK,SAAS,KAAK,QAAQ;GAC1D,MAAM,QAAQ,KAAK,OAAO,MAAM;GAChC,IAAI,OACF,KAAK,OAAO,YAAY;IAAE,MAAM;IAAS;GAAM,CAAC;EAEpD;CACF;CAEA,MAAM,OAAgC;EACpC,KAAK,QAAQ;EACb,IAAI,KAAK,SAAS,KAAK,QACrB,KAAK,OAAO,YAAY;GAAE,MAAM;GAAS;EAAM,CAAC;OAC3C;GACL,IAAI,KAAK,OAAO,UAAU,KAAK,YAAY;IACzC,aAAa,qDAAqD;IAClE,KAAK,OAAO,MAAM;GACpB;GACA,KAAK,OAAO,KAAK,KAAK;EACxB;CACF;CAEA,MAAM,QAAuB;;EAC3B,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,QAAQ;EACjC,MAAM,SAAS,KAAK;EACpB,KAAK,QAAQ;EAEb,CAAA,eAAA,OAAO,SAAA,QAAA,iBAAA,KAAA,KAAA,aAAA,KAAA,MAAM;EACb,OAAO,IAAI,SAAe,YAAY;GAKpC,MAAM,QAAQ,iBAAiB;;IAC7B,OAAO,IAAI,WAAW,OAAO;IAC7B,CAAA,gBAAA,OAAO,WAAA,QAAA,kBAAA,KAAA,KAAA,cAAA,KAAA,MAAQ;IACf,QAAQ;GACV,GAAG,KAAK,gBAAgB;GACxB,IAAI,MAAM,OAAO,MAAM,MAAM;GAE7B,MAAM,WAAW,QAA0B;IACzC,IAAI,IAAI,SAAS,WAAW;;KAC1B,aAAa,KAAK;KAClB,OAAO,IAAI,WAAW,OAAO;KAC7B,CAAA,iBAAA,OAAO,WAAA,QAAA,mBAAA,KAAA,KAAA,eAAA,KAAA,MAAQ;KACf,QAAQ;IACV;GACF;GACA,OAAO,GAAG,WAAW,OAAO;GAC5B,OAAO,YAAY,EAAE,MAAM,QAAQ,CAAC;EACtC,CAAC;CACH;;;;;;;;CASA,MAAM,QAAuB;;EAG3B,KAAK,UAAU;EACf,QAAQ,eAAe,cAAc,KAAK,YAAY;EACtD,IAAI,KAAK,cAAc;GACrB,aAAa,KAAK,YAAY;GAC9B,KAAK,eAAe;EACtB;EAEA,IAAI,CAAC,KAAK,QAAQ;EAClB,MAAM,SAAS,KAAK;EACpB,KAAK,SAAS;EAKd,KAAK,cAAc,MAAM;EACzB,OAAO,YAAY,EAAE,MAAM,WAAW,CAAC;EAEvC,CAAA,eAAA,OAAO,SAAA,QAAA,iBAAA,KAAA,KAAA,aAAA,KAAA,MAAM;EAEb,MAAM,IAAI,SAAe,YAAY;GAEnC,MAAM,QAAQ,iBAAiB;IAC7B,OAAO,UAAU,CAAC,CAAC,cAAc,QAAQ,CAAC;GAC5C,GAAG,KAAK,mBAAmB;GAC3B,IAAI,MAAM,OAAO,MAAM,MAAM;GAE7B,OAAO,KAAK,cAAc;IACxB,aAAa,KAAK;IAClB,QAAQ;GACV,CAAC;EACH,CAAC;CACH;;CAGA,MAAM,WAA0B;EAC9B,MAAM,KAAK,MAAM;CACnB;CAEA,cAAsB,QAAsB;EAC1C,OAAO,KAAK,OAAO,SAAS,GAAG;GAC7B,MAAM,QAAQ,KAAK,OAAO,MAAM;GAChC,IAAI,OAAO,OAAO,YAAY;IAAE,MAAM;IAAS;GAAM,CAAC;EACxD;CACF;AACF"}
|