logixia 1.12.5 → 1.13.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 +75 -18
- package/dist/.tsbuildinfo +1 -1
- package/dist/async-context-CSHWoHMB.js +88 -0
- package/dist/async-context-CSHWoHMB.js.map +1 -0
- package/dist/async-context-CwBNMm2E.mjs +68 -0
- package/dist/async-context-CwBNMm2E.mjs.map +1 -0
- package/dist/azure-monitor.transport-CG7ClkYj.d.ts +62 -0
- package/dist/azure-monitor.transport-CG7ClkYj.d.ts.map +1 -0
- package/dist/azure-monitor.transport-CHnjVRF-.mjs +152 -0
- package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +1 -0
- package/dist/azure-monitor.transport-Cm74kXZS.d.mts +62 -0
- package/dist/azure-monitor.transport-Cm74kXZS.d.mts.map +1 -0
- package/dist/azure-monitor.transport-CrvfHd9C.js +157 -0
- package/dist/azure-monitor.transport-CrvfHd9C.js.map +1 -0
- package/dist/azure.d.mts +3 -0
- package/dist/azure.d.ts +3 -0
- package/dist/azure.js +6 -0
- package/dist/azure.mjs +6 -0
- package/dist/batch-buffer-BY-mRWIK.js +170 -0
- package/dist/batch-buffer-BY-mRWIK.js.map +1 -0
- package/dist/batch-buffer-PGvSgbmd.mjs +132 -0
- package/dist/batch-buffer-PGvSgbmd.mjs.map +1 -0
- package/dist/browser.d.mts +427 -0
- package/dist/browser.d.mts.map +1 -0
- package/dist/browser.d.ts +427 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +590 -0
- package/dist/browser.js.map +1 -0
- package/dist/browser.mjs +586 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/chunk-DLSkYcPk.mjs +7 -0
- package/dist/chunk-nOFOJqeH.js +30 -0
- package/dist/cloudwatch.d.mts +3 -0
- package/dist/cloudwatch.d.ts +3 -0
- package/dist/cloudwatch.js +6 -0
- package/dist/cloudwatch.mjs +6 -0
- package/dist/cloudwatch.transport-CGHY4buE.d.mts +81 -0
- package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +1 -0
- package/dist/cloudwatch.transport-DK_8cKd3.d.ts +81 -0
- package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +1 -0
- package/dist/cloudwatch.transport-pNBNWJLt.js +370 -0
- package/dist/cloudwatch.transport-pNBNWJLt.js.map +1 -0
- package/dist/cloudwatch.transport-yBwm4OeO.mjs +360 -0
- package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +1 -0
- package/dist/coerce.utils-BN980Y9V.js +44 -0
- package/dist/coerce.utils-BN980Y9V.js.map +1 -0
- package/dist/coerce.utils-Dxv4_ikC.mjs +38 -0
- package/dist/coerce.utils-Dxv4_ikC.mjs.map +1 -0
- package/dist/correlation.d.mts +245 -0
- package/dist/correlation.d.mts.map +1 -0
- package/dist/correlation.d.ts +245 -0
- package/dist/correlation.d.ts.map +1 -0
- package/dist/correlation.js +272 -0
- package/dist/correlation.js.map +1 -0
- package/dist/correlation.mjs +261 -0
- package/dist/correlation.mjs.map +1 -0
- package/dist/gcp.d.mts +3 -0
- package/dist/gcp.d.ts +3 -0
- package/dist/gcp.js +6 -0
- package/dist/gcp.mjs +6 -0
- package/dist/gcp.transport-BJuNRFe_.mjs +192 -0
- package/dist/gcp.transport-BJuNRFe_.mjs.map +1 -0
- package/dist/gcp.transport-BOhkVKlI.d.ts +71 -0
- package/dist/gcp.transport-BOhkVKlI.d.ts.map +1 -0
- package/dist/gcp.transport-Bi6CUqZR.d.mts +71 -0
- package/dist/gcp.transport-Bi6CUqZR.d.mts.map +1 -0
- package/dist/gcp.transport-SHdtc1n0.js +198 -0
- package/dist/gcp.transport-SHdtc1n0.js.map +1 -0
- package/dist/{index-CwFww7VC.d.ts → index-BPKwwERo.d.mts} +18 -174
- package/dist/index-BPKwwERo.d.mts.map +1 -0
- package/dist/{index-DnyeaoZU.d.mts → index-C1X-TE5I.d.ts} +18 -174
- package/dist/index-C1X-TE5I.d.ts.map +1 -0
- package/dist/index.d.mts +41 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +40 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -147
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -132
- package/dist/index.mjs.map +1 -1
- package/dist/internal-log-BUJYMZbE.js +70 -0
- package/dist/internal-log-BUJYMZbE.js.map +1 -0
- package/dist/internal-log-Cm8SOWSV.mjs +52 -0
- package/dist/internal-log-Cm8SOWSV.mjs.map +1 -0
- package/dist/{logitron-logger.module-CpffEHec.d.mts → logitron-logger.module-BdG1Jnpk.d.mts} +23 -10
- package/dist/logitron-logger.module-BdG1Jnpk.d.mts.map +1 -0
- package/dist/{logitron-logger.module-BIGTm2V6.mjs → logitron-logger.module-Bs3XYZgF.mjs} +266 -166
- package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +1 -0
- package/dist/{logitron-logger.module-BTxDpK6u.js → logitron-logger.module-BsDZR5BH.js} +297 -203
- package/dist/logitron-logger.module-BsDZR5BH.js.map +1 -0
- package/dist/{logitron-logger.module-DvqtI43C.d.ts → logitron-logger.module-CbXy2gfW.d.ts} +23 -10
- package/dist/logitron-logger.module-CbXy2gfW.d.ts.map +1 -0
- package/dist/middleware.d.mts +5 -2
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +5 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -4
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +14 -4
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +3 -2
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +3 -2
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +6 -2
- package/dist/nest.mjs +6 -2
- package/dist/safe-stringify-DJ66tW5I.mjs +118 -0
- package/dist/safe-stringify-DJ66tW5I.mjs.map +1 -0
- package/dist/safe-stringify-DWo3aZp_.js +142 -0
- package/dist/safe-stringify-DWo3aZp_.js.map +1 -0
- package/dist/search-CFQRISUv.mjs +1596 -0
- package/dist/search-CFQRISUv.mjs.map +1 -0
- package/dist/search-CQOo-Iq6.d.mts +816 -0
- package/dist/search-CQOo-Iq6.d.mts.map +1 -0
- package/dist/search-DujhR94O.d.ts +816 -0
- package/dist/search-DujhR94O.d.ts.map +1 -0
- package/dist/search-njTI2hdo.js +1631 -0
- package/dist/search-njTI2hdo.js.map +1 -0
- package/dist/search.d.mts +4 -1
- package/dist/search.d.ts +4 -1
- package/dist/search.js +9 -1
- package/dist/search.mjs +3 -2
- package/dist/testing.d.mts +2 -1
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/{transport.manager-ciG4r5uz.js → transport.manager-BivyUTtU.js} +497 -437
- package/dist/transport.manager-BivyUTtU.js.map +1 -0
- package/dist/{transport.manager-DETpXuaY.mjs → transport.manager-D6KL3t9V.mjs} +473 -364
- package/dist/transport.manager-D6KL3t9V.mjs.map +1 -0
- package/dist/transport.types-B5vcyv2k.d.mts +187 -0
- package/dist/transport.types-B5vcyv2k.d.mts.map +1 -0
- package/dist/transport.types-BuY84TwO.d.ts +187 -0
- package/dist/transport.types-BuY84TwO.d.ts.map +1 -0
- package/dist/transports.d.mts +90 -29
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +90 -29
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +14 -2
- package/dist/transports.mjs +10 -2
- package/dist/worker-entry.js +1241 -0
- package/dist/worker.d.mts +3 -0
- package/dist/worker.d.ts +3 -0
- package/dist/worker.js +4 -0
- package/dist/worker.mjs +4 -0
- package/dist/worker.transport-BP8Q28Z2.d.mts +76 -0
- package/dist/worker.transport-BP8Q28Z2.d.mts.map +1 -0
- package/dist/worker.transport-BhMyvqQ2.mjs +167 -0
- package/dist/worker.transport-BhMyvqQ2.mjs.map +1 -0
- package/dist/worker.transport-CfH6z37Z.js +168 -0
- package/dist/worker.transport-CfH6z37Z.js.map +1 -0
- package/dist/worker.transport-mKXgNK0L.d.ts +76 -0
- package/dist/worker.transport-mKXgNK0L.d.ts.map +1 -0
- package/package.json +23 -18
- package/dist/index-CwFww7VC.d.ts.map +0 -1
- package/dist/index-DnyeaoZU.d.mts.map +0 -1
- package/dist/logitron-logger.module-BIGTm2V6.mjs.map +0 -1
- package/dist/logitron-logger.module-BTxDpK6u.js.map +0 -1
- package/dist/logitron-logger.module-CpffEHec.d.mts.map +0 -1
- package/dist/logitron-logger.module-DvqtI43C.d.ts.map +0 -1
- package/dist/search-Cg_OasF-.d.ts +0 -1
- package/dist/search-DN676Dnz.d.mts +0 -1
- package/dist/search-DOvSI-mb.mjs +0 -1
- package/dist/search-DoZF3RZj.js +0 -0
- package/dist/transport.manager-DETpXuaY.mjs.map +0 -1
- package/dist/transport.manager-ciG4r5uz.js.map +0 -1
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
|
|
3
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
4
|
+
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function.");
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/utils/safe-stringify.ts
|
|
9
|
+
/** Build a JSONPath like `$["a"][0]["b"]` for a decycle pointer. */
|
|
10
|
+
function jsonPath(parts) {
|
|
11
|
+
let path = "$";
|
|
12
|
+
for (const p of parts) path += typeof p === "number" ? `[${p}]` : `[${JSON.stringify(p)}]`;
|
|
13
|
+
return path;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Serialize any value to JSON without throwing on circular references or BigInt.
|
|
17
|
+
* Circular refs become `"[Circular]"` (or `{ $ref }` pointers when `decycle`).
|
|
18
|
+
*/
|
|
19
|
+
function safeStringify(value, options = {}) {
|
|
20
|
+
const { indent, deterministic = false, decycle = false, bigint = "string" } = options;
|
|
21
|
+
if (decycle) return JSON.stringify(decycleValue(value, bigint), void 0, indent);
|
|
22
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
23
|
+
const transform = (val) => {
|
|
24
|
+
if (typeof val === "bigint") return bigint === "number" ? Number(val) : val.toString();
|
|
25
|
+
if (typeof val === "function") return `[Function: ${val.name || "anonymous"}]`;
|
|
26
|
+
if (typeof val === "symbol") return val.toString();
|
|
27
|
+
if (val === null || typeof val !== "object") return val;
|
|
28
|
+
if (seen.has(val)) return "[Circular]";
|
|
29
|
+
seen.add(val);
|
|
30
|
+
let out;
|
|
31
|
+
const toJSON = val.toJSON;
|
|
32
|
+
if (Array.isArray(val)) out = val.map((item) => transform(item));
|
|
33
|
+
else if (typeof toJSON === "function") out = transform(toJSON.call(val));
|
|
34
|
+
else {
|
|
35
|
+
const rec = val;
|
|
36
|
+
const keys = deterministic ? Object.keys(rec).sort() : Object.keys(rec);
|
|
37
|
+
const obj = {};
|
|
38
|
+
for (const k of keys) {
|
|
39
|
+
if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
|
|
40
|
+
obj[k] = transform(rec[k]);
|
|
41
|
+
}
|
|
42
|
+
out = obj;
|
|
43
|
+
}
|
|
44
|
+
seen.delete(val);
|
|
45
|
+
return out;
|
|
46
|
+
};
|
|
47
|
+
return JSON.stringify(transform(value), void 0, indent);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Replace repeated object references with round-trippable `{ "$ref": "$..." }`
|
|
51
|
+
* JSONPath pointers (the classic Crockford decycle). Pair with {@link retrocycle}
|
|
52
|
+
* to reconstruct the original shared/circular graph.
|
|
53
|
+
*/
|
|
54
|
+
function decycleValue(value, bigint = "string") {
|
|
55
|
+
const paths = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
const walk = (val, path) => {
|
|
57
|
+
if (typeof val === "bigint") return bigint === "number" ? Number(val) : val.toString();
|
|
58
|
+
if (val === null || typeof val !== "object") return val;
|
|
59
|
+
if (val instanceof Date) return val.toISOString();
|
|
60
|
+
const existing = paths.get(val);
|
|
61
|
+
if (existing !== void 0) return { $ref: existing };
|
|
62
|
+
paths.set(val, jsonPath(path));
|
|
63
|
+
if (Array.isArray(val)) return val.map((item, i) => walk(item, [...path, i]));
|
|
64
|
+
const rec = val;
|
|
65
|
+
const obj = {};
|
|
66
|
+
for (const k of Object.keys(rec)) {
|
|
67
|
+
if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
|
|
68
|
+
obj[k] = walk(rec[k], [...path, k]);
|
|
69
|
+
}
|
|
70
|
+
return obj;
|
|
71
|
+
};
|
|
72
|
+
return walk(value, []);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `JSON.stringify` for log output. Takes the plain fast path first and only
|
|
76
|
+
* falls back to {@link safeStringify} when that throws (a cycle, a BigInt, a
|
|
77
|
+
* throwing `toJSON`), so a bad payload costs nothing until it shows up.
|
|
78
|
+
* Never throws: if even the safe walk fails, the value is replaced by a marker.
|
|
79
|
+
*/
|
|
80
|
+
function stringifyForLog(value) {
|
|
81
|
+
try {
|
|
82
|
+
return JSON.stringify(value);
|
|
83
|
+
} catch {
|
|
84
|
+
try {
|
|
85
|
+
return safeStringify(value);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
88
|
+
return JSON.stringify(`[Unserializable: ${reason}]`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/utils/otel.ts
|
|
95
|
+
let _otelApi;
|
|
96
|
+
function tryLoadOtelApi() {
|
|
97
|
+
if (_otelApi !== void 0) return _otelApi;
|
|
98
|
+
try {
|
|
99
|
+
_otelApi = __require("@opentelemetry/api");
|
|
100
|
+
return _otelApi;
|
|
101
|
+
} catch {
|
|
102
|
+
_otelApi = null;
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read the currently active OTel span context (if any) and return its fields
|
|
108
|
+
* in a plain object suitable for spreading into a log entry.
|
|
109
|
+
*
|
|
110
|
+
* Returns `undefined` when:
|
|
111
|
+
* - `@opentelemetry/api` is not installed
|
|
112
|
+
* - No active span exists (root context)
|
|
113
|
+
* - The span context is invalid (all-zeros)
|
|
114
|
+
*/
|
|
115
|
+
function getActiveOtelContext(opts = {}) {
|
|
116
|
+
const api = tryLoadOtelApi();
|
|
117
|
+
if (!api) return void 0;
|
|
118
|
+
try {
|
|
119
|
+
const ctx = api.context.active();
|
|
120
|
+
const sc = api.trace.getSpanContext(ctx);
|
|
121
|
+
if (!sc || !api.trace.isSpanContextValid(sc)) return void 0;
|
|
122
|
+
const isSampled = (sc.traceFlags & api.trace.TraceFlags.SAMPLED) === api.trace.TraceFlags.SAMPLED;
|
|
123
|
+
if (opts.sampledOnly && !isSampled) return void 0;
|
|
124
|
+
return {
|
|
125
|
+
traceId: sc.traceId,
|
|
126
|
+
spanId: sc.spanId,
|
|
127
|
+
traceFlags: sc.traceFlags,
|
|
128
|
+
isSampled
|
|
129
|
+
};
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Returns a metadata object with OTel context fields ready to merge into a log call,
|
|
136
|
+
* using the configured field names.
|
|
137
|
+
*
|
|
138
|
+
* Returns `{}` when no active span exists (safe to spread unconditionally).
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* await logger.info('Payment processed', {
|
|
143
|
+
* ...getOtelMetaFields(),
|
|
144
|
+
* orderId: 'ord_123',
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
function getOtelMetaFields(opts = {}) {
|
|
149
|
+
const { traceIdField = "traceId", spanIdField = "spanId", traceFlagsField = "traceFlags" } = opts;
|
|
150
|
+
const ctx = getActiveOtelContext(opts);
|
|
151
|
+
if (!ctx) return {};
|
|
152
|
+
return {
|
|
153
|
+
[traceIdField]: ctx.traceId,
|
|
154
|
+
[spanIdField]: ctx.spanId,
|
|
155
|
+
[traceFlagsField]: ctx.traceFlags
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
let _bridgeOptions = null;
|
|
159
|
+
/**
|
|
160
|
+
* Initialise the global OTel bridge.
|
|
161
|
+
*
|
|
162
|
+
* Once called, logixia's internal log pipeline checks for an active OTel span
|
|
163
|
+
* on **every** log call and automatically merges the span context into the
|
|
164
|
+
* entry's metadata — no per-call wiring needed.
|
|
165
|
+
*
|
|
166
|
+
* Call once at app startup, **after** the OTel SDK has been initialised:
|
|
167
|
+
* ```ts
|
|
168
|
+
* import { initOtelBridge } from 'logixia';
|
|
169
|
+
* initOtelBridge();
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
function initOtelBridge(opts = {}) {
|
|
173
|
+
_bridgeOptions = opts;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Disable the OTel bridge (useful for tests).
|
|
177
|
+
*/
|
|
178
|
+
function disableOtelBridge() {
|
|
179
|
+
_bridgeOptions = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/utils/internal-log.ts
|
|
184
|
+
/**
|
|
185
|
+
* Internal logging helpers for use within the logixia library itself.
|
|
186
|
+
*
|
|
187
|
+
* These functions write directly to stderr/stdout without going through
|
|
188
|
+
* LogixiaLogger — this avoids circular dependencies and ensures internal
|
|
189
|
+
* diagnostics still surface even when the logger is misconfigured.
|
|
190
|
+
*
|
|
191
|
+
* Library code must NEVER call console.log/warn/error directly. Use these
|
|
192
|
+
* helpers instead so that internal output can be silenced in tests by setting
|
|
193
|
+
* the LOGIXIA_SILENT_INTERNAL=1 environment variable.
|
|
194
|
+
*/
|
|
195
|
+
/**
|
|
196
|
+
* Read the silence flag on each call rather than caching it at import time, so
|
|
197
|
+
* setting LOGIXIA_SILENT_INTERNAL=1 AFTER the module is first imported (e.g. in
|
|
198
|
+
* a test setup file) still takes effect — the documented test-silencing
|
|
199
|
+
* behavior was previously unreliable because the value was frozen at load.
|
|
200
|
+
*/
|
|
201
|
+
function isSilent() {
|
|
202
|
+
return process.env.LOGIXIA_SILENT_INTERNAL === "1";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Emit an internal warning to stderr.
|
|
206
|
+
* Use when something is misconfigured but logixia can continue operating.
|
|
207
|
+
*/
|
|
208
|
+
function internalWarn(message) {
|
|
209
|
+
if (!isSilent()) process.stderr.write(`[logixia:warn] ${message}\n`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/utils/typed-logger.ts
|
|
214
|
+
/**
|
|
215
|
+
* Define a typed schema for a category of log entries.
|
|
216
|
+
*
|
|
217
|
+
* Call this once at module initialisation and pass it to `createTypedLogger`.
|
|
218
|
+
* In development, every log call is validated against the schema.
|
|
219
|
+
*/
|
|
220
|
+
function defineLogSchema(fields) {
|
|
221
|
+
return {
|
|
222
|
+
fields,
|
|
223
|
+
validate(payload) {
|
|
224
|
+
if (process.env["NODE_ENV"] === "production") return [];
|
|
225
|
+
const warnings = [];
|
|
226
|
+
for (const [key, def] of Object.entries(fields)) {
|
|
227
|
+
const value = payload[key];
|
|
228
|
+
if (def.required && (value === void 0 || value === null)) {
|
|
229
|
+
warnings.push(`Required field "${key}" is missing`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (value !== void 0 && value !== null) {
|
|
233
|
+
const actualType = Array.isArray(value) ? "array" : typeof value;
|
|
234
|
+
if (actualType !== def.type) warnings.push(`Field "${key}" expected type "${def.type}" but got "${actualType}"`);
|
|
235
|
+
if (def.validate) {
|
|
236
|
+
const msg = def.validate(value);
|
|
237
|
+
if (msg) warnings.push(`Field "${key}": ${msg}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return warnings;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Wrap any logixia logger with a type-safe field interface.
|
|
247
|
+
*
|
|
248
|
+
* @param logger Any object that implements `IBaseLogger` (e.g. the result of `createLogger()`)
|
|
249
|
+
* @param schema Optional schema for dev-time validation. Created with `defineLogSchema()`.
|
|
250
|
+
*/
|
|
251
|
+
function createTypedLogger(logger, schema) {
|
|
252
|
+
function withValidation(level, fn, message, data) {
|
|
253
|
+
if (schema && data) {
|
|
254
|
+
const warnings = schema.validate(data);
|
|
255
|
+
for (const w of warnings) internalWarn(`[logixia/schema] ${w} — level=${level} message="${message}"`);
|
|
256
|
+
}
|
|
257
|
+
return fn(message, data);
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
raw: logger,
|
|
261
|
+
error(messageOrError, data) {
|
|
262
|
+
if (schema && data) {
|
|
263
|
+
const warnings = schema.validate(data);
|
|
264
|
+
const display = messageOrError instanceof Error ? messageOrError.message : messageOrError;
|
|
265
|
+
for (const w of warnings) internalWarn(`[logixia/schema] ${w} — level=error message="${display}"`);
|
|
266
|
+
}
|
|
267
|
+
return logger.error(messageOrError, data);
|
|
268
|
+
},
|
|
269
|
+
warn: (m, d) => withValidation("warn", logger.warn.bind(logger), m, d),
|
|
270
|
+
info: (m, d) => withValidation("info", logger.info.bind(logger), m, d),
|
|
271
|
+
debug: (m, d) => withValidation("debug", logger.debug.bind(logger), m, d),
|
|
272
|
+
verbose: (m, d) => withValidation("verbose", (logger.verbose ?? logger.debug).bind(logger), m, d),
|
|
273
|
+
trace: (m, d) => withValidation("trace", (logger.trace ?? logger.debug).bind(logger), m, d)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/browser.ts
|
|
279
|
+
const LEVEL_ORDER = {
|
|
280
|
+
verbose: 0,
|
|
281
|
+
debug: 1,
|
|
282
|
+
info: 2,
|
|
283
|
+
warn: 3,
|
|
284
|
+
error: 4,
|
|
285
|
+
critical: 5
|
|
286
|
+
};
|
|
287
|
+
function shouldLog(entryLevel, minLevel) {
|
|
288
|
+
return LEVEL_ORDER[entryLevel] >= LEVEL_ORDER[minLevel];
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Default browser transport — writes to the browser console using native
|
|
292
|
+
* level methods so devtools can filter by severity.
|
|
293
|
+
*
|
|
294
|
+
* In pretty mode (default):
|
|
295
|
+
* - Groups each entry under a collapsible header with timestamp + app name
|
|
296
|
+
* - Payload displayed as an expandable object via `console.table` / `console.dir`
|
|
297
|
+
*
|
|
298
|
+
* In JSON mode:
|
|
299
|
+
* - Emits compact JSON strings via `console.log` / `console.error`
|
|
300
|
+
*/
|
|
301
|
+
var BrowserConsoleTransport = class {
|
|
302
|
+
constructor(options = {}) {
|
|
303
|
+
this.name = "browser-console";
|
|
304
|
+
this.json = options.json ?? false;
|
|
305
|
+
if (options.level !== void 0) this.level = options.level;
|
|
306
|
+
}
|
|
307
|
+
write(entry) {
|
|
308
|
+
if (this.json) this.writeJson(entry);
|
|
309
|
+
else this.writePretty(entry);
|
|
310
|
+
}
|
|
311
|
+
writeJson(entry) {
|
|
312
|
+
const line = stringifyForLog(entry);
|
|
313
|
+
switch (entry.level) {
|
|
314
|
+
case "verbose":
|
|
315
|
+
case "debug":
|
|
316
|
+
console.debug(line);
|
|
317
|
+
break;
|
|
318
|
+
case "warn":
|
|
319
|
+
console.warn(line);
|
|
320
|
+
break;
|
|
321
|
+
case "error":
|
|
322
|
+
case "critical":
|
|
323
|
+
console.error(line);
|
|
324
|
+
break;
|
|
325
|
+
default: console.log(line);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
writePretty(entry) {
|
|
329
|
+
const { timestamp, level, appName, message, payload, context } = entry;
|
|
330
|
+
const ts = timestamp.slice(11, 23);
|
|
331
|
+
const prefix = `[${appName}]${context ? ` [${context}]` : ""}`;
|
|
332
|
+
const header = `%c${ts} %c${level.toUpperCase().padEnd(8)} %c${prefix} ${message}`;
|
|
333
|
+
const tsStyle = "color: #888; font-size: 11px;";
|
|
334
|
+
const levelStyle = this.levelStyle(level);
|
|
335
|
+
const msgStyle = "color: inherit; font-weight: normal;";
|
|
336
|
+
if (payload && Object.keys(payload).length > 0) {
|
|
337
|
+
console.groupCollapsed(header, tsStyle, levelStyle, msgStyle);
|
|
338
|
+
console.dir(payload);
|
|
339
|
+
console.groupEnd();
|
|
340
|
+
} else switch (level) {
|
|
341
|
+
case "verbose":
|
|
342
|
+
case "debug":
|
|
343
|
+
console.debug(header, tsStyle, levelStyle, msgStyle);
|
|
344
|
+
break;
|
|
345
|
+
case "warn":
|
|
346
|
+
console.warn(header, tsStyle, levelStyle, msgStyle);
|
|
347
|
+
break;
|
|
348
|
+
case "error":
|
|
349
|
+
case "critical":
|
|
350
|
+
console.error(header, tsStyle, levelStyle, msgStyle);
|
|
351
|
+
break;
|
|
352
|
+
default: console.log(header, tsStyle, levelStyle, msgStyle);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
levelStyle(level) {
|
|
356
|
+
return {
|
|
357
|
+
verbose: "color: #aaa; font-weight: bold;",
|
|
358
|
+
debug: "color: #7ec8e3; font-weight: bold;",
|
|
359
|
+
info: "color: #4caf50; font-weight: bold;",
|
|
360
|
+
warn: "color: #ff9800; font-weight: bold;",
|
|
361
|
+
error: "color: #f44336; font-weight: bold;",
|
|
362
|
+
critical: "color: #fff; background: #f44336; font-weight: bold; padding: 1px 4px;"
|
|
363
|
+
}[level] ?? "color: inherit; font-weight: bold;";
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
function validateRemoteUrl(raw) {
|
|
367
|
+
if (typeof raw !== "string" || raw.length === 0) throw new TypeError("BrowserRemoteTransport: `url` must be a non-empty string");
|
|
368
|
+
if (raw.startsWith("/") && !raw.startsWith("//")) return raw;
|
|
369
|
+
let parsed;
|
|
370
|
+
try {
|
|
371
|
+
parsed = new URL(raw);
|
|
372
|
+
} catch {
|
|
373
|
+
throw new TypeError(`BrowserRemoteTransport: invalid url "${raw}"`);
|
|
374
|
+
}
|
|
375
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new TypeError(`BrowserRemoteTransport: url scheme "${parsed.protocol}" is not allowed; use http: or https:`);
|
|
376
|
+
return raw;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* UTF-8 byte length of `s` — the measure browsers use for the keepalive
|
|
380
|
+
* body-size limit. `TextEncoder` is a standard Web API (browsers, workers,
|
|
381
|
+
* Node >= 11); it needs no Node import.
|
|
382
|
+
*/
|
|
383
|
+
function byteLength(s) {
|
|
384
|
+
return new TextEncoder().encode(s).length;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Browser transport that batches log entries and sends them to a remote
|
|
388
|
+
* HTTP endpoint via `fetch` (available in all modern browsers and Edge runtimes).
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```ts
|
|
392
|
+
* const logger = createBrowserLogger({
|
|
393
|
+
* transports: [
|
|
394
|
+
* new BrowserRemoteTransport({
|
|
395
|
+
* url: '/api/logs',
|
|
396
|
+
* batchSize: 50,
|
|
397
|
+
* flushIntervalMs: 10_000,
|
|
398
|
+
* }),
|
|
399
|
+
* ],
|
|
400
|
+
* });
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
var BrowserRemoteTransport = class {
|
|
404
|
+
constructor(options) {
|
|
405
|
+
this.name = "browser-remote";
|
|
406
|
+
this.batch = [];
|
|
407
|
+
this.timer = null;
|
|
408
|
+
this.droppedEntries = 0;
|
|
409
|
+
this.url = validateRemoteUrl(options.url);
|
|
410
|
+
this.headers = options.headers ?? {};
|
|
411
|
+
this.batchSize = options.batchSize ?? 100;
|
|
412
|
+
this.flushIntervalMs = options.flushIntervalMs ?? 5e3;
|
|
413
|
+
this.maxBufferSize = options.maxBufferSize ?? 1e3;
|
|
414
|
+
if (options.level !== void 0) this.level = options.level;
|
|
415
|
+
this.scheduleFlush();
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Number of entries dropped without delivery — either because the buffer
|
|
419
|
+
* exceeded `maxBufferSize` (oldest first) or because a POST failed with a
|
|
420
|
+
* non-retryable HTTP status (a 4xx other than 429).
|
|
421
|
+
*/
|
|
422
|
+
get dropped() {
|
|
423
|
+
return this.droppedEntries;
|
|
424
|
+
}
|
|
425
|
+
write(entry) {
|
|
426
|
+
this.batch.push(entry);
|
|
427
|
+
while (this.batch.length > this.maxBufferSize) {
|
|
428
|
+
this.batch.shift();
|
|
429
|
+
this.droppedEntries += 1;
|
|
430
|
+
}
|
|
431
|
+
if (this.batch.length >= this.batchSize) this.flush();
|
|
432
|
+
}
|
|
433
|
+
async flush() {
|
|
434
|
+
while (this.batch.length > 0) {
|
|
435
|
+
const entries = this.batch.splice(0, this.batchSize);
|
|
436
|
+
const body = stringifyForLog(entries);
|
|
437
|
+
const keepalive = byteLength(body) < 6e4;
|
|
438
|
+
let res;
|
|
439
|
+
try {
|
|
440
|
+
res = await fetch(this.url, {
|
|
441
|
+
method: "POST",
|
|
442
|
+
headers: {
|
|
443
|
+
"Content-Type": "application/json",
|
|
444
|
+
...this.headers
|
|
445
|
+
},
|
|
446
|
+
body,
|
|
447
|
+
keepalive
|
|
448
|
+
});
|
|
449
|
+
} catch {
|
|
450
|
+
this.batch.unshift(...entries);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (!res.ok) {
|
|
454
|
+
if (res.status >= 500 || res.status === 429) this.batch.unshift(...entries);
|
|
455
|
+
else this.droppedEntries += entries.length;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
scheduleFlush() {
|
|
461
|
+
if (this.flushIntervalMs <= 0) return;
|
|
462
|
+
if (typeof globalThis !== "undefined" && typeof globalThis["setInterval"] === "function") this.timer = setInterval(() => {
|
|
463
|
+
this.flush().catch(() => {});
|
|
464
|
+
}, this.flushIntervalMs);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Stop the flush timer and flush remaining entries. Without the final flush,
|
|
468
|
+
* any buffered logs were lost on page unload / teardown — the browser
|
|
469
|
+
* equivalent of the "last N seconds of logs lost on deploy" problem.
|
|
470
|
+
*/
|
|
471
|
+
destroy() {
|
|
472
|
+
if (this.timer !== null) {
|
|
473
|
+
clearInterval(this.timer);
|
|
474
|
+
this.timer = null;
|
|
475
|
+
}
|
|
476
|
+
this.flush().catch(() => {});
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* Lightweight browser-safe logger.
|
|
481
|
+
*
|
|
482
|
+
* Unlike the Node.js `LogixiaLogger`, this implementation has **zero**
|
|
483
|
+
* Node.js dependencies. It can be bundled for any runtime.
|
|
484
|
+
*/
|
|
485
|
+
var BrowserLogger = class BrowserLogger {
|
|
486
|
+
constructor(config = {}) {
|
|
487
|
+
this.config = {
|
|
488
|
+
appName: config.appName ?? "App",
|
|
489
|
+
level: config.level ?? "info",
|
|
490
|
+
silent: config.silent ?? false,
|
|
491
|
+
json: config.json ?? false,
|
|
492
|
+
context: config.context
|
|
493
|
+
};
|
|
494
|
+
if (config.silent) this.transports = [];
|
|
495
|
+
else this.transports = [new BrowserConsoleTransport({ json: this.config.json }), ...config.transports ?? []];
|
|
496
|
+
}
|
|
497
|
+
verbose(message, payload) {
|
|
498
|
+
this.emit("verbose", message, payload);
|
|
499
|
+
}
|
|
500
|
+
debug(message, payload) {
|
|
501
|
+
this.emit("debug", message, payload);
|
|
502
|
+
}
|
|
503
|
+
info(message, payload) {
|
|
504
|
+
this.emit("info", message, payload);
|
|
505
|
+
}
|
|
506
|
+
log(message, payload) {
|
|
507
|
+
this.emit("info", message, payload);
|
|
508
|
+
}
|
|
509
|
+
warn(message, payload) {
|
|
510
|
+
this.emit("warn", message, payload);
|
|
511
|
+
}
|
|
512
|
+
error(message, payload) {
|
|
513
|
+
this.emit("error", message, payload);
|
|
514
|
+
}
|
|
515
|
+
critical(message, payload) {
|
|
516
|
+
this.emit("critical", message, payload);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Create a child logger that inherits config and merges extra context fields
|
|
520
|
+
* into every emitted entry.
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```ts
|
|
524
|
+
* const childLogger = logger.child({ context: 'AuthService', userId: '42' });
|
|
525
|
+
* childLogger.info('Login succeeded'); // → { context: 'AuthService', userId: '42', ... }
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
child(extraContext) {
|
|
529
|
+
return new BrowserLogger({
|
|
530
|
+
appName: this.config.appName,
|
|
531
|
+
level: this.config.level,
|
|
532
|
+
silent: this.config.silent,
|
|
533
|
+
json: this.config.json,
|
|
534
|
+
context: {
|
|
535
|
+
...this.config.context,
|
|
536
|
+
...extraContext
|
|
537
|
+
},
|
|
538
|
+
transports: this.transports.filter((t) => t.name !== "browser-console")
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
emit(level, message, payload) {
|
|
542
|
+
if (!shouldLog(level, this.config.level)) return;
|
|
543
|
+
const entry = {
|
|
544
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
545
|
+
level,
|
|
546
|
+
appName: this.config.appName,
|
|
547
|
+
message,
|
|
548
|
+
...payload || this.config.context ? { payload: {
|
|
549
|
+
...this.config.context,
|
|
550
|
+
...payload
|
|
551
|
+
} } : {}
|
|
552
|
+
};
|
|
553
|
+
for (const transport of this.transports) {
|
|
554
|
+
if (transport.level && !shouldLog(level, transport.level)) continue;
|
|
555
|
+
transport.write(entry);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* Create a browser-safe logixia logger instance.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* import { createBrowserLogger } from 'logixia/browser';
|
|
565
|
+
*
|
|
566
|
+
* const logger = createBrowserLogger({ appName: 'checkout', level: 'debug' });
|
|
567
|
+
* logger.info('Cart updated', { itemCount: 3, total: 49.99 });
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
function createBrowserLogger(config) {
|
|
571
|
+
return new BrowserLogger(config);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Default browser logger instance (singleton, console transport, info level).
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```ts
|
|
578
|
+
* import { browserLogger } from 'logixia/browser';
|
|
579
|
+
* browserLogger.info('Hello from browser');
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
const browserLogger = new BrowserLogger({ appName: "App" });
|
|
583
|
+
|
|
584
|
+
//#endregion
|
|
585
|
+
export { BrowserConsoleTransport, BrowserLogger, BrowserRemoteTransport, browserLogger, createBrowserLogger, createTypedLogger, defineLogSchema, disableOtelBridge, getActiveOtelContext, getOtelMetaFields, initOtelBridge };
|
|
586
|
+
//# sourceMappingURL=browser.mjs.map
|