@parsrun/core 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/README.md +113 -0
- package/dist/decimal.d.ts +190 -0
- package/dist/decimal.js +347 -0
- package/dist/decimal.js.map +1 -0
- package/dist/env.d.ts +101 -0
- package/dist/env.js +172 -0
- package/dist/env.js.map +1 -0
- package/dist/error-codes.d.ts +257 -0
- package/dist/error-codes.js +261 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/errors.d.ts +81 -0
- package/dist/errors.js +161 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.js +1987 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-aEibH9Mv.d.ts +262 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +327 -0
- package/dist/logger.js.map +1 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +88 -0
- package/dist/runtime.js.map +1 -0
- package/dist/transports/index.d.ts +347 -0
- package/dist/transports/index.js +657 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
// src/runtime.ts
|
|
2
|
+
function detectRuntime() {
|
|
3
|
+
if (typeof globalThis !== "undefined" && "Bun" in globalThis) {
|
|
4
|
+
return "bun";
|
|
5
|
+
}
|
|
6
|
+
if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
|
|
7
|
+
return "deno";
|
|
8
|
+
}
|
|
9
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.caches !== "undefined" && typeof globalThis.process === "undefined") {
|
|
10
|
+
return "cloudflare";
|
|
11
|
+
}
|
|
12
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.EdgeRuntime !== "undefined") {
|
|
13
|
+
return "edge";
|
|
14
|
+
}
|
|
15
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined" && typeof globalThis.document !== "undefined") {
|
|
16
|
+
return "browser";
|
|
17
|
+
}
|
|
18
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
19
|
+
return "node";
|
|
20
|
+
}
|
|
21
|
+
return "unknown";
|
|
22
|
+
}
|
|
23
|
+
var runtime = detectRuntime();
|
|
24
|
+
var runtimeInfo = {
|
|
25
|
+
runtime,
|
|
26
|
+
isNode: runtime === "node",
|
|
27
|
+
isDeno: runtime === "deno",
|
|
28
|
+
isBun: runtime === "bun",
|
|
29
|
+
isCloudflare: runtime === "cloudflare",
|
|
30
|
+
isEdge: runtime === "cloudflare" || runtime === "edge" || runtime === "deno",
|
|
31
|
+
isBrowser: runtime === "browser",
|
|
32
|
+
isServer: runtime !== "browser",
|
|
33
|
+
supportsWebCrypto: typeof globalThis.crypto?.subtle !== "undefined",
|
|
34
|
+
supportsStreams: typeof globalThis.ReadableStream !== "undefined"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// src/env.ts
|
|
38
|
+
var edgeEnvStore = {};
|
|
39
|
+
function getEnv(key, defaultValue) {
|
|
40
|
+
if (runtime === "cloudflare" || runtime === "edge") {
|
|
41
|
+
return edgeEnvStore[key] ?? defaultValue;
|
|
42
|
+
}
|
|
43
|
+
if (runtime === "deno") {
|
|
44
|
+
try {
|
|
45
|
+
return globalThis.Deno.env.get(key) ?? defaultValue;
|
|
46
|
+
} catch {
|
|
47
|
+
return defaultValue;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (typeof process !== "undefined" && process.env) {
|
|
51
|
+
return process.env[key] ?? defaultValue;
|
|
52
|
+
}
|
|
53
|
+
if (runtime === "browser" && typeof globalThis.__ENV__ !== "undefined") {
|
|
54
|
+
return globalThis.__ENV__[key] ?? defaultValue;
|
|
55
|
+
}
|
|
56
|
+
return defaultValue;
|
|
57
|
+
}
|
|
58
|
+
function isDevelopment() {
|
|
59
|
+
const env = getEnv("NODE_ENV");
|
|
60
|
+
return env === "development" || env === void 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/transports/console.ts
|
|
64
|
+
var ConsoleTransport = class {
|
|
65
|
+
name = "console";
|
|
66
|
+
pretty;
|
|
67
|
+
colors;
|
|
68
|
+
constructor(options = {}) {
|
|
69
|
+
this.pretty = options.pretty ?? isDevelopment();
|
|
70
|
+
this.colors = options.colors ?? (runtime === "node" || runtime === "bun");
|
|
71
|
+
}
|
|
72
|
+
log(entry) {
|
|
73
|
+
if (this.pretty) {
|
|
74
|
+
this.logPretty(entry);
|
|
75
|
+
} else {
|
|
76
|
+
this.logJson(entry);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
logJson(entry) {
|
|
80
|
+
const { level, message, timestamp, context, error } = entry;
|
|
81
|
+
const output = {
|
|
82
|
+
level,
|
|
83
|
+
time: timestamp,
|
|
84
|
+
msg: message
|
|
85
|
+
};
|
|
86
|
+
if (context && Object.keys(context).length > 0) {
|
|
87
|
+
Object.assign(output, context);
|
|
88
|
+
}
|
|
89
|
+
if (error) {
|
|
90
|
+
output["err"] = error;
|
|
91
|
+
}
|
|
92
|
+
console.log(JSON.stringify(output));
|
|
93
|
+
}
|
|
94
|
+
logPretty(entry) {
|
|
95
|
+
const { level, message, timestamp, context, error } = entry;
|
|
96
|
+
const levelColors = {
|
|
97
|
+
TRACE: "\x1B[90m",
|
|
98
|
+
// Gray
|
|
99
|
+
DEBUG: "\x1B[36m",
|
|
100
|
+
// Cyan
|
|
101
|
+
INFO: "\x1B[32m",
|
|
102
|
+
// Green
|
|
103
|
+
WARN: "\x1B[33m",
|
|
104
|
+
// Yellow
|
|
105
|
+
ERROR: "\x1B[31m",
|
|
106
|
+
// Red
|
|
107
|
+
FATAL: "\x1B[35m",
|
|
108
|
+
// Magenta
|
|
109
|
+
SILENT: ""
|
|
110
|
+
};
|
|
111
|
+
const reset = "\x1B[0m";
|
|
112
|
+
const color = this.colors ? levelColors[level] : "";
|
|
113
|
+
const resetCode = this.colors ? reset : "";
|
|
114
|
+
const timePart = timestamp.split("T")[1];
|
|
115
|
+
const time = timePart ? timePart.slice(0, 8) : timestamp;
|
|
116
|
+
let output = `${color}[${time}] ${level.padEnd(5)}${resetCode} ${message}`;
|
|
117
|
+
if (context && Object.keys(context).length > 0) {
|
|
118
|
+
output += ` ${JSON.stringify(context)}`;
|
|
119
|
+
}
|
|
120
|
+
if (level === "ERROR" || level === "FATAL") {
|
|
121
|
+
console.error(output);
|
|
122
|
+
if (error?.stack) {
|
|
123
|
+
console.error(error.stack);
|
|
124
|
+
}
|
|
125
|
+
} else if (level === "WARN") {
|
|
126
|
+
console.warn(output);
|
|
127
|
+
} else if (level === "DEBUG" || level === "TRACE") {
|
|
128
|
+
console.debug(output);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(output);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/transports/axiom.ts
|
|
136
|
+
var AxiomTransport = class {
|
|
137
|
+
name = "axiom";
|
|
138
|
+
buffer = [];
|
|
139
|
+
flushTimer = null;
|
|
140
|
+
isFlushing = false;
|
|
141
|
+
options;
|
|
142
|
+
constructor(options) {
|
|
143
|
+
this.options = {
|
|
144
|
+
batchSize: 100,
|
|
145
|
+
flushInterval: 5e3,
|
|
146
|
+
apiUrl: "https://api.axiom.co",
|
|
147
|
+
...options
|
|
148
|
+
};
|
|
149
|
+
if (this.options.flushInterval > 0) {
|
|
150
|
+
this.flushTimer = setInterval(
|
|
151
|
+
() => this.flush(),
|
|
152
|
+
this.options.flushInterval
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
log(entry) {
|
|
157
|
+
if (this.options.enabled === false) return;
|
|
158
|
+
const event = {
|
|
159
|
+
_time: entry.timestamp,
|
|
160
|
+
level: entry.level,
|
|
161
|
+
message: entry.message
|
|
162
|
+
};
|
|
163
|
+
if (entry.context) {
|
|
164
|
+
Object.assign(event, entry.context);
|
|
165
|
+
}
|
|
166
|
+
if (entry.error) {
|
|
167
|
+
event["error.name"] = entry.error.name;
|
|
168
|
+
event["error.message"] = entry.error.message;
|
|
169
|
+
event["error.stack"] = entry.error.stack;
|
|
170
|
+
}
|
|
171
|
+
this.buffer.push(event);
|
|
172
|
+
if (this.buffer.length >= this.options.batchSize) {
|
|
173
|
+
this.flush();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async flush() {
|
|
177
|
+
if (this.isFlushing || this.buffer.length === 0) return;
|
|
178
|
+
this.isFlushing = true;
|
|
179
|
+
const events = this.buffer;
|
|
180
|
+
this.buffer = [];
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(
|
|
183
|
+
`${this.options.apiUrl}/v1/datasets/${this.options.dataset}/ingest`,
|
|
184
|
+
{
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
Authorization: `Bearer ${this.options.token}`,
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
...this.options.orgId && {
|
|
190
|
+
"X-Axiom-Org-Id": this.options.orgId
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
body: JSON.stringify(events)
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
const errorText = await response.text();
|
|
198
|
+
throw new Error(`Axiom ingest failed: ${response.status} ${errorText}`);
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (this.options.onError) {
|
|
202
|
+
this.options.onError(
|
|
203
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
204
|
+
events.length
|
|
205
|
+
);
|
|
206
|
+
} else {
|
|
207
|
+
console.error("[Axiom] Failed to send logs:", error);
|
|
208
|
+
}
|
|
209
|
+
} finally {
|
|
210
|
+
this.isFlushing = false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async close() {
|
|
214
|
+
if (this.flushTimer) {
|
|
215
|
+
clearInterval(this.flushTimer);
|
|
216
|
+
this.flushTimer = null;
|
|
217
|
+
}
|
|
218
|
+
await this.flush();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
function createAxiomTransport(options) {
|
|
222
|
+
return new AxiomTransport(options);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/transports/sentry.ts
|
|
226
|
+
var SentryTransport = class {
|
|
227
|
+
name = "sentry";
|
|
228
|
+
client;
|
|
229
|
+
dsn;
|
|
230
|
+
options;
|
|
231
|
+
user = null;
|
|
232
|
+
contexts = /* @__PURE__ */ new Map();
|
|
233
|
+
breadcrumbs = [];
|
|
234
|
+
maxBreadcrumbs = 100;
|
|
235
|
+
constructor(options) {
|
|
236
|
+
this.options = {
|
|
237
|
+
sampleRate: 1,
|
|
238
|
+
...options
|
|
239
|
+
};
|
|
240
|
+
if (options.client) {
|
|
241
|
+
this.client = options.client;
|
|
242
|
+
} else if (options.dsn) {
|
|
243
|
+
this.dsn = this.parseDSN(options.dsn);
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error("SentryTransport requires either 'dsn' or 'client' option");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Parse Sentry DSN
|
|
250
|
+
*/
|
|
251
|
+
parseDSN(dsn) {
|
|
252
|
+
const match = dsn.match(/^(https?):\/\/([^@]+)@([^/]+)\/(.+)$/);
|
|
253
|
+
if (!match || !match[1] || !match[2] || !match[3] || !match[4]) {
|
|
254
|
+
throw new Error(`Invalid Sentry DSN: ${dsn}`);
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
protocol: match[1],
|
|
258
|
+
publicKey: match[2],
|
|
259
|
+
host: match[3],
|
|
260
|
+
projectId: match[4]
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* LogTransport implementation
|
|
265
|
+
* Only sends ERROR and FATAL level logs
|
|
266
|
+
*/
|
|
267
|
+
log(entry) {
|
|
268
|
+
if (this.options.enabled === false) return;
|
|
269
|
+
if (entry.levelValue < 50) return;
|
|
270
|
+
if (entry.error) {
|
|
271
|
+
const error = new Error(entry.error.message);
|
|
272
|
+
error.name = entry.error.name;
|
|
273
|
+
if (entry.error.stack) {
|
|
274
|
+
error.stack = entry.error.stack;
|
|
275
|
+
}
|
|
276
|
+
this.captureException(
|
|
277
|
+
error,
|
|
278
|
+
entry.context ? { extra: entry.context } : void 0
|
|
279
|
+
);
|
|
280
|
+
} else {
|
|
281
|
+
this.captureMessage(
|
|
282
|
+
entry.message,
|
|
283
|
+
entry.level === "FATAL" ? "error" : "warning",
|
|
284
|
+
entry.context ? { extra: entry.context } : void 0
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Capture an exception
|
|
290
|
+
*/
|
|
291
|
+
captureException(error, context) {
|
|
292
|
+
if (this.options.enabled === false) return;
|
|
293
|
+
if (!this.shouldSample()) return;
|
|
294
|
+
if (this.client) {
|
|
295
|
+
this.captureWithSdk(error, context);
|
|
296
|
+
} else {
|
|
297
|
+
this.captureWithHttp(error, context);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Capture a message
|
|
302
|
+
*/
|
|
303
|
+
captureMessage(message, level, context) {
|
|
304
|
+
if (this.options.enabled === false) return;
|
|
305
|
+
if (!this.shouldSample()) return;
|
|
306
|
+
if (this.client) {
|
|
307
|
+
this.client.withScope((scope) => {
|
|
308
|
+
this.applyContext(scope, context);
|
|
309
|
+
scope.setLevel(level);
|
|
310
|
+
this.client.captureMessage(message, level);
|
|
311
|
+
});
|
|
312
|
+
} else {
|
|
313
|
+
this.sendHttpEvent({
|
|
314
|
+
level: level === "warning" ? "warning" : level === "info" ? "info" : "error",
|
|
315
|
+
message: { formatted: message },
|
|
316
|
+
...this.buildEventContext(context)
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Set user context
|
|
322
|
+
*/
|
|
323
|
+
setUser(user) {
|
|
324
|
+
this.user = user;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Set custom context
|
|
328
|
+
*/
|
|
329
|
+
setContext(name, context) {
|
|
330
|
+
this.contexts.set(name, context);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Add breadcrumb
|
|
334
|
+
*/
|
|
335
|
+
addBreadcrumb(breadcrumb) {
|
|
336
|
+
this.breadcrumbs.push({
|
|
337
|
+
...breadcrumb,
|
|
338
|
+
timestamp: breadcrumb.timestamp ?? Date.now() / 1e3
|
|
339
|
+
});
|
|
340
|
+
if (this.breadcrumbs.length > this.maxBreadcrumbs) {
|
|
341
|
+
this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Flush pending events
|
|
346
|
+
*/
|
|
347
|
+
async flush() {
|
|
348
|
+
if (this.client?.flush) {
|
|
349
|
+
await this.client.flush(2e3);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// ============================================================================
|
|
353
|
+
// Private Methods
|
|
354
|
+
// ============================================================================
|
|
355
|
+
shouldSample() {
|
|
356
|
+
const rate = this.options.sampleRate ?? 1;
|
|
357
|
+
return Math.random() < rate;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Capture with SDK (BYOS mode)
|
|
361
|
+
*/
|
|
362
|
+
captureWithSdk(error, context) {
|
|
363
|
+
this.client.withScope((scope) => {
|
|
364
|
+
this.applyContext(scope, context);
|
|
365
|
+
this.client.captureException(error);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Apply context to SDK scope
|
|
370
|
+
*/
|
|
371
|
+
applyContext(scope, context) {
|
|
372
|
+
if (this.user) {
|
|
373
|
+
scope.setUser(this.user);
|
|
374
|
+
} else if (context?.userId) {
|
|
375
|
+
scope.setUser({ id: context.userId });
|
|
376
|
+
}
|
|
377
|
+
if (this.options.tags) {
|
|
378
|
+
for (const [key, value] of Object.entries(this.options.tags)) {
|
|
379
|
+
scope.setTag(key, value);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (context?.tags) {
|
|
383
|
+
for (const [key, value] of Object.entries(context.tags)) {
|
|
384
|
+
scope.setTag(key, value);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (context?.requestId) {
|
|
388
|
+
scope.setTag("requestId", context.requestId);
|
|
389
|
+
}
|
|
390
|
+
if (context?.tenantId) {
|
|
391
|
+
scope.setTag("tenantId", context.tenantId);
|
|
392
|
+
}
|
|
393
|
+
if (context?.extra) {
|
|
394
|
+
scope.setExtras(context.extra);
|
|
395
|
+
}
|
|
396
|
+
for (const bc of this.breadcrumbs) {
|
|
397
|
+
scope.addBreadcrumb(bc);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Capture with HTTP API (default mode)
|
|
402
|
+
*/
|
|
403
|
+
captureWithHttp(error, context) {
|
|
404
|
+
const stacktrace = this.parseStackTrace(error.stack);
|
|
405
|
+
const exceptionValue = {
|
|
406
|
+
type: error.name,
|
|
407
|
+
value: error.message
|
|
408
|
+
};
|
|
409
|
+
if (stacktrace) {
|
|
410
|
+
exceptionValue.stacktrace = stacktrace;
|
|
411
|
+
}
|
|
412
|
+
const event = {
|
|
413
|
+
level: "error",
|
|
414
|
+
exception: {
|
|
415
|
+
values: [exceptionValue]
|
|
416
|
+
},
|
|
417
|
+
...this.buildEventContext(context)
|
|
418
|
+
};
|
|
419
|
+
this.sendHttpEvent(event);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Build event context for HTTP API
|
|
423
|
+
*/
|
|
424
|
+
buildEventContext(context) {
|
|
425
|
+
const event = {};
|
|
426
|
+
if (this.options.environment) {
|
|
427
|
+
event.environment = this.options.environment;
|
|
428
|
+
}
|
|
429
|
+
if (this.options.release) {
|
|
430
|
+
event.release = this.options.release;
|
|
431
|
+
}
|
|
432
|
+
if (this.options.serverName) {
|
|
433
|
+
event.server_name = this.options.serverName;
|
|
434
|
+
}
|
|
435
|
+
const tags = { ...this.options.tags };
|
|
436
|
+
if (context?.tags) {
|
|
437
|
+
Object.assign(tags, context.tags);
|
|
438
|
+
}
|
|
439
|
+
if (context?.requestId) {
|
|
440
|
+
tags["requestId"] = context.requestId;
|
|
441
|
+
}
|
|
442
|
+
if (context?.tenantId) {
|
|
443
|
+
tags["tenantId"] = context.tenantId;
|
|
444
|
+
}
|
|
445
|
+
if (Object.keys(tags).length > 0) {
|
|
446
|
+
event.tags = tags;
|
|
447
|
+
}
|
|
448
|
+
if (context?.extra) {
|
|
449
|
+
event.extra = context.extra;
|
|
450
|
+
}
|
|
451
|
+
if (this.user) {
|
|
452
|
+
event.user = this.user;
|
|
453
|
+
} else if (context?.userId) {
|
|
454
|
+
event.user = { id: context.userId };
|
|
455
|
+
}
|
|
456
|
+
if (this.breadcrumbs.length > 0) {
|
|
457
|
+
event.breadcrumbs = this.breadcrumbs.map((bc) => {
|
|
458
|
+
const crumb = {};
|
|
459
|
+
if (bc.type) crumb.type = bc.type;
|
|
460
|
+
if (bc.category) crumb.category = bc.category;
|
|
461
|
+
if (bc.message) crumb.message = bc.message;
|
|
462
|
+
if (bc.data) crumb.data = bc.data;
|
|
463
|
+
if (bc.level) crumb.level = bc.level;
|
|
464
|
+
if (bc.timestamp !== void 0) crumb.timestamp = bc.timestamp;
|
|
465
|
+
return crumb;
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
if (this.contexts.size > 0) {
|
|
469
|
+
event.contexts = Object.fromEntries(this.contexts);
|
|
470
|
+
}
|
|
471
|
+
return event;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Parse error stack trace into Sentry format
|
|
475
|
+
*/
|
|
476
|
+
parseStackTrace(stack) {
|
|
477
|
+
if (!stack) return void 0;
|
|
478
|
+
const lines = stack.split("\n").slice(1);
|
|
479
|
+
const frames = [];
|
|
480
|
+
for (const line of lines) {
|
|
481
|
+
const match = line.match(/^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
|
|
482
|
+
if (match && match[3] && match[4]) {
|
|
483
|
+
const frame = {
|
|
484
|
+
function: match[1] || "<anonymous>",
|
|
485
|
+
lineno: parseInt(match[3], 10),
|
|
486
|
+
colno: parseInt(match[4], 10)
|
|
487
|
+
};
|
|
488
|
+
if (match[2]) {
|
|
489
|
+
frame.filename = match[2];
|
|
490
|
+
}
|
|
491
|
+
frames.push(frame);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
frames.reverse();
|
|
495
|
+
return frames.length > 0 ? { frames } : void 0;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Generate event ID
|
|
499
|
+
*/
|
|
500
|
+
generateEventId() {
|
|
501
|
+
const bytes = new Uint8Array(16);
|
|
502
|
+
crypto.getRandomValues(bytes);
|
|
503
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Send event via HTTP API
|
|
507
|
+
*/
|
|
508
|
+
async sendHttpEvent(eventData) {
|
|
509
|
+
if (!this.dsn) return;
|
|
510
|
+
const event = {
|
|
511
|
+
event_id: this.generateEventId(),
|
|
512
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
513
|
+
platform: "javascript",
|
|
514
|
+
level: "error",
|
|
515
|
+
...eventData
|
|
516
|
+
};
|
|
517
|
+
if (this.options.beforeSend) {
|
|
518
|
+
const result = this.options.beforeSend(event);
|
|
519
|
+
if (result === null) return;
|
|
520
|
+
}
|
|
521
|
+
const url = `${this.dsn.protocol}://${this.dsn.host}/api/${this.dsn.projectId}/store/`;
|
|
522
|
+
try {
|
|
523
|
+
const response = await fetch(url, {
|
|
524
|
+
method: "POST",
|
|
525
|
+
headers: {
|
|
526
|
+
"Content-Type": "application/json",
|
|
527
|
+
"X-Sentry-Auth": [
|
|
528
|
+
"Sentry sentry_version=7",
|
|
529
|
+
`sentry_client=pars-sentry/1.0.0`,
|
|
530
|
+
`sentry_key=${this.dsn.publicKey}`
|
|
531
|
+
].join(", ")
|
|
532
|
+
},
|
|
533
|
+
body: JSON.stringify(event)
|
|
534
|
+
});
|
|
535
|
+
if (!response.ok) {
|
|
536
|
+
throw new Error(`Sentry API error: ${response.status}`);
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
if (this.options.onError) {
|
|
540
|
+
this.options.onError(error instanceof Error ? error : new Error(String(error)));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
function createSentryTransport(options) {
|
|
546
|
+
return new SentryTransport(options);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/transports/logtape.ts
|
|
550
|
+
var FallbackLogger = class {
|
|
551
|
+
constructor(category) {
|
|
552
|
+
this.category = category;
|
|
553
|
+
}
|
|
554
|
+
log(level, message, properties) {
|
|
555
|
+
const entry = {
|
|
556
|
+
level,
|
|
557
|
+
category: this.category,
|
|
558
|
+
msg: message,
|
|
559
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
560
|
+
...properties
|
|
561
|
+
};
|
|
562
|
+
console.log(JSON.stringify(entry));
|
|
563
|
+
}
|
|
564
|
+
debug(message, properties) {
|
|
565
|
+
this.log("debug", message, properties);
|
|
566
|
+
}
|
|
567
|
+
info(message, properties) {
|
|
568
|
+
this.log("info", message, properties);
|
|
569
|
+
}
|
|
570
|
+
warn(message, properties) {
|
|
571
|
+
this.log("warn", message, properties);
|
|
572
|
+
}
|
|
573
|
+
warning(message, properties) {
|
|
574
|
+
this.log("warning", message, properties);
|
|
575
|
+
}
|
|
576
|
+
error(message, properties) {
|
|
577
|
+
this.log("error", message, properties);
|
|
578
|
+
}
|
|
579
|
+
fatal(message, properties) {
|
|
580
|
+
this.log("fatal", message, properties);
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
var LogtapeTransport = class {
|
|
584
|
+
name = "logtape";
|
|
585
|
+
logger;
|
|
586
|
+
includeTimestamp;
|
|
587
|
+
includeLevelValue;
|
|
588
|
+
enabled;
|
|
589
|
+
constructor(options = {}) {
|
|
590
|
+
this.enabled = options.enabled !== false;
|
|
591
|
+
this.includeTimestamp = options.includeTimestamp !== false;
|
|
592
|
+
this.includeLevelValue = options.includeLevelValue ?? false;
|
|
593
|
+
if (options.logger) {
|
|
594
|
+
this.logger = options.logger;
|
|
595
|
+
} else {
|
|
596
|
+
this.logger = new FallbackLogger(options.category ?? "pars");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
log(entry) {
|
|
600
|
+
if (!this.enabled) return;
|
|
601
|
+
const level = this.mapLevel(entry.level);
|
|
602
|
+
const properties = this.buildProperties(entry);
|
|
603
|
+
this.logger[level](entry.message, properties);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Map Pars log level to Logtape level
|
|
607
|
+
*/
|
|
608
|
+
mapLevel(level) {
|
|
609
|
+
const mapping = {
|
|
610
|
+
TRACE: "debug",
|
|
611
|
+
DEBUG: "debug",
|
|
612
|
+
INFO: "info",
|
|
613
|
+
WARN: "warning",
|
|
614
|
+
ERROR: "error",
|
|
615
|
+
FATAL: "fatal",
|
|
616
|
+
SILENT: "debug"
|
|
617
|
+
// Should never be logged
|
|
618
|
+
};
|
|
619
|
+
return mapping[level];
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Build properties object for Logtape
|
|
623
|
+
*/
|
|
624
|
+
buildProperties(entry) {
|
|
625
|
+
const properties = {};
|
|
626
|
+
if (this.includeTimestamp) {
|
|
627
|
+
properties["timestamp"] = entry.timestamp;
|
|
628
|
+
}
|
|
629
|
+
if (this.includeLevelValue) {
|
|
630
|
+
properties["levelValue"] = entry.levelValue;
|
|
631
|
+
}
|
|
632
|
+
if (entry.context) {
|
|
633
|
+
Object.assign(properties, entry.context);
|
|
634
|
+
}
|
|
635
|
+
if (entry.error) {
|
|
636
|
+
properties["error"] = {
|
|
637
|
+
name: entry.error.name,
|
|
638
|
+
message: entry.error.message,
|
|
639
|
+
stack: entry.error.stack
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return properties;
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
function createLogtapeTransport(options) {
|
|
646
|
+
return new LogtapeTransport(options);
|
|
647
|
+
}
|
|
648
|
+
export {
|
|
649
|
+
AxiomTransport,
|
|
650
|
+
ConsoleTransport,
|
|
651
|
+
LogtapeTransport,
|
|
652
|
+
SentryTransport,
|
|
653
|
+
createAxiomTransport,
|
|
654
|
+
createLogtapeTransport,
|
|
655
|
+
createSentryTransport
|
|
656
|
+
};
|
|
657
|
+
//# sourceMappingURL=index.js.map
|