logixia 1.2.1 → 1.3.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 (59) hide show
  1. package/dist/{index-BDRSTjUt.d.ts → index-CHIsdA9n.d.ts} +53 -2
  2. package/dist/index-CHIsdA9n.d.ts.map +1 -0
  3. package/dist/{index-Drrzn-Yg.d.mts → index-iDTW2-eY.d.mts} +53 -2
  4. package/dist/index-iDTW2-eY.d.mts.map +1 -0
  5. package/dist/index.d.mts +89 -3
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.d.ts +89 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +5 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +3 -3
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/{logitron-logger.module-iO8DPE7_.mjs → logitron-logger.module-2AzkadqZ.mjs} +226 -8
  14. package/dist/logitron-logger.module-2AzkadqZ.mjs.map +1 -0
  15. package/dist/{logitron-logger.module-X6nGDVGC.js → logitron-logger.module-BqNKp0Fs.js} +240 -4
  16. package/dist/logitron-logger.module-BqNKp0Fs.js.map +1 -0
  17. package/dist/{logitron-logger.module-CY3t8yK6.d.mts → logitron-logger.module-C0G8JGVf.d.ts} +6 -4
  18. package/dist/logitron-logger.module-C0G8JGVf.d.ts.map +1 -0
  19. package/dist/{logitron-logger.module-DgEldK9V.d.ts → logitron-logger.module-DQKaZTJL.d.mts} +6 -4
  20. package/dist/logitron-logger.module-DQKaZTJL.d.mts.map +1 -0
  21. package/dist/middleware.d.mts +83 -0
  22. package/dist/middleware.d.mts.map +1 -0
  23. package/dist/middleware.d.ts +83 -0
  24. package/dist/middleware.d.ts.map +1 -0
  25. package/dist/middleware.js +132 -0
  26. package/dist/middleware.js.map +1 -0
  27. package/dist/middleware.mjs +130 -0
  28. package/dist/middleware.mjs.map +1 -0
  29. package/dist/nest.d.mts +2 -2
  30. package/dist/nest.d.ts +2 -2
  31. package/dist/nest.js +2 -2
  32. package/dist/nest.mjs +2 -2
  33. package/dist/testing.d.mts +67 -0
  34. package/dist/testing.d.mts.map +1 -0
  35. package/dist/testing.d.ts +67 -0
  36. package/dist/testing.d.ts.map +1 -0
  37. package/dist/testing.js +164 -0
  38. package/dist/testing.js.map +1 -0
  39. package/dist/testing.mjs +163 -0
  40. package/dist/testing.mjs.map +1 -0
  41. package/dist/{transport.manager-Vi__pagn.mjs → transport.manager-5VVdqS3o.mjs} +51 -2
  42. package/dist/transport.manager-5VVdqS3o.mjs.map +1 -0
  43. package/dist/{transport.manager-C3Xr7Tvi.js → transport.manager-DCOm4uIQ.js} +51 -2
  44. package/dist/transport.manager-DCOm4uIQ.js.map +1 -0
  45. package/dist/transports.d.mts +38 -1
  46. package/dist/transports.d.mts.map +1 -1
  47. package/dist/transports.d.ts +38 -1
  48. package/dist/transports.d.ts.map +1 -1
  49. package/dist/transports.js +1 -1
  50. package/dist/transports.mjs +1 -1
  51. package/package.json +21 -1
  52. package/dist/index-BDRSTjUt.d.ts.map +0 -1
  53. package/dist/index-Drrzn-Yg.d.mts.map +0 -1
  54. package/dist/logitron-logger.module-CY3t8yK6.d.mts.map +0 -1
  55. package/dist/logitron-logger.module-DgEldK9V.d.ts.map +0 -1
  56. package/dist/logitron-logger.module-X6nGDVGC.js.map +0 -1
  57. package/dist/logitron-logger.module-iO8DPE7_.mjs.map +0 -1
  58. package/dist/transport.manager-C3Xr7Tvi.js.map +0 -1
  59. package/dist/transport.manager-Vi__pagn.mjs.map +0 -1
package/dist/nest.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import "./transport.manager-Vi__pagn.mjs";
2
- import { a as TraceMiddleware, g as setTraceId, i as WebSocketTraceInterceptor, m as getCurrentTraceId, n as LOGIXIA_LOGGER_PREFIX, o as KafkaTraceInterceptor, p as generateTraceId, r as LogixiaLoggerModule, s as LogixiaLoggerService, t as LOGIXIA_LOGGER_CONFIG } from "./logitron-logger.module-iO8DPE7_.mjs";
1
+ import { a as TraceMiddleware, g as setTraceId, i as WebSocketTraceInterceptor, m as getCurrentTraceId, n as LOGIXIA_LOGGER_PREFIX, o as KafkaTraceInterceptor, p as generateTraceId, r as LogixiaLoggerModule, s as LogixiaLoggerService, t as LOGIXIA_LOGGER_CONFIG } from "./logitron-logger.module-2AzkadqZ.mjs";
2
+ import "./transport.manager-5VVdqS3o.mjs";
3
3
 
4
4
  //#region src/core/request-context.ts
5
5
  var RequestContextManager = class {
@@ -0,0 +1,67 @@
1
+ import { l as IBaseLogger, m as LogEntry } from "./index-iDTW2-eY.mjs";
2
+
3
+ //#region src/testing/mock-logger.d.ts
4
+
5
+ interface MockLogCall {
6
+ level: string;
7
+ message: string;
8
+ data?: Record<string, unknown>;
9
+ /** Full LogEntry shape for assertions that inspect the complete record. */
10
+ entry: Pick<LogEntry, 'level' | 'message' | 'context'> & {
11
+ data?: Record<string, unknown>;
12
+ };
13
+ }
14
+ type LogMatcher = string | RegExp | ((call: MockLogCall) => boolean) | Partial<Record<'message' | 'level' | string, unknown>>;
15
+ interface MockLoggerInstance {
16
+ /** The logger itself — pass this where an IBaseLogger is expected. */
17
+ readonly logger: IBaseLogger;
18
+ /** Every call recorded across all levels, in insertion order. */
19
+ readonly calls: MockLogCall[];
20
+ /**
21
+ * Get all calls for a specific level (case-insensitive). If `level` is
22
+ * omitted, returns all calls across every level.
23
+ */
24
+ getCalls(level?: string): MockLogCall[];
25
+ /** Return the most recent call, optionally filtered by level. */
26
+ getLastCall(level?: string): MockLogCall | undefined;
27
+ /**
28
+ * Assert that at least one recorded call matches `matcher`.
29
+ *
30
+ * - string → exact message match
31
+ * - RegExp → message test
32
+ * - function → receives the full MockLogCall
33
+ * - object → every key/value must match (supports RegExp values)
34
+ *
35
+ * Throws an Error with a descriptive message if the assertion fails (works
36
+ * with both Vitest `expect` and Jest matchers via `.toThrow()`, or you can
37
+ * let it propagate directly).
38
+ */
39
+ expectLog(level: string, matcher?: LogMatcher): void;
40
+ /**
41
+ * Assert that NO recorded call matches `matcher` at the given level.
42
+ */
43
+ expectNoLog(level: string, matcher?: LogMatcher): void;
44
+ /** Clear all recorded calls. Call between tests. */
45
+ reset(): void;
46
+ /**
47
+ * Silence — when true, the mock suppresses all console output from the
48
+ * logger. Default: true.
49
+ */
50
+ silent: boolean;
51
+ }
52
+ /**
53
+ * Create a mock logger for use in tests.
54
+ *
55
+ * The returned object implements `IBaseLogger` and additionally exposes `.calls`,
56
+ * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.
57
+ *
58
+ * @param options.silent Suppress console output (default: true)
59
+ * @param options.context Optional context label written into every `entry.context`
60
+ */
61
+ declare function createMockLogger(options?: {
62
+ silent?: boolean;
63
+ context?: string;
64
+ }): MockLoggerInstance;
65
+ //#endregion
66
+ export { type LogMatcher, type MockLogCall, type MockLoggerInstance, createMockLogger };
67
+ //# sourceMappingURL=testing.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../src/testing/mock-logger.ts"],"sourcesContent":[],"mappings":";;;;AAoCmB,UAhBF,WAAA,CAgBE;EAED,KAAA,EAAA,MAAA;EAKU,OAAA,EAAA,MAAA;EAEG,IAAA,CAAA,EAtBtB,MAsBsB,CAAA,MAAA,EAAA,OAAA,CAAA;EAaM;EAIE,KAAA,EArC9B,IAqC8B,CArCzB,QAqCyB,EAAA,OAAA,GAAA,SAAA,GAAA,SAAA,CAAA,GAAA;IAAU,IAAA,CAAA,EArCmB,MAqCnB,CAAA,MAAA,EAAA,OAAA,CAAA;EA2DjC,CAAA;;KA7FJ,UAAA,YAER,iBACQ,2BACR,QAAQ;UAEK,kBAAA;;mBAEE;;kBAED;;;;;4BAKU;;+BAEG;;;;;;;;;;;;;qCAaM;;;;uCAIE;;;;;;;;;;;;;;;;;;iBA2DvB,gBAAA;;;IAEb"}
@@ -0,0 +1,67 @@
1
+ import { l as IBaseLogger, m as LogEntry } from "./index-CHIsdA9n.js";
2
+
3
+ //#region src/testing/mock-logger.d.ts
4
+
5
+ interface MockLogCall {
6
+ level: string;
7
+ message: string;
8
+ data?: Record<string, unknown>;
9
+ /** Full LogEntry shape for assertions that inspect the complete record. */
10
+ entry: Pick<LogEntry, 'level' | 'message' | 'context'> & {
11
+ data?: Record<string, unknown>;
12
+ };
13
+ }
14
+ type LogMatcher = string | RegExp | ((call: MockLogCall) => boolean) | Partial<Record<'message' | 'level' | string, unknown>>;
15
+ interface MockLoggerInstance {
16
+ /** The logger itself — pass this where an IBaseLogger is expected. */
17
+ readonly logger: IBaseLogger;
18
+ /** Every call recorded across all levels, in insertion order. */
19
+ readonly calls: MockLogCall[];
20
+ /**
21
+ * Get all calls for a specific level (case-insensitive). If `level` is
22
+ * omitted, returns all calls across every level.
23
+ */
24
+ getCalls(level?: string): MockLogCall[];
25
+ /** Return the most recent call, optionally filtered by level. */
26
+ getLastCall(level?: string): MockLogCall | undefined;
27
+ /**
28
+ * Assert that at least one recorded call matches `matcher`.
29
+ *
30
+ * - string → exact message match
31
+ * - RegExp → message test
32
+ * - function → receives the full MockLogCall
33
+ * - object → every key/value must match (supports RegExp values)
34
+ *
35
+ * Throws an Error with a descriptive message if the assertion fails (works
36
+ * with both Vitest `expect` and Jest matchers via `.toThrow()`, or you can
37
+ * let it propagate directly).
38
+ */
39
+ expectLog(level: string, matcher?: LogMatcher): void;
40
+ /**
41
+ * Assert that NO recorded call matches `matcher` at the given level.
42
+ */
43
+ expectNoLog(level: string, matcher?: LogMatcher): void;
44
+ /** Clear all recorded calls. Call between tests. */
45
+ reset(): void;
46
+ /**
47
+ * Silence — when true, the mock suppresses all console output from the
48
+ * logger. Default: true.
49
+ */
50
+ silent: boolean;
51
+ }
52
+ /**
53
+ * Create a mock logger for use in tests.
54
+ *
55
+ * The returned object implements `IBaseLogger` and additionally exposes `.calls`,
56
+ * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.
57
+ *
58
+ * @param options.silent Suppress console output (default: true)
59
+ * @param options.context Optional context label written into every `entry.context`
60
+ */
61
+ declare function createMockLogger(options?: {
62
+ silent?: boolean;
63
+ context?: string;
64
+ }): MockLoggerInstance;
65
+ //#endregion
66
+ export { type LogMatcher, type MockLogCall, type MockLoggerInstance, createMockLogger };
67
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing/mock-logger.ts"],"sourcesContent":[],"mappings":";;;;AAoCmB,UAhBF,WAAA,CAgBE;EAED,KAAA,EAAA,MAAA;EAKU,OAAA,EAAA,MAAA;EAEG,IAAA,CAAA,EAtBtB,MAsBsB,CAAA,MAAA,EAAA,OAAA,CAAA;EAaM;EAIE,KAAA,EArC9B,IAqC8B,CArCzB,QAqCyB,EAAA,OAAA,GAAA,SAAA,GAAA,SAAA,CAAA,GAAA;IAAU,IAAA,CAAA,EArCmB,MAqCnB,CAAA,MAAA,EAAA,OAAA,CAAA;EA2DjC,CAAA;;KA7FJ,UAAA,YAER,iBACQ,2BACR,QAAQ;UAEK,kBAAA;;mBAEE;;kBAED;;;;;4BAKU;;+BAEG;;;;;;;;;;;;;qCAaM;;;;uCAIE;;;;;;;;;;;;;;;;;;iBA2DvB,gBAAA;;;IAEb"}
@@ -0,0 +1,164 @@
1
+
2
+ //#region src/testing/mock-logger.ts
3
+ function resolveCallField(call, key) {
4
+ var _call$data;
5
+ if (key === "message") return call.message;
6
+ if (key === "level") return call.level;
7
+ return ((_call$data = call.data) === null || _call$data === void 0 ? void 0 : _call$data[key]) ?? call.entry[key];
8
+ }
9
+ function formatMatcherDescription(matcher) {
10
+ if (matcher === void 0) return "(any)";
11
+ if (matcher instanceof RegExp) return matcher.toString();
12
+ return JSON.stringify(matcher);
13
+ }
14
+ function matchesCall(call, matcher) {
15
+ if (matcher === void 0) return true;
16
+ if (typeof matcher === "string") return call.message === matcher;
17
+ if (matcher instanceof RegExp) return matcher.test(call.message);
18
+ if (typeof matcher === "function") return matcher(call);
19
+ for (const [key, expected] of Object.entries(matcher)) {
20
+ const actual = resolveCallField(call, key);
21
+ if (expected instanceof RegExp) {
22
+ if (!expected.test(String(actual ?? ""))) return false;
23
+ } else if (actual !== expected) return false;
24
+ }
25
+ return true;
26
+ }
27
+ function formatCalls(calls) {
28
+ if (calls.length === 0) return " (no calls recorded)";
29
+ return calls.map((c) => ` [${c.level}] "${c.message}" ${c.data ? JSON.stringify(c.data) : ""}`).join("\n");
30
+ }
31
+ /**
32
+ * Create a mock logger for use in tests.
33
+ *
34
+ * The returned object implements `IBaseLogger` and additionally exposes `.calls`,
35
+ * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.
36
+ *
37
+ * @param options.silent Suppress console output (default: true)
38
+ * @param options.context Optional context label written into every `entry.context`
39
+ */
40
+ function createMockLogger(options = {}) {
41
+ const { silent = true, context } = options;
42
+ const _calls = [];
43
+ function record(level, messageOrError, data) {
44
+ const message = messageOrError instanceof Error ? messageOrError.message : messageOrError;
45
+ const extraData = messageOrError instanceof Error ? {
46
+ ...data,
47
+ error: {
48
+ message: messageOrError.message,
49
+ stack: messageOrError.stack
50
+ }
51
+ } : data;
52
+ _calls.push({
53
+ level,
54
+ message,
55
+ ...extraData !== void 0 ? { data: extraData } : {},
56
+ entry: {
57
+ level,
58
+ message,
59
+ ...context !== void 0 ? { context } : {},
60
+ ...extraData !== void 0 ? { data: extraData } : {}
61
+ }
62
+ });
63
+ }
64
+ const logger = {
65
+ async error(messageOrError, data) {
66
+ record("error", messageOrError, data);
67
+ },
68
+ async warn(message, data) {
69
+ record("warn", message, data);
70
+ },
71
+ async info(message, data) {
72
+ record("info", message, data);
73
+ },
74
+ async debug(message, data) {
75
+ record("debug", message, data);
76
+ },
77
+ async trace(message, data) {
78
+ record("trace", message, data);
79
+ },
80
+ async verbose(message, data) {
81
+ record("verbose", message, data);
82
+ },
83
+ async logLevel(level, message, data) {
84
+ record(level, message, data);
85
+ },
86
+ time(_label) {},
87
+ async timeEnd(_label) {
88
+ return 0;
89
+ },
90
+ async timeAsync(_label, fn) {
91
+ return fn();
92
+ },
93
+ setLevel(_level) {},
94
+ getLevel() {
95
+ return "info";
96
+ },
97
+ setContext(_ctx) {},
98
+ getContext() {
99
+ return context;
100
+ },
101
+ enableField(_f) {},
102
+ disableField(_f) {},
103
+ isFieldEnabled(_f) {
104
+ return true;
105
+ },
106
+ getFieldState() {
107
+ return {};
108
+ },
109
+ resetFieldState() {},
110
+ enableTransportLevelPrompting() {},
111
+ disableTransportLevelPrompting() {},
112
+ setTransportLevels(_id, _levels) {},
113
+ getTransportLevels(_id) {},
114
+ clearTransportLevelPreferences() {},
115
+ getAvailableTransports() {
116
+ return [];
117
+ },
118
+ child(ctx, _data) {
119
+ return createMockLogger({
120
+ silent,
121
+ context: ctx
122
+ }).logger;
123
+ },
124
+ async close() {}
125
+ };
126
+ const instance = {
127
+ get logger() {
128
+ return logger;
129
+ },
130
+ get calls() {
131
+ return _calls;
132
+ },
133
+ getCalls(level) {
134
+ if (!level) return [..._calls];
135
+ const l = level.toLowerCase();
136
+ return _calls.filter((c) => c.level.toLowerCase() === l);
137
+ },
138
+ getLastCall(level) {
139
+ const filtered = level ? _calls.filter((c) => c.level.toLowerCase() === level.toLowerCase()) : _calls;
140
+ return filtered[filtered.length - 1];
141
+ },
142
+ expectLog(level, matcher) {
143
+ if (!instance.getCalls(level).some((c) => matchesCall(c, matcher))) {
144
+ const matcherDesc = formatMatcherDescription(matcher);
145
+ throw new Error(`Expected at least one [${level}] log matching ${matcherDesc}.\nRecorded calls:\n${formatCalls(_calls)}`);
146
+ }
147
+ },
148
+ expectNoLog(level, matcher) {
149
+ if (instance.getCalls(level).some((c) => matchesCall(c, matcher))) {
150
+ const matcherDesc = formatMatcherDescription(matcher);
151
+ throw new Error(`Expected NO [${level}] log matching ${matcherDesc}, but one was found.\nRecorded calls:\n${formatCalls(_calls)}`);
152
+ }
153
+ },
154
+ reset() {
155
+ _calls.length = 0;
156
+ },
157
+ silent
158
+ };
159
+ return instance;
160
+ }
161
+
162
+ //#endregion
163
+ exports.createMockLogger = createMockLogger;
164
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","names":["_calls: MockLogCall[]","logger: IBaseLogger","instance: MockLoggerInstance"],"sources":["../src/testing/mock-logger.ts"],"sourcesContent":["/**\n * logixia/testing — Zero-dependency mock logger for Vitest & Jest.\n *\n * @example\n * ```ts\n * import { createMockLogger } from 'logixia/testing';\n *\n * const mock = createMockLogger();\n * await myService.doSomething(mock.logger);\n *\n * mock.expectLog('info', { message: 'order created' });\n * mock.expectLog('error', { message: /failed/ });\n * mock.reset();\n * ```\n */\n\nimport type { IBaseLogger, LogEntry } from '../types';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface MockLogCall {\n level: string;\n message: string;\n data?: Record<string, unknown>;\n /** Full LogEntry shape for assertions that inspect the complete record. */\n entry: Pick<LogEntry, 'level' | 'message' | 'context'> & { data?: Record<string, unknown> };\n}\n\nexport type LogMatcher =\n | string\n | RegExp\n | ((call: MockLogCall) => boolean)\n | Partial<Record<'message' | 'level' | string, unknown>>;\n\nexport interface MockLoggerInstance {\n /** The logger itself — pass this where an IBaseLogger is expected. */\n readonly logger: IBaseLogger;\n /** Every call recorded across all levels, in insertion order. */\n readonly calls: MockLogCall[];\n /**\n * Get all calls for a specific level (case-insensitive). If `level` is\n * omitted, returns all calls across every level.\n */\n getCalls(level?: string): MockLogCall[];\n /** Return the most recent call, optionally filtered by level. */\n getLastCall(level?: string): MockLogCall | undefined;\n /**\n * Assert that at least one recorded call matches `matcher`.\n *\n * - string → exact message match\n * - RegExp → message test\n * - function → receives the full MockLogCall\n * - object → every key/value must match (supports RegExp values)\n *\n * Throws an Error with a descriptive message if the assertion fails (works\n * with both Vitest `expect` and Jest matchers via `.toThrow()`, or you can\n * let it propagate directly).\n */\n expectLog(level: string, matcher?: LogMatcher): void;\n /**\n * Assert that NO recorded call matches `matcher` at the given level.\n */\n expectNoLog(level: string, matcher?: LogMatcher): void;\n /** Clear all recorded calls. Call between tests. */\n reset(): void;\n /**\n * Silence — when true, the mock suppresses all console output from the\n * logger. Default: true.\n */\n silent: boolean;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction resolveCallField(call: MockLogCall, key: string): unknown {\n if (key === 'message') return call.message;\n if (key === 'level') return call.level;\n return call.data?.[key] ?? (call.entry as Record<string, unknown>)[key];\n}\n\nfunction formatMatcherDescription(matcher: LogMatcher | undefined): string {\n if (matcher === undefined) return '(any)';\n if (matcher instanceof RegExp) return matcher.toString();\n return JSON.stringify(matcher);\n}\n\nfunction matchesCall(call: MockLogCall, matcher?: LogMatcher): boolean {\n if (matcher === undefined) return true;\n if (typeof matcher === 'string') return call.message === matcher;\n if (matcher instanceof RegExp) return matcher.test(call.message);\n if (typeof matcher === 'function') return matcher(call);\n // Object shape: every key/value must match\n for (const [key, expected] of Object.entries(matcher)) {\n const actual = resolveCallField(call, key);\n if (expected instanceof RegExp) {\n if (!expected.test(String(actual ?? ''))) return false;\n } else {\n if (actual !== expected) return false;\n }\n }\n return true;\n}\n\nfunction formatCalls(calls: MockLogCall[]): string {\n if (calls.length === 0) return ' (no calls recorded)';\n return calls\n .map((c) => ` [${c.level}] \"${c.message}\" ${c.data ? JSON.stringify(c.data) : ''}`)\n .join('\\n');\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\n/**\n * Create a mock logger for use in tests.\n *\n * The returned object implements `IBaseLogger` and additionally exposes `.calls`,\n * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.\n *\n * @param options.silent Suppress console output (default: true)\n * @param options.context Optional context label written into every `entry.context`\n */\nexport function createMockLogger(\n options: { silent?: boolean; context?: string } = {}\n): MockLoggerInstance {\n const { silent = true, context } = options;\n const _calls: MockLogCall[] = [];\n\n function record(\n level: string,\n messageOrError: string | Error,\n data?: Record<string, unknown>\n ): void {\n const message = messageOrError instanceof Error ? messageOrError.message : messageOrError;\n const extraData =\n messageOrError instanceof Error\n ? { ...data, error: { message: messageOrError.message, stack: messageOrError.stack } }\n : data;\n\n _calls.push({\n level,\n message,\n ...(extraData !== undefined ? { data: extraData } : {}),\n entry: {\n level,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(extraData !== undefined ? { data: extraData } : {}),\n },\n });\n }\n\n const logger: IBaseLogger = {\n async error(messageOrError, data) {\n record('error', messageOrError as string | Error, data);\n },\n async warn(message, data) {\n record('warn', message, data);\n },\n async info(message, data) {\n record('info', message, data);\n },\n async debug(message, data) {\n record('debug', message, data);\n },\n async trace(message, data) {\n record('trace', message, data);\n },\n async verbose(message, data) {\n record('verbose', message, data);\n },\n async logLevel(level, message, data) {\n record(level, message, data);\n },\n\n time(_label) {\n /* no-op in tests */\n },\n async timeEnd(_label) {\n return 0;\n },\n async timeAsync(_label, fn) {\n return fn();\n },\n\n setLevel(_level) {\n /* no-op */\n },\n getLevel() {\n return 'info';\n },\n setContext(_ctx) {\n /* no-op */\n },\n getContext() {\n return context;\n },\n\n enableField(_f) {\n /* no-op */\n },\n disableField(_f) {\n /* no-op */\n },\n isFieldEnabled(_f) {\n return true;\n },\n getFieldState() {\n return {};\n },\n resetFieldState() {\n /* no-op */\n },\n\n enableTransportLevelPrompting() {\n /* no-op */\n },\n disableTransportLevelPrompting() {\n /* no-op */\n },\n setTransportLevels(_id, _levels) {\n /* no-op */\n },\n getTransportLevels(_id): string[] | undefined {\n return undefined;\n },\n clearTransportLevelPreferences() {\n /* no-op */\n },\n getAvailableTransports() {\n return [];\n },\n\n child(ctx, _data) {\n return createMockLogger({ silent, context: ctx }).logger;\n },\n async close() {\n /* no-op */\n },\n };\n\n const instance: MockLoggerInstance = {\n get logger() {\n return logger;\n },\n get calls() {\n return _calls;\n },\n\n getCalls(level?: string): MockLogCall[] {\n if (!level) return [..._calls];\n const l = level.toLowerCase();\n return _calls.filter((c) => c.level.toLowerCase() === l);\n },\n\n getLastCall(level?: string): MockLogCall | undefined {\n const filtered = level\n ? _calls.filter((c) => c.level.toLowerCase() === level.toLowerCase())\n : _calls;\n return filtered[filtered.length - 1];\n },\n\n expectLog(level: string, matcher?: LogMatcher): void {\n const levelCalls = instance.getCalls(level);\n const matched = levelCalls.some((c) => matchesCall(c, matcher));\n if (!matched) {\n const matcherDesc = formatMatcherDescription(matcher);\n throw new Error(\n `Expected at least one [${level}] log matching ${matcherDesc}.\\n` +\n `Recorded calls:\\n${formatCalls(_calls)}`\n );\n }\n },\n\n expectNoLog(level: string, matcher?: LogMatcher): void {\n const levelCalls = instance.getCalls(level);\n const matched = levelCalls.some((c) => matchesCall(c, matcher));\n if (matched) {\n const matcherDesc = formatMatcherDescription(matcher);\n throw new Error(\n `Expected NO [${level}] log matching ${matcherDesc}, but one was found.\\n` +\n `Recorded calls:\\n${formatCalls(_calls)}`\n );\n }\n },\n\n reset(): void {\n _calls.length = 0;\n },\n\n silent,\n };\n\n return instance;\n}\n"],"mappings":";;AA0EA,SAAS,iBAAiB,MAAmB,KAAsB;;AACjE,KAAI,QAAQ,UAAW,QAAO,KAAK;AACnC,KAAI,QAAQ,QAAS,QAAO,KAAK;AACjC,uBAAO,KAAK,8DAAO,SAAS,KAAK,MAAkC;;AAGrE,SAAS,yBAAyB,SAAyC;AACzE,KAAI,YAAY,OAAW,QAAO;AAClC,KAAI,mBAAmB,OAAQ,QAAO,QAAQ,UAAU;AACxD,QAAO,KAAK,UAAU,QAAQ;;AAGhC,SAAS,YAAY,MAAmB,SAA+B;AACrE,KAAI,YAAY,OAAW,QAAO;AAClC,KAAI,OAAO,YAAY,SAAU,QAAO,KAAK,YAAY;AACzD,KAAI,mBAAmB,OAAQ,QAAO,QAAQ,KAAK,KAAK,QAAQ;AAChE,KAAI,OAAO,YAAY,WAAY,QAAO,QAAQ,KAAK;AAEvD,MAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,QAAQ,EAAE;EACrD,MAAM,SAAS,iBAAiB,MAAM,IAAI;AAC1C,MAAI,oBAAoB,QACtB;OAAI,CAAC,SAAS,KAAK,OAAO,UAAU,GAAG,CAAC,CAAE,QAAO;aAE7C,WAAW,SAAU,QAAO;;AAGpC,QAAO;;AAGT,SAAS,YAAY,OAA8B;AACjD,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAO,MACJ,KAAK,MAAM,MAAM,EAAE,MAAM,KAAK,EAAE,QAAQ,IAAI,EAAE,OAAO,KAAK,UAAU,EAAE,KAAK,GAAG,KAAK,CACnF,KAAK,KAAK;;;;;;;;;;;AAcf,SAAgB,iBACd,UAAkD,EAAE,EAChC;CACpB,MAAM,EAAE,SAAS,MAAM,YAAY;CACnC,MAAMA,SAAwB,EAAE;CAEhC,SAAS,OACP,OACA,gBACA,MACM;EACN,MAAM,UAAU,0BAA0B,QAAQ,eAAe,UAAU;EAC3E,MAAM,YACJ,0BAA0B,QACtB;GAAE,GAAG;GAAM,OAAO;IAAE,SAAS,eAAe;IAAS,OAAO,eAAe;IAAO;GAAE,GACpF;AAEN,SAAO,KAAK;GACV;GACA;GACA,GAAI,cAAc,SAAY,EAAE,MAAM,WAAW,GAAG,EAAE;GACtD,OAAO;IACL;IACA;IACA,GAAI,YAAY,SAAY,EAAE,SAAS,GAAG,EAAE;IAC5C,GAAI,cAAc,SAAY,EAAE,MAAM,WAAW,GAAG,EAAE;IACvD;GACF,CAAC;;CAGJ,MAAMC,SAAsB;EAC1B,MAAM,MAAM,gBAAgB,MAAM;AAChC,UAAO,SAAS,gBAAkC,KAAK;;EAEzD,MAAM,KAAK,SAAS,MAAM;AACxB,UAAO,QAAQ,SAAS,KAAK;;EAE/B,MAAM,KAAK,SAAS,MAAM;AACxB,UAAO,QAAQ,SAAS,KAAK;;EAE/B,MAAM,MAAM,SAAS,MAAM;AACzB,UAAO,SAAS,SAAS,KAAK;;EAEhC,MAAM,MAAM,SAAS,MAAM;AACzB,UAAO,SAAS,SAAS,KAAK;;EAEhC,MAAM,QAAQ,SAAS,MAAM;AAC3B,UAAO,WAAW,SAAS,KAAK;;EAElC,MAAM,SAAS,OAAO,SAAS,MAAM;AACnC,UAAO,OAAO,SAAS,KAAK;;EAG9B,KAAK,QAAQ;EAGb,MAAM,QAAQ,QAAQ;AACpB,UAAO;;EAET,MAAM,UAAU,QAAQ,IAAI;AAC1B,UAAO,IAAI;;EAGb,SAAS,QAAQ;EAGjB,WAAW;AACT,UAAO;;EAET,WAAW,MAAM;EAGjB,aAAa;AACX,UAAO;;EAGT,YAAY,IAAI;EAGhB,aAAa,IAAI;EAGjB,eAAe,IAAI;AACjB,UAAO;;EAET,gBAAgB;AACd,UAAO,EAAE;;EAEX,kBAAkB;EAIlB,gCAAgC;EAGhC,iCAAiC;EAGjC,mBAAmB,KAAK,SAAS;EAGjC,mBAAmB,KAA2B;EAG9C,iCAAiC;EAGjC,yBAAyB;AACvB,UAAO,EAAE;;EAGX,MAAM,KAAK,OAAO;AAChB,UAAO,iBAAiB;IAAE;IAAQ,SAAS;IAAK,CAAC,CAAC;;EAEpD,MAAM,QAAQ;EAGf;CAED,MAAMC,WAA+B;EACnC,IAAI,SAAS;AACX,UAAO;;EAET,IAAI,QAAQ;AACV,UAAO;;EAGT,SAAS,OAA+B;AACtC,OAAI,CAAC,MAAO,QAAO,CAAC,GAAG,OAAO;GAC9B,MAAM,IAAI,MAAM,aAAa;AAC7B,UAAO,OAAO,QAAQ,MAAM,EAAE,MAAM,aAAa,KAAK,EAAE;;EAG1D,YAAY,OAAyC;GACnD,MAAM,WAAW,QACb,OAAO,QAAQ,MAAM,EAAE,MAAM,aAAa,KAAK,MAAM,aAAa,CAAC,GACnE;AACJ,UAAO,SAAS,SAAS,SAAS;;EAGpC,UAAU,OAAe,SAA4B;AAGnD,OAAI,CAFe,SAAS,SAAS,MAAM,CAChB,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,EACjD;IACZ,MAAM,cAAc,yBAAyB,QAAQ;AACrD,UAAM,IAAI,MACR,0BAA0B,MAAM,iBAAiB,YAAY,sBACvC,YAAY,OAAO,GAC1C;;;EAIL,YAAY,OAAe,SAA4B;AAGrD,OAFmB,SAAS,SAAS,MAAM,CAChB,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,EAClD;IACX,MAAM,cAAc,yBAAyB,QAAQ;AACrD,UAAM,IAAI,MACR,gBAAgB,MAAM,iBAAiB,YAAY,yCAC7B,YAAY,OAAO,GAC1C;;;EAIL,QAAc;AACZ,UAAO,SAAS;;EAGlB;EACD;AAED,QAAO"}
@@ -0,0 +1,163 @@
1
+ //#region src/testing/mock-logger.ts
2
+ function resolveCallField(call, key) {
3
+ var _call$data;
4
+ if (key === "message") return call.message;
5
+ if (key === "level") return call.level;
6
+ return ((_call$data = call.data) === null || _call$data === void 0 ? void 0 : _call$data[key]) ?? call.entry[key];
7
+ }
8
+ function formatMatcherDescription(matcher) {
9
+ if (matcher === void 0) return "(any)";
10
+ if (matcher instanceof RegExp) return matcher.toString();
11
+ return JSON.stringify(matcher);
12
+ }
13
+ function matchesCall(call, matcher) {
14
+ if (matcher === void 0) return true;
15
+ if (typeof matcher === "string") return call.message === matcher;
16
+ if (matcher instanceof RegExp) return matcher.test(call.message);
17
+ if (typeof matcher === "function") return matcher(call);
18
+ for (const [key, expected] of Object.entries(matcher)) {
19
+ const actual = resolveCallField(call, key);
20
+ if (expected instanceof RegExp) {
21
+ if (!expected.test(String(actual ?? ""))) return false;
22
+ } else if (actual !== expected) return false;
23
+ }
24
+ return true;
25
+ }
26
+ function formatCalls(calls) {
27
+ if (calls.length === 0) return " (no calls recorded)";
28
+ return calls.map((c) => ` [${c.level}] "${c.message}" ${c.data ? JSON.stringify(c.data) : ""}`).join("\n");
29
+ }
30
+ /**
31
+ * Create a mock logger for use in tests.
32
+ *
33
+ * The returned object implements `IBaseLogger` and additionally exposes `.calls`,
34
+ * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.
35
+ *
36
+ * @param options.silent Suppress console output (default: true)
37
+ * @param options.context Optional context label written into every `entry.context`
38
+ */
39
+ function createMockLogger(options = {}) {
40
+ const { silent = true, context } = options;
41
+ const _calls = [];
42
+ function record(level, messageOrError, data) {
43
+ const message = messageOrError instanceof Error ? messageOrError.message : messageOrError;
44
+ const extraData = messageOrError instanceof Error ? {
45
+ ...data,
46
+ error: {
47
+ message: messageOrError.message,
48
+ stack: messageOrError.stack
49
+ }
50
+ } : data;
51
+ _calls.push({
52
+ level,
53
+ message,
54
+ ...extraData !== void 0 ? { data: extraData } : {},
55
+ entry: {
56
+ level,
57
+ message,
58
+ ...context !== void 0 ? { context } : {},
59
+ ...extraData !== void 0 ? { data: extraData } : {}
60
+ }
61
+ });
62
+ }
63
+ const logger = {
64
+ async error(messageOrError, data) {
65
+ record("error", messageOrError, data);
66
+ },
67
+ async warn(message, data) {
68
+ record("warn", message, data);
69
+ },
70
+ async info(message, data) {
71
+ record("info", message, data);
72
+ },
73
+ async debug(message, data) {
74
+ record("debug", message, data);
75
+ },
76
+ async trace(message, data) {
77
+ record("trace", message, data);
78
+ },
79
+ async verbose(message, data) {
80
+ record("verbose", message, data);
81
+ },
82
+ async logLevel(level, message, data) {
83
+ record(level, message, data);
84
+ },
85
+ time(_label) {},
86
+ async timeEnd(_label) {
87
+ return 0;
88
+ },
89
+ async timeAsync(_label, fn) {
90
+ return fn();
91
+ },
92
+ setLevel(_level) {},
93
+ getLevel() {
94
+ return "info";
95
+ },
96
+ setContext(_ctx) {},
97
+ getContext() {
98
+ return context;
99
+ },
100
+ enableField(_f) {},
101
+ disableField(_f) {},
102
+ isFieldEnabled(_f) {
103
+ return true;
104
+ },
105
+ getFieldState() {
106
+ return {};
107
+ },
108
+ resetFieldState() {},
109
+ enableTransportLevelPrompting() {},
110
+ disableTransportLevelPrompting() {},
111
+ setTransportLevels(_id, _levels) {},
112
+ getTransportLevels(_id) {},
113
+ clearTransportLevelPreferences() {},
114
+ getAvailableTransports() {
115
+ return [];
116
+ },
117
+ child(ctx, _data) {
118
+ return createMockLogger({
119
+ silent,
120
+ context: ctx
121
+ }).logger;
122
+ },
123
+ async close() {}
124
+ };
125
+ const instance = {
126
+ get logger() {
127
+ return logger;
128
+ },
129
+ get calls() {
130
+ return _calls;
131
+ },
132
+ getCalls(level) {
133
+ if (!level) return [..._calls];
134
+ const l = level.toLowerCase();
135
+ return _calls.filter((c) => c.level.toLowerCase() === l);
136
+ },
137
+ getLastCall(level) {
138
+ const filtered = level ? _calls.filter((c) => c.level.toLowerCase() === level.toLowerCase()) : _calls;
139
+ return filtered[filtered.length - 1];
140
+ },
141
+ expectLog(level, matcher) {
142
+ if (!instance.getCalls(level).some((c) => matchesCall(c, matcher))) {
143
+ const matcherDesc = formatMatcherDescription(matcher);
144
+ throw new Error(`Expected at least one [${level}] log matching ${matcherDesc}.\nRecorded calls:\n${formatCalls(_calls)}`);
145
+ }
146
+ },
147
+ expectNoLog(level, matcher) {
148
+ if (instance.getCalls(level).some((c) => matchesCall(c, matcher))) {
149
+ const matcherDesc = formatMatcherDescription(matcher);
150
+ throw new Error(`Expected NO [${level}] log matching ${matcherDesc}, but one was found.\nRecorded calls:\n${formatCalls(_calls)}`);
151
+ }
152
+ },
153
+ reset() {
154
+ _calls.length = 0;
155
+ },
156
+ silent
157
+ };
158
+ return instance;
159
+ }
160
+
161
+ //#endregion
162
+ export { createMockLogger };
163
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":["_calls: MockLogCall[]","logger: IBaseLogger","instance: MockLoggerInstance"],"sources":["../src/testing/mock-logger.ts"],"sourcesContent":["/**\n * logixia/testing — Zero-dependency mock logger for Vitest & Jest.\n *\n * @example\n * ```ts\n * import { createMockLogger } from 'logixia/testing';\n *\n * const mock = createMockLogger();\n * await myService.doSomething(mock.logger);\n *\n * mock.expectLog('info', { message: 'order created' });\n * mock.expectLog('error', { message: /failed/ });\n * mock.reset();\n * ```\n */\n\nimport type { IBaseLogger, LogEntry } from '../types';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface MockLogCall {\n level: string;\n message: string;\n data?: Record<string, unknown>;\n /** Full LogEntry shape for assertions that inspect the complete record. */\n entry: Pick<LogEntry, 'level' | 'message' | 'context'> & { data?: Record<string, unknown> };\n}\n\nexport type LogMatcher =\n | string\n | RegExp\n | ((call: MockLogCall) => boolean)\n | Partial<Record<'message' | 'level' | string, unknown>>;\n\nexport interface MockLoggerInstance {\n /** The logger itself — pass this where an IBaseLogger is expected. */\n readonly logger: IBaseLogger;\n /** Every call recorded across all levels, in insertion order. */\n readonly calls: MockLogCall[];\n /**\n * Get all calls for a specific level (case-insensitive). If `level` is\n * omitted, returns all calls across every level.\n */\n getCalls(level?: string): MockLogCall[];\n /** Return the most recent call, optionally filtered by level. */\n getLastCall(level?: string): MockLogCall | undefined;\n /**\n * Assert that at least one recorded call matches `matcher`.\n *\n * - string → exact message match\n * - RegExp → message test\n * - function → receives the full MockLogCall\n * - object → every key/value must match (supports RegExp values)\n *\n * Throws an Error with a descriptive message if the assertion fails (works\n * with both Vitest `expect` and Jest matchers via `.toThrow()`, or you can\n * let it propagate directly).\n */\n expectLog(level: string, matcher?: LogMatcher): void;\n /**\n * Assert that NO recorded call matches `matcher` at the given level.\n */\n expectNoLog(level: string, matcher?: LogMatcher): void;\n /** Clear all recorded calls. Call between tests. */\n reset(): void;\n /**\n * Silence — when true, the mock suppresses all console output from the\n * logger. Default: true.\n */\n silent: boolean;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction resolveCallField(call: MockLogCall, key: string): unknown {\n if (key === 'message') return call.message;\n if (key === 'level') return call.level;\n return call.data?.[key] ?? (call.entry as Record<string, unknown>)[key];\n}\n\nfunction formatMatcherDescription(matcher: LogMatcher | undefined): string {\n if (matcher === undefined) return '(any)';\n if (matcher instanceof RegExp) return matcher.toString();\n return JSON.stringify(matcher);\n}\n\nfunction matchesCall(call: MockLogCall, matcher?: LogMatcher): boolean {\n if (matcher === undefined) return true;\n if (typeof matcher === 'string') return call.message === matcher;\n if (matcher instanceof RegExp) return matcher.test(call.message);\n if (typeof matcher === 'function') return matcher(call);\n // Object shape: every key/value must match\n for (const [key, expected] of Object.entries(matcher)) {\n const actual = resolveCallField(call, key);\n if (expected instanceof RegExp) {\n if (!expected.test(String(actual ?? ''))) return false;\n } else {\n if (actual !== expected) return false;\n }\n }\n return true;\n}\n\nfunction formatCalls(calls: MockLogCall[]): string {\n if (calls.length === 0) return ' (no calls recorded)';\n return calls\n .map((c) => ` [${c.level}] \"${c.message}\" ${c.data ? JSON.stringify(c.data) : ''}`)\n .join('\\n');\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\n/**\n * Create a mock logger for use in tests.\n *\n * The returned object implements `IBaseLogger` and additionally exposes `.calls`,\n * `.getCalls()`, `.getLastCall()`, `.expectLog()`, `.expectNoLog()`, and `.reset()`.\n *\n * @param options.silent Suppress console output (default: true)\n * @param options.context Optional context label written into every `entry.context`\n */\nexport function createMockLogger(\n options: { silent?: boolean; context?: string } = {}\n): MockLoggerInstance {\n const { silent = true, context } = options;\n const _calls: MockLogCall[] = [];\n\n function record(\n level: string,\n messageOrError: string | Error,\n data?: Record<string, unknown>\n ): void {\n const message = messageOrError instanceof Error ? messageOrError.message : messageOrError;\n const extraData =\n messageOrError instanceof Error\n ? { ...data, error: { message: messageOrError.message, stack: messageOrError.stack } }\n : data;\n\n _calls.push({\n level,\n message,\n ...(extraData !== undefined ? { data: extraData } : {}),\n entry: {\n level,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(extraData !== undefined ? { data: extraData } : {}),\n },\n });\n }\n\n const logger: IBaseLogger = {\n async error(messageOrError, data) {\n record('error', messageOrError as string | Error, data);\n },\n async warn(message, data) {\n record('warn', message, data);\n },\n async info(message, data) {\n record('info', message, data);\n },\n async debug(message, data) {\n record('debug', message, data);\n },\n async trace(message, data) {\n record('trace', message, data);\n },\n async verbose(message, data) {\n record('verbose', message, data);\n },\n async logLevel(level, message, data) {\n record(level, message, data);\n },\n\n time(_label) {\n /* no-op in tests */\n },\n async timeEnd(_label) {\n return 0;\n },\n async timeAsync(_label, fn) {\n return fn();\n },\n\n setLevel(_level) {\n /* no-op */\n },\n getLevel() {\n return 'info';\n },\n setContext(_ctx) {\n /* no-op */\n },\n getContext() {\n return context;\n },\n\n enableField(_f) {\n /* no-op */\n },\n disableField(_f) {\n /* no-op */\n },\n isFieldEnabled(_f) {\n return true;\n },\n getFieldState() {\n return {};\n },\n resetFieldState() {\n /* no-op */\n },\n\n enableTransportLevelPrompting() {\n /* no-op */\n },\n disableTransportLevelPrompting() {\n /* no-op */\n },\n setTransportLevels(_id, _levels) {\n /* no-op */\n },\n getTransportLevels(_id): string[] | undefined {\n return undefined;\n },\n clearTransportLevelPreferences() {\n /* no-op */\n },\n getAvailableTransports() {\n return [];\n },\n\n child(ctx, _data) {\n return createMockLogger({ silent, context: ctx }).logger;\n },\n async close() {\n /* no-op */\n },\n };\n\n const instance: MockLoggerInstance = {\n get logger() {\n return logger;\n },\n get calls() {\n return _calls;\n },\n\n getCalls(level?: string): MockLogCall[] {\n if (!level) return [..._calls];\n const l = level.toLowerCase();\n return _calls.filter((c) => c.level.toLowerCase() === l);\n },\n\n getLastCall(level?: string): MockLogCall | undefined {\n const filtered = level\n ? _calls.filter((c) => c.level.toLowerCase() === level.toLowerCase())\n : _calls;\n return filtered[filtered.length - 1];\n },\n\n expectLog(level: string, matcher?: LogMatcher): void {\n const levelCalls = instance.getCalls(level);\n const matched = levelCalls.some((c) => matchesCall(c, matcher));\n if (!matched) {\n const matcherDesc = formatMatcherDescription(matcher);\n throw new Error(\n `Expected at least one [${level}] log matching ${matcherDesc}.\\n` +\n `Recorded calls:\\n${formatCalls(_calls)}`\n );\n }\n },\n\n expectNoLog(level: string, matcher?: LogMatcher): void {\n const levelCalls = instance.getCalls(level);\n const matched = levelCalls.some((c) => matchesCall(c, matcher));\n if (matched) {\n const matcherDesc = formatMatcherDescription(matcher);\n throw new Error(\n `Expected NO [${level}] log matching ${matcherDesc}, but one was found.\\n` +\n `Recorded calls:\\n${formatCalls(_calls)}`\n );\n }\n },\n\n reset(): void {\n _calls.length = 0;\n },\n\n silent,\n };\n\n return instance;\n}\n"],"mappings":"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"}
@@ -1546,6 +1546,9 @@ var SegmentTransport = class extends AnalyticsTransport {
1546
1546
 
1547
1547
  //#endregion
1548
1548
  //#region src/transports/transport.manager.ts
1549
+ function _sleep(ms) {
1550
+ return new Promise((resolve) => setTimeout(resolve, ms));
1551
+ }
1549
1552
  var TransportManager = class extends EventEmitter {
1550
1553
  constructor(config = {}) {
1551
1554
  super();
@@ -1660,7 +1663,7 @@ var TransportManager = class extends EventEmitter {
1660
1663
  const startTime = Date.now();
1661
1664
  const metrics = this.metrics.get(id);
1662
1665
  try {
1663
- await transport.write(entry);
1666
+ await this._writeWithRetry(transport, entry);
1664
1667
  const writeTime = Date.now() - startTime;
1665
1668
  metrics.logsWritten++;
1666
1669
  metrics.lastWrite = new Date(startTime);
@@ -1672,6 +1675,52 @@ var TransportManager = class extends EventEmitter {
1672
1675
  throw error;
1673
1676
  }
1674
1677
  }
1678
+ /**
1679
+ * Feature 9: Multi-transport retry + failover.
1680
+ *
1681
+ * Attempts `transport.write()` up to `retry.maxRetries + 1` times with the
1682
+ * configured backoff strategy. If all attempts fail and a fallback transport
1683
+ * is configured, routes the entry there instead.
1684
+ */
1685
+ async _writeWithRetry(transport, entry) {
1686
+ const retryCfg = transport.retry;
1687
+ if (!retryCfg || !retryCfg.maxRetries) {
1688
+ await transport.write(entry);
1689
+ return;
1690
+ }
1691
+ const { maxRetries, backoff = "exponential", delay: baseDelay = 500, maxDelay = 3e4, fallback, onExhausted } = retryCfg;
1692
+ let lastError = /* @__PURE__ */ new Error("Unknown transport error");
1693
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
1694
+ await transport.write(entry);
1695
+ return;
1696
+ } catch (err) {
1697
+ lastError = err instanceof Error ? err : new Error(String(err));
1698
+ if (attempt < maxRetries) {
1699
+ let wait;
1700
+ switch (backoff) {
1701
+ case "fixed":
1702
+ wait = baseDelay;
1703
+ break;
1704
+ case "linear":
1705
+ wait = baseDelay * (attempt + 1);
1706
+ break;
1707
+ case "exponential":
1708
+ default: wait = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
1709
+ }
1710
+ this.emit("transport:retry", transport.name, attempt + 1, maxRetries, wait);
1711
+ await _sleep(wait);
1712
+ }
1713
+ }
1714
+ if (fallback) {
1715
+ this.emit("transport:fallback", transport.name, fallback.name);
1716
+ try {
1717
+ await fallback.write(entry);
1718
+ return;
1719
+ } catch {}
1720
+ }
1721
+ onExhausted === null || onExhausted === void 0 || onExhausted(lastError, entry);
1722
+ throw lastError;
1723
+ }
1675
1724
  shouldTransportHandle(transport, level, transportId) {
1676
1725
  if (transportId && this.transportLevelPreferences.has(transportId)) return this.transportLevelPreferences.get(transportId).includes(level.toLowerCase());
1677
1726
  if (!transport.level) return true;
@@ -1846,4 +1895,4 @@ var TransportManager = class extends EventEmitter {
1846
1895
 
1847
1896
  //#endregion
1848
1897
  export { FileTransport as a, DatabaseTransport as c, internalLog as d, internalWarn as f, GoogleAnalyticsTransport as i, ConsoleTransport as l, SegmentTransport as n, DataDogTransport as o, MixpanelTransport as r, AnalyticsTransport as s, TransportManager as t, internalError as u };
1849
- //# sourceMappingURL=transport.manager-Vi__pagn.mjs.map
1898
+ //# sourceMappingURL=transport.manager-5VVdqS3o.mjs.map