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