@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
@@ -1,4 +1,18 @@
1
+ const { SdkError } = require("./sdk-error.cjs");
2
+ const { isRecord, requireAllowedValue, requireNonEmpty, requireTraceId } = require("./validation.cjs");
3
+
4
+ /**
5
+ * @import { SupportTicketCategory, SupportTicketDraft, SupportTicketSource } from "./index.cjs"
6
+ * @typedef {string | number | boolean | null | undefined | SanitizedDiagnosticsValue[] | SanitizedDiagnostics} SanitizedDiagnosticsValue
7
+ * @typedef {{ [key: string]: SanitizedDiagnosticsValue }} SanitizedDiagnostics
8
+ * @typedef {Omit<SupportTicketDraft, "diagnostics"> & { diagnostics?: SanitizedDiagnostics }} SanitizedSupportTicketDraft
9
+ * @typedef {Omit<SanitizedSupportTicketDraft, "category" | "description" | "source" | "title">} SupportTicketContext
10
+ * @typedef {(child: unknown, childKey: string) => SanitizedDiagnosticsValue} SanitizeChild
11
+ */
12
+
13
+ /** @type {ReadonlySet<SupportTicketSource>} */
1
14
  const SUPPORT_TICKET_SOURCES = new Set(["cli", "sdk", "website", "docs", "mobile"]);
15
+ /** @type {ReadonlySet<SupportTicketCategory>} */
2
16
  const SUPPORT_TICKET_CATEGORIES = new Set([
3
17
  "sdk_install_failure",
4
18
  "ingest_failure",
@@ -11,165 +25,261 @@ const SUPPORT_TICKET_CATEGORIES = new Set([
11
25
  "billing_question",
12
26
  "other"
13
27
  ]);
28
+ const SENSITIVE_KEY_PARTS = [
29
+ "apikey",
30
+ "auth",
31
+ "authorization",
32
+ "authtoken",
33
+ "bearer",
34
+ "clientsecret",
35
+ "connectionstring",
36
+ "cookie",
37
+ "credential",
38
+ "dsn",
39
+ "email",
40
+ "password",
41
+ "passwd",
42
+ "privatekey",
43
+ "refreshtoken",
44
+ "secret",
45
+ "session",
46
+ "setcookie",
47
+ "token"
48
+ ];
14
49
  const SUPPORT_DIAGNOSTICS_MAX_DEPTH = 5;
15
50
  const SUPPORT_DIAGNOSTICS_MAX_ARRAY_LENGTH = 20;
16
51
  const SUPPORT_DIAGNOSTICS_MAX_STRING_LENGTH = 500;
52
+ const ROOT_DEPTH = 0;
53
+ const DEPTH_STEP = 1;
54
+ const TEXT_START = 0;
55
+ const KEY_SEPARATOR_PATTERN = /[^a-z0-9]/giu;
56
+ const SENSITIVE_ASSIGNMENT_PATTERN = /(?:authorization|api[_-]?key|token|secret|password|passwd|cookie)\s*[:=]/iu;
57
+ const BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/iu;
58
+ const LOGBREW_KEY_PATTERN = /\blbw_(?:ingest|client|api)_[A-Za-z0-9._-]+/iu;
59
+ const VENDOR_KEY_PATTERN = /\b(?:github_pat|ghp|gho|npm|pypi|sk_live|sk_test|xox[baprs]|AKIA)[A-Za-z0-9._-]+/u;
60
+ const LOCAL_PATH_PATTERN = /^(?:\/Users\/|\/home\/|\/var\/folders\/|[A-Za-z]:\\)/u;
61
+ const QUERY_OR_FRAGMENT_PATTERN = /[?#]/u;
17
62
 
18
- function buildCreateSupportTicketDraft({ SdkError, requireAllowedValue, requireNonEmpty, requireTraceId }) {
19
- return function createSupportTicketDraft(input) {
20
- if (!input || Array.isArray(input) || typeof input !== "object") {
21
- throw new SdkError("validation_error", "support ticket draft input must be an object");
22
- }
63
+ /**
64
+ * @param {string} key
65
+ * @returns {boolean}
66
+ */
67
+ function isSensitiveSupportKey(key) {
68
+ if (key === "") {
69
+ return false;
70
+ }
71
+ const normalized = key.replaceAll(KEY_SEPARATOR_PATTERN, "").toLowerCase();
72
+ return SENSITIVE_KEY_PARTS.some((needle) => normalized.includes(needle));
73
+ }
23
74
 
24
- requireAllowedValue("support ticket source", input.source, SUPPORT_TICKET_SOURCES);
25
- requireAllowedValue("support ticket category", input.category, SUPPORT_TICKET_CATEGORIES);
26
- const draft = {
27
- source: input.source,
28
- category: input.category,
29
- title: requiredSupportString(requireNonEmpty, "support ticket title", input.title),
30
- description: requiredSupportString(requireNonEmpty, "support ticket description", input.description)
31
- };
32
-
33
- addOptionalSupportString(draft, "project_id", "support ticket projectId", input.projectId, requireNonEmpty);
34
- addOptionalSupportString(draft, "environment", "support ticket environment", input.environment, requireNonEmpty);
35
- addOptionalSupportString(draft, "runtime", "support ticket runtime", input.runtime, requireNonEmpty);
36
- addOptionalSupportString(draft, "framework", "support ticket framework", input.framework, requireNonEmpty);
37
- addOptionalSupportString(draft, "sdk_package", "support ticket sdkPackage", input.sdkPackage, requireNonEmpty);
38
- addOptionalSupportString(draft, "sdk_version", "support ticket sdkVersion", input.sdkVersion, requireNonEmpty);
39
- addOptionalSupportString(draft, "release", "support ticket release", input.release, requireNonEmpty);
40
- if (input.traceId !== undefined) {
41
- requireTraceId(input.traceId);
42
- draft.trace_id = input.traceId.toLowerCase();
43
- }
44
- addOptionalSupportString(draft, "event_id", "support ticket eventId", input.eventId, requireNonEmpty);
45
- if (input.diagnostics !== undefined) {
46
- draft.diagnostics = sanitizeSupportDiagnostics(input.diagnostics, SdkError);
47
- }
75
+ /**
76
+ * @param {string} value
77
+ * @returns {boolean}
78
+ */
79
+ function isSensitiveSupportString(value) {
80
+ return SENSITIVE_ASSIGNMENT_PATTERN.test(value)
81
+ || BEARER_PATTERN.test(value)
82
+ || LOGBREW_KEY_PATTERN.test(value)
83
+ || VENDOR_KEY_PATTERN.test(value);
84
+ }
48
85
 
49
- return draft;
50
- };
86
+ /**
87
+ * @param {string} value
88
+ * @returns {string}
89
+ */
90
+ function redactUrl(value) {
91
+ try {
92
+ const url = new URL(value);
93
+ return `[redacted-url]${url.pathname || "/"}`;
94
+ } catch {
95
+ const [beforeQuery = value] = value.split(QUERY_OR_FRAGMENT_PATTERN);
96
+ return beforeQuery;
97
+ }
51
98
  }
52
99
 
53
- function requiredSupportString(requireNonEmpty, label, value) {
54
- requireNonEmpty(label, value);
55
- return value.trim();
100
+ /**
101
+ * @param {string} value
102
+ * @returns {string}
103
+ */
104
+ function sanitizeSupportDiagnosticString(value) {
105
+ const trimmed = value.trim();
106
+ if (trimmed === "") {
107
+ return "";
108
+ }
109
+ if (isSensitiveSupportString(trimmed)) {
110
+ return "[redacted]";
111
+ }
112
+ const urlRedacted = redactUrl(LOCAL_PATH_PATTERN.test(trimmed) ? "[redacted-path]" : trimmed);
113
+ return urlRedacted.length > SUPPORT_DIAGNOSTICS_MAX_STRING_LENGTH
114
+ ? `${urlRedacted.slice(TEXT_START, SUPPORT_DIAGNOSTICS_MAX_STRING_LENGTH)}...`
115
+ : urlRedacted;
116
+ }
117
+
118
+ /**
119
+ * @param {Readonly<Error>} error
120
+ * @returns {SanitizedDiagnostics}
121
+ */
122
+ function errorDiagnostics(error) {
123
+ return { name: error.name || "Error" };
56
124
  }
57
125
 
58
- function addOptionalSupportString(target, key, label, value, requireNonEmpty) {
59
- if (value === undefined) {
60
- return;
126
+ /**
127
+ * @param {unknown} value
128
+ * @returns {SanitizedDiagnosticsValue}
129
+ */
130
+ function sanitizeSupportDiagnosticScalar(value) {
131
+ if (value === null || (typeof value === "number" && Number.isFinite(value)) || typeof value === "boolean") {
132
+ return value;
61
133
  }
62
- target[key] = requiredSupportString(requireNonEmpty, label, value);
134
+ return typeof value === "string" ? sanitizeSupportDiagnosticString(value) : undefined;
63
135
  }
64
136
 
65
- function sanitizeSupportDiagnostics(diagnostics, SdkError) {
66
- if (!diagnostics || Array.isArray(diagnostics) || typeof diagnostics !== "object") {
67
- throw new SdkError("validation_error", "support ticket diagnostics must be an object");
137
+ /**
138
+ * @param {readonly unknown[]} value
139
+ * @param {SanitizeChild} sanitizeChild
140
+ * @returns {SanitizedDiagnosticsValue[]}
141
+ */
142
+ function sanitizeSupportDiagnosticList(value, sanitizeChild) {
143
+ return value.slice(TEXT_START, SUPPORT_DIAGNOSTICS_MAX_ARRAY_LENGTH).map((item) => sanitizeChild(item, ""));
144
+ }
145
+
146
+ /**
147
+ * @param {Readonly<Record<string, unknown>>} value
148
+ * @param {SanitizeChild} sanitizeChild
149
+ * @returns {SanitizedDiagnostics}
150
+ */
151
+ function sanitizeSupportDiagnosticRecord(value, sanitizeChild) {
152
+ /** @type {SanitizedDiagnostics} */
153
+ const safe = {};
154
+ for (const [childKey, childValue] of Object.entries(value)) {
155
+ const sanitized = sanitizeChild(childValue, childKey);
156
+ if (sanitized !== undefined) {
157
+ safe[childKey] = sanitized;
158
+ }
68
159
  }
69
- return sanitizeSupportDiagnosticValue(diagnostics, 0, "");
160
+ return safe;
70
161
  }
71
162
 
163
+ /**
164
+ * @param {unknown} value
165
+ * @param {number} depth
166
+ * @param {string} key
167
+ * @returns {SanitizedDiagnosticsValue}
168
+ */
72
169
  function sanitizeSupportDiagnosticValue(value, depth, key) {
73
170
  if (isSensitiveSupportKey(key)) {
74
171
  return "[redacted]";
75
172
  }
76
173
  if (value instanceof Error) {
77
- return { name: value.name || "Error" };
174
+ return errorDiagnostics(value);
78
175
  }
79
- if (value === null || typeof value === "number" && Number.isFinite(value) || typeof value === "boolean") {
80
- return value;
176
+ if (!Array.isArray(value) && !isRecord(value)) {
177
+ return sanitizeSupportDiagnosticScalar(value);
81
178
  }
82
- if (typeof value === "string") {
83
- return sanitizeSupportDiagnosticString(value);
179
+ if (depth >= SUPPORT_DIAGNOSTICS_MAX_DEPTH) {
180
+ return "[max-depth]";
84
181
  }
85
- if (Array.isArray(value)) {
86
- if (depth >= SUPPORT_DIAGNOSTICS_MAX_DEPTH) {
87
- return "[max-depth]";
88
- }
89
- return value
90
- .slice(0, SUPPORT_DIAGNOSTICS_MAX_ARRAY_LENGTH)
91
- .map((item) => sanitizeSupportDiagnosticValue(item, depth + 1, ""));
182
+ const childDepth = depth + DEPTH_STEP;
183
+ return isRecord(value)
184
+ ? sanitizeSupportDiagnosticRecord(value, (child, childKey) => sanitizeSupportDiagnosticValue(child, childDepth, childKey))
185
+ : sanitizeSupportDiagnosticList(value, (child, childKey) => sanitizeSupportDiagnosticValue(child, childDepth, childKey));
186
+ }
187
+
188
+ /**
189
+ * @param {unknown} diagnostics
190
+ * @returns {{ diagnostics?: SanitizedDiagnostics }}
191
+ */
192
+ function optionalDiagnostics(diagnostics) {
193
+ if (diagnostics === undefined) {
194
+ return {};
92
195
  }
93
- if (value && typeof value === "object") {
94
- if (depth >= SUPPORT_DIAGNOSTICS_MAX_DEPTH) {
95
- return "[max-depth]";
96
- }
97
- const safe = {};
98
- for (const [childKey, childValue] of Object.entries(value)) {
99
- const sanitized = sanitizeSupportDiagnosticValue(childValue, depth + 1, childKey);
100
- if (sanitized !== undefined) {
101
- safe[childKey] = sanitized;
102
- }
103
- }
104
- return safe;
196
+ if (!isRecord(diagnostics)) {
197
+ throw new SdkError("validation_error", "support ticket diagnostics must be an object");
198
+ }
199
+ if (diagnostics instanceof Error) {
200
+ return { diagnostics: errorDiagnostics(diagnostics) };
105
201
  }
106
- return undefined;
202
+ return {
203
+ diagnostics: sanitizeSupportDiagnosticRecord(
204
+ diagnostics,
205
+ (child, childKey) => sanitizeSupportDiagnosticValue(child, ROOT_DEPTH + DEPTH_STEP, childKey)
206
+ )
207
+ };
107
208
  }
108
209
 
109
- function isSensitiveSupportKey(key) {
110
- if (typeof key !== "string" || key === "") {
111
- return false;
112
- }
113
- const normalized = key.replace(/[^a-z0-9]/giu, "").toLowerCase();
114
- return [
115
- "apikey",
116
- "auth",
117
- "authorization",
118
- "authtoken",
119
- "bearer",
120
- "clientsecret",
121
- "connectionstring",
122
- "cookie",
123
- "credential",
124
- "dsn",
125
- "email",
126
- "password",
127
- "passwd",
128
- "privatekey",
129
- "refreshtoken",
130
- "secret",
131
- "session",
132
- "setcookie",
133
- "token"
134
- ].some((needle) => normalized.includes(needle));
210
+ /**
211
+ * @param {string} label
212
+ * @param {unknown} value
213
+ * @returns {string}
214
+ */
215
+ function requiredSupportString(label, value) {
216
+ return requireNonEmpty(label, value).trim();
135
217
  }
136
218
 
137
- function sanitizeSupportDiagnosticString(value) {
138
- const trimmed = value.trim();
139
- if (trimmed === "") {
140
- return "";
219
+ /**
220
+ * @template {string} Key
221
+ * @param {Key} key
222
+ * @param {string} label
223
+ * @param {unknown} value
224
+ * @returns {{ [Field in Key]?: string }}
225
+ */
226
+ function optionalSupportString(key, label, value) {
227
+ /** @type {{ [Field in Key]?: string }} */
228
+ const field = {};
229
+ if (value !== undefined) {
230
+ field[key] = requiredSupportString(label, value);
141
231
  }
142
- if (isSensitiveSupportString(trimmed)) {
143
- return "[redacted]";
144
- }
145
- const pathRedacted = redactLocalPath(trimmed);
146
- const urlRedacted = redactUrl(pathRedacted);
147
- return urlRedacted.length > SUPPORT_DIAGNOSTICS_MAX_STRING_LENGTH
148
- ? `${urlRedacted.slice(0, SUPPORT_DIAGNOSTICS_MAX_STRING_LENGTH)}...`
149
- : urlRedacted;
232
+ return field;
150
233
  }
151
234
 
152
- function isSensitiveSupportString(value) {
153
- return /(?:authorization|api[_-]?key|token|secret|password|passwd|cookie)\s*[:=]/iu.test(value)
154
- || /\bBearer\s+[A-Za-z0-9._~+/-]+=*/iu.test(value)
155
- || /\blbw_(?:ingest|client|api)_[A-Za-z0-9._-]+/iu.test(value)
156
- || /\b(?:github_pat|ghp|gho|npm|pypi|sk_live|sk_test|xox[baprs]|AKIA)[A-Za-z0-9._-]+/u.test(value);
235
+ /**
236
+ * @param {unknown} traceId
237
+ * @returns {{ trace_id?: string }}
238
+ */
239
+ function optionalTraceId(traceId) {
240
+ return traceId === undefined ? {} : { trace_id: requireTraceId(traceId).toLowerCase() };
157
241
  }
158
242
 
159
- function redactLocalPath(value) {
160
- if (/^(?:\/Users\/|\/home\/|\/var\/folders\/|[A-Za-z]:\\)/u.test(value)) {
161
- return "[redacted-path]";
162
- }
163
- return value;
243
+ /**
244
+ * @param {Readonly<Record<string, unknown>>} input
245
+ * @returns {SupportTicketContext}
246
+ */
247
+ function supportTicketContext(input) {
248
+ return {
249
+ ...optionalSupportString("project_id", "support ticket projectId", input["projectId"]),
250
+ ...optionalSupportString("environment", "support ticket environment", input["environment"]),
251
+ ...optionalSupportString("runtime", "support ticket runtime", input["runtime"]),
252
+ ...optionalSupportString("framework", "support ticket framework", input["framework"]),
253
+ ...optionalSupportString("sdk_package", "support ticket sdkPackage", input["sdkPackage"]),
254
+ ...optionalSupportString("sdk_version", "support ticket sdkVersion", input["sdkVersion"]),
255
+ ...optionalSupportString("release", "support ticket release", input["release"]),
256
+ ...optionalTraceId(input["traceId"]),
257
+ ...optionalSupportString("event_id", "support ticket eventId", input["eventId"]),
258
+ ...optionalDiagnostics(input["diagnostics"])
259
+ };
164
260
  }
165
261
 
166
- function redactUrl(value) {
167
- try {
168
- const url = new URL(value);
169
- return `[redacted-url]${url.pathname || "/"}`;
170
- } catch {
171
- return value.split(/[?#]/u)[0];
262
+ /**
263
+ * @param {unknown} input
264
+ * @returns {SanitizedSupportTicketDraft}
265
+ */
266
+ function createSupportTicketDraft(input) {
267
+ if (!isRecord(input)) {
268
+ throw new SdkError("validation_error", "support ticket draft input must be an object");
172
269
  }
270
+ const source = requireAllowedValue("support ticket source", input["source"], SUPPORT_TICKET_SOURCES);
271
+ const category = requireAllowedValue("support ticket category", input["category"], SUPPORT_TICKET_CATEGORIES);
272
+ const title = requiredSupportString("support ticket title", input["title"]);
273
+ const description = requiredSupportString("support ticket description", input["description"]);
274
+ return {
275
+ source,
276
+
277
+ category,
278
+ title,
279
+
280
+ description,
281
+ ...supportTicketContext(input)
282
+ };
173
283
  }
174
284
 
175
- module.exports = { buildCreateSupportTicketDraft };
285
+ module.exports = { createSupportTicketDraft };