@alchemy/common 0.0.0-alpha.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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/actions/addBreadCrumb.d.ts +14 -0
  3. package/dist/esm/actions/addBreadCrumb.js +27 -0
  4. package/dist/esm/actions/addBreadCrumb.js.map +1 -0
  5. package/dist/esm/chains.d.ts +234 -0
  6. package/dist/esm/chains.js +113 -0
  7. package/dist/esm/chains.js.map +1 -0
  8. package/dist/esm/errors/AccountNotFoundError.d.ts +10 -0
  9. package/dist/esm/errors/AccountNotFoundError.js +19 -0
  10. package/dist/esm/errors/AccountNotFoundError.js.map +1 -0
  11. package/dist/esm/errors/BaseError.d.ts +23 -0
  12. package/dist/esm/errors/BaseError.js +40 -0
  13. package/dist/esm/errors/BaseError.js.map +1 -0
  14. package/dist/esm/errors/ChainNotFoundError.d.ts +11 -0
  15. package/dist/esm/errors/ChainNotFoundError.js +19 -0
  16. package/dist/esm/errors/ChainNotFoundError.js.map +1 -0
  17. package/dist/esm/errors/ConnectionConfigError.d.ts +13 -0
  18. package/dist/esm/errors/ConnectionConfigError.js +25 -0
  19. package/dist/esm/errors/ConnectionConfigError.js.map +1 -0
  20. package/dist/esm/errors/FetchError.d.ts +15 -0
  21. package/dist/esm/errors/FetchError.js +25 -0
  22. package/dist/esm/errors/FetchError.js.map +1 -0
  23. package/dist/esm/errors/InvalidRequestError.d.ts +13 -0
  24. package/dist/esm/errors/InvalidRequestError.js +22 -0
  25. package/dist/esm/errors/InvalidRequestError.js.map +1 -0
  26. package/dist/esm/errors/MethodUnsupportedError.d.ts +13 -0
  27. package/dist/esm/errors/MethodUnsupportedError.js +21 -0
  28. package/dist/esm/errors/MethodUnsupportedError.js.map +1 -0
  29. package/dist/esm/errors/ServerError.d.ts +15 -0
  30. package/dist/esm/errors/ServerError.js +25 -0
  31. package/dist/esm/errors/ServerError.js.map +1 -0
  32. package/dist/esm/index.d.ts +29 -0
  33. package/dist/esm/index.js +27 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/logging/config.d.ts +190 -0
  36. package/dist/esm/logging/config.js +279 -0
  37. package/dist/esm/logging/config.js.map +1 -0
  38. package/dist/esm/logging/index.d.ts +6 -0
  39. package/dist/esm/logging/index.js +5 -0
  40. package/dist/esm/logging/index.js.map +1 -0
  41. package/dist/esm/logging/local.d.ts +10 -0
  42. package/dist/esm/logging/local.js +35 -0
  43. package/dist/esm/logging/local.js.map +1 -0
  44. package/dist/esm/logging/logger.d.ts +80 -0
  45. package/dist/esm/logging/logger.js +111 -0
  46. package/dist/esm/logging/logger.js.map +1 -0
  47. package/dist/esm/logging/noop.d.ts +6 -0
  48. package/dist/esm/logging/noop.js +12 -0
  49. package/dist/esm/logging/noop.js.map +1 -0
  50. package/dist/esm/logging/sinks.d.ts +90 -0
  51. package/dist/esm/logging/sinks.js +111 -0
  52. package/dist/esm/logging/sinks.js.map +1 -0
  53. package/dist/esm/logging/types.d.ts +96 -0
  54. package/dist/esm/logging/types.js +2 -0
  55. package/dist/esm/logging/types.js.map +1 -0
  56. package/dist/esm/logging/utils.d.ts +7 -0
  57. package/dist/esm/logging/utils.js +21 -0
  58. package/dist/esm/logging/utils.js.map +1 -0
  59. package/dist/esm/rest/restClient.d.ts +34 -0
  60. package/dist/esm/rest/restClient.js +55 -0
  61. package/dist/esm/rest/restClient.js.map +1 -0
  62. package/dist/esm/rest/types.d.ts +24 -0
  63. package/dist/esm/rest/types.js +2 -0
  64. package/dist/esm/rest/types.js.map +1 -0
  65. package/dist/esm/tracing/traceHeader.d.ts +82 -0
  66. package/dist/esm/tracing/traceHeader.js +145 -0
  67. package/dist/esm/tracing/traceHeader.js.map +1 -0
  68. package/dist/esm/tracing/updateHeaders.d.ts +24 -0
  69. package/dist/esm/tracing/updateHeaders.js +61 -0
  70. package/dist/esm/tracing/updateHeaders.js.map +1 -0
  71. package/dist/esm/transport/alchemy.d.ts +110 -0
  72. package/dist/esm/transport/alchemy.js +164 -0
  73. package/dist/esm/transport/alchemy.js.map +1 -0
  74. package/dist/esm/transport/chainRegistry.d.ts +31 -0
  75. package/dist/esm/transport/chainRegistry.js +95 -0
  76. package/dist/esm/transport/chainRegistry.js.map +1 -0
  77. package/dist/esm/transport/connection.d.ts +20 -0
  78. package/dist/esm/transport/connection.js +2 -0
  79. package/dist/esm/transport/connection.js.map +1 -0
  80. package/dist/esm/transport/connectionSchema.d.ts +124 -0
  81. package/dist/esm/transport/connectionSchema.js +121 -0
  82. package/dist/esm/transport/connectionSchema.js.map +1 -0
  83. package/dist/esm/utils/assertNever.d.ts +8 -0
  84. package/dist/esm/utils/assertNever.js +12 -0
  85. package/dist/esm/utils/assertNever.js.map +1 -0
  86. package/dist/esm/utils/bigint.d.ts +24 -0
  87. package/dist/esm/utils/bigint.js +37 -0
  88. package/dist/esm/utils/bigint.js.map +1 -0
  89. package/dist/esm/utils/createEip1193HandlerFactory.d.ts +18 -0
  90. package/dist/esm/utils/createEip1193HandlerFactory.js +11 -0
  91. package/dist/esm/utils/createEip1193HandlerFactory.js.map +1 -0
  92. package/dist/esm/utils/headers.d.ts +7 -0
  93. package/dist/esm/utils/headers.js +29 -0
  94. package/dist/esm/utils/headers.js.map +1 -0
  95. package/dist/esm/utils/lowerAddress.d.ts +8 -0
  96. package/dist/esm/utils/lowerAddress.js +9 -0
  97. package/dist/esm/utils/lowerAddress.js.map +1 -0
  98. package/dist/esm/utils/raise.d.ts +8 -0
  99. package/dist/esm/utils/raise.js +14 -0
  100. package/dist/esm/utils/raise.js.map +1 -0
  101. package/dist/esm/utils/types.d.ts +10 -0
  102. package/dist/esm/utils/types.js +2 -0
  103. package/dist/esm/utils/types.js.map +1 -0
  104. package/dist/esm/version.d.ts +1 -0
  105. package/dist/esm/version.js +4 -0
  106. package/dist/esm/version.js.map +1 -0
  107. package/dist/types/actions/addBreadCrumb.d.ts +15 -0
  108. package/dist/types/actions/addBreadCrumb.d.ts.map +1 -0
  109. package/dist/types/chains.d.ts +235 -0
  110. package/dist/types/chains.d.ts.map +1 -0
  111. package/dist/types/errors/AccountNotFoundError.d.ts +11 -0
  112. package/dist/types/errors/AccountNotFoundError.d.ts.map +1 -0
  113. package/dist/types/errors/BaseError.d.ts +24 -0
  114. package/dist/types/errors/BaseError.d.ts.map +1 -0
  115. package/dist/types/errors/ChainNotFoundError.d.ts +12 -0
  116. package/dist/types/errors/ChainNotFoundError.d.ts.map +1 -0
  117. package/dist/types/errors/ConnectionConfigError.d.ts +14 -0
  118. package/dist/types/errors/ConnectionConfigError.d.ts.map +1 -0
  119. package/dist/types/errors/FetchError.d.ts +16 -0
  120. package/dist/types/errors/FetchError.d.ts.map +1 -0
  121. package/dist/types/errors/InvalidRequestError.d.ts +14 -0
  122. package/dist/types/errors/InvalidRequestError.d.ts.map +1 -0
  123. package/dist/types/errors/MethodUnsupportedError.d.ts +14 -0
  124. package/dist/types/errors/MethodUnsupportedError.d.ts.map +1 -0
  125. package/dist/types/errors/ServerError.d.ts +16 -0
  126. package/dist/types/errors/ServerError.d.ts.map +1 -0
  127. package/dist/types/index.d.ts +30 -0
  128. package/dist/types/index.d.ts.map +1 -0
  129. package/dist/types/logging/config.d.ts +191 -0
  130. package/dist/types/logging/config.d.ts.map +1 -0
  131. package/dist/types/logging/index.d.ts +7 -0
  132. package/dist/types/logging/index.d.ts.map +1 -0
  133. package/dist/types/logging/local.d.ts +11 -0
  134. package/dist/types/logging/local.d.ts.map +1 -0
  135. package/dist/types/logging/logger.d.ts +81 -0
  136. package/dist/types/logging/logger.d.ts.map +1 -0
  137. package/dist/types/logging/noop.d.ts +7 -0
  138. package/dist/types/logging/noop.d.ts.map +1 -0
  139. package/dist/types/logging/sinks.d.ts +91 -0
  140. package/dist/types/logging/sinks.d.ts.map +1 -0
  141. package/dist/types/logging/types.d.ts +97 -0
  142. package/dist/types/logging/types.d.ts.map +1 -0
  143. package/dist/types/logging/utils.d.ts +8 -0
  144. package/dist/types/logging/utils.d.ts.map +1 -0
  145. package/dist/types/rest/restClient.d.ts +35 -0
  146. package/dist/types/rest/restClient.d.ts.map +1 -0
  147. package/dist/types/rest/types.d.ts +25 -0
  148. package/dist/types/rest/types.d.ts.map +1 -0
  149. package/dist/types/tracing/traceHeader.d.ts +83 -0
  150. package/dist/types/tracing/traceHeader.d.ts.map +1 -0
  151. package/dist/types/tracing/updateHeaders.d.ts +25 -0
  152. package/dist/types/tracing/updateHeaders.d.ts.map +1 -0
  153. package/dist/types/transport/alchemy.d.ts +111 -0
  154. package/dist/types/transport/alchemy.d.ts.map +1 -0
  155. package/dist/types/transport/chainRegistry.d.ts +32 -0
  156. package/dist/types/transport/chainRegistry.d.ts.map +1 -0
  157. package/dist/types/transport/connection.d.ts +21 -0
  158. package/dist/types/transport/connection.d.ts.map +1 -0
  159. package/dist/types/transport/connectionSchema.d.ts +125 -0
  160. package/dist/types/transport/connectionSchema.d.ts.map +1 -0
  161. package/dist/types/utils/assertNever.d.ts +9 -0
  162. package/dist/types/utils/assertNever.d.ts.map +1 -0
  163. package/dist/types/utils/bigint.d.ts +25 -0
  164. package/dist/types/utils/bigint.d.ts.map +1 -0
  165. package/dist/types/utils/createEip1193HandlerFactory.d.ts +19 -0
  166. package/dist/types/utils/createEip1193HandlerFactory.d.ts.map +1 -0
  167. package/dist/types/utils/headers.d.ts +8 -0
  168. package/dist/types/utils/headers.d.ts.map +1 -0
  169. package/dist/types/utils/lowerAddress.d.ts +9 -0
  170. package/dist/types/utils/lowerAddress.d.ts.map +1 -0
  171. package/dist/types/utils/raise.d.ts +9 -0
  172. package/dist/types/utils/raise.d.ts.map +1 -0
  173. package/dist/types/utils/types.d.ts +11 -0
  174. package/dist/types/utils/types.d.ts.map +1 -0
  175. package/dist/types/version.d.ts +2 -0
  176. package/dist/types/version.d.ts.map +1 -0
  177. package/package.json +67 -0
  178. package/src/actions/addBreadCrumb.ts +38 -0
  179. package/src/chains.ts +118 -0
  180. package/src/errors/AccountNotFoundError.ts +16 -0
  181. package/src/errors/BaseError.ts +51 -0
  182. package/src/errors/ChainNotFoundError.ts +15 -0
  183. package/src/errors/ConnectionConfigError.ts +22 -0
  184. package/src/errors/FetchError.ts +21 -0
  185. package/src/errors/InvalidRequestError.ts +19 -0
  186. package/src/errors/MethodUnsupportedError.ts +17 -0
  187. package/src/errors/ServerError.ts +21 -0
  188. package/src/index.ts +60 -0
  189. package/src/logging/config.ts +365 -0
  190. package/src/logging/index.ts +20 -0
  191. package/src/logging/local.ts +39 -0
  192. package/src/logging/logger.ts +194 -0
  193. package/src/logging/noop.ts +13 -0
  194. package/src/logging/sinks.ts +115 -0
  195. package/src/logging/types.ts +111 -0
  196. package/src/logging/utils.ts +31 -0
  197. package/src/rest/restClient.ts +64 -0
  198. package/src/rest/types.ts +42 -0
  199. package/src/tracing/traceHeader.ts +154 -0
  200. package/src/tracing/updateHeaders.ts +66 -0
  201. package/src/transport/alchemy.ts +242 -0
  202. package/src/transport/chainRegistry.ts +115 -0
  203. package/src/transport/connection.ts +19 -0
  204. package/src/transport/connectionSchema.ts +145 -0
  205. package/src/utils/assertNever.ts +12 -0
  206. package/src/utils/bigint.ts +58 -0
  207. package/src/utils/createEip1193HandlerFactory.ts +25 -0
  208. package/src/utils/headers.ts +48 -0
  209. package/src/utils/lowerAddress.ts +10 -0
  210. package/src/utils/raise.ts +14 -0
  211. package/src/utils/types.ts +14 -0
  212. package/src/version.ts +3 -0
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Log level constants for controlling diagnostics output.
3
+ * Lower numeric values indicate higher priority (more restrictive filtering).
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { LogLevel, setGlobalLoggerConfig } from "@alchemy/common";
8
+ *
9
+ * setGlobalLoggerConfig({ level: LogLevel.DEBUG });
10
+ * ```
11
+ */
12
+ export const LogLevel = {
13
+ /** Critical errors only */
14
+ ERROR: 0,
15
+ /** Warnings and errors */
16
+ WARN: 1,
17
+ /** Informational messages, warnings, and errors */
18
+ INFO: 2,
19
+ /** Debug messages and all above */
20
+ DEBUG: 3,
21
+ /** All messages including verbose details */
22
+ VERBOSE: 4,
23
+ };
24
+ let globalConfig;
25
+ function defaultConfig() {
26
+ const env = (typeof process !== "undefined" && process.env) || {};
27
+ // Default level: INFO in dev, ERROR in prod
28
+ const isDev = env.NODE_ENV === "development";
29
+ const defaultLevel = isDev ? LogLevel.INFO : LogLevel.ERROR;
30
+ let level = defaultLevel;
31
+ const envLevel = env.ALCHEMY_LOG_LEVEL;
32
+ if (envLevel) {
33
+ const normalized = envLevel.toUpperCase();
34
+ if (normalized in LogLevel) {
35
+ level = LogLevel[normalized];
36
+ }
37
+ else {
38
+ console.warn(`[alchemy/common] Invalid ALCHEMY_LOG_LEVEL value: "${envLevel}". Expected one of: ERROR, WARN, INFO, DEBUG, VERBOSE. Using default: ${isDev ? "INFO" : "ERROR"}`);
39
+ }
40
+ }
41
+ return {
42
+ level,
43
+ redact: {
44
+ keys: (key) => /^(authorization|apiKey|jwt|privateKey|secret|password)$/i.test(key),
45
+ replacer: () => "[REDACTED]",
46
+ },
47
+ sinks: [consoleSink],
48
+ disableTelemetryHeaders: false,
49
+ enabledNamespaces: [],
50
+ };
51
+ }
52
+ /**
53
+ * Gets the current global logger configuration.
54
+ * If not yet initialized, returns default configuration based on environment.
55
+ *
56
+ * @returns {Required<DiagnosticsConfig>} The current global configuration with all fields populated
57
+ * @example
58
+ * ```ts
59
+ * import { getGlobalLoggerConfig } from "@alchemy/common";
60
+ *
61
+ * const config = getGlobalLoggerConfig();
62
+ * console.log("Current log level:", config.level);
63
+ * ```
64
+ */
65
+ export function getGlobalLoggerConfig() {
66
+ globalConfig ?? (globalConfig = defaultConfig());
67
+ return globalConfig;
68
+ }
69
+ /**
70
+ * Sets the global logger configuration.
71
+ * Partial configuration is supported - unspecified fields retain their current values.
72
+ *
73
+ * Configuration precedence (highest to lowest):
74
+ * 1. Explicit setGlobalLoggerConfig calls
75
+ * 2. ALCHEMY_LOG_LEVEL environment variable
76
+ * 3. Defaults (INFO in dev, ERROR in prod)
77
+ *
78
+ * @param {DiagnosticsConfig} cfg - Partial configuration to apply
79
+ * @example
80
+ * ```ts
81
+ * import { setGlobalLoggerConfig, LogLevel } from "@alchemy/common";
82
+ *
83
+ * // Set log level only
84
+ * setGlobalLoggerConfig({ level: LogLevel.DEBUG });
85
+ *
86
+ * // Add custom sink
87
+ * setGlobalLoggerConfig({
88
+ * sinks: [(entry) => console.log(JSON.stringify(entry))]
89
+ * });
90
+ *
91
+ * // Filter to specific namespaces
92
+ * setGlobalLoggerConfig({
93
+ * enabledNamespaces: ["aa-infra", "wallet-apis"]
94
+ * });
95
+ * ```
96
+ */
97
+ export function setGlobalLoggerConfig(cfg) {
98
+ const current = getGlobalLoggerConfig();
99
+ globalConfig = {
100
+ level: cfg.level ?? current.level,
101
+ redact: cfg.redact ?? current.redact,
102
+ sinks: cfg.sinks ?? current.sinks,
103
+ disableTelemetryHeaders: cfg.disableTelemetryHeaders ?? current.disableTelemetryHeaders,
104
+ enabledNamespaces: "enabledNamespaces" in cfg
105
+ ? (cfg.enabledNamespaces ?? [])
106
+ : current.enabledNamespaces,
107
+ };
108
+ }
109
+ /**
110
+ * Checks if a given log level is enabled based on current global configuration.
111
+ * Used internally by logger to short-circuit disabled log statements.
112
+ *
113
+ * @param {LogLevel} level - The log level to check
114
+ * @returns {boolean} True if the level is enabled (will be logged), false otherwise
115
+ * @example
116
+ * ```ts
117
+ * import { isLevelEnabled, LogLevel } from "@alchemy/common";
118
+ *
119
+ * if (isLevelEnabled(LogLevel.DEBUG)) {
120
+ * // Perform expensive debug computation
121
+ * }
122
+ * ```
123
+ */
124
+ export function isLevelEnabled(level) {
125
+ return level <= getGlobalLoggerConfig().level;
126
+ }
127
+ /**
128
+ * Checks if a given namespace is enabled based on current global configuration.
129
+ * Used internally by logger to short-circuit logs from disabled namespaces.
130
+ *
131
+ * @param {string | undefined} namespace - The namespace to check
132
+ * @returns {boolean} True if the namespace is enabled (will be logged), false otherwise
133
+ * @example
134
+ * ```ts
135
+ * import { isNamespaceEnabled } from "@alchemy/common";
136
+ *
137
+ * if (isNamespaceEnabled("aa-infra")) {
138
+ * // This namespace is enabled
139
+ * }
140
+ * ```
141
+ */
142
+ export function isNamespaceEnabled(namespace) {
143
+ const { enabledNamespaces } = getGlobalLoggerConfig();
144
+ // If no filter is set or it's an empty array, all namespaces are enabled
145
+ if (!enabledNamespaces || enabledNamespaces.length === 0) {
146
+ return true;
147
+ }
148
+ // If namespace is undefined, disable it (only named namespaces can be filtered)
149
+ if (namespace === undefined) {
150
+ return false;
151
+ }
152
+ // Check if the namespace is in the enabled list
153
+ return enabledNamespaces.includes(namespace);
154
+ }
155
+ /**
156
+ * Redacts sensitive keys in an object based on global redaction configuration.
157
+ * Performs deep redaction by recursively processing nested objects and arrays.
158
+ * Default redaction includes: authorization, apiKey, jwt, privateKey, secret, password.
159
+ *
160
+ * @param {Record<string, unknown> | undefined} obj - The object to redact
161
+ * @returns {Record<string, unknown> | undefined} A new object with sensitive values redacted, or undefined if input was undefined
162
+ * @example
163
+ * ```ts
164
+ * import { redactObject } from "@alchemy/common";
165
+ *
166
+ * const data = {
167
+ * apiKey: "secret123",
168
+ * userId: "user-456",
169
+ * nested: { secret: "hidden" }
170
+ * };
171
+ *
172
+ * const redacted = redactObject(data);
173
+ * // { apiKey: "[REDACTED]", userId: "user-456", nested: { secret: "[REDACTED]" } }
174
+ * ```
175
+ */
176
+ export function redactObject(obj) {
177
+ if (!obj)
178
+ return obj;
179
+ const { keys, replacer } = getGlobalLoggerConfig().redact;
180
+ const out = {};
181
+ for (const [k, v] of Object.entries(obj)) {
182
+ if (keys && keys(k)) {
183
+ out[k] = replacer ? replacer(v, k) : "[REDACTED]";
184
+ }
185
+ else if (Array.isArray(v)) {
186
+ // Recursively redact array elements
187
+ out[k] = v.map((item) => item != null && typeof item === "object"
188
+ ? redactObject(item)
189
+ : item);
190
+ }
191
+ else if (v != null && typeof v === "object") {
192
+ // Recursively redact nested objects
193
+ out[k] = redactObject(v);
194
+ }
195
+ else {
196
+ out[k] = v;
197
+ }
198
+ }
199
+ return out;
200
+ }
201
+ /**
202
+ * Format timestamp as HH:MM:SS.mmm
203
+ *
204
+ * @param {number} ts - Timestamp in milliseconds since epoch
205
+ * @returns {string} Formatted timestamp string
206
+ */
207
+ function formatTimestamp(ts) {
208
+ const date = new Date(ts);
209
+ const hours = String(date.getHours()).padStart(2, "0");
210
+ const minutes = String(date.getMinutes()).padStart(2, "0");
211
+ const seconds = String(date.getSeconds()).padStart(2, "0");
212
+ const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
213
+ return `${hours}:${minutes}:${seconds}.${milliseconds}`;
214
+ }
215
+ /**
216
+ * Format data as a JSON object for console output.
217
+ * Filters out context fields (package, version) to avoid duplication.
218
+ * Handles BigInt values by converting them to strings.
219
+ *
220
+ * @param {Record<string, unknown> | undefined} data - The data object to format
221
+ * @returns {string} Formatted JSON string with leading space, or empty string if no data
222
+ */
223
+ function formatData(data) {
224
+ if (!data || Object.keys(data).length === 0)
225
+ return "";
226
+ // Filter out context fields (package, version) and create clean object
227
+ const filtered = Object.entries(data)
228
+ .filter(([key]) => key !== "package" && key !== "version")
229
+ .reduce((acc, [key, value]) => {
230
+ acc[key] = value;
231
+ return acc;
232
+ }, {});
233
+ if (Object.keys(filtered).length === 0)
234
+ return "";
235
+ return (" " +
236
+ JSON.stringify(filtered, (_key, value) => typeof value === "bigint" ? value.toString() : value));
237
+ }
238
+ /**
239
+ * Default console sink for diagnostics logging.
240
+ * Routes log entries to appropriate console methods based on log level.
241
+ *
242
+ * Format: [HH:MM:SS.mmm] [package@version] message {data}
243
+ *
244
+ * @param {LogEntry} entry - The log entry to output
245
+ * @example
246
+ * ```ts
247
+ * import { consoleSink, setGlobalLoggerConfig } from "@alchemy/common";
248
+ *
249
+ * // Console sink is used by default, but can be explicitly configured
250
+ * setGlobalLoggerConfig({
251
+ * sinks: [consoleSink]
252
+ * });
253
+ * ```
254
+ */
255
+ export function consoleSink(entry) {
256
+ const { ts, level, message, data, context } = entry;
257
+ const timestamp = formatTimestamp(ts);
258
+ const prefix = `[${timestamp}] [${context.package}@${context.version}]`;
259
+ const dataStr = formatData(data);
260
+ const output = `${prefix} ${message}${dataStr}`;
261
+ switch (level) {
262
+ case LogLevel.ERROR:
263
+ console.error(output);
264
+ break;
265
+ case LogLevel.WARN:
266
+ console.warn(output);
267
+ break;
268
+ case LogLevel.INFO:
269
+ console.info(output);
270
+ break;
271
+ case LogLevel.DEBUG:
272
+ console.debug(output);
273
+ break;
274
+ case LogLevel.VERBOSE:
275
+ console.log(output);
276
+ break;
277
+ }
278
+ }
279
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/logging/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,2BAA2B;IAC3B,KAAK,EAAE,CAAC;IACR,0BAA0B;IAC1B,IAAI,EAAE,CAAC;IACP,mDAAmD;IACnD,IAAI,EAAE,CAAC;IACP,mCAAmC;IACnC,KAAK,EAAE,CAAC;IACR,6CAA6C;IAC7C,OAAO,EAAE,CAAC;CACF,CAAC;AA4DX,IAAI,YAAqD,CAAC;AAE1D,SAAS,aAAa;IACpB,MAAM,GAAG,GAAG,CAAC,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAElE,4CAA4C;IAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IAC7C,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;IAE5D,IAAI,KAAK,GAAa,YAAY,CAAC;IACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,iBAAiB,CAAC;IACvC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,EAA2B,CAAC;QACnE,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;YAC3B,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CACV,sDAAsD,QAAQ,yEAAyE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAClK,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK;QACL,MAAM,EAAE;YACN,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CACpB,0DAA0D,CAAC,IAAI,CAAC,GAAG,CAAC;YACtE,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY;SAC7B;QACD,KAAK,EAAE,CAAC,WAAW,CAAC;QACpB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB;IACnC,YAAY,KAAZ,YAAY,GAAK,aAAa,EAAE,EAAC;IACjC,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAsB;IAC1D,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;IACxC,YAAY,GAAG;QACb,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QACjC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACpC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;QACjC,uBAAuB,EACrB,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC,uBAAuB;QAChE,iBAAiB,EACf,mBAAmB,IAAI,GAAG;YACxB,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC;YAC/B,CAAC,CAAC,OAAO,CAAC,iBAAiB;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,KAAe;IAC5C,OAAO,KAAK,IAAI,qBAAqB,EAAE,CAAC,KAAK,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAA6B;IAC9D,MAAM,EAAE,iBAAiB,EAAE,GAAG,qBAAqB,EAAE,CAAC;IAEtD,yEAAyE;IACzE,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gFAAgF;IAChF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gDAAgD;IAChD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAwC;IAExC,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,qBAAqB,EAAE,CAAC,MAAM,CAAC;IAC1D,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpB,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;QACpD,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,oCAAoC;YACpC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACtB,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;gBACtC,CAAC,CAAC,YAAY,CAAC,IAA+B,CAAC;gBAC/C,CAAC,CAAC,IAAI,CACT,CAAC;QACJ,CAAC;aAAM,IAAI,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC9C,oCAAoC;YACpC,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAA4B,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,EAAU;IACjC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACrE,OAAO,GAAG,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,IAAyC;IAC3D,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvD,uEAAuE;IACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,CAAC;SACzD,MAAM,CACL,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACpB,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACjB,OAAO,GAAG,CAAC;IACb,CAAC,EACD,EAA6B,CAC9B,CAAC;IAEJ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElD,OAAO,CACL,GAAG;QACH,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACvC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CACrD,CACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACpD,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,SAAS,MAAM,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC;IACxE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;IAEhD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ,CAAC,KAAK;YACjB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM;QACR,KAAK,QAAQ,CAAC,IAAI;YAChB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM;QACR,KAAK,QAAQ,CAAC,IAAI;YAChB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM;QACR,KAAK,QAAQ,CAAC,KAAK;YACjB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM;QACR,KAAK,QAAQ,CAAC,OAAO;YACnB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpB,MAAM;IACV,CAAC;AACH,CAAC","sourcesContent":["/**\n * Log level constants for controlling diagnostics output.\n * Lower numeric values indicate higher priority (more restrictive filtering).\n *\n * @example\n * ```ts\n * import { LogLevel, setGlobalLoggerConfig } from \"@alchemy/common\";\n *\n * setGlobalLoggerConfig({ level: LogLevel.DEBUG });\n * ```\n */\nexport const LogLevel = {\n /** Critical errors only */\n ERROR: 0,\n /** Warnings and errors */\n WARN: 1,\n /** Informational messages, warnings, and errors */\n INFO: 2,\n /** Debug messages and all above */\n DEBUG: 3,\n /** All messages including verbose details */\n VERBOSE: 4,\n} as const;\n\n// eslint-disable-next-line @typescript-eslint/no-redeclare\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\n/**\n * Configuration for redacting sensitive data in log output.\n */\nexport type RedactConfig = {\n /** Predicate function to test if a key should be redacted. Returns true if the key should be redacted. */\n keys?: (key: string) => boolean;\n /** Function to replace redacted values. Defaults to returning \"[REDACTED]\". */\n replacer?: (value: unknown, key?: string) => unknown;\n};\n\n/**\n * Configuration for the diagnostics logging system.\n * All properties are optional and will use sensible defaults if not provided.\n */\nexport type DiagnosticsConfig = {\n /** Minimum log level to emit. Defaults to INFO in development, ERROR in production. */\n level?: LogLevel;\n /** Configuration for redacting sensitive data in logs. */\n redact?: RedactConfig;\n /** Array of sink functions to receive log entries. Defaults to console sink. */\n sinks?: Array<(entry: LogEntry) => void>;\n /** Disable telemetry headers in requests. Defaults to false. */\n disableTelemetryHeaders?: boolean;\n /** Array of exact namespace strings to allow. If undefined or empty, all namespaces are enabled. Example: [\"aa-infra\", \"wallet-apis\"] */\n enabledNamespaces?: string[];\n};\n\n/**\n * Base context attached to all log entries from a logger instance.\n */\nexport type LoggerContextBase = {\n /** Package name (e.g., \"@alchemy/aa-infra\") */\n package: string;\n /** Package version (e.g., \"1.0.0\") */\n version: string;\n};\n\n/**\n * A single log entry emitted by the diagnostics logger.\n */\nexport type LogEntry = {\n /** Timestamp in milliseconds since epoch (Date.now()) */\n ts: number;\n /** Log level of this entry */\n level: LogLevel;\n /** Optional namespace for filtering/grouping (e.g., \"aa-infra\", \"wallet-apis\") */\n namespace: string | undefined;\n /** The log message */\n message: string;\n /** Optional structured data attached to this log entry */\n data?: Record<string, unknown>;\n /** Package context (name and version) */\n context: LoggerContextBase;\n};\n\nlet globalConfig: Required<DiagnosticsConfig> | undefined;\n\nfunction defaultConfig(): Required<DiagnosticsConfig> {\n const env = (typeof process !== \"undefined\" && process.env) || {};\n\n // Default level: INFO in dev, ERROR in prod\n const isDev = env.NODE_ENV === \"development\";\n const defaultLevel = isDev ? LogLevel.INFO : LogLevel.ERROR;\n\n let level: LogLevel = defaultLevel;\n const envLevel = env.ALCHEMY_LOG_LEVEL;\n if (envLevel) {\n const normalized = envLevel.toUpperCase() as keyof typeof LogLevel;\n if (normalized in LogLevel) {\n level = LogLevel[normalized];\n } else {\n console.warn(\n `[alchemy/common] Invalid ALCHEMY_LOG_LEVEL value: \"${envLevel}\". Expected one of: ERROR, WARN, INFO, DEBUG, VERBOSE. Using default: ${isDev ? \"INFO\" : \"ERROR\"}`,\n );\n }\n }\n\n return {\n level,\n redact: {\n keys: (key: string) =>\n /^(authorization|apiKey|jwt|privateKey|secret|password)$/i.test(key),\n replacer: () => \"[REDACTED]\",\n },\n sinks: [consoleSink],\n disableTelemetryHeaders: false,\n enabledNamespaces: [],\n };\n}\n\n/**\n * Gets the current global logger configuration.\n * If not yet initialized, returns default configuration based on environment.\n *\n * @returns {Required<DiagnosticsConfig>} The current global configuration with all fields populated\n * @example\n * ```ts\n * import { getGlobalLoggerConfig } from \"@alchemy/common\";\n *\n * const config = getGlobalLoggerConfig();\n * console.log(\"Current log level:\", config.level);\n * ```\n */\nexport function getGlobalLoggerConfig(): Required<DiagnosticsConfig> {\n globalConfig ??= defaultConfig();\n return globalConfig;\n}\n\n/**\n * Sets the global logger configuration.\n * Partial configuration is supported - unspecified fields retain their current values.\n *\n * Configuration precedence (highest to lowest):\n * 1. Explicit setGlobalLoggerConfig calls\n * 2. ALCHEMY_LOG_LEVEL environment variable\n * 3. Defaults (INFO in dev, ERROR in prod)\n *\n * @param {DiagnosticsConfig} cfg - Partial configuration to apply\n * @example\n * ```ts\n * import { setGlobalLoggerConfig, LogLevel } from \"@alchemy/common\";\n *\n * // Set log level only\n * setGlobalLoggerConfig({ level: LogLevel.DEBUG });\n *\n * // Add custom sink\n * setGlobalLoggerConfig({\n * sinks: [(entry) => console.log(JSON.stringify(entry))]\n * });\n *\n * // Filter to specific namespaces\n * setGlobalLoggerConfig({\n * enabledNamespaces: [\"aa-infra\", \"wallet-apis\"]\n * });\n * ```\n */\nexport function setGlobalLoggerConfig(cfg: DiagnosticsConfig): void {\n const current = getGlobalLoggerConfig();\n globalConfig = {\n level: cfg.level ?? current.level,\n redact: cfg.redact ?? current.redact,\n sinks: cfg.sinks ?? current.sinks,\n disableTelemetryHeaders:\n cfg.disableTelemetryHeaders ?? current.disableTelemetryHeaders,\n enabledNamespaces:\n \"enabledNamespaces\" in cfg\n ? (cfg.enabledNamespaces ?? [])\n : current.enabledNamespaces,\n };\n}\n\n/**\n * Checks if a given log level is enabled based on current global configuration.\n * Used internally by logger to short-circuit disabled log statements.\n *\n * @param {LogLevel} level - The log level to check\n * @returns {boolean} True if the level is enabled (will be logged), false otherwise\n * @example\n * ```ts\n * import { isLevelEnabled, LogLevel } from \"@alchemy/common\";\n *\n * if (isLevelEnabled(LogLevel.DEBUG)) {\n * // Perform expensive debug computation\n * }\n * ```\n */\nexport function isLevelEnabled(level: LogLevel): boolean {\n return level <= getGlobalLoggerConfig().level;\n}\n\n/**\n * Checks if a given namespace is enabled based on current global configuration.\n * Used internally by logger to short-circuit logs from disabled namespaces.\n *\n * @param {string | undefined} namespace - The namespace to check\n * @returns {boolean} True if the namespace is enabled (will be logged), false otherwise\n * @example\n * ```ts\n * import { isNamespaceEnabled } from \"@alchemy/common\";\n *\n * if (isNamespaceEnabled(\"aa-infra\")) {\n * // This namespace is enabled\n * }\n * ```\n */\nexport function isNamespaceEnabled(namespace: string | undefined): boolean {\n const { enabledNamespaces } = getGlobalLoggerConfig();\n\n // If no filter is set or it's an empty array, all namespaces are enabled\n if (!enabledNamespaces || enabledNamespaces.length === 0) {\n return true;\n }\n\n // If namespace is undefined, disable it (only named namespaces can be filtered)\n if (namespace === undefined) {\n return false;\n }\n\n // Check if the namespace is in the enabled list\n return enabledNamespaces.includes(namespace);\n}\n\n/**\n * Redacts sensitive keys in an object based on global redaction configuration.\n * Performs deep redaction by recursively processing nested objects and arrays.\n * Default redaction includes: authorization, apiKey, jwt, privateKey, secret, password.\n *\n * @param {Record<string, unknown> | undefined} obj - The object to redact\n * @returns {Record<string, unknown> | undefined} A new object with sensitive values redacted, or undefined if input was undefined\n * @example\n * ```ts\n * import { redactObject } from \"@alchemy/common\";\n *\n * const data = {\n * apiKey: \"secret123\",\n * userId: \"user-456\",\n * nested: { secret: \"hidden\" }\n * };\n *\n * const redacted = redactObject(data);\n * // { apiKey: \"[REDACTED]\", userId: \"user-456\", nested: { secret: \"[REDACTED]\" } }\n * ```\n */\nexport function redactObject(\n obj: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!obj) return obj;\n const { keys, replacer } = getGlobalLoggerConfig().redact;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (keys && keys(k)) {\n out[k] = replacer ? replacer(v, k) : \"[REDACTED]\";\n } else if (Array.isArray(v)) {\n // Recursively redact array elements\n out[k] = v.map((item) =>\n item != null && typeof item === \"object\"\n ? redactObject(item as Record<string, unknown>)\n : item,\n );\n } else if (v != null && typeof v === \"object\") {\n // Recursively redact nested objects\n out[k] = redactObject(v as Record<string, unknown>);\n } else {\n out[k] = v;\n }\n }\n return out;\n}\n\n/**\n * Format timestamp as HH:MM:SS.mmm\n *\n * @param {number} ts - Timestamp in milliseconds since epoch\n * @returns {string} Formatted timestamp string\n */\nfunction formatTimestamp(ts: number): string {\n const date = new Date(ts);\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n const milliseconds = String(date.getMilliseconds()).padStart(3, \"0\");\n return `${hours}:${minutes}:${seconds}.${milliseconds}`;\n}\n\n/**\n * Format data as a JSON object for console output.\n * Filters out context fields (package, version) to avoid duplication.\n * Handles BigInt values by converting them to strings.\n *\n * @param {Record<string, unknown> | undefined} data - The data object to format\n * @returns {string} Formatted JSON string with leading space, or empty string if no data\n */\nfunction formatData(data: Record<string, unknown> | undefined): string {\n if (!data || Object.keys(data).length === 0) return \"\";\n\n // Filter out context fields (package, version) and create clean object\n const filtered = Object.entries(data)\n .filter(([key]) => key !== \"package\" && key !== \"version\")\n .reduce(\n (acc, [key, value]) => {\n acc[key] = value;\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n if (Object.keys(filtered).length === 0) return \"\";\n\n return (\n \" \" +\n JSON.stringify(filtered, (_key, value) =>\n typeof value === \"bigint\" ? value.toString() : value,\n )\n );\n}\n\n/**\n * Default console sink for diagnostics logging.\n * Routes log entries to appropriate console methods based on log level.\n *\n * Format: [HH:MM:SS.mmm] [package@version] message {data}\n *\n * @param {LogEntry} entry - The log entry to output\n * @example\n * ```ts\n * import { consoleSink, setGlobalLoggerConfig } from \"@alchemy/common\";\n *\n * // Console sink is used by default, but can be explicitly configured\n * setGlobalLoggerConfig({\n * sinks: [consoleSink]\n * });\n * ```\n */\nexport function consoleSink(entry: LogEntry): void {\n const { ts, level, message, data, context } = entry;\n const timestamp = formatTimestamp(ts);\n const prefix = `[${timestamp}] [${context.package}@${context.version}]`;\n const dataStr = formatData(data);\n const output = `${prefix} ${message}${dataStr}`;\n\n switch (level) {\n case LogLevel.ERROR:\n console.error(output);\n break;\n case LogLevel.WARN:\n console.warn(output);\n break;\n case LogLevel.INFO:\n console.info(output);\n break;\n case LogLevel.DEBUG:\n console.debug(output);\n break;\n case LogLevel.VERBOSE:\n console.log(output);\n break;\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { createLogger, LogLevel, consoleSink } from "./logger.js";
2
+ export type { DiagnosticsLogger } from "./logger.js";
3
+ export { setGlobalLoggerConfig, getGlobalLoggerConfig, isLevelEnabled, isNamespaceEnabled, redactObject, } from "./config.js";
4
+ export type { LogEntry, DiagnosticsConfig, RedactConfig, LoggerContextBase, } from "./config.js";
5
+ export { InMemorySink } from "./sinks.js";
6
+ export type * from "./types.js";
@@ -0,0 +1,5 @@
1
+ export { createLogger, LogLevel, consoleSink } from "./logger.js";
2
+ export { setGlobalLoggerConfig, getGlobalLoggerConfig, isLevelEnabled, isNamespaceEnabled, redactObject, } from "./config.js";
3
+ // Testing utilities
4
+ export { InMemorySink } from "./sinks.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,YAAY,GACb,MAAM,aAAa,CAAC;AAQrB,oBAAoB;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { createLogger, LogLevel, consoleSink } from \"./logger.js\";\nexport type { DiagnosticsLogger } from \"./logger.js\";\nexport {\n setGlobalLoggerConfig,\n getGlobalLoggerConfig,\n isLevelEnabled,\n isNamespaceEnabled,\n redactObject,\n} from \"./config.js\";\nexport type {\n LogEntry,\n DiagnosticsConfig,\n RedactConfig,\n LoggerContextBase,\n} from \"./config.js\";\n\n// Testing utilities\nexport { InMemorySink } from \"./sinks.js\";\n\nexport type * from \"./types.js\";\n"]}
@@ -0,0 +1,10 @@
1
+ import type { EventsSchema, InnerLogger, LoggerContext } from "./types.js";
2
+ /**
3
+ * Creates a local-only logger that outputs events to the console in development mode.
4
+ * This logger does not send data to external services and is safe for all environments.
5
+ *
6
+ * @template Schema - The events schema defining allowed events and their data structures
7
+ * @param {LoggerContext} context - Context information to attach to all events
8
+ * @returns {InnerLogger<Schema>} A logger instance that logs to console in dev mode
9
+ */
10
+ export declare function createLocalLogger<Schema extends EventsSchema = []>(context: LoggerContext): InnerLogger<Schema>;
@@ -0,0 +1,35 @@
1
+ import { isClientDevMode } from "./utils.js";
2
+ /**
3
+ * Creates a local-only logger that outputs events to the console in development mode.
4
+ * This logger does not send data to external services and is safe for all environments.
5
+ *
6
+ * @template Schema - The events schema defining allowed events and their data structures
7
+ * @param {LoggerContext} context - Context information to attach to all events
8
+ * @returns {InnerLogger<Schema>} A logger instance that logs to console in dev mode
9
+ */
10
+ export function createLocalLogger(context) {
11
+ const isDev = isClientDevMode();
12
+ // Generate a simple anonymous ID for local logging
13
+ const anonId = `local-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
14
+ return {
15
+ _internal: {
16
+ ready: Promise.resolve(),
17
+ anonId,
18
+ },
19
+ trackEvent: async ({ name, data }) => {
20
+ if (isDev) {
21
+ try {
22
+ console.log(`[${context.package}] Event: ${name}`, {
23
+ ...data,
24
+ ...context,
25
+ timestamp: new Date().toISOString(),
26
+ });
27
+ }
28
+ catch {
29
+ // Silently ignore console logging errors
30
+ }
31
+ }
32
+ },
33
+ };
34
+ }
35
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/logging/local.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAsB;IAEtB,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAEhC,mDAAmD;IACnD,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAEnF,OAAO;QACL,SAAS,EAAE;YACT,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;YACxB,MAAM;SACP;QACD,UAAU,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;YACnC,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC;oBACH,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,YAAY,IAAI,EAAE,EAAE;wBACjD,GAAG,IAAI;wBACP,GAAG,OAAO;wBACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACpC,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,yCAAyC;gBAC3C,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { EventsSchema, InnerLogger, LoggerContext } from \"./types.js\";\nimport { isClientDevMode } from \"./utils.js\";\n\n/**\n * Creates a local-only logger that outputs events to the console in development mode.\n * This logger does not send data to external services and is safe for all environments.\n *\n * @template Schema - The events schema defining allowed events and their data structures\n * @param {LoggerContext} context - Context information to attach to all events\n * @returns {InnerLogger<Schema>} A logger instance that logs to console in dev mode\n */\nexport function createLocalLogger<Schema extends EventsSchema = []>(\n context: LoggerContext,\n): InnerLogger<Schema> {\n const isDev = isClientDevMode();\n\n // Generate a simple anonymous ID for local logging\n const anonId = `local-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n\n return {\n _internal: {\n ready: Promise.resolve(),\n anonId,\n },\n trackEvent: async ({ name, data }) => {\n if (isDev) {\n try {\n console.log(`[${context.package}] Event: ${name}`, {\n ...data,\n ...context,\n timestamp: new Date().toISOString(),\n });\n } catch {\n // Silently ignore console logging errors\n }\n }\n },\n };\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { LogLevel, consoleSink, type LoggerContextBase } from "./config.js";
2
+ /**
3
+ * Lazy message supplier function for expensive log computations.
4
+ * Only evaluated when the log level is enabled.
5
+ *
6
+ * @returns {[string, Record<string, unknown>?]} Tuple of [message, optional data]
7
+ */
8
+ type MessageSupplier = () => [string, Record<string, unknown>?];
9
+ /**
10
+ * Diagnostics logger instance for developer-facing debugging output.
11
+ * Provides level-based logging (error, warn, info, debug, verbose) with structured data.
12
+ */
13
+ export type DiagnosticsLogger = {
14
+ /** Log debug messages for development and troubleshooting */
15
+ debug: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;
16
+ /** Log informational messages for key operations */
17
+ info: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;
18
+ /** Log warnings for recoverable issues */
19
+ warn: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;
20
+ /** Log errors for failures */
21
+ error: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;
22
+ /** Log verbose details for very detailed tracing */
23
+ verbose: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;
24
+ /** Create a child logger with additional context merged into all log entries */
25
+ withContext: (extra: Record<string, unknown>) => DiagnosticsLogger;
26
+ /**
27
+ * Wrap a function to measure and log its execution time at DEBUG level.
28
+ * Supports both synchronous and asynchronous functions.
29
+ *
30
+ * @template TArgs - Function argument types
31
+ * @template TRet - Function return type
32
+ * @param name - Name for profiling logs
33
+ * @param func - Function to profile
34
+ * @returns Wrapped function that logs execution time
35
+ */
36
+ profiled<TArgs extends any[], TRet>(name: string, func: (...args: TArgs) => TRet): (...args: TArgs) => TRet;
37
+ };
38
+ /**
39
+ * Parameters for creating a diagnostics logger instance.
40
+ */
41
+ export type CreateLoggerParams = LoggerContextBase & {
42
+ /** Optional namespace for filtering/grouping (e.g., "aa-infra", "wallet-apis") */
43
+ namespace?: string;
44
+ /** Optional base context merged into all log entries from this logger */
45
+ baseContext?: Record<string, unknown>;
46
+ };
47
+ /**
48
+ * Creates a diagnostics logger instance for a package.
49
+ * Loggers emit structured log entries to configured sinks (default: console).
50
+ *
51
+ * All log methods support:
52
+ * - String messages: `logger.info("message", { data })`
53
+ * - Lazy suppliers: `logger.debug(() => ["expensive", computeData()])` (only evaluated when level enabled)
54
+ *
55
+ * @param {CreateLoggerParams} params - Logger configuration
56
+ * @returns {DiagnosticsLogger} A logger instance
57
+ * @example
58
+ * ```ts
59
+ * import { createLogger } from "@alchemy/common";
60
+ *
61
+ * const logger = createLogger({
62
+ * package: "@alchemy/aa-infra",
63
+ * version: "1.0.0",
64
+ * namespace: "aa-infra"
65
+ * });
66
+ *
67
+ * logger.info("processing request", { chainId: 1 });
68
+ * logger.debug("detailed state", { userOp: op });
69
+ *
70
+ * // Child logger with additional context
71
+ * const childLogger = logger.withContext({ requestId: "123" });
72
+ * childLogger.info("step complete"); // includes requestId in all logs
73
+ *
74
+ * // Profile a function
75
+ * const sendWithProfiling = logger.profiled("sendUserOp", sendUserOp);
76
+ * await sendWithProfiling(op); // logs execution time at DEBUG level
77
+ * ```
78
+ */
79
+ export declare function createLogger(params: CreateLoggerParams): DiagnosticsLogger;
80
+ export { LogLevel, consoleSink };
@@ -0,0 +1,111 @@
1
+ import { LogLevel, consoleSink, getGlobalLoggerConfig, isLevelEnabled, isNamespaceEnabled, redactObject, } from "./config.js";
2
+ /**
3
+ * Creates a diagnostics logger instance for a package.
4
+ * Loggers emit structured log entries to configured sinks (default: console).
5
+ *
6
+ * All log methods support:
7
+ * - String messages: `logger.info("message", { data })`
8
+ * - Lazy suppliers: `logger.debug(() => ["expensive", computeData()])` (only evaluated when level enabled)
9
+ *
10
+ * @param {CreateLoggerParams} params - Logger configuration
11
+ * @returns {DiagnosticsLogger} A logger instance
12
+ * @example
13
+ * ```ts
14
+ * import { createLogger } from "@alchemy/common";
15
+ *
16
+ * const logger = createLogger({
17
+ * package: "@alchemy/aa-infra",
18
+ * version: "1.0.0",
19
+ * namespace: "aa-infra"
20
+ * });
21
+ *
22
+ * logger.info("processing request", { chainId: 1 });
23
+ * logger.debug("detailed state", { userOp: op });
24
+ *
25
+ * // Child logger with additional context
26
+ * const childLogger = logger.withContext({ requestId: "123" });
27
+ * childLogger.info("step complete"); // includes requestId in all logs
28
+ *
29
+ * // Profile a function
30
+ * const sendWithProfiling = logger.profiled("sendUserOp", sendUserOp);
31
+ * await sendWithProfiling(op); // logs execution time at DEBUG level
32
+ * ```
33
+ */
34
+ export function createLogger(params) {
35
+ const { namespace, baseContext, ...ctx } = params;
36
+ function emit(level, msgOrFn, data) {
37
+ if (!isLevelEnabled(level))
38
+ return;
39
+ if (!isNamespaceEnabled(namespace))
40
+ return;
41
+ let message;
42
+ let payload = data;
43
+ if (typeof msgOrFn === "function") {
44
+ const [m, d] = msgOrFn();
45
+ message = m;
46
+ payload = d ?? data;
47
+ }
48
+ else {
49
+ message = msgOrFn;
50
+ }
51
+ const entry = {
52
+ ts: Date.now(),
53
+ level,
54
+ namespace,
55
+ message,
56
+ data: redactObject({ ...(baseContext || {}), ...(payload || {}) }),
57
+ context: ctx,
58
+ };
59
+ for (const sink of getGlobalLoggerConfig().sinks) {
60
+ try {
61
+ sink(entry);
62
+ }
63
+ catch (e) {
64
+ // Silently continue if a sink throws - don't break other sinks
65
+ }
66
+ }
67
+ }
68
+ const logger = {
69
+ debug: (m, d) => emit(LogLevel.DEBUG, m, d),
70
+ info: (m, d) => emit(LogLevel.INFO, m, d),
71
+ warn: (m, d) => emit(LogLevel.WARN, m, d),
72
+ error: (m, d) => emit(LogLevel.ERROR, m, d),
73
+ verbose: (m, d) => emit(LogLevel.VERBOSE, m, d),
74
+ withContext(extra) {
75
+ return createLogger({
76
+ ...params,
77
+ baseContext: { ...(baseContext || {}), ...extra },
78
+ });
79
+ },
80
+ profiled(name, func) {
81
+ return function profiledWrapper(...args) {
82
+ const start = Date.now();
83
+ const result = func.apply(this, args);
84
+ const finish = (ok) => {
85
+ const dur = Date.now() - start;
86
+ const msg = ok ? `profiled ${name}` : `profiled ${name} (failed)`;
87
+ // Use DEBUG for timing
88
+ emit(LogLevel.DEBUG, msg, {
89
+ executionTimeMs: dur,
90
+ functionName: name,
91
+ });
92
+ };
93
+ const maybePromise = result;
94
+ if (maybePromise && typeof maybePromise.then === "function") {
95
+ return result.then((r) => {
96
+ finish(true);
97
+ return r;
98
+ }, (e) => {
99
+ finish(false);
100
+ throw e;
101
+ });
102
+ }
103
+ finish(true);
104
+ return result;
105
+ };
106
+ },
107
+ };
108
+ return logger;
109
+ }
110
+ export { LogLevel, consoleSink };
111
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,YAAY,GAGb,MAAM,aAAa,CAAC;AA8DrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC;IAElD,SAAS,IAAI,CACX,KAAe,EACf,OAAiC,EACjC,IAA8B;QAE9B,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO;QACnC,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC;YAAE,OAAO;QAE3C,IAAI,OAAe,CAAC;QACpB,IAAI,OAAO,GAAwC,IAAI,CAAC;QAExD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YAClC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC,CAAC;YACZ,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,OAAO,CAAC;QACpB,CAAC;QAED,MAAM,KAAK,GAAa;YACtB,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;YACd,KAAK;YACL,SAAS;YACT,OAAO;YACP,IAAI,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,OAAO,EAAE,GAAG;SACb,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,qBAAqB,EAAE,CAAC,KAAK,EAAE,CAAC;YACjD,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,CAAC,CAAC;YACd,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,+DAA+D;YACjE,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAsB;QAChC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACzC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/C,WAAW,CAAC,KAAK;YACf,OAAO,YAAY,CAAC;gBAClB,GAAG,MAAM;gBACT,WAAW,EAAE,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE;aAClD,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,CACN,IAAY,EACZ,IAA8B;YAE9B,OAAO,SAAS,eAAe,CAAY,GAAG,IAAW;gBACvD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAS,CAAC;gBAC9C,MAAM,MAAM,GAAG,CAAC,EAAW,EAAE,EAAE;oBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;oBAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,WAAW,CAAC;oBAClE,uBAAuB;oBACvB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE;wBACxB,eAAe,EAAE,GAAG;wBACpB,YAAY,EAAE,IAAI;qBACnB,CAAC,CAAC;gBACL,CAAC,CAAC;gBACF,MAAM,YAAY,GAAG,MAAwC,CAAC;gBAC9D,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBAC5D,OAAQ,MAAsC,CAAC,IAAI,CACjD,CAAC,CAAC,EAAE,EAAE;wBACJ,MAAM,CAAC,IAAI,CAAC,CAAC;wBACb,OAAO,CAAS,CAAC;oBACnB,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;wBACJ,MAAM,CAAC,KAAK,CAAC,CAAC;wBACd,MAAM,CAAC,CAAC;oBACV,CAAC,CACiB,CAAC;gBACvB,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,CAAC;gBACb,OAAO,MAAc,CAAC;YACxB,CAAC,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC","sourcesContent":["import {\n LogLevel,\n consoleSink,\n getGlobalLoggerConfig,\n isLevelEnabled,\n isNamespaceEnabled,\n redactObject,\n type LoggerContextBase,\n type LogEntry,\n} from \"./config.js\";\n\n/**\n * Lazy message supplier function for expensive log computations.\n * Only evaluated when the log level is enabled.\n *\n * @returns {[string, Record<string, unknown>?]} Tuple of [message, optional data]\n */\ntype MessageSupplier = () => [string, Record<string, unknown>?];\n\n/**\n * Diagnostics logger instance for developer-facing debugging output.\n * Provides level-based logging (error, warn, info, debug, verbose) with structured data.\n */\nexport type DiagnosticsLogger = {\n /** Log debug messages for development and troubleshooting */\n debug: (\n msg: string | MessageSupplier,\n data?: Record<string, unknown>,\n ) => void;\n /** Log informational messages for key operations */\n info: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;\n /** Log warnings for recoverable issues */\n warn: (msg: string | MessageSupplier, data?: Record<string, unknown>) => void;\n /** Log errors for failures */\n error: (\n msg: string | MessageSupplier,\n data?: Record<string, unknown>,\n ) => void;\n /** Log verbose details for very detailed tracing */\n verbose: (\n msg: string | MessageSupplier,\n data?: Record<string, unknown>,\n ) => void;\n /** Create a child logger with additional context merged into all log entries */\n withContext: (extra: Record<string, unknown>) => DiagnosticsLogger;\n /**\n * Wrap a function to measure and log its execution time at DEBUG level.\n * Supports both synchronous and asynchronous functions.\n *\n * @template TArgs - Function argument types\n * @template TRet - Function return type\n * @param name - Name for profiling logs\n * @param func - Function to profile\n * @returns Wrapped function that logs execution time\n */\n profiled<TArgs extends any[], TRet>(\n name: string,\n func: (...args: TArgs) => TRet,\n ): (...args: TArgs) => TRet;\n};\n\n/**\n * Parameters for creating a diagnostics logger instance.\n */\nexport type CreateLoggerParams = LoggerContextBase & {\n /** Optional namespace for filtering/grouping (e.g., \"aa-infra\", \"wallet-apis\") */\n namespace?: string;\n /** Optional base context merged into all log entries from this logger */\n baseContext?: Record<string, unknown>;\n};\n\n/**\n * Creates a diagnostics logger instance for a package.\n * Loggers emit structured log entries to configured sinks (default: console).\n *\n * All log methods support:\n * - String messages: `logger.info(\"message\", { data })`\n * - Lazy suppliers: `logger.debug(() => [\"expensive\", computeData()])` (only evaluated when level enabled)\n *\n * @param {CreateLoggerParams} params - Logger configuration\n * @returns {DiagnosticsLogger} A logger instance\n * @example\n * ```ts\n * import { createLogger } from \"@alchemy/common\";\n *\n * const logger = createLogger({\n * package: \"@alchemy/aa-infra\",\n * version: \"1.0.0\",\n * namespace: \"aa-infra\"\n * });\n *\n * logger.info(\"processing request\", { chainId: 1 });\n * logger.debug(\"detailed state\", { userOp: op });\n *\n * // Child logger with additional context\n * const childLogger = logger.withContext({ requestId: \"123\" });\n * childLogger.info(\"step complete\"); // includes requestId in all logs\n *\n * // Profile a function\n * const sendWithProfiling = logger.profiled(\"sendUserOp\", sendUserOp);\n * await sendWithProfiling(op); // logs execution time at DEBUG level\n * ```\n */\nexport function createLogger(params: CreateLoggerParams): DiagnosticsLogger {\n const { namespace, baseContext, ...ctx } = params;\n\n function emit(\n level: LogLevel,\n msgOrFn: string | MessageSupplier,\n data?: Record<string, unknown>,\n ) {\n if (!isLevelEnabled(level)) return;\n if (!isNamespaceEnabled(namespace)) return;\n\n let message: string;\n let payload: Record<string, unknown> | undefined = data;\n\n if (typeof msgOrFn === \"function\") {\n const [m, d] = msgOrFn();\n message = m;\n payload = d ?? data;\n } else {\n message = msgOrFn;\n }\n\n const entry: LogEntry = {\n ts: Date.now(),\n level,\n namespace,\n message,\n data: redactObject({ ...(baseContext || {}), ...(payload || {}) }),\n context: ctx,\n };\n\n for (const sink of getGlobalLoggerConfig().sinks) {\n try {\n sink(entry);\n } catch (e) {\n // Silently continue if a sink throws - don't break other sinks\n }\n }\n }\n\n const logger: DiagnosticsLogger = {\n debug: (m, d) => emit(LogLevel.DEBUG, m, d),\n info: (m, d) => emit(LogLevel.INFO, m, d),\n warn: (m, d) => emit(LogLevel.WARN, m, d),\n error: (m, d) => emit(LogLevel.ERROR, m, d),\n verbose: (m, d) => emit(LogLevel.VERBOSE, m, d),\n withContext(extra) {\n return createLogger({\n ...params,\n baseContext: { ...(baseContext || {}), ...extra },\n });\n },\n profiled<TArgs extends any[], TRet>(\n name: string,\n func: (...args: TArgs) => TRet,\n ) {\n return function profiledWrapper(this: any, ...args: TArgs): TRet {\n const start = Date.now();\n const result = func.apply(this, args) as TRet;\n const finish = (ok: boolean) => {\n const dur = Date.now() - start;\n const msg = ok ? `profiled ${name}` : `profiled ${name} (failed)`;\n // Use DEBUG for timing\n emit(LogLevel.DEBUG, msg, {\n executionTimeMs: dur,\n functionName: name,\n });\n };\n const maybePromise = result as unknown as { then?: Function };\n if (maybePromise && typeof maybePromise.then === \"function\") {\n return (result as unknown as Promise<unknown>).then(\n (r) => {\n finish(true);\n return r as TRet;\n },\n (e) => {\n finish(false);\n throw e;\n },\n ) as unknown as TRet;\n }\n finish(true);\n return result as TRet;\n };\n },\n };\n\n return logger;\n}\n\nexport { LogLevel, consoleSink };\n"]}
@@ -0,0 +1,6 @@
1
+ import type { InnerLogger } from "./types.js";
2
+ /**
3
+ * No-operation logger that discards all events.
4
+ * Used as a fallback when logger initialization fails or in disabled states.
5
+ */
6
+ export declare const noopLogger: InnerLogger<any>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * No-operation logger that discards all events.
3
+ * Used as a fallback when logger initialization fails or in disabled states.
4
+ */
5
+ export const noopLogger = {
6
+ trackEvent: async () => { },
7
+ _internal: {
8
+ ready: Promise.resolve(),
9
+ anonId: "",
10
+ },
11
+ };
12
+ //# sourceMappingURL=noop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noop.js","sourceRoot":"","sources":["../../../src/logging/noop.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAqB;IAC1C,UAAU,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;IAC1B,SAAS,EAAE;QACT,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;QACxB,MAAM,EAAE,EAAE;KACX;CACF,CAAC","sourcesContent":["import type { InnerLogger } from \"./types.js\";\n\n/**\n * No-operation logger that discards all events.\n * Used as a fallback when logger initialization fails or in disabled states.\n */\nexport const noopLogger: InnerLogger<any> = {\n trackEvent: async () => {},\n _internal: {\n ready: Promise.resolve(),\n anonId: \"\",\n },\n};\n"]}