@kyncode/sdk 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1434 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Loly: () => Loly,
24
+ SDK_VERSION: () => SDK_VERSION,
25
+ default: () => index_default,
26
+ onRequestError: () => onRequestError,
27
+ register: () => register
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/sanitizer.ts
32
+ var MAX_SANITIZE_DEPTH = 6;
33
+ var MAX_SANITIZE_NODES = 500;
34
+ var MAX_SANITIZED_STRING_LENGTH = 4096;
35
+ var MAX_SANITIZED_COLLECTION_SIZE = 100;
36
+ var MAX_SANITIZER_INPUT_LENGTH = MAX_SANITIZED_STRING_LENGTH * 4;
37
+ var REDACTED_SECRET = "[REDACTED_SECRET]";
38
+ var REDACTED_BEARER_TOKEN = "[REDACTED_BEARER_TOKEN]";
39
+ var REDACTED_JWT = "[REDACTED_JWT]";
40
+ var REDACTED_CARD = "[REDACTED_CARD]";
41
+ var REDACTED_PRIVATE_KEY = "[REDACTED_PRIVATE_KEY]";
42
+ var REDACTED_DATABASE_URL = "[REDACTED_DATABASE_URL]";
43
+ var CIRCULAR_REFERENCE = "[CIRCULAR]";
44
+ var DEPTH_LIMIT = "[TRUNCATED_DEPTH]";
45
+ var NODE_LIMIT = "[TRUNCATED_NODES]";
46
+ var COLLECTION_LIMIT_KEY = "__kyncode_truncated__";
47
+ var SENSITIVE_KEY_NAMES = /* @__PURE__ */ new Set([
48
+ "accesskey",
49
+ "accesstoken",
50
+ "apikey",
51
+ "auth",
52
+ "authorization",
53
+ "bearer",
54
+ "card",
55
+ "clientsecret",
56
+ "connectionstring",
57
+ "cookie",
58
+ "creditcard",
59
+ "credential",
60
+ "credentials",
61
+ "cvv",
62
+ "databaseurl",
63
+ "dburl",
64
+ "code",
65
+ "csrftoken",
66
+ "idtoken",
67
+ "invitecode",
68
+ "jwt",
69
+ "nonce",
70
+ "otp",
71
+ "pan",
72
+ "pass",
73
+ "passwd",
74
+ "password",
75
+ "pin",
76
+ "privatekey",
77
+ "refreshtoken",
78
+ "resetcode",
79
+ "secret",
80
+ "session",
81
+ "setcookie",
82
+ "sig",
83
+ "signature",
84
+ "state",
85
+ "token",
86
+ "verificationcode",
87
+ "xapikey"
88
+ ]);
89
+ var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([
90
+ "authorization",
91
+ "cookie",
92
+ "credential",
93
+ "cvv",
94
+ "jwt",
95
+ "passwd",
96
+ "password",
97
+ "secret",
98
+ "session",
99
+ "token"
100
+ ]);
101
+ var PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z\d ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z\d ]*PRIVATE KEY-----/giu;
102
+ var PARTIAL_PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z\d ]*PRIVATE KEY-----[\s\S]*$/giu;
103
+ var DATABASE_URL_PATTERN = /\b(?:mongodb(?:\+srv)?|mysql|postgres(?:ql)?|redis|rediss):\/\/[^\s'"<>]+/giu;
104
+ var URL_CREDENTIAL_PATTERN = /(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu;
105
+ var BEARER_TOKEN_PATTERN = /\bBearer\s+[A-Za-z0-9\-._~+/]{8,}=*/giu;
106
+ var JWT_PATTERN = /(?<![A-Za-z0-9_-])[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_+/=-]{8,}(?![A-Za-z0-9_+/=-])/gu;
107
+ var KYNCODE_KEY_PATTERN = /\bkyn_(?:live|test)_[a-f0-9]{16,}\b/giu;
108
+ var PROVIDER_TOKEN_PATTERN = /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|xox[baprs]-[A-Za-z0-9-]{10,}|(?:AKIA|ASIA)[A-Z0-9]{16}|sk-[A-Za-z0-9_-]{20,})\b/gu;
109
+ var SECRET_ASSIGNMENT_HEADER_PATTERN = /(?<![A-Za-z0-9_-])(["'`]?)([A-Za-z0-9_-]{1,256})\1\s*[:=]\s*/gimu;
110
+ var CARD_PATTERN = /(?<!\d)(?:\d[ -]?){12,18}\d(?!\d)/gu;
111
+ function sanitizeClientData(input) {
112
+ return sanitizeValue(input, { nodes: 0, seen: /* @__PURE__ */ new WeakSet() }, 0);
113
+ }
114
+ function sanitizeStringRecord(input) {
115
+ const entries = Object.entries(input).slice(0, MAX_SANITIZED_COLLECTION_SIZE);
116
+ const sanitized = entries.map(([key, value], index) => [
117
+ sanitizeOutputKey(key, index),
118
+ isSensitiveKey(key) ? REDACTED_SECRET : sanitizeText(value)
119
+ ]);
120
+ if (Object.keys(input).length > entries.length) {
121
+ sanitized.push([COLLECTION_LIMIT_KEY, "Additional fields were omitted"]);
122
+ }
123
+ return Object.fromEntries(sanitized);
124
+ }
125
+ function sanitizeText(value) {
126
+ const boundedValue = value.slice(0, MAX_SANITIZER_INPUT_LENGTH);
127
+ const omittedInputCharacters = value.length - boundedValue.length;
128
+ const embeddedSecretsRedacted = boundedValue.replace(PRIVATE_KEY_PATTERN, REDACTED_PRIVATE_KEY).replace(PARTIAL_PRIVATE_KEY_PATTERN, REDACTED_PRIVATE_KEY).replace(DATABASE_URL_PATTERN, REDACTED_DATABASE_URL).replace(URL_CREDENTIAL_PATTERN, `$1${REDACTED_SECRET}@`).replace(BEARER_TOKEN_PATTERN, REDACTED_BEARER_TOKEN).replace(JWT_PATTERN, REDACTED_JWT).replace(KYNCODE_KEY_PATTERN, REDACTED_SECRET).replace(PROVIDER_TOKEN_PATTERN, REDACTED_SECRET);
129
+ const assignmentsRedacted = redactSecretAssignments(embeddedSecretsRedacted);
130
+ const redacted = assignmentsRedacted.replace(CARD_PATTERN, REDACTED_CARD);
131
+ return truncateString(redacted, omittedInputCharacters);
132
+ }
133
+ function redactSecretAssignments(value) {
134
+ SECRET_ASSIGNMENT_HEADER_PATTERN.lastIndex = 0;
135
+ let cursor = 0;
136
+ let output = "";
137
+ let match;
138
+ while ((match = SECRET_ASSIGNMENT_HEADER_PATTERN.exec(value)) !== null) {
139
+ const quote = match[1] ?? "";
140
+ const key = match[2];
141
+ if (key === void 0 || !isSensitiveKey(key)) continue;
142
+ const valueStart = SECRET_ASSIGNMENT_HEADER_PATTERN.lastIndex;
143
+ const valueEnd = findAssignmentValueEnd(value, valueStart, match.index, quote);
144
+ output += value.slice(cursor, match.index);
145
+ output += `${quote}${key}${quote}=${REDACTED_SECRET}`;
146
+ cursor = valueEnd;
147
+ SECRET_ASSIGNMENT_HEADER_PATTERN.lastIndex = Math.max(valueEnd, valueStart);
148
+ }
149
+ SECRET_ASSIGNMENT_HEADER_PATTERN.lastIndex = 0;
150
+ return `${output}${value.slice(cursor)}`;
151
+ }
152
+ function findAssignmentValueEnd(value, valueStart, headerStart, keyQuote) {
153
+ const valueQuote = value[valueStart];
154
+ if (isQuote(valueQuote)) return scanToClosingQuote(value, valueStart + 1, valueQuote, true);
155
+ const enclosingQuote = keyQuote === "" ? value[headerStart - 1] : void 0;
156
+ if (isQuote(enclosingQuote) && !isEscapedAt(value, headerStart - 1)) {
157
+ return scanToClosingQuote(value, valueStart, enclosingQuote, false);
158
+ }
159
+ let cursor = valueStart;
160
+ while (cursor < value.length && !isUnquotedAssignmentDelimiter(value[cursor])) cursor += 1;
161
+ return cursor;
162
+ }
163
+ function scanToClosingQuote(value, start, quote, includeClosingQuote) {
164
+ let escaped = false;
165
+ for (let cursor = start; cursor < value.length; cursor += 1) {
166
+ const character = value[cursor];
167
+ if (character === "\r" || character === "\n") return cursor;
168
+ if (escaped) {
169
+ escaped = false;
170
+ continue;
171
+ }
172
+ if (character === "\\") {
173
+ escaped = true;
174
+ continue;
175
+ }
176
+ if (character === quote) return includeClosingQuote ? cursor + 1 : cursor;
177
+ }
178
+ return value.length;
179
+ }
180
+ function isEscapedAt(value, index) {
181
+ let backslashes = 0;
182
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
183
+ backslashes += 1;
184
+ }
185
+ return backslashes % 2 === 1;
186
+ }
187
+ function isQuote(value) {
188
+ return value === '"' || value === "'" || value === "`";
189
+ }
190
+ function isUnquotedAssignmentDelimiter(value) {
191
+ return value === void 0 || value === "\r" || value === "\n" || value === "," || value === ";" || value === "&";
192
+ }
193
+ function isSensitiveKey(key) {
194
+ const separated = key.replace(/([a-z\d])([A-Z])/gu, "$1_$2").toLowerCase();
195
+ const segments = separated.split(/[^a-z0-9]+/u).filter((segment) => segment !== "");
196
+ const canonical = segments.join("");
197
+ return SENSITIVE_KEY_NAMES.has(canonical) || segments.some((segment) => SENSITIVE_SEGMENTS.has(segment)) || segments.includes("api") && segments.includes("key") || segments.includes("private") && segments.includes("key") || segments.includes("database") && segments.includes("url") || segments.includes("connection") && segments.includes("string");
198
+ }
199
+ function sanitizeValue(value, state, depth) {
200
+ state.nodes += 1;
201
+ if (state.nodes > MAX_SANITIZE_NODES) return NODE_LIMIT;
202
+ if (depth > MAX_SANITIZE_DEPTH) return DEPTH_LIMIT;
203
+ if (value === null || value === void 0 || typeof value === "boolean") return value;
204
+ if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
205
+ if (typeof value === "string") return sanitizeText(value);
206
+ if (typeof value === "bigint") return value.toString();
207
+ if (typeof value === "function" || typeof value === "symbol") {
208
+ return `[${(typeof value).toUpperCase()}]`;
209
+ }
210
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return "[REDACTED_BUFFER]";
211
+ if (isDateLike(value)) return sanitizeDate(value);
212
+ if (state.seen.has(value)) return CIRCULAR_REFERENCE;
213
+ state.seen.add(value);
214
+ const sanitized = Array.isArray(value) ? sanitizeArray(value, state, depth) : sanitizeObject(value, state, depth);
215
+ state.seen.delete(value);
216
+ return sanitized;
217
+ }
218
+ function sanitizeArray(value, state, depth) {
219
+ const sanitized = value.slice(0, MAX_SANITIZED_COLLECTION_SIZE).map((item) => sanitizeValue(item, state, depth + 1));
220
+ return value.length > sanitized.length ? [...sanitized, `[TRUNCATED_${String(value.length - sanitized.length)}_ITEMS]`] : sanitized;
221
+ }
222
+ function sanitizeObject(value, state, depth) {
223
+ const entries = safeEntries(value);
224
+ if (entries === void 0) return "[UNREADABLE_OBJECT]";
225
+ const limitedEntries = entries.slice(0, MAX_SANITIZED_COLLECTION_SIZE);
226
+ const sanitized = limitedEntries.map(([key, childValue], index) => [
227
+ sanitizeOutputKey(key, index),
228
+ isSensitiveKey(key) ? REDACTED_SECRET : sanitizeValue(childValue, state, depth + 1)
229
+ ]);
230
+ if (entries.length > limitedEntries.length) {
231
+ sanitized.push([
232
+ COLLECTION_LIMIT_KEY,
233
+ `Omitted ${String(entries.length - limitedEntries.length)} fields`
234
+ ]);
235
+ }
236
+ return Object.fromEntries(sanitized);
237
+ }
238
+ function safeEntries(value) {
239
+ try {
240
+ return Object.entries(value);
241
+ } catch {
242
+ return void 0;
243
+ }
244
+ }
245
+ function isDateLike(value) {
246
+ try {
247
+ return Object.prototype.toString.call(value) === "[object Date]";
248
+ } catch {
249
+ return false;
250
+ }
251
+ }
252
+ function sanitizeDate(value) {
253
+ try {
254
+ const milliseconds = Date.prototype.getTime.call(value);
255
+ return Number.isNaN(milliseconds) ? "[INVALID_DATE]" : new Date(milliseconds).toISOString();
256
+ } catch {
257
+ return "[INVALID_DATE]";
258
+ }
259
+ }
260
+ function truncateString(value, omittedInputCharacters = 0) {
261
+ const omittedOutputCharacters = Math.max(0, value.length - MAX_SANITIZED_STRING_LENGTH);
262
+ const totalOmittedCharacters = omittedInputCharacters + omittedOutputCharacters;
263
+ if (totalOmittedCharacters === 0) return value;
264
+ return `${value.slice(0, MAX_SANITIZED_STRING_LENGTH)}[TRUNCATED_${String(totalOmittedCharacters)}_CHARS]`;
265
+ }
266
+ function sanitizeOutputKey(key, index) {
267
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
268
+ return `[UNSAFE_KEY_${String(index)}]`;
269
+ }
270
+ return sanitizeText(key) === key ? key : `[REDACTED_KEY_${String(index)}]`;
271
+ }
272
+
273
+ // src/request-capture.ts
274
+ var BODY_CAPTURE_TIMEOUT_MS = 50;
275
+ var MAX_CAPTURED_BODY_BYTES = 32768;
276
+ var REDACTED_QUERY_VALUE = "[REDACTED_QUERY_VALUE]";
277
+ var ALLOWED_HEADER_NAMES = /* @__PURE__ */ new Set([
278
+ "content-type",
279
+ "sentry-trace",
280
+ "traceparent",
281
+ "user-agent",
282
+ "x-correlation-id",
283
+ "x-request-id",
284
+ "x-vercel-id"
285
+ ]);
286
+ var VALID_METHODS = /* @__PURE__ */ new Set([
287
+ "DELETE",
288
+ "GET",
289
+ "HEAD",
290
+ "OPTIONS",
291
+ "PATCH",
292
+ "POST",
293
+ "PUT"
294
+ ]);
295
+ function captureStructuredRequest(input, options = {}) {
296
+ const route = input.route ?? "unknown";
297
+ const method = normalizeMethod(input.method);
298
+ const baseContext = {
299
+ headers: captureAllowedHeaders(input.headers),
300
+ ...method === void 0 ? {} : { method },
301
+ queryParams: extractQueryParams(route),
302
+ route: extractRoute(route)
303
+ };
304
+ if (options.captureBody !== true || input.body === void 0) return baseContext;
305
+ return {
306
+ ...baseContext,
307
+ body: toBodyRecord(sanitizeClientData(input.body))
308
+ };
309
+ }
310
+ function prepareRequestCapture(request, context, handlerArguments, options = {}) {
311
+ if (isWebRequestLike(request)) return prepareWebRequestCapture(request, options);
312
+ const requestRecord = asRecord(request);
313
+ const contextRecord = asRecord(context);
314
+ const route = readString(requestRecord, "path") ?? readString(contextRecord, "routePath") ?? "server-action";
315
+ const method = readString(requestRecord, "method");
316
+ const normalizedMethod = normalizeMethod(method);
317
+ const framework = captureNextFrameworkContext(contextRecord);
318
+ const baseSnapshot = {
319
+ ...framework === void 0 ? {} : { framework },
320
+ headers: captureAllowedHeaders(readUnknown(requestRecord, "headers")),
321
+ ...normalizedMethod === void 0 ? {} : { method: normalizedMethod },
322
+ queryParams: extractQueryParams(route),
323
+ route: extractRoute(route)
324
+ };
325
+ if (options.captureBody !== true || handlerArguments === void 0) {
326
+ return () => Promise.resolve(baseSnapshot);
327
+ }
328
+ const body = toBodyRecord(sanitizeClientData({ arguments: handlerArguments }));
329
+ const snapshot = { ...baseSnapshot, body };
330
+ return () => Promise.resolve(snapshot);
331
+ }
332
+ function prepareWebRequestCapture(request, options) {
333
+ const method = normalizeMethod(request.method);
334
+ const baseContext = {
335
+ headers: captureAllowedHeaders(request.headers),
336
+ ...method === void 0 ? {} : { method },
337
+ queryParams: extractQueryParams(request.url),
338
+ route: extractRoute(request.url)
339
+ };
340
+ if (options.captureBody !== true || method === "GET" || method === "HEAD") {
341
+ return () => Promise.resolve(baseContext);
342
+ }
343
+ const declaredContentLength = readHeader(request.headers, "content-length");
344
+ if (declaredContentLength !== void 0 && Number(declaredContentLength) > MAX_CAPTURED_BODY_BYTES) {
345
+ return () => Promise.resolve({
346
+ ...baseContext,
347
+ body: { value: "[BODY_OMITTED_SIZE_LIMIT]" }
348
+ });
349
+ }
350
+ const clonedRequest = safeClone(request);
351
+ if (clonedRequest === void 0) return () => Promise.resolve(baseContext);
352
+ return async () => {
353
+ const body = await readRequestBody(clonedRequest);
354
+ return body === void 0 ? baseContext : { ...baseContext, body };
355
+ };
356
+ }
357
+ async function readRequestBody(request) {
358
+ const contentLength = readHeader(request.headers, "content-length");
359
+ if (contentLength !== void 0 && Number(contentLength) > MAX_CAPTURED_BODY_BYTES) {
360
+ cancelReadableBody(request.body, "KYNCODE body capture declared size limit");
361
+ return { value: "[BODY_OMITTED_SIZE_LIMIT]" };
362
+ }
363
+ const body = request.body;
364
+ if (body === null || body === void 0) return void 0;
365
+ if (!hasBodyReader(body)) {
366
+ cancelReadableBody(body, "KYNCODE body capture received an unreadable clone");
367
+ return void 0;
368
+ }
369
+ const bytes = await readBoundedBody(body);
370
+ if (bytes === "limit") return { value: "[BODY_OMITTED_SIZE_LIMIT]" };
371
+ if (bytes === void 0 || bytes.byteLength === 0) return void 0;
372
+ const text = new TextDecoder().decode(bytes);
373
+ const contentType = readHeader(request.headers, "content-type")?.toLowerCase() ?? "";
374
+ if (contentType.includes("application/json") || contentType.includes("+json")) {
375
+ try {
376
+ const parsed = JSON.parse(text);
377
+ return toBodyRecord(sanitizeClientData(parsed));
378
+ } catch {
379
+ return { value: "[INVALID_JSON_BODY]" };
380
+ }
381
+ }
382
+ if (contentType.includes("application/x-www-form-urlencoded")) {
383
+ return toBodyRecord(sanitizeClientData(Object.fromEntries(new URLSearchParams(text))));
384
+ }
385
+ return { value: "[BODY_OMITTED_UNSUPPORTED_CONTENT_TYPE]" };
386
+ }
387
+ function captureAllowedHeaders(value) {
388
+ const headers = normalizeHeaderRecord(value);
389
+ const allowed = Object.fromEntries(
390
+ Object.entries(headers).filter(([key]) => ALLOWED_HEADER_NAMES.has(key.toLowerCase()))
391
+ );
392
+ return sanitizeStringRecord(allowed);
393
+ }
394
+ function normalizeHeaderRecord(value) {
395
+ const entries = readHeaderEntries(value);
396
+ if (entries !== void 0) return Object.fromEntries(entries);
397
+ const record = asRecord(value);
398
+ if (record === void 0) return {};
399
+ return Object.fromEntries(
400
+ safeObjectEntries(record).flatMap(([key, headerValue]) => {
401
+ if (typeof headerValue === "string") return [[key.toLowerCase(), headerValue]];
402
+ if (Array.isArray(headerValue) && headerValue.every((item) => typeof item === "string")) {
403
+ return [[key.toLowerCase(), headerValue.join(", ")]];
404
+ }
405
+ return [];
406
+ })
407
+ );
408
+ }
409
+ function readHeaderEntries(value) {
410
+ const record = asRecord(value);
411
+ const entriesMethod = record?.["entries"];
412
+ if (typeof entriesMethod !== "function") return void 0;
413
+ try {
414
+ const rawEntries = Reflect.apply(entriesMethod, value, []);
415
+ if (!isIterable(rawEntries)) return void 0;
416
+ const entries = [];
417
+ for (const entry of rawEntries) {
418
+ if (Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "string") {
419
+ entries.push([entry[0].toLowerCase(), entry[1]]);
420
+ }
421
+ }
422
+ return entries;
423
+ } catch {
424
+ return void 0;
425
+ }
426
+ }
427
+ function extractRoute(value) {
428
+ if (value === "server-action" || value === "unknown") return value;
429
+ try {
430
+ return sanitizeText(new URL(value, "https://sdk.kyncode.invalid").pathname);
431
+ } catch {
432
+ return sanitizeText(value.split("?")[0] ?? value);
433
+ }
434
+ }
435
+ function extractQueryParams(value) {
436
+ try {
437
+ const url = new URL(value, "https://sdk.kyncode.invalid");
438
+ return sanitizeStringRecord(
439
+ Object.fromEntries(
440
+ [...url.searchParams.keys()].map((key) => [key, REDACTED_QUERY_VALUE])
441
+ )
442
+ );
443
+ } catch {
444
+ return {};
445
+ }
446
+ }
447
+ function normalizeMethod(value) {
448
+ const candidate = value?.toUpperCase();
449
+ return candidate !== void 0 && VALID_METHODS.has(candidate) ? candidate : void 0;
450
+ }
451
+ function captureNextFrameworkContext(context) {
452
+ if (context === void 0) return void 0;
453
+ const routePath = readString(context, "routePath");
454
+ const routeType = readString(context, "routeType");
455
+ const routerKind = readString(context, "routerKind");
456
+ const renderSource = readString(context, "renderSource");
457
+ if (routePath === void 0 && routeType === void 0 && routerKind === void 0 && renderSource === void 0)
458
+ return void 0;
459
+ return {
460
+ name: "nextjs",
461
+ ...renderSource === void 0 ? {} : { renderSource: sanitizeText(renderSource) },
462
+ ...routePath === void 0 ? {} : { routePath: sanitizeText(routePath) },
463
+ ...routeType === void 0 ? {} : { routeType: sanitizeText(routeType) },
464
+ ...routerKind === void 0 ? {} : { routerKind: sanitizeText(routerKind) }
465
+ };
466
+ }
467
+ function toBodyRecord(value) {
468
+ return isPlainRecord(value) ? value : { value };
469
+ }
470
+ function asRecord(value) {
471
+ return isPlainRecord(value) ? value : void 0;
472
+ }
473
+ function isPlainRecord(value) {
474
+ return typeof value === "object" && value !== null && !Array.isArray(value);
475
+ }
476
+ function readString(record, key) {
477
+ const value = readUnknown(record, key);
478
+ return typeof value === "string" ? value : void 0;
479
+ }
480
+ function readUnknown(record, key) {
481
+ try {
482
+ return record?.[key];
483
+ } catch {
484
+ return void 0;
485
+ }
486
+ }
487
+ function safeObjectEntries(record) {
488
+ try {
489
+ return Object.entries(record);
490
+ } catch {
491
+ return [];
492
+ }
493
+ }
494
+ function isWebRequestLike(value) {
495
+ const record = asRecord(value);
496
+ return typeof readUnknown(record, "url") === "string" && typeof readUnknown(record, "method") === "string";
497
+ }
498
+ function safeClone(request) {
499
+ if (typeof request.clone !== "function") return void 0;
500
+ try {
501
+ const clone = request.clone.call(request);
502
+ if (isWebRequestLike(clone)) return clone;
503
+ cancelReadableBody(
504
+ readUnknown(asRecord(clone), "body"),
505
+ "KYNCODE body capture received an invalid clone"
506
+ );
507
+ return void 0;
508
+ } catch {
509
+ return void 0;
510
+ }
511
+ }
512
+ function readHeader(headers, name) {
513
+ return normalizeHeaderRecord(headers)[name];
514
+ }
515
+ async function readBoundedBody(body) {
516
+ let reader;
517
+ try {
518
+ reader = body.getReader();
519
+ } catch {
520
+ cancelReadableBody(body, "KYNCODE body capture could not acquire a reader");
521
+ return void 0;
522
+ }
523
+ const chunks = [];
524
+ let totalBytes = 0;
525
+ const deadline = Date.now() + BODY_CAPTURE_TIMEOUT_MS;
526
+ try {
527
+ while (Date.now() < deadline) {
528
+ const remainingMs = Math.max(1, deadline - Date.now());
529
+ const result = await readWithin(reader, remainingMs);
530
+ if (result === void 0) {
531
+ cancelReader(reader, "KYNCODE body capture timeout");
532
+ return void 0;
533
+ }
534
+ if (result.done) return concatenateBytes(chunks, totalBytes);
535
+ const chunk = toByteChunk(result.value);
536
+ if (chunk === void 0) {
537
+ cancelReader(reader, "KYNCODE body capture received a non-byte chunk");
538
+ return void 0;
539
+ }
540
+ totalBytes += chunk.byteLength;
541
+ if (totalBytes > MAX_CAPTURED_BODY_BYTES) {
542
+ cancelReader(reader, "KYNCODE body capture size limit");
543
+ return "limit";
544
+ }
545
+ chunks.push(chunk);
546
+ }
547
+ cancelReader(reader, "KYNCODE body capture timeout");
548
+ return void 0;
549
+ } catch {
550
+ cancelReader(reader, "KYNCODE body capture failed");
551
+ return void 0;
552
+ }
553
+ }
554
+ async function readWithin(reader, timeoutMs) {
555
+ let timeoutId;
556
+ const timeout = new Promise((resolve) => {
557
+ timeoutId = setTimeout(() => {
558
+ resolve(void 0);
559
+ }, timeoutMs);
560
+ });
561
+ try {
562
+ return await Promise.race([reader.read(), timeout]);
563
+ } finally {
564
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
565
+ }
566
+ }
567
+ function cancelReader(reader, reason) {
568
+ try {
569
+ const cancellation = reader.cancel(reason);
570
+ if (cancellation !== void 0) void cancellation.catch(() => void 0);
571
+ } catch {
572
+ }
573
+ }
574
+ function cancelReadableBody(body, reason) {
575
+ const record = asRecord(body);
576
+ const cancel = readUnknown(record, "cancel");
577
+ if (typeof cancel !== "function") return;
578
+ try {
579
+ const cancellation = Reflect.apply(cancel, body, [reason]);
580
+ if (isPromiseLike(cancellation)) {
581
+ void Promise.resolve(cancellation).catch(() => void 0);
582
+ }
583
+ } catch {
584
+ }
585
+ }
586
+ function concatenateBytes(chunks, totalBytes) {
587
+ const result = new Uint8Array(totalBytes);
588
+ let offset = 0;
589
+ for (const chunk of chunks) {
590
+ result.set(chunk, offset);
591
+ offset += chunk.byteLength;
592
+ }
593
+ return result;
594
+ }
595
+ function toByteChunk(value) {
596
+ if (!ArrayBuffer.isView(value)) return void 0;
597
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
598
+ }
599
+ function isIterable(value) {
600
+ if (typeof value !== "object" || value === null) return false;
601
+ try {
602
+ return Symbol.iterator in value;
603
+ } catch {
604
+ return false;
605
+ }
606
+ }
607
+ function hasBodyReader(value) {
608
+ const record = asRecord(value);
609
+ return typeof readUnknown(record, "getReader") === "function";
610
+ }
611
+ function isPromiseLike(value) {
612
+ if (typeof value !== "object" || value === null) return false;
613
+ try {
614
+ return typeof value["then"] === "function";
615
+ } catch {
616
+ return false;
617
+ }
618
+ }
619
+
620
+ // src/adapters/express.ts
621
+ function createExpressErrorHandler(capture, shouldCaptureBody = () => false) {
622
+ return (error, request, response, next) => {
623
+ void response;
624
+ try {
625
+ capture(
626
+ error,
627
+ captureStructuredRequest(
628
+ {
629
+ ...request.body === void 0 ? {} : { body: request.body },
630
+ ...request.headers === void 0 ? {} : { headers: request.headers },
631
+ ...request.method === void 0 ? {} : { method: request.method },
632
+ route: request.originalUrl ?? request.url ?? "unknown"
633
+ },
634
+ { captureBody: shouldCaptureBody() }
635
+ ),
636
+ "express"
637
+ );
638
+ } catch {
639
+ }
640
+ next(error);
641
+ };
642
+ }
643
+
644
+ // src/adapters/fastify.ts
645
+ function createFastifyErrorHandler(capture, shouldCaptureBody = () => false) {
646
+ return (error, request, reply) => {
647
+ try {
648
+ capture(
649
+ error,
650
+ captureStructuredRequest(
651
+ {
652
+ ...request.body === void 0 ? {} : { body: request.body },
653
+ ...request.headers === void 0 && request.raw?.headers === void 0 ? {} : { headers: request.headers ?? request.raw?.headers },
654
+ ...request.method === void 0 ? {} : { method: request.method },
655
+ route: request.raw?.url ?? request.url ?? "unknown"
656
+ },
657
+ { captureBody: shouldCaptureBody() }
658
+ ),
659
+ "fastify"
660
+ );
661
+ } catch {
662
+ }
663
+ return reply.send(error);
664
+ };
665
+ }
666
+
667
+ // src/adapters/node.ts
668
+ function registerNodeProcessListeners(capture) {
669
+ const onUncaughtExceptionMonitor = (error, origin) => {
670
+ captureProcessError(capture, error, origin);
671
+ };
672
+ process.on("uncaughtExceptionMonitor", onUncaughtExceptionMonitor);
673
+ return () => {
674
+ process.off("uncaughtExceptionMonitor", onUncaughtExceptionMonitor);
675
+ };
676
+ }
677
+ function captureProcessError(capture, error, eventName) {
678
+ try {
679
+ capture(
680
+ error,
681
+ {
682
+ headers: {},
683
+ queryParams: {},
684
+ route: `/process/${eventName}`
685
+ },
686
+ "node"
687
+ );
688
+ } catch {
689
+ }
690
+ }
691
+
692
+ // src/dispatch.ts
693
+ var DISPATCH_TIMEOUT_MS = 2e3;
694
+ var MAX_PAYLOAD_BYTES = 64 * 1024;
695
+ var MAX_PENDING_EVENTS = 100;
696
+ var MAX_CONCURRENT_DISPATCHES = 2;
697
+ var MAX_DISPATCHES_PER_MINUTE = 60;
698
+ var MAX_DELIVERY_ATTEMPTS = 3;
699
+ var STARTUP_CONNECTIVITY_TIMEOUT_MS = 500;
700
+ var DEFAULT_FLUSH_TIMEOUT_MS = 2e3;
701
+ var RATE_WINDOW_MS = 6e4;
702
+ var RETRY_BASE_DELAY_MS = 100;
703
+ var RUNTIME_REQUEST_CONTEXT_SYMBOLS = [
704
+ /* @__PURE__ */ Symbol.for("@vercel/request-context"),
705
+ /* @__PURE__ */ Symbol.for("@next/request-context")
706
+ ];
707
+ var BoundedDispatcher = class {
708
+ #active = 0;
709
+ #pending = [];
710
+ #recentDispatches = [];
711
+ #flushWaiters = /* @__PURE__ */ new Set();
712
+ enqueue(payload, config, maxDeliveryAttempts = MAX_DELIVERY_ATTEMPTS) {
713
+ if (this.#pending.length >= MAX_PENDING_EVENTS) {
714
+ debug(config, "Event dropped because the bounded delivery queue is full");
715
+ return Promise.resolve({
716
+ message: "The bounded delivery queue is full",
717
+ ok: false,
718
+ status: "queue_full"
719
+ });
720
+ }
721
+ const serializedBody = serializePayload(payload);
722
+ return new Promise((resolve) => {
723
+ this.#pending.push({
724
+ config,
725
+ idempotencyKey: payload.eventId,
726
+ maxDeliveryAttempts,
727
+ resolve,
728
+ serializedBody
729
+ });
730
+ this.#pump();
731
+ });
732
+ }
733
+ async flush(options = {}) {
734
+ if (this.#isIdle()) return { completed: true, pending: 0, timedOut: false };
735
+ const timeoutMs = normalizeFlushTimeout(options.timeoutMs);
736
+ return new Promise((resolve) => {
737
+ let settled = false;
738
+ const settle = (timedOut) => {
739
+ if (settled) return;
740
+ settled = true;
741
+ this.#flushWaiters.delete(onIdle);
742
+ clearTimeout(timeoutId);
743
+ const pending = this.#active + this.#pending.length;
744
+ resolve({ completed: pending === 0, pending, timedOut });
745
+ };
746
+ const onIdle = () => {
747
+ settle(false);
748
+ };
749
+ const timeoutId = setTimeout(() => {
750
+ settle(true);
751
+ }, timeoutMs);
752
+ this.#flushWaiters.add(onIdle);
753
+ if (this.#isIdle()) settle(false);
754
+ });
755
+ }
756
+ reset() {
757
+ const dropped = {
758
+ message: "Delivery state was reset",
759
+ ok: false,
760
+ status: "failed"
761
+ };
762
+ for (const queued of this.#pending.splice(0)) queued.resolve(dropped);
763
+ this.#recentDispatches.splice(0);
764
+ this.#notifyIfIdle();
765
+ }
766
+ #pump() {
767
+ while (this.#active < MAX_CONCURRENT_DISPATCHES) {
768
+ const queued = this.#pending.shift();
769
+ if (queued === void 0) break;
770
+ if (!this.#consumeRateSlot()) {
771
+ queued.resolve({
772
+ message: "The SDK delivery rate limit was reached",
773
+ ok: false,
774
+ status: "rate_limited"
775
+ });
776
+ debug(queued.config, "Event dropped by the client-side delivery rate limit");
777
+ continue;
778
+ }
779
+ this.#active += 1;
780
+ void postSerializedJson(
781
+ queued.config.endpoint,
782
+ queued.serializedBody,
783
+ queued.idempotencyKey,
784
+ queued.config,
785
+ queued.maxDeliveryAttempts
786
+ ).then(queued.resolve).finally(() => {
787
+ this.#active -= 1;
788
+ this.#pump();
789
+ this.#notifyIfIdle();
790
+ });
791
+ }
792
+ this.#notifyIfIdle();
793
+ }
794
+ #consumeRateSlot() {
795
+ const now = Date.now();
796
+ while ((this.#recentDispatches[0] ?? now) <= now - RATE_WINDOW_MS) {
797
+ this.#recentDispatches.shift();
798
+ }
799
+ if (this.#recentDispatches.length >= MAX_DISPATCHES_PER_MINUTE) return false;
800
+ this.#recentDispatches.push(now);
801
+ return true;
802
+ }
803
+ #isIdle() {
804
+ return this.#active === 0 && this.#pending.length === 0;
805
+ }
806
+ #notifyIfIdle() {
807
+ if (!this.#isIdle()) return;
808
+ for (const waiter of [...this.#flushWaiters]) waiter();
809
+ }
810
+ };
811
+ var dispatcher = new BoundedDispatcher();
812
+ function dispatchInBackground(payload, config) {
813
+ const delivery = dispatcher.enqueue(payload, config);
814
+ void scheduleWithRuntime(delivery, config);
815
+ }
816
+ async function dispatchWithRuntime(payload, config) {
817
+ const waitUntil = resolveRuntimeWaitUntil();
818
+ if (waitUntil === void 0) {
819
+ debug(config, "Runtime waitUntil unavailable; using one bounded delivery attempt");
820
+ await Promise.allSettled([dispatcher.enqueue(payload, config, 1)]);
821
+ return;
822
+ }
823
+ let settleRuntimeDelivery = () => void 0;
824
+ const runtimeDelivery = new Promise((resolve) => {
825
+ settleRuntimeDelivery = resolve;
826
+ });
827
+ try {
828
+ waitUntil(runtimeDelivery);
829
+ } catch (error) {
830
+ settleRuntimeDelivery();
831
+ debug(config, "Runtime waitUntil rejected delivery; using one bounded delivery attempt", error);
832
+ await Promise.allSettled([dispatcher.enqueue(payload, config, 1)]);
833
+ return;
834
+ }
835
+ void dispatcher.enqueue(payload, config).then(() => {
836
+ settleRuntimeDelivery();
837
+ });
838
+ }
839
+ function dispatchAndWait(payload, config) {
840
+ return dispatcher.enqueue(payload, config);
841
+ }
842
+ async function dispatchConnectivity(config, runtime, sdkVersion) {
843
+ const idempotencyKey = `connect_${createRandomIdentifier()}`;
844
+ const serializedBody = serializeConnectivity(runtime, sdkVersion);
845
+ return postSerializedJson(
846
+ connectivityEndpoint(config.endpoint),
847
+ serializedBody,
848
+ idempotencyKey,
849
+ config
850
+ );
851
+ }
852
+ function dispatchStartupConnectivity(config, runtime, sdkVersion) {
853
+ return postOnce(
854
+ connectivityEndpoint(config.endpoint),
855
+ serializeConnectivity(runtime, sdkVersion),
856
+ `connect_${createRandomIdentifier()}`,
857
+ config,
858
+ STARTUP_CONNECTIVITY_TIMEOUT_MS
859
+ );
860
+ }
861
+ function flushDispatches(options) {
862
+ return dispatcher.flush(options);
863
+ }
864
+ function resetDispatchState() {
865
+ dispatcher.reset();
866
+ }
867
+ async function scheduleWithRuntime(dispatchPromise, config) {
868
+ try {
869
+ const waitUntil = resolveRuntimeWaitUntil();
870
+ if (waitUntil === void 0) throw new Error("waitUntil is unavailable");
871
+ waitUntil(dispatchPromise);
872
+ } catch (error) {
873
+ debug(config, "Runtime waitUntil unavailable; using the bounded Node queue", error);
874
+ await Promise.allSettled([dispatchPromise]);
875
+ }
876
+ }
877
+ function resolveRuntimeWaitUntil() {
878
+ for (const contextSymbol of RUNTIME_REQUEST_CONTEXT_SYMBOLS) {
879
+ try {
880
+ const globalRecord = globalThis;
881
+ const accessor = globalRecord[contextSymbol];
882
+ if (typeof accessor !== "object" || accessor === null) continue;
883
+ const get = accessor["get"];
884
+ if (typeof get !== "function") continue;
885
+ const context = get.call(accessor);
886
+ if (typeof context !== "object" || context === null) continue;
887
+ const waitUntil = context["waitUntil"];
888
+ if (typeof waitUntil !== "function") continue;
889
+ return (promise) => {
890
+ void waitUntil.call(context, promise);
891
+ };
892
+ } catch {
893
+ }
894
+ }
895
+ return void 0;
896
+ }
897
+ async function postSerializedJson(endpoint, serializedBody, idempotencyKey, config, maxDeliveryAttempts = MAX_DELIVERY_ATTEMPTS) {
898
+ let lastOutcome = {
899
+ message: "KYNCODE dispatch failed safely",
900
+ ok: false,
901
+ status: "failed"
902
+ };
903
+ for (let attempt = 1; attempt <= maxDeliveryAttempts; attempt += 1) {
904
+ lastOutcome = await postOnce(endpoint, serializedBody, idempotencyKey, config);
905
+ if (lastOutcome.ok || !isRetryable(lastOutcome) || attempt === maxDeliveryAttempts) {
906
+ return lastOutcome;
907
+ }
908
+ await delay(RETRY_BASE_DELAY_MS * attempt);
909
+ }
910
+ return lastOutcome;
911
+ }
912
+ async function postOnce(endpoint, serializedBody, idempotencyKey, config, timeoutMs = DISPATCH_TIMEOUT_MS) {
913
+ const controller = new AbortController();
914
+ let timeoutId;
915
+ const timeout = new Promise((resolve) => {
916
+ timeoutId = setTimeout(() => {
917
+ controller.abort();
918
+ resolve(void 0);
919
+ }, timeoutMs);
920
+ });
921
+ try {
922
+ const response = await Promise.race([
923
+ fetch(endpoint, {
924
+ body: serializedBody,
925
+ credentials: "omit",
926
+ headers: {
927
+ Authorization: `Bearer ${config.apiKey}`,
928
+ "Content-Type": "application/json",
929
+ "Idempotency-Key": idempotencyKey
930
+ },
931
+ method: "POST",
932
+ redirect: "error",
933
+ referrerPolicy: "no-referrer",
934
+ signal: controller.signal
935
+ }),
936
+ timeout
937
+ ]);
938
+ if (response === void 0) {
939
+ const message = `KYNCODE dispatch timed out after ${String(timeoutMs)}ms`;
940
+ debug(config, message);
941
+ return { message, ok: false, status: "failed" };
942
+ }
943
+ cancelResponseBody(response);
944
+ if (!response.ok) {
945
+ debug(config, `KYNCODE ingestion returned HTTP ${String(response.status)}`);
946
+ return {
947
+ httpStatus: response.status,
948
+ message: `KYNCODE ingestion returned HTTP ${String(response.status)}`,
949
+ ok: false,
950
+ status: response.status === 429 ? "rate_limited" : "failed"
951
+ };
952
+ }
953
+ return {
954
+ httpStatus: response.status,
955
+ message: "Event delivered to KYNCODE",
956
+ ok: true,
957
+ status: "sent"
958
+ };
959
+ } catch (error) {
960
+ const message = controller.signal.aborted ? `KYNCODE dispatch timed out after ${String(timeoutMs)}ms` : "KYNCODE dispatch failed safely";
961
+ debug(config, message, error);
962
+ return { message, ok: false, status: "failed" };
963
+ } finally {
964
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
965
+ }
966
+ }
967
+ function serializeConnectivity(runtime, sdkVersion) {
968
+ return JSON.stringify({ runtime, sdk: "@kyncode/sdk", sdkVersion });
969
+ }
970
+ function serializePayload(payload) {
971
+ const serialized = JSON.stringify(payload);
972
+ if (byteLength(serialized) <= MAX_PAYLOAD_BYTES) return serialized;
973
+ const compactPayload = {
974
+ ...payload,
975
+ context: {
976
+ ...payload.context.framework === void 0 ? {} : { framework: payload.context.framework },
977
+ headers: payload.context.headers,
978
+ ...payload.context.method === void 0 ? {} : { method: payload.context.method },
979
+ queryParams: {},
980
+ route: payload.context.route
981
+ },
982
+ error: {
983
+ ...payload.error.digest === void 0 ? {} : { digest: payload.error.digest },
984
+ message: payload.error.message,
985
+ name: payload.error.name,
986
+ stack: payload.error.stack
987
+ },
988
+ metadata: {
989
+ ...payload.metadata ?? {},
990
+ payloadTruncated: "true",
991
+ truncationReason: "payload_size_limit"
992
+ }
993
+ };
994
+ const compact = JSON.stringify(compactPayload);
995
+ if (byteLength(compact) <= MAX_PAYLOAD_BYTES) return compact;
996
+ const minimalPayload = {
997
+ context: {
998
+ headers: {},
999
+ ...compactPayload.context.method === void 0 ? {} : { method: compactPayload.context.method },
1000
+ queryParams: {},
1001
+ route: compactPayload.context.route.slice(0, 1024)
1002
+ },
1003
+ environment: compactPayload.environment,
1004
+ error: {
1005
+ message: compactPayload.error.message.slice(0, 1024),
1006
+ name: compactPayload.error.name.slice(0, 128),
1007
+ stack: compactPayload.error.stack.slice(0, 4096)
1008
+ },
1009
+ eventId: compactPayload.eventId,
1010
+ ...compactPayload.runtime === void 0 ? {} : { runtime: compactPayload.runtime },
1011
+ sdkVersion: compactPayload.sdkVersion,
1012
+ source: "sdk_code",
1013
+ timestamp: compactPayload.timestamp
1014
+ };
1015
+ const minimal = JSON.stringify(minimalPayload);
1016
+ if (byteLength(minimal) <= MAX_PAYLOAD_BYTES) return minimal;
1017
+ return JSON.stringify({
1018
+ ...minimalPayload,
1019
+ context: {
1020
+ headers: {},
1021
+ ...minimalPayload.context.method === void 0 ? {} : { method: minimalPayload.context.method },
1022
+ queryParams: {},
1023
+ route: "unknown"
1024
+ },
1025
+ error: {
1026
+ message: "Application error exceeded the SDK payload limit",
1027
+ name: "Error",
1028
+ stack: "Error details omitted by the SDK payload limit"
1029
+ }
1030
+ });
1031
+ }
1032
+ function connectivityEndpoint(ingestionEndpoint) {
1033
+ const endpoint = new URL(ingestionEndpoint);
1034
+ endpoint.pathname = "/v1/telemetry/ping";
1035
+ endpoint.search = "";
1036
+ endpoint.hash = "";
1037
+ return endpoint.toString();
1038
+ }
1039
+ function normalizeFlushTimeout(value) {
1040
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(Math.floor(value), 3e4) : DEFAULT_FLUSH_TIMEOUT_MS;
1041
+ }
1042
+ function isRetryable(outcome) {
1043
+ if (outcome.ok) return false;
1044
+ if (outcome.httpStatus === void 0) return true;
1045
+ return outcome.httpStatus === 408 || outcome.httpStatus === 425 || outcome.httpStatus === 429 || outcome.httpStatus >= 500;
1046
+ }
1047
+ function delay(milliseconds) {
1048
+ return new Promise((resolve) => {
1049
+ setTimeout(resolve, milliseconds);
1050
+ });
1051
+ }
1052
+ function createRandomIdentifier() {
1053
+ try {
1054
+ if (typeof globalThis.crypto.randomUUID === "function") return globalThis.crypto.randomUUID();
1055
+ } catch {
1056
+ }
1057
+ return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
1058
+ }
1059
+ function cancelResponseBody(response) {
1060
+ try {
1061
+ const cancellation = response.body?.cancel();
1062
+ if (cancellation !== void 0) void cancellation.catch(() => void 0);
1063
+ } catch {
1064
+ }
1065
+ }
1066
+ function byteLength(value) {
1067
+ if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(value).byteLength;
1068
+ return value.length;
1069
+ }
1070
+ function debug(config, message, detail) {
1071
+ if (!config.debug) return;
1072
+ const detailMessage = readErrorMessage(detail);
1073
+ console.debug(
1074
+ `[KYNCODE SDK] ${sanitizeText(message)}`,
1075
+ detailMessage === void 0 ? "" : sanitizeText(detailMessage)
1076
+ );
1077
+ }
1078
+ function readErrorMessage(value) {
1079
+ if (typeof value === "string") return value;
1080
+ if (typeof value !== "object" || value === null) return void 0;
1081
+ try {
1082
+ const message = value["message"];
1083
+ return typeof message === "string" ? message : void 0;
1084
+ } catch {
1085
+ return void 0;
1086
+ }
1087
+ }
1088
+
1089
+ // src/loly.ts
1090
+ var SDK_VERSION = true ? "0.1.0" : "0.1.0";
1091
+ var DEFAULT_ENDPOINT = "https://api.kyncode.com/v1/ingest";
1092
+ var LIVE_API_KEY_PATTERN = /^kyn_live_[a-f0-9]{64}$/u;
1093
+ var DEDUPE_WINDOW_MS = 5e3;
1094
+ var MAX_PRIMITIVE_DEDUPE_ENTRIES = 256;
1095
+ var LolyClient = class {
1096
+ #configOverrides = {};
1097
+ #processEventCleanup;
1098
+ #recentErrors = /* @__PURE__ */ new WeakMap();
1099
+ #recentPrimitiveErrors = /* @__PURE__ */ new Map();
1100
+ init = (config = {}) => {
1101
+ enableRuntimeSourceMaps();
1102
+ this.#configOverrides = { ...config };
1103
+ this.#recentErrors = /* @__PURE__ */ new WeakMap();
1104
+ this.#recentPrimitiveErrors.clear();
1105
+ if (readEnvironment("NODE_ENV") === "test") resetDispatchState();
1106
+ };
1107
+ connect = async (runtime = "Node.js") => {
1108
+ enableRuntimeSourceMaps();
1109
+ const config = this.#resolveConfig();
1110
+ const unavailable = this.#connectConfigurationDiagnostic(config, runtime);
1111
+ if (unavailable !== void 0) return unavailable;
1112
+ const outcome = await dispatchConnectivity(
1113
+ { apiKey: config.apiKey, debug: config.debug, endpoint: config.endpoint },
1114
+ runtime,
1115
+ SDK_VERSION
1116
+ );
1117
+ return connectivityDiagnosticFromOutcome(runtime, outcome);
1118
+ };
1119
+ connectForStartup = async (runtime) => {
1120
+ enableRuntimeSourceMaps();
1121
+ const config = this.#resolveConfig();
1122
+ const unavailable = this.#connectConfigurationDiagnostic(config, runtime);
1123
+ if (unavailable !== void 0) return unavailable;
1124
+ const outcome = await dispatchStartupConnectivity(
1125
+ { apiKey: config.apiKey, debug: config.debug, endpoint: config.endpoint },
1126
+ runtime,
1127
+ SDK_VERSION
1128
+ );
1129
+ return connectivityDiagnosticFromOutcome(runtime, outcome);
1130
+ };
1131
+ expressErrorHandler = () => createExpressErrorHandler(
1132
+ (error, requestContext, runtime) => {
1133
+ this.#captureSafely(error, requestContext, runtime);
1134
+ },
1135
+ () => this.#resolveConfig().captureBody
1136
+ );
1137
+ fastifyErrorHandler = () => createFastifyErrorHandler(
1138
+ (error, requestContext, runtime) => {
1139
+ this.#captureSafely(error, requestContext, runtime);
1140
+ },
1141
+ () => this.#resolveConfig().captureBody
1142
+ );
1143
+ flush = (options) => flushDispatches(options);
1144
+ listenToProcessEvents = () => {
1145
+ if (this.#processEventCleanup !== void 0) return this.#processEventCleanup;
1146
+ const unregister = registerNodeProcessListeners((error, requestContext, runtime) => {
1147
+ this.#captureSafely(error, requestContext, runtime);
1148
+ });
1149
+ const cleanup = () => {
1150
+ unregister();
1151
+ if (this.#processEventCleanup === cleanup) this.#processEventCleanup = void 0;
1152
+ };
1153
+ this.#processEventCleanup = cleanup;
1154
+ return cleanup;
1155
+ };
1156
+ shield = (handler) => {
1157
+ return async (...arguments_) => {
1158
+ const config = this.#resolveConfig();
1159
+ const captureRequest = this.#prepareRequestSafely(arguments_, config.captureBody);
1160
+ try {
1161
+ return await handler(...arguments_);
1162
+ } catch (error) {
1163
+ if (this.#canSend(config) && this.#reserveError(error)) {
1164
+ scheduleMicrotask(() => {
1165
+ void captureRequest().catch((captureError) => {
1166
+ this.#debug(config, "Request capture failed safely", captureError);
1167
+ return createFallbackRequestContext();
1168
+ }).then((requestContext) => {
1169
+ this.#captureReserved(error, requestContext, config);
1170
+ });
1171
+ });
1172
+ }
1173
+ throw error;
1174
+ }
1175
+ };
1176
+ };
1177
+ captureGlobalError = async (error, request, context) => {
1178
+ const config = this.#resolveConfig();
1179
+ if (!this.#canSend(config) || !this.#reserveError(error)) return;
1180
+ const requestContext = await this.#captureGlobalRequestContext(request, context, config);
1181
+ try {
1182
+ const runtime = requestContext.framework?.name === "nextjs" ? "nextjs" : void 0;
1183
+ const payload = this.#preparePayload(error, requestContext, config.environment, runtime);
1184
+ await dispatchAndWait(payload, toDispatchConfig(config));
1185
+ } catch (captureError) {
1186
+ this.#debug(config, "Global error dispatch failed safely", captureError);
1187
+ }
1188
+ };
1189
+ captureGlobalErrorForInstrumentation = async (error, request, context) => {
1190
+ const config = this.#resolveConfig();
1191
+ if (!this.#canSend(config) || !this.#reserveError(error)) return;
1192
+ const requestContext = await this.#captureGlobalRequestContext(request, context, config);
1193
+ try {
1194
+ const runtime = requestContext.framework?.name === "nextjs" ? "nextjs" : void 0;
1195
+ const payload = this.#preparePayload(error, requestContext, config.environment, runtime);
1196
+ await dispatchWithRuntime(payload, toDispatchConfig(config));
1197
+ } catch (captureError) {
1198
+ this.#debug(config, "Global instrumentation dispatch failed safely", captureError);
1199
+ }
1200
+ };
1201
+ async #captureGlobalRequestContext(request, context, config) {
1202
+ try {
1203
+ return await prepareRequestCapture(request, context, void 0, {
1204
+ captureBody: config.captureBody
1205
+ })();
1206
+ } catch (captureError) {
1207
+ this.#debug(config, "Global error capture failed safely", captureError);
1208
+ return createFallbackRequestContext();
1209
+ }
1210
+ }
1211
+ #prepareRequestSafely(arguments_, captureBody) {
1212
+ try {
1213
+ return prepareRequestCapture(arguments_[0], arguments_[1], arguments_, { captureBody });
1214
+ } catch {
1215
+ return () => Promise.resolve(createFallbackRequestContext());
1216
+ }
1217
+ }
1218
+ #captureSafely(error, requestContext, runtime) {
1219
+ const config = this.#resolveConfig();
1220
+ if (!this.#canSend(config) || !this.#reserveError(error)) return;
1221
+ this.#captureReserved(error, requestContext, config, runtime);
1222
+ }
1223
+ #captureReserved(error, requestContext, config, runtime) {
1224
+ try {
1225
+ const payload = this.#preparePayload(error, requestContext, config.environment, runtime);
1226
+ dispatchInBackground(payload, toDispatchConfig(config));
1227
+ } catch (captureError) {
1228
+ this.#debug(config, "Error normalization failed safely", captureError);
1229
+ }
1230
+ }
1231
+ #preparePayload(error, requestContext, environment, runtime) {
1232
+ const basePayload = {
1233
+ context: requestContext,
1234
+ environment,
1235
+ error: normalizeError(error),
1236
+ eventId: createEventId(),
1237
+ sdkVersion: SDK_VERSION,
1238
+ source: "sdk_code",
1239
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1240
+ };
1241
+ return runtime === void 0 ? basePayload : { ...basePayload, runtime };
1242
+ }
1243
+ #reserveError(error) {
1244
+ const now = Date.now();
1245
+ if (typeof error === "object" && error !== null) {
1246
+ const capturedAt2 = this.#recentErrors.get(error);
1247
+ if (capturedAt2 !== void 0 && now - capturedAt2 < DEDUPE_WINDOW_MS) return false;
1248
+ this.#recentErrors.set(error, now);
1249
+ return true;
1250
+ }
1251
+ for (const [fingerprint2, capturedAt2] of this.#recentPrimitiveErrors) {
1252
+ if (now - capturedAt2 >= DEDUPE_WINDOW_MS) this.#recentPrimitiveErrors.delete(fingerprint2);
1253
+ }
1254
+ const fingerprint = `${typeof error}:${sanitizeText(String(error))}`;
1255
+ const capturedAt = this.#recentPrimitiveErrors.get(fingerprint);
1256
+ if (capturedAt !== void 0 && now - capturedAt < DEDUPE_WINDOW_MS) return false;
1257
+ if (this.#recentPrimitiveErrors.size >= MAX_PRIMITIVE_DEDUPE_ENTRIES) {
1258
+ const oldest = this.#recentPrimitiveErrors.keys().next().value;
1259
+ if (typeof oldest === "string") this.#recentPrimitiveErrors.delete(oldest);
1260
+ }
1261
+ this.#recentPrimitiveErrors.set(fingerprint, now);
1262
+ return true;
1263
+ }
1264
+ #canSend(config) {
1265
+ if (!config.enabled) {
1266
+ this.#debug(config, "SDK capture is disabled; event was not sent");
1267
+ return false;
1268
+ }
1269
+ if (!LIVE_API_KEY_PATTERN.test(config.apiKey)) {
1270
+ this.#debug(config, "KYNCODE_API_KEY is missing or invalid; event was not sent");
1271
+ return false;
1272
+ }
1273
+ return true;
1274
+ }
1275
+ #connectConfigurationDiagnostic(config, runtime) {
1276
+ if (!config.enabled) {
1277
+ return {
1278
+ message: "KYNCODE SDK is disabled in this runtime",
1279
+ ok: false,
1280
+ runtime,
1281
+ status: "disabled"
1282
+ };
1283
+ }
1284
+ if (!LIVE_API_KEY_PATTERN.test(config.apiKey)) {
1285
+ return {
1286
+ message: "KYNCODE_API_KEY is missing or invalid",
1287
+ ok: false,
1288
+ runtime,
1289
+ status: "invalid_api_key"
1290
+ };
1291
+ }
1292
+ return void 0;
1293
+ }
1294
+ #resolveConfig() {
1295
+ return resolveConfig(this.#configOverrides);
1296
+ }
1297
+ #debug(config, message, detail) {
1298
+ if (!config.debug) return;
1299
+ console.debug(
1300
+ `[KYNCODE SDK] ${sanitizeText(message)}`,
1301
+ detail === void 0 ? "" : sanitizeText(readErrorMessage2(detail) ?? "Unknown diagnostic")
1302
+ );
1303
+ }
1304
+ };
1305
+ var lolyClient = new LolyClient();
1306
+ var Loly = lolyClient;
1307
+ var onRequestError = (error, request, context) => lolyClient.captureGlobalErrorForInstrumentation(error, request, context);
1308
+ var register = async () => {
1309
+ enableRuntimeSourceMaps();
1310
+ await lolyClient.connectForStartup("Next.js instrumentation");
1311
+ };
1312
+ function enableRuntimeSourceMaps() {
1313
+ const processValue = globalThis.process;
1314
+ if (processValue === null || typeof processValue !== "object") return;
1315
+ const setter = processValue["setSourceMapsEnabled"];
1316
+ if (typeof setter !== "function") return;
1317
+ try {
1318
+ Reflect.apply(setter, processValue, [true]);
1319
+ } catch {
1320
+ }
1321
+ }
1322
+ function resolveConfig(config) {
1323
+ const nodeEnvironment = readEnvironment("NODE_ENV");
1324
+ const environment = config.environment ?? resolveNodeEnvironment(nodeEnvironment);
1325
+ const enabled = config.enabled ?? readBooleanEnvironment("KYNCODE_SDK_ENABLED") ?? nodeEnvironment !== "test";
1326
+ return {
1327
+ apiKey: config.apiKey ?? readEnvironment("KYNCODE_API_KEY") ?? "",
1328
+ captureBody: config.captureBody ?? readBooleanEnvironment("KYNCODE_CAPTURE_REQUEST_BODY") ?? false,
1329
+ debug: config.debug ?? readBooleanEnvironment("KYNCODE_DEBUG") ?? false,
1330
+ enabled,
1331
+ endpoint: DEFAULT_ENDPOINT,
1332
+ environment
1333
+ };
1334
+ }
1335
+ function resolveNodeEnvironment(value) {
1336
+ return value === "development" || value === "staging" || value === "production" ? value : "production";
1337
+ }
1338
+ function normalizeError(error) {
1339
+ const errorRecord = asRecord2(error);
1340
+ const rawMessage = readString2(errorRecord, "message") ?? (typeof error === "string" ? error : "Unknown application error");
1341
+ const message = nonEmptySanitized(rawMessage, "Unknown application error");
1342
+ const name = nonEmptySanitized(readString2(errorRecord, "name") ?? "Error", "Error");
1343
+ const rawStack = readString2(errorRecord, "stack") ?? new Error(message).stack ?? `${name}: ${message}`;
1344
+ const stack = nonEmptySanitized(rawStack, `${name}: ${message}`);
1345
+ const rawDigest = readString2(errorRecord, "digest");
1346
+ const normalized = { message, name, stack };
1347
+ if (rawDigest === void 0) return normalized;
1348
+ const digest = sanitizeText(rawDigest);
1349
+ return digest === "" ? normalized : { ...normalized, digest };
1350
+ }
1351
+ function createFallbackRequestContext() {
1352
+ return { headers: {}, queryParams: {}, route: "unknown" };
1353
+ }
1354
+ function asRecord2(value) {
1355
+ return typeof value === "object" && value !== null ? value : void 0;
1356
+ }
1357
+ function readString2(record, key) {
1358
+ try {
1359
+ const value = record?.[key];
1360
+ return typeof value === "string" ? value : void 0;
1361
+ } catch {
1362
+ return void 0;
1363
+ }
1364
+ }
1365
+ function nonEmptySanitized(value, fallback) {
1366
+ const sanitized = sanitizeText(value).trim();
1367
+ return sanitized === "" ? fallback : sanitized;
1368
+ }
1369
+ function readEnvironment(name) {
1370
+ try {
1371
+ return typeof process === "undefined" ? void 0 : process.env[name];
1372
+ } catch {
1373
+ return void 0;
1374
+ }
1375
+ }
1376
+ function readBooleanEnvironment(name) {
1377
+ const value = readEnvironment(name)?.trim().toLowerCase();
1378
+ if (value === "true" || value === "1") return true;
1379
+ if (value === "false" || value === "0") return false;
1380
+ return void 0;
1381
+ }
1382
+ function toDispatchConfig(config) {
1383
+ return { apiKey: config.apiKey, debug: config.debug, endpoint: config.endpoint };
1384
+ }
1385
+ function createEventId() {
1386
+ try {
1387
+ if (typeof globalThis.crypto.randomUUID === "function") {
1388
+ return `evt_${globalThis.crypto.randomUUID().replaceAll("-", "")}`;
1389
+ }
1390
+ } catch {
1391
+ }
1392
+ return `evt_${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
1393
+ }
1394
+ function scheduleMicrotask(callback) {
1395
+ if (typeof queueMicrotask === "function") {
1396
+ queueMicrotask(callback);
1397
+ return;
1398
+ }
1399
+ void Promise.resolve().then(callback);
1400
+ }
1401
+ function connectivityDiagnosticFromOutcome(runtime, outcome) {
1402
+ if (outcome.ok) {
1403
+ return {
1404
+ ...outcome.httpStatus === void 0 ? {} : { httpStatus: outcome.httpStatus },
1405
+ message: "KYNCODE connectivity verified",
1406
+ ok: true,
1407
+ runtime,
1408
+ status: "connected"
1409
+ };
1410
+ }
1411
+ const credentialsRejected = outcome.httpStatus === 401 || outcome.httpStatus === 403;
1412
+ return {
1413
+ ...outcome.httpStatus === void 0 ? {} : { httpStatus: outcome.httpStatus },
1414
+ message: outcome.message,
1415
+ ok: false,
1416
+ runtime,
1417
+ status: credentialsRejected ? "invalid_api_key" : outcome.status === "rate_limited" ? "rate_limited" : "failed"
1418
+ };
1419
+ }
1420
+ function readErrorMessage2(value) {
1421
+ if (typeof value === "string") return value;
1422
+ return readString2(asRecord2(value), "message");
1423
+ }
1424
+
1425
+ // src/index.ts
1426
+ var index_default = Loly;
1427
+ // Annotate the CommonJS export names for ESM import in node:
1428
+ 0 && (module.exports = {
1429
+ Loly,
1430
+ SDK_VERSION,
1431
+ onRequestError,
1432
+ register
1433
+ });
1434
+ //# sourceMappingURL=index.cjs.map