@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/dist/index.js ADDED
@@ -0,0 +1,1987 @@
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
+ function isNode() {
37
+ return runtime === "node";
38
+ }
39
+ function isDeno() {
40
+ return runtime === "deno";
41
+ }
42
+ function isBun() {
43
+ return runtime === "bun";
44
+ }
45
+ function isCloudflare() {
46
+ return runtime === "cloudflare";
47
+ }
48
+ function isEdge() {
49
+ return runtimeInfo.isEdge;
50
+ }
51
+ function isBrowser() {
52
+ return runtime === "browser";
53
+ }
54
+ function isServer() {
55
+ return runtimeInfo.isServer;
56
+ }
57
+ function getRuntimeVersion() {
58
+ switch (runtime) {
59
+ case "node":
60
+ return `Node.js ${process.versions.node}`;
61
+ case "bun":
62
+ return `Bun ${globalThis.Bun.version}`;
63
+ case "deno":
64
+ return `Deno ${globalThis.Deno.version.deno}`;
65
+ case "cloudflare":
66
+ return "Cloudflare Workers";
67
+ case "edge":
68
+ return "Edge Runtime";
69
+ case "browser":
70
+ return typeof navigator !== "undefined" ? navigator.userAgent : "Browser";
71
+ default:
72
+ return "Unknown";
73
+ }
74
+ }
75
+
76
+ // src/env.ts
77
+ var edgeEnvStore = {};
78
+ function setEdgeEnv(env) {
79
+ edgeEnvStore = { ...edgeEnvStore, ...env };
80
+ }
81
+ function clearEdgeEnv() {
82
+ edgeEnvStore = {};
83
+ }
84
+ function getEnv(key, defaultValue) {
85
+ if (runtime === "cloudflare" || runtime === "edge") {
86
+ return edgeEnvStore[key] ?? defaultValue;
87
+ }
88
+ if (runtime === "deno") {
89
+ try {
90
+ return globalThis.Deno.env.get(key) ?? defaultValue;
91
+ } catch {
92
+ return defaultValue;
93
+ }
94
+ }
95
+ if (typeof process !== "undefined" && process.env) {
96
+ return process.env[key] ?? defaultValue;
97
+ }
98
+ if (runtime === "browser" && typeof globalThis.__ENV__ !== "undefined") {
99
+ return globalThis.__ENV__[key] ?? defaultValue;
100
+ }
101
+ return defaultValue;
102
+ }
103
+ function requireEnv(key) {
104
+ const value = getEnv(key);
105
+ if (value === void 0 || value === "") {
106
+ throw new Error(`Required environment variable "${key}" is not set`);
107
+ }
108
+ return value;
109
+ }
110
+ function getEnvNumber(key, defaultValue) {
111
+ const value = getEnv(key);
112
+ if (value === void 0 || value === "") {
113
+ return defaultValue;
114
+ }
115
+ const parsed = parseInt(value, 10);
116
+ return isNaN(parsed) ? defaultValue : parsed;
117
+ }
118
+ function getEnvFloat(key, defaultValue) {
119
+ const value = getEnv(key);
120
+ if (value === void 0 || value === "") {
121
+ return defaultValue;
122
+ }
123
+ const parsed = parseFloat(value);
124
+ return isNaN(parsed) ? defaultValue : parsed;
125
+ }
126
+ function getEnvBoolean(key, defaultValue = false) {
127
+ const value = getEnv(key);
128
+ if (value === void 0 || value === "") {
129
+ return defaultValue;
130
+ }
131
+ return value === "true" || value === "1" || value === "yes";
132
+ }
133
+ function getEnvArray(key, defaultValue = []) {
134
+ const value = getEnv(key);
135
+ if (value === void 0 || value === "") {
136
+ return defaultValue;
137
+ }
138
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
139
+ }
140
+ function getEnvJson(key, defaultValue) {
141
+ const value = getEnv(key);
142
+ if (value === void 0 || value === "") {
143
+ return defaultValue;
144
+ }
145
+ try {
146
+ return JSON.parse(value);
147
+ } catch {
148
+ return defaultValue;
149
+ }
150
+ }
151
+ function isDevelopment() {
152
+ const env = getEnv("NODE_ENV");
153
+ return env === "development" || env === void 0;
154
+ }
155
+ function isProduction() {
156
+ return getEnv("NODE_ENV") === "production";
157
+ }
158
+ function isTest() {
159
+ return getEnv("NODE_ENV") === "test";
160
+ }
161
+ function getEnvMode() {
162
+ const env = getEnv("NODE_ENV");
163
+ if (env === "production") return "production";
164
+ if (env === "test") return "test";
165
+ return "development";
166
+ }
167
+ function createEnvConfig(schema) {
168
+ const result = {};
169
+ for (const [key, config] of Object.entries(schema)) {
170
+ const envConfig = config;
171
+ let value;
172
+ switch (envConfig.type) {
173
+ case "number":
174
+ value = getEnvNumber(key, envConfig.default);
175
+ break;
176
+ case "boolean":
177
+ value = getEnvBoolean(key, envConfig.default);
178
+ break;
179
+ case "array":
180
+ value = getEnvArray(key, envConfig.default);
181
+ break;
182
+ case "json":
183
+ value = getEnvJson(key, envConfig.default);
184
+ break;
185
+ default:
186
+ value = getEnv(key, envConfig.default);
187
+ }
188
+ if (envConfig.required && (value === void 0 || value === "")) {
189
+ throw new Error(`Required environment variable "${key}" is not set`);
190
+ }
191
+ result[key] = value;
192
+ }
193
+ return result;
194
+ }
195
+
196
+ // src/transports/console.ts
197
+ var ConsoleTransport = class {
198
+ name = "console";
199
+ pretty;
200
+ colors;
201
+ constructor(options = {}) {
202
+ this.pretty = options.pretty ?? isDevelopment();
203
+ this.colors = options.colors ?? (runtime === "node" || runtime === "bun");
204
+ }
205
+ log(entry) {
206
+ if (this.pretty) {
207
+ this.logPretty(entry);
208
+ } else {
209
+ this.logJson(entry);
210
+ }
211
+ }
212
+ logJson(entry) {
213
+ const { level, message, timestamp, context, error } = entry;
214
+ const output = {
215
+ level,
216
+ time: timestamp,
217
+ msg: message
218
+ };
219
+ if (context && Object.keys(context).length > 0) {
220
+ Object.assign(output, context);
221
+ }
222
+ if (error) {
223
+ output["err"] = error;
224
+ }
225
+ console.log(JSON.stringify(output));
226
+ }
227
+ logPretty(entry) {
228
+ const { level, message, timestamp, context, error } = entry;
229
+ const levelColors = {
230
+ TRACE: "\x1B[90m",
231
+ // Gray
232
+ DEBUG: "\x1B[36m",
233
+ // Cyan
234
+ INFO: "\x1B[32m",
235
+ // Green
236
+ WARN: "\x1B[33m",
237
+ // Yellow
238
+ ERROR: "\x1B[31m",
239
+ // Red
240
+ FATAL: "\x1B[35m",
241
+ // Magenta
242
+ SILENT: ""
243
+ };
244
+ const reset = "\x1B[0m";
245
+ const color = this.colors ? levelColors[level] : "";
246
+ const resetCode = this.colors ? reset : "";
247
+ const timePart = timestamp.split("T")[1];
248
+ const time = timePart ? timePart.slice(0, 8) : timestamp;
249
+ let output = `${color}[${time}] ${level.padEnd(5)}${resetCode} ${message}`;
250
+ if (context && Object.keys(context).length > 0) {
251
+ output += ` ${JSON.stringify(context)}`;
252
+ }
253
+ if (level === "ERROR" || level === "FATAL") {
254
+ console.error(output);
255
+ if (error?.stack) {
256
+ console.error(error.stack);
257
+ }
258
+ } else if (level === "WARN") {
259
+ console.warn(output);
260
+ } else if (level === "DEBUG" || level === "TRACE") {
261
+ console.debug(output);
262
+ } else {
263
+ console.log(output);
264
+ }
265
+ }
266
+ };
267
+
268
+ // src/logger.ts
269
+ var LogLevel = {
270
+ TRACE: 10,
271
+ DEBUG: 20,
272
+ INFO: 30,
273
+ WARN: 40,
274
+ ERROR: 50,
275
+ FATAL: 60,
276
+ SILENT: 100
277
+ };
278
+ function redactFields(obj, fields) {
279
+ const result = { ...obj };
280
+ for (const field of fields) {
281
+ if (field in result) {
282
+ result[field] = "[REDACTED]";
283
+ }
284
+ const parts = field.split(".");
285
+ if (parts.length > 1) {
286
+ let current = result;
287
+ for (let i = 0; i < parts.length - 1; i++) {
288
+ const part = parts[i];
289
+ if (part && current && typeof current === "object" && part in current) {
290
+ const val = current[part];
291
+ if (val && typeof val === "object") {
292
+ current = val;
293
+ } else {
294
+ current = void 0;
295
+ break;
296
+ }
297
+ } else {
298
+ current = void 0;
299
+ break;
300
+ }
301
+ }
302
+ const lastPart = parts[parts.length - 1];
303
+ if (lastPart && current && typeof current === "object" && lastPart in current) {
304
+ current[lastPart] = "[REDACTED]";
305
+ }
306
+ }
307
+ }
308
+ return result;
309
+ }
310
+ var DEFAULT_REDACT_FIELDS = [
311
+ "password",
312
+ "secret",
313
+ "token",
314
+ "accessToken",
315
+ "refreshToken",
316
+ "apiKey",
317
+ "authorization",
318
+ "cookie",
319
+ "creditCard",
320
+ "ssn"
321
+ ];
322
+ var Logger = class _Logger {
323
+ level;
324
+ name;
325
+ context;
326
+ transports;
327
+ redactFields;
328
+ timestampFn;
329
+ constructor(config = {}) {
330
+ const levelName = config.level ?? getEnv("LOG_LEVEL") ?? "INFO";
331
+ this.level = LogLevel[levelName] ?? LogLevel.INFO;
332
+ this.name = config.name;
333
+ this.context = config.context ?? {};
334
+ this.transports = config.transports ?? [
335
+ new ConsoleTransport(
336
+ config.pretty !== void 0 ? { pretty: config.pretty } : {}
337
+ )
338
+ ];
339
+ this.redactFields = [...DEFAULT_REDACT_FIELDS, ...config.redact ?? []];
340
+ if (config.timestamp === false) {
341
+ this.timestampFn = () => "";
342
+ } else if (typeof config.timestamp === "function") {
343
+ this.timestampFn = config.timestamp;
344
+ } else {
345
+ this.timestampFn = () => (/* @__PURE__ */ new Date()).toISOString();
346
+ }
347
+ }
348
+ /**
349
+ * Create a child logger with additional context
350
+ */
351
+ child(context) {
352
+ const levelEntry = Object.entries(LogLevel).find(([_, v]) => v === this.level);
353
+ const levelName = levelEntry ? levelEntry[0] : "INFO";
354
+ const child = new _Logger({
355
+ level: levelName,
356
+ name: this.name,
357
+ context: { ...this.context, ...context },
358
+ transports: this.transports,
359
+ redact: this.redactFields
360
+ });
361
+ return child;
362
+ }
363
+ /**
364
+ * Log a message
365
+ */
366
+ log(level, message, context, error) {
367
+ const levelValue = LogLevel[level];
368
+ if (levelValue < this.level) return;
369
+ let finalContext = { ...this.context };
370
+ if (this.name) {
371
+ finalContext["module"] = this.name;
372
+ }
373
+ if (context) {
374
+ finalContext = { ...finalContext, ...context };
375
+ }
376
+ finalContext = redactFields(finalContext, this.redactFields);
377
+ const entry = {
378
+ level,
379
+ levelValue,
380
+ message,
381
+ timestamp: this.timestampFn(),
382
+ context: Object.keys(finalContext).length > 0 ? finalContext : void 0,
383
+ error: error ? {
384
+ name: error.name,
385
+ message: error.message,
386
+ stack: error.stack
387
+ } : void 0
388
+ };
389
+ for (const transport of this.transports) {
390
+ transport.log(entry);
391
+ }
392
+ }
393
+ trace(message, context) {
394
+ this.log("TRACE", message, context);
395
+ }
396
+ debug(message, context) {
397
+ this.log("DEBUG", message, context);
398
+ }
399
+ info(message, context) {
400
+ this.log("INFO", message, context);
401
+ }
402
+ warn(message, context) {
403
+ this.log("WARN", message, context);
404
+ }
405
+ error(message, error, context) {
406
+ const err2 = error instanceof Error ? error : void 0;
407
+ const ctx = error instanceof Error ? context : error;
408
+ this.log("ERROR", message, ctx, err2);
409
+ }
410
+ fatal(message, error, context) {
411
+ const err2 = error instanceof Error ? error : void 0;
412
+ const ctx = error instanceof Error ? context : error;
413
+ this.log("FATAL", message, ctx, err2);
414
+ }
415
+ };
416
+ function createLogger(config) {
417
+ return new Logger(config);
418
+ }
419
+ var logger = createLogger();
420
+ function logError(log, error, message, context) {
421
+ if (error instanceof Error) {
422
+ log.error(message, error, context);
423
+ } else {
424
+ log.error(message, { error: String(error), ...context });
425
+ }
426
+ }
427
+ async function measureTime(log, operation, fn) {
428
+ const start = Date.now();
429
+ try {
430
+ const result = await fn();
431
+ const duration = Date.now() - start;
432
+ log.info(`${operation} completed`, { operation, durationMs: duration });
433
+ return result;
434
+ } catch (error) {
435
+ const duration = Date.now() - start;
436
+ logError(log, error, `${operation} failed`, { operation, durationMs: duration });
437
+ throw error;
438
+ }
439
+ }
440
+ function createRequestLogger(baseLogger, request) {
441
+ const pathname = request.url ? new URL(request.url).pathname : void 0;
442
+ return baseLogger.child({
443
+ requestId: request.requestId,
444
+ method: request.method,
445
+ path: pathname,
446
+ userId: request.userId,
447
+ tenantId: request.tenantId
448
+ });
449
+ }
450
+
451
+ // src/decimal.ts
452
+ var PRECISION = 20;
453
+ var Decimal = class _Decimal {
454
+ value;
455
+ constructor(value) {
456
+ if (value instanceof _Decimal) {
457
+ this.value = value.value;
458
+ } else if (typeof value === "number") {
459
+ this.value = this.normalizeNumber(value);
460
+ } else {
461
+ this.value = this.normalizeString(value);
462
+ }
463
+ }
464
+ normalizeNumber(n) {
465
+ if (!isFinite(n)) {
466
+ throw new Error(`Invalid number: ${n}`);
467
+ }
468
+ return n.toFixed(PRECISION).replace(/\.?0+$/, "") || "0";
469
+ }
470
+ normalizeString(s) {
471
+ const trimmed = s.trim();
472
+ if (!/^-?\d*\.?\d+$/.test(trimmed)) {
473
+ throw new Error(`Invalid decimal string: ${s}`);
474
+ }
475
+ return trimmed.replace(/^(-?)0+(?=\d)/, "$1").replace(/\.?0+$/, "") || "0";
476
+ }
477
+ /**
478
+ * Add two decimals
479
+ */
480
+ add(other) {
481
+ const a = parseFloat(this.value);
482
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
483
+ return new _Decimal(a + b);
484
+ }
485
+ /**
486
+ * Subtract
487
+ */
488
+ sub(other) {
489
+ const a = parseFloat(this.value);
490
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
491
+ return new _Decimal(a - b);
492
+ }
493
+ /**
494
+ * Multiply
495
+ */
496
+ mul(other) {
497
+ const a = parseFloat(this.value);
498
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
499
+ return new _Decimal(a * b);
500
+ }
501
+ /**
502
+ * Divide
503
+ */
504
+ div(other) {
505
+ const a = parseFloat(this.value);
506
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
507
+ if (b === 0) {
508
+ throw new Error("Division by zero");
509
+ }
510
+ return new _Decimal(a / b);
511
+ }
512
+ /**
513
+ * Modulo
514
+ */
515
+ mod(other) {
516
+ const a = parseFloat(this.value);
517
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
518
+ return new _Decimal(a % b);
519
+ }
520
+ /**
521
+ * Power
522
+ */
523
+ pow(exp) {
524
+ const a = parseFloat(this.value);
525
+ return new _Decimal(Math.pow(a, exp));
526
+ }
527
+ /**
528
+ * Square root
529
+ */
530
+ sqrt() {
531
+ const a = parseFloat(this.value);
532
+ if (a < 0) {
533
+ throw new Error("Square root of negative number");
534
+ }
535
+ return new _Decimal(Math.sqrt(a));
536
+ }
537
+ /**
538
+ * Absolute value
539
+ */
540
+ abs() {
541
+ const a = parseFloat(this.value);
542
+ return new _Decimal(Math.abs(a));
543
+ }
544
+ /**
545
+ * Negate
546
+ */
547
+ neg() {
548
+ const a = parseFloat(this.value);
549
+ return new _Decimal(-a);
550
+ }
551
+ /**
552
+ * Round to decimal places
553
+ */
554
+ round(decimals = 0) {
555
+ const a = parseFloat(this.value);
556
+ const factor = Math.pow(10, decimals);
557
+ return new _Decimal(Math.round(a * factor) / factor);
558
+ }
559
+ /**
560
+ * Floor to decimal places
561
+ */
562
+ floor(decimals = 0) {
563
+ const a = parseFloat(this.value);
564
+ const factor = Math.pow(10, decimals);
565
+ return new _Decimal(Math.floor(a * factor) / factor);
566
+ }
567
+ /**
568
+ * Ceiling to decimal places
569
+ */
570
+ ceil(decimals = 0) {
571
+ const a = parseFloat(this.value);
572
+ const factor = Math.pow(10, decimals);
573
+ return new _Decimal(Math.ceil(a * factor) / factor);
574
+ }
575
+ /**
576
+ * Compare: returns -1, 0, or 1
577
+ */
578
+ cmp(other) {
579
+ const a = parseFloat(this.value);
580
+ const b = parseFloat(other instanceof _Decimal ? other.value : String(other));
581
+ if (a < b) return -1;
582
+ if (a > b) return 1;
583
+ return 0;
584
+ }
585
+ /**
586
+ * Equality check
587
+ */
588
+ eq(other) {
589
+ return this.cmp(other) === 0;
590
+ }
591
+ /**
592
+ * Greater than
593
+ */
594
+ gt(other) {
595
+ return this.cmp(other) === 1;
596
+ }
597
+ /**
598
+ * Greater than or equal
599
+ */
600
+ gte(other) {
601
+ return this.cmp(other) >= 0;
602
+ }
603
+ /**
604
+ * Less than
605
+ */
606
+ lt(other) {
607
+ return this.cmp(other) === -1;
608
+ }
609
+ /**
610
+ * Less than or equal
611
+ */
612
+ lte(other) {
613
+ return this.cmp(other) <= 0;
614
+ }
615
+ /**
616
+ * Check if zero
617
+ */
618
+ isZero() {
619
+ return parseFloat(this.value) === 0;
620
+ }
621
+ /**
622
+ * Check if positive
623
+ */
624
+ isPositive() {
625
+ return parseFloat(this.value) > 0;
626
+ }
627
+ /**
628
+ * Check if negative
629
+ */
630
+ isNegative() {
631
+ return parseFloat(this.value) < 0;
632
+ }
633
+ /**
634
+ * Convert to number
635
+ */
636
+ toNumber() {
637
+ return parseFloat(this.value);
638
+ }
639
+ /**
640
+ * Convert to string
641
+ */
642
+ toString() {
643
+ return this.value;
644
+ }
645
+ /**
646
+ * Format with fixed decimal places
647
+ */
648
+ toFixed(decimals = 2) {
649
+ return parseFloat(this.value).toFixed(decimals);
650
+ }
651
+ /**
652
+ * Convert to JSON (string representation)
653
+ */
654
+ toJSON() {
655
+ return this.value;
656
+ }
657
+ /**
658
+ * Static: Create from value
659
+ */
660
+ static from(value) {
661
+ return new _Decimal(value);
662
+ }
663
+ /**
664
+ * Static: Sum array of values
665
+ */
666
+ static sum(values) {
667
+ return values.reduce(
668
+ (acc, val) => acc.add(val),
669
+ new _Decimal(0)
670
+ );
671
+ }
672
+ /**
673
+ * Static: Average of array
674
+ */
675
+ static avg(values) {
676
+ if (values.length === 0) return new _Decimal(0);
677
+ return _Decimal.sum(values).div(values.length);
678
+ }
679
+ /**
680
+ * Static: Min of array
681
+ */
682
+ static min(...values) {
683
+ if (values.length === 0) throw new Error("No values provided");
684
+ return values.reduce((min, val) => {
685
+ const d = new _Decimal(val);
686
+ return d.lt(min) ? d : min;
687
+ }, new _Decimal(values[0]));
688
+ }
689
+ /**
690
+ * Static: Max of array
691
+ */
692
+ static max(...values) {
693
+ if (values.length === 0) throw new Error("No values provided");
694
+ return values.reduce((max, val) => {
695
+ const d = new _Decimal(val);
696
+ return d.gt(max) ? d : max;
697
+ }, new _Decimal(values[0]));
698
+ }
699
+ };
700
+ var DecimalUtils = {
701
+ /**
702
+ * Convert number to database decimal string
703
+ */
704
+ toDecimalString(value) {
705
+ if (value === null || value === void 0) return null;
706
+ return new Decimal(value).toString();
707
+ },
708
+ /**
709
+ * Convert database decimal string to number
710
+ */
711
+ fromDecimalString(value) {
712
+ if (!value) return 0;
713
+ return new Decimal(value).toNumber();
714
+ },
715
+ /**
716
+ * Perform precise decimal multiplication
717
+ */
718
+ multiply(a, b) {
719
+ return new Decimal(a).mul(b).toString();
720
+ },
721
+ /**
722
+ * Perform precise decimal addition
723
+ */
724
+ add(a, b) {
725
+ return new Decimal(a).add(b).toString();
726
+ },
727
+ /**
728
+ * Perform precise decimal subtraction
729
+ */
730
+ subtract(a, b) {
731
+ return new Decimal(a).sub(b).toString();
732
+ },
733
+ /**
734
+ * Perform precise decimal division
735
+ */
736
+ divide(a, b) {
737
+ return new Decimal(a).div(b).toString();
738
+ },
739
+ /**
740
+ * Format decimal for display
741
+ */
742
+ format(value, decimalPlaces = 2) {
743
+ return new Decimal(value).toFixed(decimalPlaces);
744
+ },
745
+ /**
746
+ * Format as currency
747
+ */
748
+ formatCurrency(value, options = {}) {
749
+ const { currency = "USD", locale = "en-US", decimals = 2 } = options;
750
+ const num = new Decimal(value).toNumber();
751
+ return new Intl.NumberFormat(locale, {
752
+ style: "currency",
753
+ currency,
754
+ minimumFractionDigits: decimals,
755
+ maximumFractionDigits: decimals
756
+ }).format(num);
757
+ },
758
+ /**
759
+ * Convert object with decimal fields for database insert/update
760
+ */
761
+ prepareForDatabase(data, decimalFields) {
762
+ const result = { ...data };
763
+ for (const field of decimalFields) {
764
+ if (field in result && result[field] !== void 0 && result[field] !== null) {
765
+ const value = result[field];
766
+ if (typeof value === "number") {
767
+ result[field] = DecimalUtils.toDecimalString(value);
768
+ }
769
+ }
770
+ }
771
+ return result;
772
+ },
773
+ /**
774
+ * Convert object with decimal fields from database
775
+ */
776
+ parseFromDatabase(data, decimalFields) {
777
+ const result = { ...data };
778
+ for (const field of decimalFields) {
779
+ if (field in result && result[field] !== void 0 && result[field] !== null) {
780
+ const value = result[field];
781
+ if (typeof value === "string") {
782
+ result[field] = DecimalUtils.fromDecimalString(value);
783
+ }
784
+ }
785
+ }
786
+ return result;
787
+ }
788
+ };
789
+ function decimal(value) {
790
+ return new Decimal(value);
791
+ }
792
+
793
+ // src/types.ts
794
+ function ok(data) {
795
+ return { success: true, data };
796
+ }
797
+ function err(error) {
798
+ return { success: false, error };
799
+ }
800
+
801
+ // src/errors.ts
802
+ var ParsError = class extends Error {
803
+ code;
804
+ statusCode;
805
+ details;
806
+ constructor(message, code, statusCode = 500, details) {
807
+ super(message);
808
+ this.name = "ParsError";
809
+ this.code = code;
810
+ this.statusCode = statusCode;
811
+ this.details = details ?? void 0;
812
+ Error.captureStackTrace?.(this, this.constructor);
813
+ }
814
+ toJSON() {
815
+ return {
816
+ name: this.name,
817
+ message: this.message,
818
+ code: this.code,
819
+ statusCode: this.statusCode,
820
+ details: this.details
821
+ };
822
+ }
823
+ };
824
+ var AuthError = class extends ParsError {
825
+ constructor(message, code = "AUTH_ERROR", statusCode = 401, details) {
826
+ super(message, code, statusCode, details);
827
+ this.name = "AuthError";
828
+ }
829
+ };
830
+ var UnauthorizedError = class extends AuthError {
831
+ constructor(message = "Unauthorized", details) {
832
+ super(message, "UNAUTHORIZED", 401, details);
833
+ this.name = "UnauthorizedError";
834
+ }
835
+ };
836
+ var ForbiddenError = class extends AuthError {
837
+ constructor(message = "Forbidden", details) {
838
+ super(message, "FORBIDDEN", 403, details);
839
+ this.name = "ForbiddenError";
840
+ }
841
+ };
842
+ var InvalidCredentialsError = class extends AuthError {
843
+ constructor(message = "Invalid credentials", details) {
844
+ super(message, "INVALID_CREDENTIALS", 401, details);
845
+ this.name = "InvalidCredentialsError";
846
+ }
847
+ };
848
+ var SessionExpiredError = class extends AuthError {
849
+ constructor(message = "Session expired", details) {
850
+ super(message, "SESSION_EXPIRED", 401, details);
851
+ this.name = "SessionExpiredError";
852
+ }
853
+ };
854
+ var TwoFactorRequiredError = class extends AuthError {
855
+ constructor(message = "Two-factor authentication required", challengeId, details) {
856
+ super(message, "TWO_FACTOR_REQUIRED", 403, { ...details, challengeId });
857
+ this.challengeId = challengeId;
858
+ this.name = "TwoFactorRequiredError";
859
+ }
860
+ };
861
+ var AccountLockedError = class extends AuthError {
862
+ constructor(message = "Account locked", lockedUntil, details) {
863
+ super(message, "ACCOUNT_LOCKED", 423, { ...details, lockedUntil });
864
+ this.lockedUntil = lockedUntil;
865
+ this.name = "AccountLockedError";
866
+ }
867
+ };
868
+ var TenantError = class extends ParsError {
869
+ constructor(message, code = "TENANT_ERROR", statusCode = 400, details) {
870
+ super(message, code, statusCode, details);
871
+ this.name = "TenantError";
872
+ }
873
+ };
874
+ var TenantNotFoundError = class extends TenantError {
875
+ constructor(message = "Tenant not found", details) {
876
+ super(message, "TENANT_NOT_FOUND", 404, details);
877
+ this.name = "TenantNotFoundError";
878
+ }
879
+ };
880
+ var TenantSuspendedError = class extends TenantError {
881
+ constructor(message = "Tenant suspended", details) {
882
+ super(message, "TENANT_SUSPENDED", 403, details);
883
+ this.name = "TenantSuspendedError";
884
+ }
885
+ };
886
+ var MembershipError = class extends TenantError {
887
+ constructor(message = "Membership error", code = "MEMBERSHIP_ERROR", statusCode = 400, details) {
888
+ super(message, code, statusCode, details);
889
+ this.name = "MembershipError";
890
+ }
891
+ };
892
+ var MembershipNotFoundError = class extends MembershipError {
893
+ constructor(message = "Membership not found", details) {
894
+ super(message, "MEMBERSHIP_NOT_FOUND", 404, details);
895
+ this.name = "MembershipNotFoundError";
896
+ }
897
+ };
898
+ var MembershipExpiredError = class extends MembershipError {
899
+ constructor(message = "Membership expired", details) {
900
+ super(message, "MEMBERSHIP_EXPIRED", 403, details);
901
+ this.name = "MembershipExpiredError";
902
+ }
903
+ };
904
+ var ValidationError = class extends ParsError {
905
+ constructor(message = "Validation failed", errors, details) {
906
+ super(message, "VALIDATION_ERROR", 400, { ...details, errors });
907
+ this.errors = errors;
908
+ this.name = "ValidationError";
909
+ }
910
+ };
911
+ var RateLimitError = class extends ParsError {
912
+ constructor(message = "Rate limit exceeded", retryAfter, details) {
913
+ super(message, "RATE_LIMIT_EXCEEDED", 429, { ...details, retryAfter });
914
+ this.retryAfter = retryAfter;
915
+ this.name = "RateLimitError";
916
+ }
917
+ };
918
+ var NotFoundError = class extends ParsError {
919
+ constructor(resource = "Resource", message, details) {
920
+ super(message ?? `${resource} not found`, "NOT_FOUND", 404, { ...details, resource });
921
+ this.name = "NotFoundError";
922
+ }
923
+ };
924
+ var ConflictError = class extends ParsError {
925
+ constructor(message = "Conflict", details) {
926
+ super(message, "CONFLICT", 409, details);
927
+ this.name = "ConflictError";
928
+ }
929
+ };
930
+ var DuplicateError = class extends ConflictError {
931
+ constructor(resource = "Resource", field, details) {
932
+ super(`${resource} already exists${field ? ` with this ${field}` : ""}`, {
933
+ ...details,
934
+ resource,
935
+ field
936
+ });
937
+ this.name = "DuplicateError";
938
+ }
939
+ };
940
+
941
+ // src/error-codes.ts
942
+ var ErrorCodes = {
943
+ // ============================================================================
944
+ // Authentication Errors (401, 403, 423)
945
+ // ============================================================================
946
+ AUTH_ERROR: {
947
+ code: "AUTH_ERROR",
948
+ status: 401,
949
+ category: "auth"
950
+ },
951
+ UNAUTHORIZED: {
952
+ code: "UNAUTHORIZED",
953
+ status: 401,
954
+ category: "auth"
955
+ },
956
+ FORBIDDEN: {
957
+ code: "FORBIDDEN",
958
+ status: 403,
959
+ category: "auth"
960
+ },
961
+ INVALID_CREDENTIALS: {
962
+ code: "INVALID_CREDENTIALS",
963
+ status: 401,
964
+ category: "auth"
965
+ },
966
+ SESSION_EXPIRED: {
967
+ code: "SESSION_EXPIRED",
968
+ status: 401,
969
+ category: "auth"
970
+ },
971
+ TOKEN_EXPIRED: {
972
+ code: "TOKEN_EXPIRED",
973
+ status: 401,
974
+ category: "auth"
975
+ },
976
+ TOKEN_INVALID: {
977
+ code: "TOKEN_INVALID",
978
+ status: 401,
979
+ category: "auth"
980
+ },
981
+ TWO_FACTOR_REQUIRED: {
982
+ code: "TWO_FACTOR_REQUIRED",
983
+ status: 403,
984
+ category: "auth"
985
+ },
986
+ TWO_FACTOR_INVALID: {
987
+ code: "TWO_FACTOR_INVALID",
988
+ status: 401,
989
+ category: "auth"
990
+ },
991
+ ACCOUNT_LOCKED: {
992
+ code: "ACCOUNT_LOCKED",
993
+ status: 423,
994
+ category: "auth"
995
+ },
996
+ ACCOUNT_DISABLED: {
997
+ code: "ACCOUNT_DISABLED",
998
+ status: 403,
999
+ category: "auth"
1000
+ },
1001
+ PASSWORD_RESET_REQUIRED: {
1002
+ code: "PASSWORD_RESET_REQUIRED",
1003
+ status: 403,
1004
+ category: "auth"
1005
+ },
1006
+ // ============================================================================
1007
+ // Tenant Errors (400, 403, 404)
1008
+ // ============================================================================
1009
+ TENANT_ERROR: {
1010
+ code: "TENANT_ERROR",
1011
+ status: 400,
1012
+ category: "tenant"
1013
+ },
1014
+ TENANT_NOT_FOUND: {
1015
+ code: "TENANT_NOT_FOUND",
1016
+ status: 404,
1017
+ category: "tenant"
1018
+ },
1019
+ TENANT_SUSPENDED: {
1020
+ code: "TENANT_SUSPENDED",
1021
+ status: 403,
1022
+ category: "tenant"
1023
+ },
1024
+ TENANT_LIMIT_EXCEEDED: {
1025
+ code: "TENANT_LIMIT_EXCEEDED",
1026
+ status: 403,
1027
+ category: "tenant"
1028
+ },
1029
+ MEMBERSHIP_ERROR: {
1030
+ code: "MEMBERSHIP_ERROR",
1031
+ status: 400,
1032
+ category: "tenant"
1033
+ },
1034
+ MEMBERSHIP_NOT_FOUND: {
1035
+ code: "MEMBERSHIP_NOT_FOUND",
1036
+ status: 404,
1037
+ category: "tenant"
1038
+ },
1039
+ MEMBERSHIP_EXPIRED: {
1040
+ code: "MEMBERSHIP_EXPIRED",
1041
+ status: 403,
1042
+ category: "tenant"
1043
+ },
1044
+ // ============================================================================
1045
+ // Validation Errors (400, 422)
1046
+ // ============================================================================
1047
+ VALIDATION_ERROR: {
1048
+ code: "VALIDATION_ERROR",
1049
+ status: 400,
1050
+ category: "validation"
1051
+ },
1052
+ BAD_REQUEST: {
1053
+ code: "BAD_REQUEST",
1054
+ status: 400,
1055
+ category: "validation"
1056
+ },
1057
+ INVALID_INPUT: {
1058
+ code: "INVALID_INPUT",
1059
+ status: 422,
1060
+ category: "validation"
1061
+ },
1062
+ MISSING_REQUIRED_FIELD: {
1063
+ code: "MISSING_REQUIRED_FIELD",
1064
+ status: 400,
1065
+ category: "validation"
1066
+ },
1067
+ INVALID_FORMAT: {
1068
+ code: "INVALID_FORMAT",
1069
+ status: 400,
1070
+ category: "validation"
1071
+ },
1072
+ // ============================================================================
1073
+ // Resource Errors (404, 409, 410)
1074
+ // ============================================================================
1075
+ NOT_FOUND: {
1076
+ code: "NOT_FOUND",
1077
+ status: 404,
1078
+ category: "resource"
1079
+ },
1080
+ CONFLICT: {
1081
+ code: "CONFLICT",
1082
+ status: 409,
1083
+ category: "resource"
1084
+ },
1085
+ DUPLICATE: {
1086
+ code: "DUPLICATE",
1087
+ status: 409,
1088
+ category: "resource"
1089
+ },
1090
+ GONE: {
1091
+ code: "GONE",
1092
+ status: 410,
1093
+ category: "resource"
1094
+ },
1095
+ RESOURCE_LOCKED: {
1096
+ code: "RESOURCE_LOCKED",
1097
+ status: 423,
1098
+ category: "resource"
1099
+ },
1100
+ // ============================================================================
1101
+ // Rate Limiting (429)
1102
+ // ============================================================================
1103
+ RATE_LIMIT_EXCEEDED: {
1104
+ code: "RATE_LIMIT_EXCEEDED",
1105
+ status: 429,
1106
+ category: "rate_limit",
1107
+ retryable: true
1108
+ },
1109
+ QUOTA_EXCEEDED: {
1110
+ code: "QUOTA_EXCEEDED",
1111
+ status: 429,
1112
+ category: "rate_limit"
1113
+ },
1114
+ // ============================================================================
1115
+ // Server Errors (500, 502, 503, 504)
1116
+ // ============================================================================
1117
+ INTERNAL_ERROR: {
1118
+ code: "INTERNAL_ERROR",
1119
+ status: 500,
1120
+ category: "server"
1121
+ },
1122
+ BAD_GATEWAY: {
1123
+ code: "BAD_GATEWAY",
1124
+ status: 502,
1125
+ category: "server",
1126
+ retryable: true
1127
+ },
1128
+ SERVICE_UNAVAILABLE: {
1129
+ code: "SERVICE_UNAVAILABLE",
1130
+ status: 503,
1131
+ category: "server",
1132
+ retryable: true
1133
+ },
1134
+ GATEWAY_TIMEOUT: {
1135
+ code: "GATEWAY_TIMEOUT",
1136
+ status: 504,
1137
+ category: "server",
1138
+ retryable: true
1139
+ },
1140
+ // ============================================================================
1141
+ // Database Errors (500)
1142
+ // ============================================================================
1143
+ DATABASE_ERROR: {
1144
+ code: "DATABASE_ERROR",
1145
+ status: 500,
1146
+ category: "database"
1147
+ },
1148
+ CONNECTION_ERROR: {
1149
+ code: "CONNECTION_ERROR",
1150
+ status: 503,
1151
+ category: "database",
1152
+ retryable: true
1153
+ },
1154
+ TRANSACTION_ERROR: {
1155
+ code: "TRANSACTION_ERROR",
1156
+ status: 500,
1157
+ category: "database"
1158
+ },
1159
+ RLS_ERROR: {
1160
+ code: "RLS_ERROR",
1161
+ status: 500,
1162
+ category: "database"
1163
+ },
1164
+ // ============================================================================
1165
+ // External Service Errors (502, 503)
1166
+ // ============================================================================
1167
+ EXTERNAL_SERVICE_ERROR: {
1168
+ code: "EXTERNAL_SERVICE_ERROR",
1169
+ status: 502,
1170
+ category: "external",
1171
+ retryable: true
1172
+ },
1173
+ EXTERNAL_TIMEOUT: {
1174
+ code: "EXTERNAL_TIMEOUT",
1175
+ status: 504,
1176
+ category: "external",
1177
+ retryable: true
1178
+ }
1179
+ };
1180
+ function getErrorCode(code) {
1181
+ return ErrorCodes[code];
1182
+ }
1183
+ function getErrorCodesByCategory(category) {
1184
+ return Object.values(ErrorCodes).filter((e) => e.category === category);
1185
+ }
1186
+ function isRetryableError(code) {
1187
+ const errorCode = getErrorCode(code);
1188
+ return errorCode?.retryable === true;
1189
+ }
1190
+ function getStatusForCode(code) {
1191
+ const errorCode = getErrorCode(code);
1192
+ return errorCode?.status ?? 500;
1193
+ }
1194
+
1195
+ // src/transports/axiom.ts
1196
+ var AxiomTransport = class {
1197
+ name = "axiom";
1198
+ buffer = [];
1199
+ flushTimer = null;
1200
+ isFlushing = false;
1201
+ options;
1202
+ constructor(options) {
1203
+ this.options = {
1204
+ batchSize: 100,
1205
+ flushInterval: 5e3,
1206
+ apiUrl: "https://api.axiom.co",
1207
+ ...options
1208
+ };
1209
+ if (this.options.flushInterval > 0) {
1210
+ this.flushTimer = setInterval(
1211
+ () => this.flush(),
1212
+ this.options.flushInterval
1213
+ );
1214
+ }
1215
+ }
1216
+ log(entry) {
1217
+ if (this.options.enabled === false) return;
1218
+ const event = {
1219
+ _time: entry.timestamp,
1220
+ level: entry.level,
1221
+ message: entry.message
1222
+ };
1223
+ if (entry.context) {
1224
+ Object.assign(event, entry.context);
1225
+ }
1226
+ if (entry.error) {
1227
+ event["error.name"] = entry.error.name;
1228
+ event["error.message"] = entry.error.message;
1229
+ event["error.stack"] = entry.error.stack;
1230
+ }
1231
+ this.buffer.push(event);
1232
+ if (this.buffer.length >= this.options.batchSize) {
1233
+ this.flush();
1234
+ }
1235
+ }
1236
+ async flush() {
1237
+ if (this.isFlushing || this.buffer.length === 0) return;
1238
+ this.isFlushing = true;
1239
+ const events = this.buffer;
1240
+ this.buffer = [];
1241
+ try {
1242
+ const response = await fetch(
1243
+ `${this.options.apiUrl}/v1/datasets/${this.options.dataset}/ingest`,
1244
+ {
1245
+ method: "POST",
1246
+ headers: {
1247
+ Authorization: `Bearer ${this.options.token}`,
1248
+ "Content-Type": "application/json",
1249
+ ...this.options.orgId && {
1250
+ "X-Axiom-Org-Id": this.options.orgId
1251
+ }
1252
+ },
1253
+ body: JSON.stringify(events)
1254
+ }
1255
+ );
1256
+ if (!response.ok) {
1257
+ const errorText = await response.text();
1258
+ throw new Error(`Axiom ingest failed: ${response.status} ${errorText}`);
1259
+ }
1260
+ } catch (error) {
1261
+ if (this.options.onError) {
1262
+ this.options.onError(
1263
+ error instanceof Error ? error : new Error(String(error)),
1264
+ events.length
1265
+ );
1266
+ } else {
1267
+ console.error("[Axiom] Failed to send logs:", error);
1268
+ }
1269
+ } finally {
1270
+ this.isFlushing = false;
1271
+ }
1272
+ }
1273
+ async close() {
1274
+ if (this.flushTimer) {
1275
+ clearInterval(this.flushTimer);
1276
+ this.flushTimer = null;
1277
+ }
1278
+ await this.flush();
1279
+ }
1280
+ };
1281
+ function createAxiomTransport(options) {
1282
+ return new AxiomTransport(options);
1283
+ }
1284
+
1285
+ // src/transports/sentry.ts
1286
+ var SentryTransport = class {
1287
+ name = "sentry";
1288
+ client;
1289
+ dsn;
1290
+ options;
1291
+ user = null;
1292
+ contexts = /* @__PURE__ */ new Map();
1293
+ breadcrumbs = [];
1294
+ maxBreadcrumbs = 100;
1295
+ constructor(options) {
1296
+ this.options = {
1297
+ sampleRate: 1,
1298
+ ...options
1299
+ };
1300
+ if (options.client) {
1301
+ this.client = options.client;
1302
+ } else if (options.dsn) {
1303
+ this.dsn = this.parseDSN(options.dsn);
1304
+ } else {
1305
+ throw new Error("SentryTransport requires either 'dsn' or 'client' option");
1306
+ }
1307
+ }
1308
+ /**
1309
+ * Parse Sentry DSN
1310
+ */
1311
+ parseDSN(dsn) {
1312
+ const match = dsn.match(/^(https?):\/\/([^@]+)@([^/]+)\/(.+)$/);
1313
+ if (!match || !match[1] || !match[2] || !match[3] || !match[4]) {
1314
+ throw new Error(`Invalid Sentry DSN: ${dsn}`);
1315
+ }
1316
+ return {
1317
+ protocol: match[1],
1318
+ publicKey: match[2],
1319
+ host: match[3],
1320
+ projectId: match[4]
1321
+ };
1322
+ }
1323
+ /**
1324
+ * LogTransport implementation
1325
+ * Only sends ERROR and FATAL level logs
1326
+ */
1327
+ log(entry) {
1328
+ if (this.options.enabled === false) return;
1329
+ if (entry.levelValue < 50) return;
1330
+ if (entry.error) {
1331
+ const error = new Error(entry.error.message);
1332
+ error.name = entry.error.name;
1333
+ if (entry.error.stack) {
1334
+ error.stack = entry.error.stack;
1335
+ }
1336
+ this.captureException(
1337
+ error,
1338
+ entry.context ? { extra: entry.context } : void 0
1339
+ );
1340
+ } else {
1341
+ this.captureMessage(
1342
+ entry.message,
1343
+ entry.level === "FATAL" ? "error" : "warning",
1344
+ entry.context ? { extra: entry.context } : void 0
1345
+ );
1346
+ }
1347
+ }
1348
+ /**
1349
+ * Capture an exception
1350
+ */
1351
+ captureException(error, context) {
1352
+ if (this.options.enabled === false) return;
1353
+ if (!this.shouldSample()) return;
1354
+ if (this.client) {
1355
+ this.captureWithSdk(error, context);
1356
+ } else {
1357
+ this.captureWithHttp(error, context);
1358
+ }
1359
+ }
1360
+ /**
1361
+ * Capture a message
1362
+ */
1363
+ captureMessage(message, level, context) {
1364
+ if (this.options.enabled === false) return;
1365
+ if (!this.shouldSample()) return;
1366
+ if (this.client) {
1367
+ this.client.withScope((scope) => {
1368
+ this.applyContext(scope, context);
1369
+ scope.setLevel(level);
1370
+ this.client.captureMessage(message, level);
1371
+ });
1372
+ } else {
1373
+ this.sendHttpEvent({
1374
+ level: level === "warning" ? "warning" : level === "info" ? "info" : "error",
1375
+ message: { formatted: message },
1376
+ ...this.buildEventContext(context)
1377
+ });
1378
+ }
1379
+ }
1380
+ /**
1381
+ * Set user context
1382
+ */
1383
+ setUser(user) {
1384
+ this.user = user;
1385
+ }
1386
+ /**
1387
+ * Set custom context
1388
+ */
1389
+ setContext(name, context) {
1390
+ this.contexts.set(name, context);
1391
+ }
1392
+ /**
1393
+ * Add breadcrumb
1394
+ */
1395
+ addBreadcrumb(breadcrumb) {
1396
+ this.breadcrumbs.push({
1397
+ ...breadcrumb,
1398
+ timestamp: breadcrumb.timestamp ?? Date.now() / 1e3
1399
+ });
1400
+ if (this.breadcrumbs.length > this.maxBreadcrumbs) {
1401
+ this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);
1402
+ }
1403
+ }
1404
+ /**
1405
+ * Flush pending events
1406
+ */
1407
+ async flush() {
1408
+ if (this.client?.flush) {
1409
+ await this.client.flush(2e3);
1410
+ }
1411
+ }
1412
+ // ============================================================================
1413
+ // Private Methods
1414
+ // ============================================================================
1415
+ shouldSample() {
1416
+ const rate = this.options.sampleRate ?? 1;
1417
+ return Math.random() < rate;
1418
+ }
1419
+ /**
1420
+ * Capture with SDK (BYOS mode)
1421
+ */
1422
+ captureWithSdk(error, context) {
1423
+ this.client.withScope((scope) => {
1424
+ this.applyContext(scope, context);
1425
+ this.client.captureException(error);
1426
+ });
1427
+ }
1428
+ /**
1429
+ * Apply context to SDK scope
1430
+ */
1431
+ applyContext(scope, context) {
1432
+ if (this.user) {
1433
+ scope.setUser(this.user);
1434
+ } else if (context?.userId) {
1435
+ scope.setUser({ id: context.userId });
1436
+ }
1437
+ if (this.options.tags) {
1438
+ for (const [key, value] of Object.entries(this.options.tags)) {
1439
+ scope.setTag(key, value);
1440
+ }
1441
+ }
1442
+ if (context?.tags) {
1443
+ for (const [key, value] of Object.entries(context.tags)) {
1444
+ scope.setTag(key, value);
1445
+ }
1446
+ }
1447
+ if (context?.requestId) {
1448
+ scope.setTag("requestId", context.requestId);
1449
+ }
1450
+ if (context?.tenantId) {
1451
+ scope.setTag("tenantId", context.tenantId);
1452
+ }
1453
+ if (context?.extra) {
1454
+ scope.setExtras(context.extra);
1455
+ }
1456
+ for (const bc of this.breadcrumbs) {
1457
+ scope.addBreadcrumb(bc);
1458
+ }
1459
+ }
1460
+ /**
1461
+ * Capture with HTTP API (default mode)
1462
+ */
1463
+ captureWithHttp(error, context) {
1464
+ const stacktrace = this.parseStackTrace(error.stack);
1465
+ const exceptionValue = {
1466
+ type: error.name,
1467
+ value: error.message
1468
+ };
1469
+ if (stacktrace) {
1470
+ exceptionValue.stacktrace = stacktrace;
1471
+ }
1472
+ const event = {
1473
+ level: "error",
1474
+ exception: {
1475
+ values: [exceptionValue]
1476
+ },
1477
+ ...this.buildEventContext(context)
1478
+ };
1479
+ this.sendHttpEvent(event);
1480
+ }
1481
+ /**
1482
+ * Build event context for HTTP API
1483
+ */
1484
+ buildEventContext(context) {
1485
+ const event = {};
1486
+ if (this.options.environment) {
1487
+ event.environment = this.options.environment;
1488
+ }
1489
+ if (this.options.release) {
1490
+ event.release = this.options.release;
1491
+ }
1492
+ if (this.options.serverName) {
1493
+ event.server_name = this.options.serverName;
1494
+ }
1495
+ const tags = { ...this.options.tags };
1496
+ if (context?.tags) {
1497
+ Object.assign(tags, context.tags);
1498
+ }
1499
+ if (context?.requestId) {
1500
+ tags["requestId"] = context.requestId;
1501
+ }
1502
+ if (context?.tenantId) {
1503
+ tags["tenantId"] = context.tenantId;
1504
+ }
1505
+ if (Object.keys(tags).length > 0) {
1506
+ event.tags = tags;
1507
+ }
1508
+ if (context?.extra) {
1509
+ event.extra = context.extra;
1510
+ }
1511
+ if (this.user) {
1512
+ event.user = this.user;
1513
+ } else if (context?.userId) {
1514
+ event.user = { id: context.userId };
1515
+ }
1516
+ if (this.breadcrumbs.length > 0) {
1517
+ event.breadcrumbs = this.breadcrumbs.map((bc) => {
1518
+ const crumb = {};
1519
+ if (bc.type) crumb.type = bc.type;
1520
+ if (bc.category) crumb.category = bc.category;
1521
+ if (bc.message) crumb.message = bc.message;
1522
+ if (bc.data) crumb.data = bc.data;
1523
+ if (bc.level) crumb.level = bc.level;
1524
+ if (bc.timestamp !== void 0) crumb.timestamp = bc.timestamp;
1525
+ return crumb;
1526
+ });
1527
+ }
1528
+ if (this.contexts.size > 0) {
1529
+ event.contexts = Object.fromEntries(this.contexts);
1530
+ }
1531
+ return event;
1532
+ }
1533
+ /**
1534
+ * Parse error stack trace into Sentry format
1535
+ */
1536
+ parseStackTrace(stack) {
1537
+ if (!stack) return void 0;
1538
+ const lines = stack.split("\n").slice(1);
1539
+ const frames = [];
1540
+ for (const line of lines) {
1541
+ const match = line.match(/^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
1542
+ if (match && match[3] && match[4]) {
1543
+ const frame = {
1544
+ function: match[1] || "<anonymous>",
1545
+ lineno: parseInt(match[3], 10),
1546
+ colno: parseInt(match[4], 10)
1547
+ };
1548
+ if (match[2]) {
1549
+ frame.filename = match[2];
1550
+ }
1551
+ frames.push(frame);
1552
+ }
1553
+ }
1554
+ frames.reverse();
1555
+ return frames.length > 0 ? { frames } : void 0;
1556
+ }
1557
+ /**
1558
+ * Generate event ID
1559
+ */
1560
+ generateEventId() {
1561
+ const bytes = new Uint8Array(16);
1562
+ crypto.getRandomValues(bytes);
1563
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1564
+ }
1565
+ /**
1566
+ * Send event via HTTP API
1567
+ */
1568
+ async sendHttpEvent(eventData) {
1569
+ if (!this.dsn) return;
1570
+ const event = {
1571
+ event_id: this.generateEventId(),
1572
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1573
+ platform: "javascript",
1574
+ level: "error",
1575
+ ...eventData
1576
+ };
1577
+ if (this.options.beforeSend) {
1578
+ const result = this.options.beforeSend(event);
1579
+ if (result === null) return;
1580
+ }
1581
+ const url = `${this.dsn.protocol}://${this.dsn.host}/api/${this.dsn.projectId}/store/`;
1582
+ try {
1583
+ const response = await fetch(url, {
1584
+ method: "POST",
1585
+ headers: {
1586
+ "Content-Type": "application/json",
1587
+ "X-Sentry-Auth": [
1588
+ "Sentry sentry_version=7",
1589
+ `sentry_client=pars-sentry/1.0.0`,
1590
+ `sentry_key=${this.dsn.publicKey}`
1591
+ ].join(", ")
1592
+ },
1593
+ body: JSON.stringify(event)
1594
+ });
1595
+ if (!response.ok) {
1596
+ throw new Error(`Sentry API error: ${response.status}`);
1597
+ }
1598
+ } catch (error) {
1599
+ if (this.options.onError) {
1600
+ this.options.onError(error instanceof Error ? error : new Error(String(error)));
1601
+ }
1602
+ }
1603
+ }
1604
+ };
1605
+ function createSentryTransport(options) {
1606
+ return new SentryTransport(options);
1607
+ }
1608
+
1609
+ // src/transports/logtape.ts
1610
+ var FallbackLogger = class {
1611
+ constructor(category) {
1612
+ this.category = category;
1613
+ }
1614
+ log(level, message, properties) {
1615
+ const entry = {
1616
+ level,
1617
+ category: this.category,
1618
+ msg: message,
1619
+ time: (/* @__PURE__ */ new Date()).toISOString(),
1620
+ ...properties
1621
+ };
1622
+ console.log(JSON.stringify(entry));
1623
+ }
1624
+ debug(message, properties) {
1625
+ this.log("debug", message, properties);
1626
+ }
1627
+ info(message, properties) {
1628
+ this.log("info", message, properties);
1629
+ }
1630
+ warn(message, properties) {
1631
+ this.log("warn", message, properties);
1632
+ }
1633
+ warning(message, properties) {
1634
+ this.log("warning", message, properties);
1635
+ }
1636
+ error(message, properties) {
1637
+ this.log("error", message, properties);
1638
+ }
1639
+ fatal(message, properties) {
1640
+ this.log("fatal", message, properties);
1641
+ }
1642
+ };
1643
+ var LogtapeTransport = class {
1644
+ name = "logtape";
1645
+ logger;
1646
+ includeTimestamp;
1647
+ includeLevelValue;
1648
+ enabled;
1649
+ constructor(options = {}) {
1650
+ this.enabled = options.enabled !== false;
1651
+ this.includeTimestamp = options.includeTimestamp !== false;
1652
+ this.includeLevelValue = options.includeLevelValue ?? false;
1653
+ if (options.logger) {
1654
+ this.logger = options.logger;
1655
+ } else {
1656
+ this.logger = new FallbackLogger(options.category ?? "pars");
1657
+ }
1658
+ }
1659
+ log(entry) {
1660
+ if (!this.enabled) return;
1661
+ const level = this.mapLevel(entry.level);
1662
+ const properties = this.buildProperties(entry);
1663
+ this.logger[level](entry.message, properties);
1664
+ }
1665
+ /**
1666
+ * Map Pars log level to Logtape level
1667
+ */
1668
+ mapLevel(level) {
1669
+ const mapping = {
1670
+ TRACE: "debug",
1671
+ DEBUG: "debug",
1672
+ INFO: "info",
1673
+ WARN: "warning",
1674
+ ERROR: "error",
1675
+ FATAL: "fatal",
1676
+ SILENT: "debug"
1677
+ // Should never be logged
1678
+ };
1679
+ return mapping[level];
1680
+ }
1681
+ /**
1682
+ * Build properties object for Logtape
1683
+ */
1684
+ buildProperties(entry) {
1685
+ const properties = {};
1686
+ if (this.includeTimestamp) {
1687
+ properties["timestamp"] = entry.timestamp;
1688
+ }
1689
+ if (this.includeLevelValue) {
1690
+ properties["levelValue"] = entry.levelValue;
1691
+ }
1692
+ if (entry.context) {
1693
+ Object.assign(properties, entry.context);
1694
+ }
1695
+ if (entry.error) {
1696
+ properties["error"] = {
1697
+ name: entry.error.name,
1698
+ message: entry.error.message,
1699
+ stack: entry.error.stack
1700
+ };
1701
+ }
1702
+ return properties;
1703
+ }
1704
+ };
1705
+ function createLogtapeTransport(options) {
1706
+ return new LogtapeTransport(options);
1707
+ }
1708
+
1709
+ // src/index.ts
1710
+ async function generateRandomString(length) {
1711
+ const bytes = new Uint8Array(length);
1712
+ crypto.getRandomValues(bytes);
1713
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1714
+ }
1715
+ function generateId() {
1716
+ return crypto.randomUUID();
1717
+ }
1718
+ async function sha256(input) {
1719
+ const encoder = new TextEncoder();
1720
+ const data = encoder.encode(input);
1721
+ const hash = await crypto.subtle.digest("SHA-256", data);
1722
+ return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
1723
+ }
1724
+ async function sha256Bytes(input) {
1725
+ const encoder = new TextEncoder();
1726
+ const data = encoder.encode(input);
1727
+ return crypto.subtle.digest("SHA-256", data);
1728
+ }
1729
+ function constantTimeEquals(a, b) {
1730
+ if (a.length !== b.length) {
1731
+ return false;
1732
+ }
1733
+ const aBytes = new TextEncoder().encode(a);
1734
+ const bBytes = new TextEncoder().encode(b);
1735
+ let result = 0;
1736
+ for (let i = 0; i < aBytes.length; i++) {
1737
+ result |= aBytes[i] ^ bBytes[i];
1738
+ }
1739
+ return result === 0;
1740
+ }
1741
+ function sleep(ms) {
1742
+ return new Promise((resolve) => setTimeout(resolve, ms));
1743
+ }
1744
+ async function retry(fn, options = {}) {
1745
+ const {
1746
+ maxRetries = 3,
1747
+ initialDelayMs = 1e3,
1748
+ maxDelayMs = 3e4,
1749
+ backoffMultiplier = 2,
1750
+ shouldRetry = () => true,
1751
+ onRetry
1752
+ } = options;
1753
+ let lastError;
1754
+ let delay = initialDelayMs;
1755
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1756
+ try {
1757
+ return await fn();
1758
+ } catch (error) {
1759
+ lastError = error;
1760
+ if (attempt === maxRetries || !shouldRetry(error)) {
1761
+ throw error;
1762
+ }
1763
+ onRetry?.(error, attempt + 1);
1764
+ await sleep(delay);
1765
+ delay = Math.min(delay * backoffMultiplier, maxDelayMs);
1766
+ }
1767
+ }
1768
+ throw lastError;
1769
+ }
1770
+ function omit(obj, keys) {
1771
+ const result = { ...obj };
1772
+ for (const key of keys) {
1773
+ delete result[key];
1774
+ }
1775
+ return result;
1776
+ }
1777
+ function pick(obj, keys) {
1778
+ const result = {};
1779
+ for (const key of keys) {
1780
+ if (key in obj) {
1781
+ result[key] = obj[key];
1782
+ }
1783
+ }
1784
+ return result;
1785
+ }
1786
+ function deepMerge(target, source) {
1787
+ const result = { ...target };
1788
+ for (const key of Object.keys(source)) {
1789
+ const sourceValue = source[key];
1790
+ const targetValue = target[key];
1791
+ if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) {
1792
+ result[key] = deepMerge(
1793
+ targetValue,
1794
+ sourceValue
1795
+ );
1796
+ } else if (sourceValue !== void 0) {
1797
+ result[key] = sourceValue;
1798
+ }
1799
+ }
1800
+ return result;
1801
+ }
1802
+ function deepClone(obj) {
1803
+ if (obj === null || typeof obj !== "object") {
1804
+ return obj;
1805
+ }
1806
+ if (Array.isArray(obj)) {
1807
+ return obj.map(deepClone);
1808
+ }
1809
+ if (obj instanceof Date) {
1810
+ return new Date(obj.getTime());
1811
+ }
1812
+ const cloned = {};
1813
+ for (const key of Object.keys(obj)) {
1814
+ cloned[key] = deepClone(obj[key]);
1815
+ }
1816
+ return cloned;
1817
+ }
1818
+ function isPlainObject(value) {
1819
+ return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === "[object Object]";
1820
+ }
1821
+ function isNil(value) {
1822
+ return value === null || value === void 0;
1823
+ }
1824
+ function isEmpty(value) {
1825
+ if (isNil(value)) return true;
1826
+ if (typeof value === "string") return value.trim() === "";
1827
+ if (Array.isArray(value)) return value.length === 0;
1828
+ if (isPlainObject(value)) return Object.keys(value).length === 0;
1829
+ return false;
1830
+ }
1831
+ function normalizeEmail(email) {
1832
+ return email.toLowerCase().trim();
1833
+ }
1834
+ function isValidEmail(email) {
1835
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1836
+ return emailRegex.test(email);
1837
+ }
1838
+ function slugify(str) {
1839
+ return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1840
+ }
1841
+ function truncate(str, maxLength, suffix = "...") {
1842
+ if (str.length <= maxLength) return str;
1843
+ return str.slice(0, maxLength - suffix.length) + suffix;
1844
+ }
1845
+ function debounce(fn, wait) {
1846
+ let timeoutId = null;
1847
+ return (...args) => {
1848
+ if (timeoutId) {
1849
+ clearTimeout(timeoutId);
1850
+ }
1851
+ timeoutId = setTimeout(() => {
1852
+ fn(...args);
1853
+ timeoutId = null;
1854
+ }, wait);
1855
+ };
1856
+ }
1857
+ function throttle(fn, wait) {
1858
+ let lastTime = 0;
1859
+ return (...args) => {
1860
+ const now = Date.now();
1861
+ if (now - lastTime >= wait) {
1862
+ lastTime = now;
1863
+ fn(...args);
1864
+ }
1865
+ };
1866
+ }
1867
+ function createDeferred() {
1868
+ let resolve;
1869
+ let reject;
1870
+ const promise = new Promise((res, rej) => {
1871
+ resolve = res;
1872
+ reject = rej;
1873
+ });
1874
+ return { promise, resolve, reject };
1875
+ }
1876
+ async function pLimit(tasks, concurrency) {
1877
+ const results = [];
1878
+ const executing = [];
1879
+ for (const [index, task] of tasks.entries()) {
1880
+ const p = Promise.resolve().then(async () => {
1881
+ results[index] = await task();
1882
+ });
1883
+ executing.push(p);
1884
+ if (executing.length >= concurrency) {
1885
+ await Promise.race(executing);
1886
+ executing.splice(
1887
+ executing.findIndex((e) => e === p),
1888
+ 1
1889
+ );
1890
+ }
1891
+ }
1892
+ await Promise.all(executing);
1893
+ return results;
1894
+ }
1895
+ export {
1896
+ AccountLockedError,
1897
+ AuthError,
1898
+ AxiomTransport,
1899
+ ConflictError,
1900
+ ConsoleTransport,
1901
+ Decimal,
1902
+ DecimalUtils,
1903
+ DuplicateError,
1904
+ ErrorCodes,
1905
+ ForbiddenError,
1906
+ InvalidCredentialsError,
1907
+ LogLevel,
1908
+ Logger,
1909
+ LogtapeTransport,
1910
+ MembershipError,
1911
+ MembershipExpiredError,
1912
+ MembershipNotFoundError,
1913
+ NotFoundError,
1914
+ ParsError,
1915
+ RateLimitError,
1916
+ SentryTransport,
1917
+ SessionExpiredError,
1918
+ TenantError,
1919
+ TenantNotFoundError,
1920
+ TenantSuspendedError,
1921
+ TwoFactorRequiredError,
1922
+ UnauthorizedError,
1923
+ ValidationError,
1924
+ clearEdgeEnv,
1925
+ constantTimeEquals,
1926
+ createAxiomTransport,
1927
+ createDeferred,
1928
+ createEnvConfig,
1929
+ createLogger,
1930
+ createLogtapeTransport,
1931
+ createRequestLogger,
1932
+ createSentryTransport,
1933
+ debounce,
1934
+ decimal,
1935
+ deepClone,
1936
+ deepMerge,
1937
+ detectRuntime,
1938
+ err,
1939
+ generateId,
1940
+ generateRandomString,
1941
+ getEnv,
1942
+ getEnvArray,
1943
+ getEnvBoolean,
1944
+ getEnvFloat,
1945
+ getEnvJson,
1946
+ getEnvMode,
1947
+ getEnvNumber,
1948
+ getErrorCode,
1949
+ getErrorCodesByCategory,
1950
+ getRuntimeVersion,
1951
+ getStatusForCode,
1952
+ isBrowser,
1953
+ isBun,
1954
+ isCloudflare,
1955
+ isDeno,
1956
+ isDevelopment,
1957
+ isEdge,
1958
+ isEmpty,
1959
+ isNil,
1960
+ isNode,
1961
+ isPlainObject,
1962
+ isProduction,
1963
+ isRetryableError,
1964
+ isServer,
1965
+ isTest,
1966
+ isValidEmail,
1967
+ logError,
1968
+ logger,
1969
+ measureTime,
1970
+ normalizeEmail,
1971
+ ok,
1972
+ omit,
1973
+ pLimit,
1974
+ pick,
1975
+ requireEnv,
1976
+ retry,
1977
+ runtime,
1978
+ runtimeInfo,
1979
+ setEdgeEnv,
1980
+ sha256,
1981
+ sha256Bytes,
1982
+ sleep,
1983
+ slugify,
1984
+ throttle,
1985
+ truncate
1986
+ };
1987
+ //# sourceMappingURL=index.js.map