@logbrew/sdk 0.1.37 → 0.1.39

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 (103) hide show
  1. package/automatic-delivery.cjs +271 -0
  2. package/batch-sender.cjs +164 -0
  3. package/bounded-number.cjs +34 -0
  4. package/browser-breadcrumbs.cjs +346 -136
  5. package/capture-support.cjs +100 -0
  6. package/client-options.cjs +292 -0
  7. package/client-report.cjs +232 -0
  8. package/client.cjs +447 -0
  9. package/console-arguments.cjs +151 -0
  10. package/console-capture.cjs +194 -0
  11. package/core.cjs +35 -2984
  12. package/delivery-health.cjs +133 -0
  13. package/error-causes.cjs +130 -0
  14. package/error-details.cjs +124 -0
  15. package/error-issue.cjs +187 -0
  16. package/event-clone.cjs +55 -0
  17. package/event-metadata.cjs +40 -0
  18. package/event-pipeline.cjs +325 -0
  19. package/event-queue.cjs +306 -0
  20. package/event-store.cjs +237 -0
  21. package/event-validation.cjs +159 -0
  22. package/examples/agent-timeline.cjs +57 -46
  23. package/examples/agent-timeline.mjs +41 -35
  24. package/examples/index.mjs +122 -94
  25. package/examples/readme-example.cjs +36 -27
  26. package/examples/readme-example.mjs +21 -17
  27. package/examples/real-user-smoke.cjs +41 -32
  28. package/examples/real-user-smoke.mjs +26 -22
  29. package/exception-chain.cjs +205 -0
  30. package/index.d.ts +144 -1076
  31. package/index.js +1 -1
  32. package/issue-breadcrumbs.cjs +143 -0
  33. package/issue-diagnostics.cjs +94 -582
  34. package/issue-evidence.cjs +256 -0
  35. package/issue-exception.cjs +268 -0
  36. package/issue-stack-frames.cjs +220 -0
  37. package/issue-stack-text.cjs +101 -0
  38. package/issue-stack.cjs +188 -225
  39. package/issue-text.cjs +72 -0
  40. package/log-context.cjs +87 -67
  41. package/metric-validation.cjs +95 -0
  42. package/network-milestone.cjs +155 -0
  43. package/null-value.cjs +4 -0
  44. package/opentelemetry-bridge.cjs +330 -0
  45. package/opentelemetry-context.cjs +245 -0
  46. package/opentelemetry-exceptions.cjs +69 -0
  47. package/opentelemetry-keys.cjs +183 -0
  48. package/opentelemetry-span.cjs +286 -0
  49. package/opentelemetry-summary.cjs +213 -0
  50. package/opentelemetry-time.cjs +131 -0
  51. package/opentelemetry.cjs +99 -1068
  52. package/package.json +64 -1
  53. package/pino-destination.cjs +108 -0
  54. package/pino-record.cjs +237 -0
  55. package/product-action.cjs +163 -0
  56. package/provided-breadcrumbs.cjs +117 -0
  57. package/random-hex.cjs +29 -0
  58. package/react-native.js +1 -1
  59. package/recording-transport.cjs +68 -0
  60. package/release-artifacts-build.cjs +263 -84
  61. package/release-artifacts-common.js +347 -66
  62. package/release-artifacts-debug-id.js +178 -0
  63. package/release-artifacts-inspect.js +213 -0
  64. package/release-artifacts-issue-event.js +99 -0
  65. package/release-artifacts-manifest-artifact.js +107 -0
  66. package/release-artifacts-manifest.js +77 -0
  67. package/release-artifacts-mappings.js +265 -0
  68. package/release-artifacts-prepare.js +297 -0
  69. package/release-artifacts-source-context.js +210 -0
  70. package/release-artifacts-symbolication.js +272 -421
  71. package/release-artifacts-upload-files.js +122 -0
  72. package/release-artifacts-upload-request.js +246 -0
  73. package/release-artifacts-upload.js +155 -300
  74. package/release-artifacts-urls.js +49 -0
  75. package/release-artifacts.js +189 -662
  76. package/retry-delay.cjs +32 -0
  77. package/sdk-error.cjs +36 -0
  78. package/span-validation.cjs +191 -0
  79. package/stored-events.cjs +243 -0
  80. package/support-ticket.cjs +232 -122
  81. package/telemetry-context.cjs +343 -251
  82. package/trace-context.cjs +376 -179
  83. package/transport-error.cjs +23 -0
  84. package/types/client.d.ts +67 -0
  85. package/types/delivery.d.ts +125 -0
  86. package/types/issues.d.ts +249 -0
  87. package/types/logging.d.ts +175 -0
  88. package/types/opentelemetry.d.ts +205 -0
  89. package/types/product.d.ts +128 -0
  90. package/types/recording-transport.d.ts +15 -0
  91. package/types/sdk-error.d.ts +9 -0
  92. package/types/telemetry.d.ts +171 -0
  93. package/types/trace-context.d.ts +110 -0
  94. package/types/transport-error.d.ts +10 -0
  95. package/utf8.cjs +37 -0
  96. package/validation.cjs +327 -0
  97. package/vite-release-artifacts-config.cjs +120 -0
  98. package/vite-release-artifacts.cjs +199 -99
  99. package/vite-release-artifacts.d.cts +65 -40
  100. package/vite-release-artifacts.d.ts +65 -40
  101. package/vite-release-artifacts.js +2 -2
  102. package/winston-info.cjs +205 -0
  103. package/winston.cjs +189 -290
package/winston.cjs CHANGED
@@ -1,324 +1,223 @@
1
1
  const { Writable } = require("node:stream");
2
2
 
3
- const { LogBrewClient, SdkError } = require("./core.cjs");
4
- const { buildLogContextHelpers } = require("./log-context.cjs");
3
+ const { callableOrUndefined, captureSettings, settledFlush } = require("./capture-support.cjs");
4
+ const { LogBrewClient } = require("./client.cjs");
5
+ const { traceFromProvider } = require("./log-context.cjs");
6
+ const { NULL_VALUE } = require("./null-value.cjs");
7
+ const { SdkError } = require("./sdk-error.cjs");
8
+ const { isObjectValue, isRecord, isTruthy, optionalProperty } = require("./validation.cjs");
9
+ const { logAttributesFromWinstonInfo, timestampFromWinstonInfo, winstonMessage } = require("./winston-info.cjs");
10
+
11
+ /**
12
+ * @import { IssueBreadcrumbLevel } from "./index.cjs"
13
+ * @import { CaptureClient } from "./capture-support.cjs"
14
+ * @typedef {CaptureClient & Readonly<{
15
+ * breadcrumbProvider: unknown,
16
+ * addBreadcrumb: (breadcrumb: unknown) => unknown
17
+ * }>} WinstonClient
18
+ * @typedef {{ type: "debug", category: string, level: IssueBreadcrumbLevel, message: string }} WinstonBreadcrumb
19
+ */
5
20
 
6
- // Runtime packages with request scopes (such as @logbrew/node) register a recorder that keeps
7
- // breadcrumbs with the active request instead of the whole client.
8
21
  const BREADCRUMB_RECORDER = Symbol.for("@logbrew/sdk.breadcrumbRecorder");
9
22
  const MAX_BREADCRUMB_MESSAGE_LENGTH = 512;
10
- const WINSTON_RESERVED_FIELDS = new Set([
11
- "level",
12
- "message",
13
- "timestamp",
14
- "time",
15
- "err",
16
- "error",
17
- "stack"
23
+ const FIRST_CHARACTER = 0;
24
+ const NO_PENDING_EVENTS = 0;
25
+ /** @type {ReadonlyMap<string, IssueBreadcrumbLevel>} */
26
+ const BREADCRUMB_LEVELS = new Map([
27
+ ["error", "error"],
28
+ ["crit", "error"],
29
+ ["critical", "error"],
30
+ ["fatal", "error"],
31
+ ["alert", "error"],
32
+ ["emerg", "error"],
33
+ ["warn", "warning"],
34
+ ["warning", "warning"],
35
+ ["verbose", "debug"],
36
+ ["debug", "debug"],
37
+ ["silly", "debug"]
18
38
  ]);
19
- const {
20
- compactMetadata,
21
- isMetadataValue,
22
- traceFromProvider,
23
- traceMetadataFromLogContext
24
- } = buildLogContextHelpers({ SdkError });
25
39
 
26
- function createLogBrewWinstonTransport(config) {
27
- if (!config || typeof config !== "object") {
28
- throw new SdkError("validation_error", "Winston transport config must be an object");
29
- }
30
-
31
- const client = config.client;
32
- if (!(client instanceof LogBrewClient)) {
33
- throw new SdkError("validation_error", "Winston transport client must be a LogBrewClient");
34
- }
35
-
36
- const transport = config.transport;
37
- const breadcrumbs = config.breadcrumbs !== false;
38
- const flushOnWrite = config.flushOnWrite === true;
39
- const includeErrorStack = config.includeErrorStack === true;
40
- const logger = config.logger ?? "winston";
41
- const metadata = compactMetadata(config.metadata);
42
- const timestamp = typeof config.timestamp === "function"
43
- ? config.timestamp
44
- : () => new Date().toISOString();
45
- const eventIdPrefix = config.eventIdPrefix ?? "winston";
46
- const onError = typeof config.onError === "function" ? config.onError : () => {};
47
- const traceProvider = typeof config.traceProvider === "function" ? config.traceProvider : null;
48
- const state = {
49
- captured: 0,
50
- pendingFlush: Promise.resolve(null)
51
- };
40
+ /**
41
+ * @param {unknown} level
42
+ * @returns {IssueBreadcrumbLevel}
43
+ */
44
+ function winstonBreadcrumbLevel(level) {
45
+ return BREADCRUMB_LEVELS.get(String(level).toLowerCase()) ?? "info";
46
+ }
52
47
 
53
- const winstonTransport = new Writable({
54
- objectMode: true,
55
- write(info, _encoding, callback) {
56
- try {
57
- if (breadcrumbs) {
58
- recordWinstonBreadcrumb(client, info);
59
- }
60
- captureWinstonInfo({
61
- client,
62
- eventIdPrefix,
63
- flushOnWrite,
64
- includeErrorStack,
65
- info,
66
- logger,
67
- metadata,
68
- onError,
69
- state,
70
- timestamp,
71
- traceProvider,
72
- transport
73
- });
74
- } catch (error) {
75
- onError(error);
76
- } finally {
77
- callback();
48
+ class WinstonCapture {
49
+ /**
50
+ * @param {Readonly<Record<string, unknown>>} config
51
+ * @param {WinstonClient} client
52
+ */
53
+ constructor(config, client) {
54
+ this.client = client;
55
+ this.breadcrumbs = config["breadcrumbs"] !== false;
56
+ this.settings = captureSettings(config, "flushOnWrite", "winston");
57
+ this.traceProvider = callableOrUndefined(config["traceProvider"]);
58
+ this.captured = 0;
59
+ /** @type {Promise<unknown>} */
60
+ this.pendingFlush = Promise.resolve(NULL_VALUE);
61
+ }
62
+
63
+ /**
64
+ * @param {unknown} info
65
+ * @returns {void}
66
+ */
67
+ write(info) {
68
+ try {
69
+ if (this.breadcrumbs) {
70
+ this.recordBreadcrumb(info);
78
71
  }
72
+ this.capture(info);
73
+ } catch (error) {
74
+ this.settings.onError(error);
79
75
  }
80
- });
76
+ }
81
77
 
82
- winstonTransport.log = function log(info, callback) {
83
- this.write(info);
84
- if (typeof callback === "function") {
85
- callback();
78
+ /**
79
+ * @param {unknown} info
80
+ * @returns {boolean}
81
+ */
82
+ recordBreadcrumb(info) {
83
+ if (!isRecord(info) || info["silent"] === true) {
84
+ return false;
86
85
  }
87
- };
88
- winstonTransport.flush = async () => {
89
- if (transport && client.pendingEvents() > 0) {
90
- state.pendingFlush = Promise.resolve(client.flush(transport)).catch((error) => {
91
- onError(error);
92
- return null;
93
- });
86
+ /** @type {WinstonBreadcrumb} */
87
+ const breadcrumb = {
88
+ type: "debug",
89
+
90
+ category: "log.winston",
91
+ level: winstonBreadcrumbLevel(info["level"]),
92
+ message: [...winstonMessage(info)].slice(FIRST_CHARACTER, MAX_BREADCRUMB_MESSAGE_LENGTH).join("")
93
+ };
94
+ /** @type {unknown} */
95
+ const recorder = isTruthy(this.client.breadcrumbProvider) ? Reflect.get(globalThis, BREADCRUMB_RECORDER) : undefined;
96
+ try {
97
+ if (typeof recorder === "function") {
98
+ Reflect.apply(recorder, undefined, [breadcrumb]);
99
+ } else {
100
+ this.client.addBreadcrumb(breadcrumb);
101
+ }
102
+ return true;
103
+ } catch {
104
+ return false;
94
105
  }
95
- return state.pendingFlush;
96
- };
97
- if (typeof config.level === "string" && config.level.trim() !== "") {
98
- winstonTransport.level = config.level;
99
106
  }
100
- if (config.name !== undefined) {
101
- winstonTransport.name = String(config.name);
102
- }
103
- if (config.silent === true) {
104
- winstonTransport.silent = true;
105
- }
106
- if (config.handleExceptions === true) {
107
- winstonTransport.handleExceptions = true;
108
- }
109
- if (config.handleRejections === true) {
110
- winstonTransport.handleRejections = true;
111
- }
112
-
113
- return winstonTransport;
114
- }
115
-
116
- function captureWinstonInfo(config) {
117
- if (config.info?.silent === true) {
118
- return;
119
- }
120
- config.state.captured += 1;
121
- config.client.log(
122
- `${config.eventIdPrefix}_${config.state.captured}`,
123
- timestampFromWinstonInfo(config.info, config.timestamp),
124
- logAttributesFromWinstonInfo(config.info, {
125
- includeErrorStack: config.includeErrorStack,
126
- logger: config.logger,
127
- metadata: config.metadata,
128
- trace: traceFromProvider(config.traceProvider, config.onError)
129
- })
130
- );
131
- if (config.flushOnWrite && config.transport) {
132
- config.state.pendingFlush = Promise.resolve(config.client.flush(config.transport)).catch((error) => {
133
- config.onError(error);
134
- return null;
135
- });
136
- }
137
- }
138
107
 
139
- function recordWinstonBreadcrumb(client, info) {
140
- if (!info || Array.isArray(info) || typeof info !== "object" || info.silent === true) {
141
- return;
142
- }
143
- const breadcrumb = {
144
- type: "debug",
145
- category: "log.winston",
146
- level: winstonBreadcrumbLevel(info.level),
147
- message: Array.from(winstonMessage(info)).slice(0, MAX_BREADCRUMB_MESSAGE_LENGTH).join("")
148
- };
149
- const recorder = client.breadcrumbProvider ? globalThis[BREADCRUMB_RECORDER] : undefined;
150
- try {
151
- if (typeof recorder === "function") {
152
- recorder(breadcrumb);
153
- } else {
154
- client.addBreadcrumb(breadcrumb);
108
+ /**
109
+ * @param {unknown} info
110
+ * @returns {void}
111
+ */
112
+ capture(info) {
113
+ if (optionalProperty(info, "silent") === true) {
114
+ return;
155
115
  }
156
- } catch {
157
- // Breadcrumbs are advisory; the log event below still goes out.
158
- }
159
- }
160
-
161
- function winstonBreadcrumbLevel(level) {
162
- switch (String(level).toLowerCase()) {
163
- case "error":
164
- case "crit":
165
- case "critical":
166
- case "fatal":
167
- case "alert":
168
- case "emerg":
169
- return "error";
170
- case "warn":
171
- case "warning":
172
- return "warning";
173
- case "verbose":
174
- case "debug":
175
- case "silly":
176
- return "debug";
177
- default:
178
- return "info";
179
- }
180
- }
181
-
182
- function logAttributesFromWinstonInfo(info, options = {}) {
183
- if (!info || Array.isArray(info) || typeof info !== "object") {
184
- throw new SdkError("validation_error", "Winston info must be an object");
185
- }
186
-
187
- const level = logbrewLevelFromWinstonLevel(info.level);
188
- const metadata = {
189
- ...compactMetadata(options.metadata),
190
- winstonLevel: winstonLevelLabel(info.level),
191
- ...winstonContextMetadata(info),
192
- ...traceMetadataFromLogContext(options.trace)
193
- };
194
- addWinstonErrorMetadata(metadata, info, options.includeErrorStack === true);
195
-
196
- return {
197
- message: winstonMessage(info),
198
- level,
199
- ...(options.logger ? { logger: options.logger } : {}),
200
- metadata
201
- };
202
- }
203
-
204
- function timestampFromWinstonInfo(info, fallbackTimestamp) {
205
- const value = info?.timestamp ?? info?.time;
206
- if (typeof value === "number" && Number.isFinite(value)) {
207
- return new Date(value).toISOString();
208
- }
209
- if (typeof value === "string" && value.trim() !== "") {
210
- const parsed = new Date(value);
211
- if (!Number.isNaN(parsed.valueOf())) {
212
- return parsed.toISOString();
116
+ const { settings } = this;
117
+ this.captured += 1;
118
+ this.client.log(
119
+ [settings.eventIdPrefix, this.captured].join("_"),
120
+ timestampFromWinstonInfo(info, settings.timestamp),
121
+ logAttributesFromWinstonInfo(info, {
122
+ includeErrorStack: settings.includeErrorStack,
123
+ logger: settings.logger,
124
+ metadata: settings.metadata,
125
+ trace: traceFromProvider(this.traceProvider, settings.onError)
126
+ })
127
+ );
128
+ if (settings.flushOnEvent && isTruthy(settings.transport)) {
129
+ this.pendingFlush = settledFlush(this.client.flush(settings.transport), settings.onError);
213
130
  }
214
- return value;
215
131
  }
216
- if (value instanceof Date && !Number.isNaN(value.valueOf())) {
217
- return value.toISOString();
218
- }
219
- return fallbackTimestamp();
220
- }
221
132
 
222
- function logbrewLevelFromWinstonLevel(level) {
223
- switch (String(level).toLowerCase()) {
224
- case "debug":
225
- case "silly":
226
- return "info";
227
- case "warn":
228
- case "warning":
229
- return "warning";
230
- case "error":
231
- return "error";
232
- case "crit":
233
- case "critical":
234
- case "fatal":
235
- return "critical";
236
- case "http":
237
- case "verbose":
238
- case "info":
239
- default:
240
- return "info";
133
+ /**
134
+ * @returns {void}
135
+ */
136
+ flushPending() {
137
+ const { settings } = this;
138
+ if (isTruthy(settings.transport) && this.client.pendingEvents() > NO_PENDING_EVENTS) {
139
+ this.pendingFlush = settledFlush(this.client.flush(settings.transport), settings.onError);
140
+ }
241
141
  }
242
- }
243
142
 
244
- function winstonLevelLabel(level) {
245
- return typeof level === "string" && level.trim() !== "" ? level : "info";
246
- }
247
-
248
- function winstonMessage(info) {
249
- if (typeof info.message === "string" && info.message.trim() !== "") {
250
- return info.message;
251
- }
252
- const error = info.err ?? info.error;
253
- if (error && typeof error === "object" && typeof error.message === "string" && error.message.trim() !== "") {
254
- return error.message;
143
+ /**
144
+ * @returns {() => Promise<unknown>}
145
+ */
146
+ flushHandler() {
147
+ return async () => {
148
+ this.flushPending();
149
+ return this.pendingFlush;
150
+ };
255
151
  }
256
- return "winston event";
257
152
  }
258
153
 
259
- function winstonContextMetadata(info) {
260
- const metadata = {};
261
- for (const [key, value] of Object.entries(info)) {
262
- if (!WINSTON_RESERVED_FIELDS.has(key) && isMetadataValue(value)) {
263
- metadata[`context.${key}`] = value;
264
- }
154
+ /**
155
+ * @this {Readonly<{ write: (chunk: unknown) => boolean }>}
156
+ * @param {unknown} info
157
+ * @param {unknown} callback
158
+ * @returns {void}
159
+ */
160
+ function log(info, callback) {
161
+ this.write(info);
162
+ if (typeof callback === "function") {
163
+ Reflect.apply(callback, undefined, []);
265
164
  }
266
- return metadata;
267
165
  }
268
166
 
269
- function addWinstonErrorMetadata(metadata, info, includeErrorStack) {
270
- const error = info.err ?? info.error;
271
- addWinstonNestedErrorMetadata(metadata, error, includeErrorStack);
272
- if (typeof info.stack === "string" && info.stack.trim() !== "") {
273
- const firstLine = info.stack.split(/\r?\n/u)[0] ?? "";
274
- const match = /^([A-Za-z][A-Za-z0-9_.]*(?:Error|Exception)?):\s*(.*)$/u.exec(firstLine);
275
- if (match && metadata.errorName === undefined) {
276
- metadata.errorName = match[1];
277
- }
278
- if (match && match[2] && metadata.errorMessage === undefined) {
279
- metadata.errorMessage = match[2];
280
- }
281
- if (includeErrorStack) {
282
- metadata.errorStack = info.stack;
283
- }
284
- }
167
+ /**
168
+ * @param {Readonly<Record<string, unknown>>} config
169
+ * @returns {Readonly<Record<string, unknown>>}
170
+ */
171
+ function winstonTransportOptions(config) {
172
+ const { level, name } = config;
173
+ const nameText = String(name);
174
+ return {
175
+ ...(typeof level === "string" && level.trim() !== "" ? { level } : {}),
176
+ ...(name === undefined ? {} : { name: nameText }),
177
+ ...(config["silent"] === true ? { silent: true } : {}),
178
+ ...(config["handleExceptions"] === true ? { handleExceptions: true } : {}),
179
+ ...(config["handleRejections"] === true ? { handleRejections: true } : {})
180
+ };
285
181
  }
286
182
 
287
- function addWinstonNestedErrorMetadata(metadata, error, includeErrorStack) {
288
- if (!error) {
289
- return;
183
+ /**
184
+ * @param {unknown} config
185
+ * @returns {Writable}
186
+ */
187
+ function createLogBrewWinstonTransport(config) {
188
+ if (!isObjectValue(config)) {
189
+ throw new SdkError("validation_error", "Winston transport config must be an object");
290
190
  }
291
- if (error instanceof Error) {
292
- metadata.errorName = error.name || "Error";
293
- if (error.message) {
294
- metadata.errorMessage = error.message;
295
- }
296
- if (includeErrorStack && error.stack) {
297
- metadata.errorStack = error.stack;
298
- }
299
- return;
191
+ const { client } = config;
192
+ if (!(client instanceof LogBrewClient)) {
193
+ throw new SdkError("validation_error", "Winston transport client must be a LogBrewClient");
300
194
  }
301
- if (typeof error === "object") {
302
- const name = error.type ?? error.name;
303
- const message = error.message;
304
- const stack = error.stack;
305
- if (typeof name === "string" && name.trim() !== "") {
306
- metadata.errorName = name;
307
- }
308
- if (typeof message === "string" && message.trim() !== "") {
309
- metadata.errorMessage = message;
310
- }
311
- if (includeErrorStack && typeof stack === "string" && stack.trim() !== "") {
312
- metadata.errorStack = stack;
195
+ const capture = new WinstonCapture(config, client);
196
+ const winstonTransport = new Writable({
197
+ objectMode: true,
198
+ /**
199
+ * @param {unknown} info
200
+ * @param {unknown} _encoding
201
+ * @param {() => void} callback
202
+ * @returns {void}
203
+ */
204
+ write(info, _encoding, callback) {
205
+ try {
206
+ capture.write(info);
207
+ } finally {
208
+ Reflect.apply(callback, undefined, []);
209
+ }
313
210
  }
314
- return;
315
- }
316
- if (typeof error === "string" && error.trim() !== "") {
317
- metadata.errorMessage = error;
318
- }
211
+ });
212
+ return Object.assign(
213
+ winstonTransport,
214
+ {
215
+ log,
216
+
217
+ flush: capture.flushHandler()
218
+ },
219
+ winstonTransportOptions(config)
220
+ );
319
221
  }
320
222
 
321
- module.exports = {
322
- createLogBrewWinstonTransport,
323
- logAttributesFromWinstonInfo
324
- };
223
+ module.exports = { createLogBrewWinstonTransport, logAttributesFromWinstonInfo };