@parsrun/core 0.1.29 → 0.1.31
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/decimal.d.ts +176 -46
- package/dist/decimal.js +123 -40
- package/dist/decimal.js.map +1 -1
- package/dist/errors.d.ts +39 -3
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +184 -15
- package/dist/index.js +155 -40
- package/dist/index.js.map +1 -1
- package/dist/{logger-3oVznpFY.d.ts → logger-DrxxwI7C.d.ts} +136 -11
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +32 -0
- package/dist/logger.js.map +1 -1
- package/dist/transports/index.d.ts +58 -7
- package/dist/transports/index.js.map +1 -1
- package/dist/types.d.ts +176 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/runtime.ts","../../src/env.ts","../../src/transports/console.ts","../../src/transports/axiom.ts","../../src/transports/sentry.ts","../../src/transports/logtape.ts"],"sourcesContent":["/**\n * @module\n * Runtime detection for Node.js, Deno, Bun, and Cloudflare Workers.\n * Detects the current JavaScript runtime and provides helper functions.\n *\n * @example\n * ```typescript\n * import { runtime, isNode, isDeno, isBun, isCloudflare, getRuntimeVersion } from '@parsrun/core';\n *\n * console.log(`Running on ${runtime}`); // \"node\", \"deno\", \"bun\", \"cloudflare\", etc.\n *\n * if (isNode()) {\n * // Node.js specific code\n * } else if (isCloudflare()) {\n * // Cloudflare Workers specific code\n * }\n *\n * console.log(getRuntimeVersion()); // \"Node.js 20.0.0\"\n * ```\n */\n\nexport type Runtime = \"node\" | \"deno\" | \"bun\" | \"cloudflare\" | \"edge\" | \"browser\" | \"unknown\";\n\n/**\n * Detect the current JavaScript runtime\n */\nexport function detectRuntime(): Runtime {\n // Bun check (must be before Node since Bun also has process)\n if (typeof globalThis !== \"undefined\" && \"Bun\" in globalThis) {\n return \"bun\";\n }\n\n // Deno check\n if (typeof globalThis !== \"undefined\" && \"Deno\" in globalThis) {\n return \"deno\";\n }\n\n // Cloudflare Workers check (has caches but no process)\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).caches !== \"undefined\" &&\n typeof (globalThis as any).process === \"undefined\"\n ) {\n return \"cloudflare\";\n }\n\n // Generic Edge runtime check (Vercel Edge, etc.)\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).EdgeRuntime !== \"undefined\"\n ) {\n return \"edge\";\n }\n\n // Browser check\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).window !== \"undefined\" &&\n typeof (globalThis as any).document !== \"undefined\"\n ) {\n return \"browser\";\n }\n\n // Node.js check\n if (\n typeof process !== \"undefined\" &&\n process.versions &&\n process.versions.node\n ) {\n return \"node\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Current runtime (cached)\n */\nexport const runtime = detectRuntime();\n\n/**\n * Runtime information helpers\n */\nexport const runtimeInfo = {\n runtime,\n isNode: runtime === \"node\",\n isDeno: runtime === \"deno\",\n isBun: runtime === \"bun\",\n isCloudflare: runtime === \"cloudflare\",\n isEdge: runtime === \"cloudflare\" || runtime === \"edge\" || runtime === \"deno\",\n isBrowser: runtime === \"browser\",\n isServer: runtime !== \"browser\",\n supportsWebCrypto: typeof globalThis.crypto?.subtle !== \"undefined\",\n supportsStreams: typeof globalThis.ReadableStream !== \"undefined\",\n} as const;\n\n/**\n * Check if running in Node.js\n */\nexport function isNode(): boolean {\n return runtime === \"node\";\n}\n\n/**\n * Check if running in Deno\n */\nexport function isDeno(): boolean {\n return runtime === \"deno\";\n}\n\n/**\n * Check if running in Bun\n */\nexport function isBun(): boolean {\n return runtime === \"bun\";\n}\n\n/**\n * Check if running in Cloudflare Workers\n */\nexport function isCloudflare(): boolean {\n return runtime === \"cloudflare\";\n}\n\n/**\n * Check if running in any edge environment\n */\nexport function isEdge(): boolean {\n return runtimeInfo.isEdge;\n}\n\n/**\n * Check if running in browser\n */\nexport function isBrowser(): boolean {\n return runtime === \"browser\";\n}\n\n/**\n * Check if running on server (not browser)\n */\nexport function isServer(): boolean {\n return runtimeInfo.isServer;\n}\n\n/**\n * Get runtime version string\n */\nexport function getRuntimeVersion(): string {\n switch (runtime) {\n case \"node\":\n return `Node.js ${process?.versions?.node ?? \"unknown\"}`;\n case \"bun\":\n return `Bun ${(globalThis as any).Bun.version}`;\n case \"deno\":\n return `Deno ${(globalThis as any).Deno.version.deno}`;\n case \"cloudflare\":\n return \"Cloudflare Workers\";\n case \"edge\":\n return \"Edge Runtime\";\n case \"browser\":\n return typeof navigator !== \"undefined\" ? navigator.userAgent : \"Browser\";\n default:\n return \"Unknown\";\n }\n}\n","/**\n * @module\n * Runtime-agnostic environment variable access.\n * Works in Node.js, Deno, Bun, and Cloudflare Workers.\n *\n * @example\n * ```typescript\n * import { getEnv, requireEnv, getEnvNumber, isDevelopment } from '@parsrun/core';\n *\n * // Get optional env var with default\n * const port = getEnvNumber('PORT', 3000);\n *\n * // Get required env var (throws if missing)\n * const apiKey = requireEnv('API_KEY');\n *\n * // Check environment\n * if (isDevelopment()) {\n * console.log('Running in development mode');\n * }\n * ```\n */\n\nimport { runtime } from \"./runtime.js\";\n\n/**\n * Environment variable store for edge runtimes\n * Must be set from the request handler's env parameter\n */\nlet edgeEnvStore: Record<string, string | undefined> = {};\n\n/**\n * Set environment variables for edge runtimes (Cloudflare Workers, etc.)\n * Call this from your worker's fetch handler with the env parameter\n *\n * @example\n * ```typescript\n * export default {\n * async fetch(request, env) {\n * setEdgeEnv(env);\n * // ... rest of handler\n * }\n * }\n * ```\n */\nexport function setEdgeEnv(env: Record<string, string | undefined>): void {\n edgeEnvStore = { ...edgeEnvStore, ...env };\n}\n\n/**\n * Clear edge environment store\n */\nexport function clearEdgeEnv(): void {\n edgeEnvStore = {};\n}\n\n/**\n * Get an environment variable value\n * Works across all runtimes\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n // Edge runtimes (Cloudflare Workers, etc.)\n if (runtime === \"cloudflare\" || runtime === \"edge\") {\n return edgeEnvStore[key] ?? defaultValue;\n }\n\n // Deno\n if (runtime === \"deno\") {\n try {\n return (globalThis as any).Deno.env.get(key) ?? defaultValue;\n } catch {\n return defaultValue;\n }\n }\n\n // Node.js / Bun (both use process.env)\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[key] ?? defaultValue;\n }\n\n // Browser - check for injected env\n if (runtime === \"browser\" && typeof (globalThis as any).__ENV__ !== \"undefined\") {\n return (globalThis as any).__ENV__[key] ?? defaultValue;\n }\n\n return defaultValue;\n}\n\n/**\n * Get an environment variable, throwing if not found\n */\nexport function requireEnv(key: string): string {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n throw new Error(`Required environment variable \"${key}\" is not set`);\n }\n return value;\n}\n\n/**\n * Get an environment variable as a number\n */\nexport function getEnvNumber(key: string, defaultValue?: number): number | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get an environment variable as a float\n */\nexport function getEnvFloat(key: string, defaultValue?: number): number | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n const parsed = parseFloat(value);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get an environment variable as a boolean\n */\nexport function getEnvBoolean(key: string, defaultValue: boolean = false): boolean {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n return value === \"true\" || value === \"1\" || value === \"yes\";\n}\n\n/**\n * Get an environment variable as an array (comma-separated)\n */\nexport function getEnvArray(key: string, defaultValue: string[] = []): string[] {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n return value.split(\",\").map((s) => s.trim()).filter(Boolean);\n}\n\n/**\n * Get an environment variable as JSON\n */\nexport function getEnvJson<T>(key: string, defaultValue?: T): T | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n}\n\n/**\n * Check if running in development mode\n */\nexport function isDevelopment(): boolean {\n const env = getEnv(\"NODE_ENV\");\n return env === \"development\" || env === undefined;\n}\n\n/**\n * Check if running in production mode\n */\nexport function isProduction(): boolean {\n return getEnv(\"NODE_ENV\") === \"production\";\n}\n\n/**\n * Check if running in test mode\n */\nexport function isTest(): boolean {\n return getEnv(\"NODE_ENV\") === \"test\";\n}\n\n/**\n * Environment mode\n */\nexport type EnvMode = \"development\" | \"production\" | \"test\";\n\n/**\n * Get current environment mode\n */\nexport function getEnvMode(): EnvMode {\n const env = getEnv(\"NODE_ENV\");\n if (env === \"production\") return \"production\";\n if (env === \"test\") return \"test\";\n return \"development\";\n}\n\n/**\n * Create a typed environment configuration object\n *\n * @example\n * ```typescript\n * const env = createEnvConfig({\n * DATABASE_URL: { required: true },\n * PORT: { type: 'number', default: 3000 },\n * DEBUG: { type: 'boolean', default: false },\n * });\n *\n * env.DATABASE_URL // string\n * env.PORT // number\n * env.DEBUG // boolean\n * ```\n */\nexport function createEnvConfig<T extends EnvSchema>(schema: T): EnvResult<T> {\n const result: Record<string, unknown> = {};\n\n for (const [key, config] of Object.entries(schema)) {\n const envConfig = config as EnvConfigItem;\n let value: unknown;\n\n switch (envConfig.type) {\n case \"number\":\n value = getEnvNumber(key, envConfig.default as number | undefined);\n break;\n case \"boolean\":\n value = getEnvBoolean(key, envConfig.default as boolean | undefined);\n break;\n case \"array\":\n value = getEnvArray(key, envConfig.default as string[] | undefined);\n break;\n case \"json\":\n value = getEnvJson(key, envConfig.default);\n break;\n default:\n value = getEnv(key, envConfig.default as string | undefined);\n }\n\n if (envConfig.required && (value === undefined || value === \"\")) {\n throw new Error(`Required environment variable \"${key}\" is not set`);\n }\n\n result[key] = value;\n }\n\n return result as EnvResult<T>;\n}\n\n// Type helpers for createEnvConfig\ntype EnvConfigItem = {\n type?: \"string\" | \"number\" | \"boolean\" | \"array\" | \"json\";\n required?: boolean;\n default?: unknown;\n};\n\ntype EnvSchema = Record<string, EnvConfigItem>;\n\ntype EnvResult<T extends EnvSchema> = {\n [K in keyof T]: T[K][\"type\"] extends \"number\"\n ? number\n : T[K][\"type\"] extends \"boolean\"\n ? boolean\n : T[K][\"type\"] extends \"array\"\n ? string[]\n : T[K][\"type\"] extends \"json\"\n ? unknown\n : string;\n};\n","/**\n * @parsrun/core - Console Transport\n * Default transport that outputs to console\n */\n\nimport { runtime } from \"../runtime.js\";\nimport { isDevelopment } from \"../env.js\";\nimport type { LogEntry, LogLevelName } from \"../logger.js\";\nimport type { LogTransport } from \"./types.js\";\n\n/**\n * Console transport options\n */\nexport interface ConsoleTransportOptions {\n /** Enable pretty printing (default: true in development) */\n pretty?: boolean;\n /** Enable ANSI colors (default: true in Node/Bun) */\n colors?: boolean;\n}\n\n/**\n * Console transport\n * Outputs logs to console with optional pretty printing and colors\n */\nexport class ConsoleTransport implements LogTransport {\n readonly name = \"console\";\n\n private pretty: boolean;\n private colors: boolean;\n\n constructor(options: ConsoleTransportOptions = {}) {\n this.pretty = options.pretty ?? isDevelopment();\n this.colors = options.colors ?? (runtime === \"node\" || runtime === \"bun\");\n }\n\n log(entry: LogEntry): void {\n if (this.pretty) {\n this.logPretty(entry);\n } else {\n this.logJson(entry);\n }\n }\n\n private logJson(entry: LogEntry): void {\n const { level, message, timestamp, context, error } = entry;\n\n const output: Record<string, unknown> = {\n level,\n time: timestamp,\n msg: message,\n };\n\n if (context && Object.keys(context).length > 0) {\n Object.assign(output, context);\n }\n\n if (error) {\n output[\"err\"] = error;\n }\n\n console.log(JSON.stringify(output));\n }\n\n private logPretty(entry: LogEntry): void {\n const { level, message, timestamp, context, error } = entry;\n\n const levelColors: Record<LogLevelName, string> = {\n TRACE: \"\\x1b[90m\", // Gray\n DEBUG: \"\\x1b[36m\", // Cyan\n INFO: \"\\x1b[32m\", // Green\n WARN: \"\\x1b[33m\", // Yellow\n ERROR: \"\\x1b[31m\", // Red\n FATAL: \"\\x1b[35m\", // Magenta\n SILENT: \"\",\n };\n\n const reset = \"\\x1b[0m\";\n const color = this.colors ? levelColors[level] : \"\";\n const resetCode = this.colors ? reset : \"\";\n\n // Extract time part from ISO timestamp\n const timePart = timestamp.split(\"T\")[1];\n const time = timePart ? timePart.slice(0, 8) : timestamp;\n\n let output = `${color}[${time}] ${level.padEnd(5)}${resetCode} ${message}`;\n\n if (context && Object.keys(context).length > 0) {\n output += ` ${JSON.stringify(context)}`;\n }\n\n // Route to appropriate console method\n if (level === \"ERROR\" || level === \"FATAL\") {\n console.error(output);\n if (error?.stack) {\n console.error(error.stack);\n }\n } else if (level === \"WARN\") {\n console.warn(output);\n } else if (level === \"DEBUG\" || level === \"TRACE\") {\n console.debug(output);\n } else {\n console.log(output);\n }\n }\n}\n","/**\n * @parsrun/core - Axiom Transport\n * Log ingestion transport for Axiom (axiom.co)\n * Uses native fetch - works on all runtimes (Node, Deno, Bun, Workers)\n */\n\nimport type { LogEntry } from \"../logger.js\";\nimport type { LogTransport, BatchTransportOptions } from \"./types.js\";\n\n/**\n * Axiom transport options\n */\nexport interface AxiomTransportOptions extends BatchTransportOptions {\n /** Axiom API token */\n token: string;\n /** Dataset name to ingest logs into */\n dataset: string;\n /** Organization ID (optional, for personal tokens) */\n orgId?: string;\n /** Custom Axiom API URL (default: https://api.axiom.co) */\n apiUrl?: string;\n /** Callback for errors during ingestion */\n onError?: (error: Error, droppedCount: number) => void;\n}\n\n/**\n * Axiom log event structure\n */\ninterface AxiomEvent {\n _time: string;\n level: string;\n message: string;\n [key: string]: unknown;\n}\n\n/**\n * Axiom Transport\n * Batches logs and sends them to Axiom's ingest API\n */\nexport class AxiomTransport implements LogTransport {\n readonly name = \"axiom\";\n\n private buffer: AxiomEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private readonly options: Required<\n Pick<AxiomTransportOptions, \"batchSize\" | \"flushInterval\" | \"apiUrl\">\n > &\n AxiomTransportOptions;\n\n constructor(options: AxiomTransportOptions) {\n this.options = {\n batchSize: 100,\n flushInterval: 5000,\n apiUrl: \"https://api.axiom.co\",\n ...options,\n };\n\n // Start flush interval if enabled\n if (this.options.flushInterval > 0) {\n this.flushTimer = setInterval(\n () => this.flush(),\n this.options.flushInterval\n );\n }\n }\n\n log(entry: LogEntry): void {\n if (this.options.enabled === false) return;\n\n const event: AxiomEvent = {\n _time: entry.timestamp,\n level: entry.level,\n message: entry.message,\n };\n\n // Add context fields\n if (entry.context) {\n Object.assign(event, entry.context);\n }\n\n // Add error fields\n if (entry.error) {\n event[\"error.name\"] = entry.error.name;\n event[\"error.message\"] = entry.error.message;\n event[\"error.stack\"] = entry.error.stack;\n }\n\n this.buffer.push(event);\n\n // Flush if buffer is full\n if (this.buffer.length >= this.options.batchSize) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n // Prevent concurrent flushes\n if (this.isFlushing || this.buffer.length === 0) return;\n\n this.isFlushing = true;\n const events = this.buffer;\n this.buffer = [];\n\n try {\n const response = await fetch(\n `${this.options.apiUrl}/v1/datasets/${this.options.dataset}/ingest`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.token}`,\n \"Content-Type\": \"application/json\",\n ...(this.options.orgId && {\n \"X-Axiom-Org-Id\": this.options.orgId,\n }),\n },\n body: JSON.stringify(events),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Axiom ingest failed: ${response.status} ${errorText}`);\n }\n } catch (error) {\n // Call error handler if provided\n if (this.options.onError) {\n this.options.onError(\n error instanceof Error ? error : new Error(String(error)),\n events.length\n );\n } else {\n // Silent fail by default - don't crash the app for logging failures\n console.error(\"[Axiom] Failed to send logs:\", error);\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n async close(): Promise<void> {\n // Stop flush timer\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Final flush\n await this.flush();\n }\n}\n\n/**\n * Create Axiom transport\n */\nexport function createAxiomTransport(\n options: AxiomTransportOptions\n): AxiomTransport {\n return new AxiomTransport(options);\n}\n","/**\n * @parsrun/core - Sentry Transport\n * Error tracking transport for Sentry\n *\n * Supports two modes:\n * 1. HTTP API mode (default) - Zero dependency, works on all runtimes\n * 2. SDK mode (BYOS) - Full features with user-provided Sentry SDK\n *\n * @example HTTP API mode (simple, universal)\n * ```typescript\n * const sentry = new SentryTransport({\n * dsn: 'https://xxx@sentry.io/123',\n * environment: 'production',\n * });\n * ```\n *\n * @example SDK mode (full features)\n * ```typescript\n * import * as Sentry from '@sentry/cloudflare'; // or @sentry/node\n *\n * Sentry.init({ dsn: '...' });\n *\n * const sentry = new SentryTransport({\n * client: Sentry,\n * });\n * ```\n */\n\nimport type { LogEntry } from \"../logger.js\";\nimport type {\n LogTransport,\n ErrorTransport,\n ErrorContext,\n ErrorUser,\n Breadcrumb,\n BaseTransportOptions,\n} from \"./types.js\";\n\n/**\n * Sentry SDK interface (minimal interface for BYOS)\n * Compatible with @sentry/node, @sentry/cloudflare, @sentry/browser, etc.\n */\nexport interface SentryClient {\n captureException(error: Error, hint?: unknown): string;\n captureMessage(message: string, level?: string): string;\n withScope(callback: (scope: SentryScope) => void): void;\n flush?(timeout?: number): Promise<boolean>;\n}\n\nexport interface SentryScope {\n setTag(key: string, value: string): void;\n setUser(user: { id: string; email?: string; [key: string]: unknown } | null): void;\n setExtra(key: string, value: unknown): void;\n setExtras(extras: Record<string, unknown>): void;\n setLevel(level: string): void;\n addBreadcrumb(breadcrumb: unknown): void;\n}\n\n/**\n * Parsed DSN components\n */\ninterface ParsedDSN {\n protocol: string;\n publicKey: string;\n host: string;\n projectId: string;\n}\n\n/**\n * Sentry transport options\n */\nexport interface SentryTransportOptions extends BaseTransportOptions {\n /**\n * Sentry DSN (required for HTTP mode)\n * Format: https://{publicKey}@{host}/{projectId}\n */\n dsn?: string;\n\n /**\n * Sentry SDK client (for BYOS mode)\n * Pass your initialized Sentry client for full SDK features\n */\n client?: SentryClient;\n\n /** Environment name (e.g., 'production', 'staging') */\n environment?: string;\n\n /** Release version */\n release?: string;\n\n /** Server name */\n serverName?: string;\n\n /** Sample rate for error events (0.0 to 1.0) */\n sampleRate?: number;\n\n /** Additional tags to add to all events */\n tags?: Record<string, string>;\n\n /** Callback before sending (return null to drop event) */\n beforeSend?: (event: SentryEvent) => SentryEvent | null;\n\n /** Callback for transport errors */\n onError?: (error: Error) => void;\n}\n\n/**\n * Sentry event structure (simplified)\n */\nexport interface SentryEvent {\n event_id: string;\n timestamp: string;\n platform: string;\n level: \"fatal\" | \"error\" | \"warning\" | \"info\" | \"debug\";\n logger?: string;\n transaction?: string;\n server_name?: string;\n release?: string;\n environment?: string;\n message?: { formatted: string };\n exception?: {\n values: Array<{\n type: string;\n value: string;\n stacktrace?: {\n frames: Array<{\n filename?: string;\n function?: string;\n lineno?: number;\n colno?: number;\n in_app?: boolean;\n }>;\n };\n }>;\n };\n tags?: Record<string, string>;\n extra?: Record<string, unknown>;\n user?: {\n id?: string;\n email?: string;\n username?: string;\n [key: string]: unknown;\n };\n breadcrumbs?: Array<{\n type?: string;\n category?: string;\n message?: string;\n data?: Record<string, unknown>;\n level?: string;\n timestamp?: number;\n }>;\n contexts?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Sentry Transport\n * Implements both LogTransport and ErrorTransport\n */\nexport class SentryTransport implements LogTransport, ErrorTransport {\n readonly name = \"sentry\";\n\n private readonly client?: SentryClient;\n private readonly dsn?: ParsedDSN;\n private readonly options: SentryTransportOptions;\n private user: ErrorUser | null = null;\n private contexts: Map<string, Record<string, unknown>> = new Map();\n private breadcrumbs: Breadcrumb[] = [];\n private readonly maxBreadcrumbs = 100;\n\n constructor(options: SentryTransportOptions) {\n this.options = {\n sampleRate: 1.0,\n ...options,\n };\n\n if (options.client) {\n this.client = options.client;\n } else if (options.dsn) {\n this.dsn = this.parseDSN(options.dsn);\n } else {\n throw new Error(\"SentryTransport requires either 'dsn' or 'client' option\");\n }\n }\n\n /**\n * Parse Sentry DSN\n */\n private parseDSN(dsn: string): ParsedDSN {\n const match = dsn.match(/^(https?):\\/\\/([^@]+)@([^/]+)\\/(.+)$/);\n if (!match || !match[1] || !match[2] || !match[3] || !match[4]) {\n throw new Error(`Invalid Sentry DSN: ${dsn}`);\n }\n return {\n protocol: match[1],\n publicKey: match[2],\n host: match[3],\n projectId: match[4],\n };\n }\n\n /**\n * LogTransport implementation\n * Only sends ERROR and FATAL level logs\n */\n log(entry: LogEntry): void {\n if (this.options.enabled === false) return;\n\n // Only capture errors\n if (entry.levelValue < 50) return; // ERROR = 50, FATAL = 60\n\n if (entry.error) {\n const error = new Error(entry.error.message);\n error.name = entry.error.name;\n if (entry.error.stack) {\n error.stack = entry.error.stack;\n }\n\n this.captureException(\n error,\n entry.context ? { extra: entry.context } : undefined\n );\n } else {\n this.captureMessage(\n entry.message,\n entry.level === \"FATAL\" ? \"error\" : \"warning\",\n entry.context ? { extra: entry.context } : undefined\n );\n }\n }\n\n /**\n * Capture an exception\n */\n captureException(error: Error, context?: ErrorContext): void {\n if (this.options.enabled === false) return;\n if (!this.shouldSample()) return;\n\n if (this.client) {\n this.captureWithSdk(error, context);\n } else {\n this.captureWithHttp(error, context);\n }\n }\n\n /**\n * Capture a message\n */\n captureMessage(\n message: string,\n level: \"info\" | \"warning\" | \"error\",\n context?: ErrorContext\n ): void {\n if (this.options.enabled === false) return;\n if (!this.shouldSample()) return;\n\n if (this.client) {\n this.client.withScope((scope) => {\n this.applyContext(scope, context);\n scope.setLevel(level);\n this.client!.captureMessage(message, level);\n });\n } else {\n this.sendHttpEvent({\n level: level === \"warning\" ? \"warning\" : level === \"info\" ? \"info\" : \"error\",\n message: { formatted: message },\n ...this.buildEventContext(context),\n });\n }\n }\n\n /**\n * Set user context\n */\n setUser(user: ErrorUser | null): void {\n this.user = user;\n }\n\n /**\n * Set custom context\n */\n setContext(name: string, context: Record<string, unknown>): void {\n this.contexts.set(name, context);\n }\n\n /**\n * Add breadcrumb\n */\n addBreadcrumb(breadcrumb: Breadcrumb): void {\n this.breadcrumbs.push({\n ...breadcrumb,\n timestamp: breadcrumb.timestamp ?? Date.now() / 1000,\n });\n\n // Keep only last N breadcrumbs\n if (this.breadcrumbs.length > this.maxBreadcrumbs) {\n this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);\n }\n }\n\n /**\n * Flush pending events\n */\n async flush(): Promise<void> {\n if (this.client?.flush) {\n await this.client.flush(2000);\n }\n }\n\n // ============================================================================\n // Private Methods\n // ============================================================================\n\n private shouldSample(): boolean {\n const rate = this.options.sampleRate ?? 1.0;\n return Math.random() < rate;\n }\n\n /**\n * Capture with SDK (BYOS mode)\n */\n private captureWithSdk(error: Error, context?: ErrorContext): void {\n this.client!.withScope((scope) => {\n this.applyContext(scope, context);\n this.client!.captureException(error);\n });\n }\n\n /**\n * Apply context to SDK scope\n */\n private applyContext(scope: SentryScope, context?: ErrorContext): void {\n // User\n if (this.user) {\n scope.setUser(this.user);\n } else if (context?.userId) {\n scope.setUser({ id: context.userId });\n }\n\n // Tags\n if (this.options.tags) {\n for (const [key, value] of Object.entries(this.options.tags)) {\n scope.setTag(key, value);\n }\n }\n if (context?.tags) {\n for (const [key, value] of Object.entries(context.tags)) {\n scope.setTag(key, value);\n }\n }\n if (context?.requestId) {\n scope.setTag(\"requestId\", context.requestId);\n }\n if (context?.tenantId) {\n scope.setTag(\"tenantId\", context.tenantId);\n }\n\n // Extra\n if (context?.extra) {\n scope.setExtras(context.extra);\n }\n\n // Breadcrumbs\n for (const bc of this.breadcrumbs) {\n scope.addBreadcrumb(bc);\n }\n }\n\n /**\n * Capture with HTTP API (default mode)\n */\n private captureWithHttp(error: Error, context?: ErrorContext): void {\n const stacktrace = this.parseStackTrace(error.stack);\n const exceptionValue: {\n type: string;\n value: string;\n stacktrace?: { frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> };\n } = {\n type: error.name,\n value: error.message,\n };\n\n if (stacktrace) {\n exceptionValue.stacktrace = stacktrace;\n }\n\n const event: Partial<SentryEvent> = {\n level: \"error\",\n exception: {\n values: [exceptionValue],\n },\n ...this.buildEventContext(context),\n };\n\n this.sendHttpEvent(event);\n }\n\n /**\n * Build event context for HTTP API\n */\n private buildEventContext(context?: ErrorContext): Partial<SentryEvent> {\n const event: Partial<SentryEvent> = {};\n\n // Environment & Release\n if (this.options.environment) {\n event.environment = this.options.environment;\n }\n if (this.options.release) {\n event.release = this.options.release;\n }\n if (this.options.serverName) {\n event.server_name = this.options.serverName;\n }\n\n // Tags\n const tags: Record<string, string> = { ...this.options.tags };\n if (context?.tags) {\n Object.assign(tags, context.tags);\n }\n if (context?.requestId) {\n tags[\"requestId\"] = context.requestId;\n }\n if (context?.tenantId) {\n tags[\"tenantId\"] = context.tenantId;\n }\n if (Object.keys(tags).length > 0) {\n event.tags = tags;\n }\n\n // Extra\n if (context?.extra) {\n event.extra = context.extra;\n }\n\n // User\n if (this.user) {\n event.user = this.user;\n } else if (context?.userId) {\n event.user = { id: context.userId };\n }\n\n // Breadcrumbs\n if (this.breadcrumbs.length > 0) {\n event.breadcrumbs = this.breadcrumbs.map((bc) => {\n const crumb: {\n type?: string;\n category?: string;\n message?: string;\n data?: Record<string, unknown>;\n level?: string;\n timestamp?: number;\n } = {};\n if (bc.type) crumb.type = bc.type;\n if (bc.category) crumb.category = bc.category;\n if (bc.message) crumb.message = bc.message;\n if (bc.data) crumb.data = bc.data;\n if (bc.level) crumb.level = bc.level;\n if (bc.timestamp !== undefined) crumb.timestamp = bc.timestamp;\n return crumb;\n });\n }\n\n // Contexts\n if (this.contexts.size > 0) {\n event.contexts = Object.fromEntries(this.contexts);\n }\n\n return event;\n }\n\n /**\n * Parse error stack trace into Sentry format\n */\n private parseStackTrace(\n stack?: string\n ): { frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> } | undefined {\n if (!stack) return undefined;\n\n const lines = stack.split(\"\\n\").slice(1); // Skip first line (error message)\n const frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> = [];\n\n for (const line of lines) {\n // Parse V8 stack trace format\n const match = line.match(/^\\s*at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/);\n if (match && match[3] && match[4]) {\n const frame: { filename?: string; function?: string; lineno?: number; colno?: number } = {\n function: match[1] || \"<anonymous>\",\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n };\n if (match[2]) {\n frame.filename = match[2];\n }\n frames.push(frame);\n }\n }\n\n // Sentry expects oldest frame first\n frames.reverse();\n\n return frames.length > 0 ? { frames } : undefined;\n }\n\n /**\n * Generate event ID\n */\n private generateEventId(): string {\n // 32 character hex string\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n /**\n * Send event via HTTP API\n */\n private async sendHttpEvent(eventData: Partial<SentryEvent>): Promise<void> {\n if (!this.dsn) return;\n\n const event: SentryEvent = {\n event_id: this.generateEventId(),\n timestamp: new Date().toISOString(),\n platform: \"javascript\",\n level: \"error\",\n ...eventData,\n };\n\n // Apply beforeSend hook\n if (this.options.beforeSend) {\n const result = this.options.beforeSend(event);\n if (result === null) return;\n }\n\n const url = `${this.dsn.protocol}://${this.dsn.host}/api/${this.dsn.projectId}/store/`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Sentry-Auth\": [\n \"Sentry sentry_version=7\",\n `sentry_client=pars-sentry/1.0.0`,\n `sentry_key=${this.dsn.publicKey}`,\n ].join(\", \"),\n },\n body: JSON.stringify(event),\n });\n\n if (!response.ok) {\n throw new Error(`Sentry API error: ${response.status}`);\n }\n } catch (error) {\n if (this.options.onError) {\n this.options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n // Silent fail - don't crash the app for logging failures\n }\n }\n}\n\n/**\n * Create Sentry transport\n */\nexport function createSentryTransport(options: SentryTransportOptions): SentryTransport {\n return new SentryTransport(options);\n}\n","/**\n * @parsrun/core - Logtape Transport\n * Structured logging transport for Logtape (@logtape/logtape)\n *\n * Logtape is a TypeScript-first structured logging library.\n * This transport bridges Pars Logger to Logtape for advanced logging scenarios.\n *\n * @example BYOS (Bring Your Own SDK)\n * ```typescript\n * import { getLogger, configure } from '@logtape/logtape';\n *\n * // Configure Logtape\n * await configure({\n * sinks: { console: consoleSink() },\n * loggers: [{ category: 'pars', sinks: ['console'], level: 'info' }],\n * });\n *\n * const logtapeLogger = getLogger('pars');\n * const transport = new LogtapeTransport({ logger: logtapeLogger });\n * ```\n *\n * @example Simple mode (creates internal logger)\n * ```typescript\n * const transport = new LogtapeTransport({\n * category: 'my-app',\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName } from \"../logger.js\";\nimport type { LogTransport, BaseTransportOptions } from \"./types.js\";\n\n/**\n * Logtape Logger interface (minimal interface for BYOS)\n * Compatible with @logtape/logtape getLogger() return type\n */\nexport interface LogtapeLogger {\n debug(message: string, properties?: Record<string, unknown>): void;\n info(message: string, properties?: Record<string, unknown>): void;\n warn(message: string, properties?: Record<string, unknown>): void;\n warning(message: string, properties?: Record<string, unknown>): void;\n error(message: string, properties?: Record<string, unknown>): void;\n fatal(message: string, properties?: Record<string, unknown>): void;\n}\n\n/**\n * Level mapping from Pars to Logtape\n */\ntype LogtapeLevel = \"debug\" | \"info\" | \"warning\" | \"error\" | \"fatal\";\n\n/**\n * Logtape transport options\n */\nexport interface LogtapeTransportOptions extends BaseTransportOptions {\n /**\n * Logtape logger instance (for BYOS mode)\n * Get this from @logtape/logtape's getLogger()\n */\n logger?: LogtapeLogger;\n\n /**\n * Category name for the logger\n * Only used if logger is not provided (creates a simple fallback logger)\n */\n category?: string;\n\n /**\n * Include timestamp in properties\n * @default true\n */\n includeTimestamp?: boolean;\n\n /**\n * Include level value in properties\n * @default false\n */\n includeLevelValue?: boolean;\n}\n\n/**\n * Simple fallback logger when no Logtape instance is provided\n * Just outputs structured JSON - users should use BYOS for full features\n */\nclass FallbackLogger implements LogtapeLogger {\n constructor(private category: string) {}\n\n private log(level: string, message: string, properties?: Record<string, unknown>): void {\n const entry = {\n level,\n category: this.category,\n msg: message,\n time: new Date().toISOString(),\n ...properties,\n };\n console.log(JSON.stringify(entry));\n }\n\n debug(message: string, properties?: Record<string, unknown>): void {\n this.log(\"debug\", message, properties);\n }\n\n info(message: string, properties?: Record<string, unknown>): void {\n this.log(\"info\", message, properties);\n }\n\n warn(message: string, properties?: Record<string, unknown>): void {\n this.log(\"warn\", message, properties);\n }\n\n warning(message: string, properties?: Record<string, unknown>): void {\n this.log(\"warning\", message, properties);\n }\n\n error(message: string, properties?: Record<string, unknown>): void {\n this.log(\"error\", message, properties);\n }\n\n fatal(message: string, properties?: Record<string, unknown>): void {\n this.log(\"fatal\", message, properties);\n }\n}\n\n/**\n * Logtape Transport\n * Bridges Pars Logger to Logtape\n */\nexport class LogtapeTransport implements LogTransport {\n readonly name = \"logtape\";\n\n private readonly logger: LogtapeLogger;\n private readonly includeTimestamp: boolean;\n private readonly includeLevelValue: boolean;\n private readonly enabled: boolean;\n\n constructor(options: LogtapeTransportOptions = {}) {\n this.enabled = options.enabled !== false;\n this.includeTimestamp = options.includeTimestamp !== false;\n this.includeLevelValue = options.includeLevelValue ?? false;\n\n if (options.logger) {\n this.logger = options.logger;\n } else {\n // Create fallback logger\n this.logger = new FallbackLogger(options.category ?? \"pars\");\n }\n }\n\n log(entry: LogEntry): void {\n if (!this.enabled) return;\n\n const level = this.mapLevel(entry.level);\n const properties = this.buildProperties(entry);\n\n // Call the appropriate log method\n this.logger[level](entry.message, properties);\n }\n\n /**\n * Map Pars log level to Logtape level\n */\n private mapLevel(level: LogLevelName): LogtapeLevel {\n const mapping: Record<LogLevelName, LogtapeLevel> = {\n TRACE: \"debug\",\n DEBUG: \"debug\",\n INFO: \"info\",\n WARN: \"warning\",\n ERROR: \"error\",\n FATAL: \"fatal\",\n SILENT: \"debug\", // Should never be logged\n };\n return mapping[level];\n }\n\n /**\n * Build properties object for Logtape\n */\n private buildProperties(entry: LogEntry): Record<string, unknown> {\n const properties: Record<string, unknown> = {};\n\n // Add timestamp if enabled\n if (this.includeTimestamp) {\n properties[\"timestamp\"] = entry.timestamp;\n }\n\n // Add level value if enabled\n if (this.includeLevelValue) {\n properties[\"levelValue\"] = entry.levelValue;\n }\n\n // Add context\n if (entry.context) {\n Object.assign(properties, entry.context);\n }\n\n // Add error info\n if (entry.error) {\n properties[\"error\"] = {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n };\n }\n\n return properties;\n }\n}\n\n/**\n * Create Logtape transport\n */\nexport function createLogtapeTransport(\n options?: LogtapeTransportOptions\n): LogtapeTransport {\n return new LogtapeTransport(options);\n}\n"],"mappings":";AA0BO,SAAS,gBAAyB;AAEvC,MAAI,OAAO,eAAe,eAAe,SAAS,YAAY;AAC5D,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,eAAe,eAAe,UAAU,YAAY;AAC7D,WAAO;AAAA,EACT;AAGA,MACE,OAAO,eAAe,eACtB,OAAQ,WAAmB,WAAW,eACtC,OAAQ,WAAmB,YAAY,aACvC;AACA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,eAAe,eACtB,OAAQ,WAAmB,gBAAgB,aAC3C;AACA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,eAAe,eACtB,OAAQ,WAAmB,WAAW,eACtC,OAAQ,WAAmB,aAAa,aACxC;AACA,WAAO;AAAA,EACT;AAGA,MACE,OAAO,YAAY,eACnB,QAAQ,YACR,QAAQ,SAAS,MACjB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,IAAM,UAAU,cAAc;AAK9B,IAAM,cAAc;AAAA,EACzB;AAAA,EACA,QAAQ,YAAY;AAAA,EACpB,QAAQ,YAAY;AAAA,EACpB,OAAO,YAAY;AAAA,EACnB,cAAc,YAAY;AAAA,EAC1B,QAAQ,YAAY,gBAAgB,YAAY,UAAU,YAAY;AAAA,EACtE,WAAW,YAAY;AAAA,EACvB,UAAU,YAAY;AAAA,EACtB,mBAAmB,OAAO,WAAW,QAAQ,WAAW;AAAA,EACxD,iBAAiB,OAAO,WAAW,mBAAmB;AACxD;;;AClEA,IAAI,eAAmD,CAAC;AA+BjD,SAAS,OAAO,KAAa,cAA2C;AAE7E,MAAI,YAAY,gBAAgB,YAAY,QAAQ;AAClD,WAAO,aAAa,GAAG,KAAK;AAAA,EAC9B;AAGA,MAAI,YAAY,QAAQ;AACtB,QAAI;AACF,aAAQ,WAAmB,KAAK,IAAI,IAAI,GAAG,KAAK;AAAA,IAClD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,WAAO,QAAQ,IAAI,GAAG,KAAK;AAAA,EAC7B;AAGA,MAAI,YAAY,aAAa,OAAQ,WAAmB,YAAY,aAAa;AAC/E,WAAQ,WAAmB,QAAQ,GAAG,KAAK;AAAA,EAC7C;AAEA,SAAO;AACT;AA6EO,SAAS,gBAAyB;AACvC,QAAM,MAAM,OAAO,UAAU;AAC7B,SAAO,QAAQ,iBAAiB,QAAQ;AAC1C;;;AC7IO,IAAM,mBAAN,MAA+C;AAAA,EAC3C,OAAO;AAAA,EAER;AAAA,EACA;AAAA,EAER,YAAY,UAAmC,CAAC,GAAG;AACjD,SAAK,SAAS,QAAQ,UAAU,cAAc;AAC9C,SAAK,SAAS,QAAQ,WAAW,YAAY,UAAU,YAAY;AAAA,EACrE;AAAA,EAEA,IAAI,OAAuB;AACzB,QAAI,KAAK,QAAQ;AACf,WAAK,UAAU,KAAK;AAAA,IACtB,OAAO;AACL,WAAK,QAAQ,KAAK;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAuB;AACrC,UAAM,EAAE,OAAO,SAAS,WAAW,SAAS,MAAM,IAAI;AAEtD,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA,MAAM;AAAA,MACN,KAAK;AAAA,IACP;AAEA,QAAI,WAAW,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AAC9C,aAAO,OAAO,QAAQ,OAAO;AAAA,IAC/B;AAEA,QAAI,OAAO;AACT,aAAO,KAAK,IAAI;AAAA,IAClB;AAEA,YAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,EACpC;AAAA,EAEQ,UAAU,OAAuB;AACvC,UAAM,EAAE,OAAO,SAAS,WAAW,SAAS,MAAM,IAAI;AAEtD,UAAM,cAA4C;AAAA,MAChD,OAAO;AAAA;AAAA,MACP,OAAO;AAAA;AAAA,MACP,MAAM;AAAA;AAAA,MACN,MAAM;AAAA;AAAA,MACN,OAAO;AAAA;AAAA,MACP,OAAO;AAAA;AAAA,MACP,QAAQ;AAAA,IACV;AAEA,UAAM,QAAQ;AACd,UAAM,QAAQ,KAAK,SAAS,YAAY,KAAK,IAAI;AACjD,UAAM,YAAY,KAAK,SAAS,QAAQ;AAGxC,UAAM,WAAW,UAAU,MAAM,GAAG,EAAE,CAAC;AACvC,UAAM,OAAO,WAAW,SAAS,MAAM,GAAG,CAAC,IAAI;AAE/C,QAAI,SAAS,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,IAAI,OAAO;AAExE,QAAI,WAAW,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AAC9C,gBAAU,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,IACvC;AAGA,QAAI,UAAU,WAAW,UAAU,SAAS;AAC1C,cAAQ,MAAM,MAAM;AACpB,UAAI,OAAO,OAAO;AAChB,gBAAQ,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,IACF,WAAW,UAAU,QAAQ;AAC3B,cAAQ,KAAK,MAAM;AAAA,IACrB,WAAW,UAAU,WAAW,UAAU,SAAS;AACjD,cAAQ,MAAM,MAAM;AAAA,IACtB,OAAO;AACL,cAAQ,IAAI,MAAM;AAAA,IACpB;AAAA,EACF;AACF;;;ACjEO,IAAM,iBAAN,MAA6C;AAAA,EACzC,OAAO;AAAA,EAER,SAAuB,CAAC;AAAA,EACxB,aAAoD;AAAA,EACpD,aAAa;AAAA,EACJ;AAAA,EAKjB,YAAY,SAAgC;AAC1C,SAAK,UAAU;AAAA,MACb,WAAW;AAAA,MACX,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,GAAG;AAAA,IACL;AAGA,QAAI,KAAK,QAAQ,gBAAgB,GAAG;AAClC,WAAK,aAAa;AAAA,QAChB,MAAM,KAAK,MAAM;AAAA,QACjB,KAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,OAAuB;AACzB,QAAI,KAAK,QAAQ,YAAY,MAAO;AAEpC,UAAM,QAAoB;AAAA,MACxB,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,IACjB;AAGA,QAAI,MAAM,SAAS;AACjB,aAAO,OAAO,OAAO,MAAM,OAAO;AAAA,IACpC;AAGA,QAAI,MAAM,OAAO;AACf,YAAM,YAAY,IAAI,MAAM,MAAM;AAClC,YAAM,eAAe,IAAI,MAAM,MAAM;AACrC,YAAM,aAAa,IAAI,MAAM,MAAM;AAAA,IACrC;AAEA,SAAK,OAAO,KAAK,KAAK;AAGtB,QAAI,KAAK,OAAO,UAAU,KAAK,QAAQ,WAAW;AAChD,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAE3B,QAAI,KAAK,cAAc,KAAK,OAAO,WAAW,EAAG;AAEjD,SAAK,aAAa;AAClB,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS,CAAC;AAEf,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,KAAK,QAAQ,MAAM,gBAAgB,KAAK,QAAQ,OAAO;AAAA,QAC1D;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,KAAK,QAAQ,KAAK;AAAA,YAC3C,gBAAgB;AAAA,YAChB,GAAI,KAAK,QAAQ,SAAS;AAAA,cACxB,kBAAkB,KAAK,QAAQ;AAAA,YACjC;AAAA,UACF;AAAA,UACA,MAAM,KAAK,UAAU,MAAM;AAAA,QAC7B;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,IAAI,SAAS,EAAE;AAAA,MACxE;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,KAAK,QAAQ,SAAS;AACxB,aAAK,QAAQ;AAAA,UACX,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,UACxD,OAAO;AAAA,QACT;AAAA,MACF,OAAO;AAEL,gBAAQ,MAAM,gCAAgC,KAAK;AAAA,MACrD;AAAA,IACF,UAAE;AACA,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAE3B,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AAGA,UAAM,KAAK,MAAM;AAAA,EACnB;AACF;AAKO,SAAS,qBACd,SACgB;AAChB,SAAO,IAAI,eAAe,OAAO;AACnC;;;ACDO,IAAM,kBAAN,MAA8D;AAAA,EAC1D,OAAO;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACT,OAAyB;AAAA,EACzB,WAAiD,oBAAI,IAAI;AAAA,EACzD,cAA4B,CAAC;AAAA,EACpB,iBAAiB;AAAA,EAElC,YAAY,SAAiC;AAC3C,SAAK,UAAU;AAAA,MACb,YAAY;AAAA,MACZ,GAAG;AAAA,IACL;AAEA,QAAI,QAAQ,QAAQ;AAClB,WAAK,SAAS,QAAQ;AAAA,IACxB,WAAW,QAAQ,KAAK;AACtB,WAAK,MAAM,KAAK,SAAS,QAAQ,GAAG;AAAA,IACtC,OAAO;AACL,YAAM,IAAI,MAAM,0DAA0D;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,KAAwB;AACvC,UAAM,QAAQ,IAAI,MAAM,sCAAsC;AAC9D,QAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;AAC9D,YAAM,IAAI,MAAM,uBAAuB,GAAG,EAAE;AAAA,IAC9C;AACA,WAAO;AAAA,MACL,UAAU,MAAM,CAAC;AAAA,MACjB,WAAW,MAAM,CAAC;AAAA,MAClB,MAAM,MAAM,CAAC;AAAA,MACb,WAAW,MAAM,CAAC;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAuB;AACzB,QAAI,KAAK,QAAQ,YAAY,MAAO;AAGpC,QAAI,MAAM,aAAa,GAAI;AAE3B,QAAI,MAAM,OAAO;AACf,YAAM,QAAQ,IAAI,MAAM,MAAM,MAAM,OAAO;AAC3C,YAAM,OAAO,MAAM,MAAM;AACzB,UAAI,MAAM,MAAM,OAAO;AACrB,cAAM,QAAQ,MAAM,MAAM;AAAA,MAC5B;AAEA,WAAK;AAAA,QACH;AAAA,QACA,MAAM,UAAU,EAAE,OAAO,MAAM,QAAQ,IAAI;AAAA,MAC7C;AAAA,IACF,OAAO;AACL,WAAK;AAAA,QACH,MAAM;AAAA,QACN,MAAM,UAAU,UAAU,UAAU;AAAA,QACpC,MAAM,UAAU,EAAE,OAAO,MAAM,QAAQ,IAAI;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,OAAc,SAA8B;AAC3D,QAAI,KAAK,QAAQ,YAAY,MAAO;AACpC,QAAI,CAAC,KAAK,aAAa,EAAG;AAE1B,QAAI,KAAK,QAAQ;AACf,WAAK,eAAe,OAAO,OAAO;AAAA,IACpC,OAAO;AACL,WAAK,gBAAgB,OAAO,OAAO;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eACE,SACA,OACA,SACM;AACN,QAAI,KAAK,QAAQ,YAAY,MAAO;AACpC,QAAI,CAAC,KAAK,aAAa,EAAG;AAE1B,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,UAAU,CAAC,UAAU;AAC/B,aAAK,aAAa,OAAO,OAAO;AAChC,cAAM,SAAS,KAAK;AACpB,aAAK,OAAQ,eAAe,SAAS,KAAK;AAAA,MAC5C,CAAC;AAAA,IACH,OAAO;AACL,WAAK,cAAc;AAAA,QACjB,OAAO,UAAU,YAAY,YAAY,UAAU,SAAS,SAAS;AAAA,QACrE,SAAS,EAAE,WAAW,QAAQ;AAAA,QAC9B,GAAG,KAAK,kBAAkB,OAAO;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,MAA8B;AACpC,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,MAAc,SAAwC;AAC/D,SAAK,SAAS,IAAI,MAAM,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,YAA8B;AAC1C,SAAK,YAAY,KAAK;AAAA,MACpB,GAAG;AAAA,MACH,WAAW,WAAW,aAAa,KAAK,IAAI,IAAI;AAAA,IAClD,CAAC;AAGD,QAAI,KAAK,YAAY,SAAS,KAAK,gBAAgB;AACjD,WAAK,cAAc,KAAK,YAAY,MAAM,CAAC,KAAK,cAAc;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAQ,OAAO;AACtB,YAAM,KAAK,OAAO,MAAM,GAAI;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAwB;AAC9B,UAAM,OAAO,KAAK,QAAQ,cAAc;AACxC,WAAO,KAAK,OAAO,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAc,SAA8B;AACjE,SAAK,OAAQ,UAAU,CAAC,UAAU;AAChC,WAAK,aAAa,OAAO,OAAO;AAChC,WAAK,OAAQ,iBAAiB,KAAK;AAAA,IACrC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAoB,SAA8B;AAErE,QAAI,KAAK,MAAM;AACb,YAAM,QAAQ,KAAK,IAAI;AAAA,IACzB,WAAW,SAAS,QAAQ;AAC1B,YAAM,QAAQ,EAAE,IAAI,QAAQ,OAAO,CAAC;AAAA,IACtC;AAGA,QAAI,KAAK,QAAQ,MAAM;AACrB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAC5D,cAAM,OAAO,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AACA,QAAI,SAAS,MAAM;AACjB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,IAAI,GAAG;AACvD,cAAM,OAAO,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AACA,QAAI,SAAS,WAAW;AACtB,YAAM,OAAO,aAAa,QAAQ,SAAS;AAAA,IAC7C;AACA,QAAI,SAAS,UAAU;AACrB,YAAM,OAAO,YAAY,QAAQ,QAAQ;AAAA,IAC3C;AAGA,QAAI,SAAS,OAAO;AAClB,YAAM,UAAU,QAAQ,KAAK;AAAA,IAC/B;AAGA,eAAW,MAAM,KAAK,aAAa;AACjC,YAAM,cAAc,EAAE;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAAc,SAA8B;AAClE,UAAM,aAAa,KAAK,gBAAgB,MAAM,KAAK;AACnD,UAAM,iBAIF;AAAA,MACF,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,IACf;AAEA,QAAI,YAAY;AACd,qBAAe,aAAa;AAAA,IAC9B;AAEA,UAAM,QAA8B;AAAA,MAClC,OAAO;AAAA,MACP,WAAW;AAAA,QACT,QAAQ,CAAC,cAAc;AAAA,MACzB;AAAA,MACA,GAAG,KAAK,kBAAkB,OAAO;AAAA,IACnC;AAEA,SAAK,cAAc,KAAK;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,SAA8C;AACtE,UAAM,QAA8B,CAAC;AAGrC,QAAI,KAAK,QAAQ,aAAa;AAC5B,YAAM,cAAc,KAAK,QAAQ;AAAA,IACnC;AACA,QAAI,KAAK,QAAQ,SAAS;AACxB,YAAM,UAAU,KAAK,QAAQ;AAAA,IAC/B;AACA,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,cAAc,KAAK,QAAQ;AAAA,IACnC;AAGA,UAAM,OAA+B,EAAE,GAAG,KAAK,QAAQ,KAAK;AAC5D,QAAI,SAAS,MAAM;AACjB,aAAO,OAAO,MAAM,QAAQ,IAAI;AAAA,IAClC;AACA,QAAI,SAAS,WAAW;AACtB,WAAK,WAAW,IAAI,QAAQ;AAAA,IAC9B;AACA,QAAI,SAAS,UAAU;AACrB,WAAK,UAAU,IAAI,QAAQ;AAAA,IAC7B;AACA,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AAChC,YAAM,OAAO;AAAA,IACf;AAGA,QAAI,SAAS,OAAO;AAClB,YAAM,QAAQ,QAAQ;AAAA,IACxB;AAGA,QAAI,KAAK,MAAM;AACb,YAAM,OAAO,KAAK;AAAA,IACpB,WAAW,SAAS,QAAQ;AAC1B,YAAM,OAAO,EAAE,IAAI,QAAQ,OAAO;AAAA,IACpC;AAGA,QAAI,KAAK,YAAY,SAAS,GAAG;AAC/B,YAAM,cAAc,KAAK,YAAY,IAAI,CAAC,OAAO;AAC/C,cAAM,QAOF,CAAC;AACL,YAAI,GAAG,KAAM,OAAM,OAAO,GAAG;AAC7B,YAAI,GAAG,SAAU,OAAM,WAAW,GAAG;AACrC,YAAI,GAAG,QAAS,OAAM,UAAU,GAAG;AACnC,YAAI,GAAG,KAAM,OAAM,OAAO,GAAG;AAC7B,YAAI,GAAG,MAAO,OAAM,QAAQ,GAAG;AAC/B,YAAI,GAAG,cAAc,OAAW,OAAM,YAAY,GAAG;AACrD,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAGA,QAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,YAAM,WAAW,OAAO,YAAY,KAAK,QAAQ;AAAA,IACnD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,OAC0G;AAC1G,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,MAAM,CAAC;AACvC,UAAM,SAA2F,CAAC;AAElG,eAAW,QAAQ,OAAO;AAExB,YAAM,QAAQ,KAAK,MAAM,+CAA+C;AACxE,UAAI,SAAS,MAAM,CAAC,KAAK,MAAM,CAAC,GAAG;AACjC,cAAM,QAAmF;AAAA,UACvF,UAAU,MAAM,CAAC,KAAK;AAAA,UACtB,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,UAC7B,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,QAC9B;AACA,YAAI,MAAM,CAAC,GAAG;AACZ,gBAAM,WAAW,MAAM,CAAC;AAAA,QAC1B;AACA,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAGA,WAAO,QAAQ;AAEf,WAAO,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAA0B;AAEhC,UAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,WAAO,gBAAgB,KAAK;AAC5B,WAAO,MAAM,KAAK,KAAK,EACpB,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,WAAgD;AAC1E,QAAI,CAAC,KAAK,IAAK;AAEf,UAAM,QAAqB;AAAA,MACzB,UAAU,KAAK,gBAAgB;AAAA,MAC/B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,UAAU;AAAA,MACV,OAAO;AAAA,MACP,GAAG;AAAA,IACL;AAGA,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,SAAS,KAAK,QAAQ,WAAW,KAAK;AAC5C,UAAI,WAAW,KAAM;AAAA,IACvB;AAEA,UAAM,MAAM,GAAG,KAAK,IAAI,QAAQ,MAAM,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,SAAS;AAE7E,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,YACf;AAAA,YACA;AAAA,YACA,cAAc,KAAK,IAAI,SAAS;AAAA,UAClC,EAAE,KAAK,IAAI;AAAA,QACb;AAAA,QACA,MAAM,KAAK,UAAU,KAAK;AAAA,MAC5B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,qBAAqB,SAAS,MAAM,EAAE;AAAA,MACxD;AAAA,IACF,SAAS,OAAO;AACd,UAAI,KAAK,QAAQ,SAAS;AACxB,aAAK,QAAQ,QAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MAChF;AAAA,IAEF;AAAA,EACF;AACF;AAKO,SAAS,sBAAsB,SAAkD;AACtF,SAAO,IAAI,gBAAgB,OAAO;AACpC;;;ACpeA,IAAM,iBAAN,MAA8C;AAAA,EAC5C,YAAoB,UAAkB;AAAlB;AAAA,EAAmB;AAAA,EAE/B,IAAI,OAAe,SAAiB,YAA4C;AACtF,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,UAAU,KAAK;AAAA,MACf,KAAK;AAAA,MACL,OAAM,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC7B,GAAG;AAAA,IACL;AACA,YAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,EACnC;AAAA,EAEA,MAAM,SAAiB,YAA4C;AACjE,SAAK,IAAI,SAAS,SAAS,UAAU;AAAA,EACvC;AAAA,EAEA,KAAK,SAAiB,YAA4C;AAChE,SAAK,IAAI,QAAQ,SAAS,UAAU;AAAA,EACtC;AAAA,EAEA,KAAK,SAAiB,YAA4C;AAChE,SAAK,IAAI,QAAQ,SAAS,UAAU;AAAA,EACtC;AAAA,EAEA,QAAQ,SAAiB,YAA4C;AACnE,SAAK,IAAI,WAAW,SAAS,UAAU;AAAA,EACzC;AAAA,EAEA,MAAM,SAAiB,YAA4C;AACjE,SAAK,IAAI,SAAS,SAAS,UAAU;AAAA,EACvC;AAAA,EAEA,MAAM,SAAiB,YAA4C;AACjE,SAAK,IAAI,SAAS,SAAS,UAAU;AAAA,EACvC;AACF;AAMO,IAAM,mBAAN,MAA+C;AAAA,EAC3C,OAAO;AAAA,EAEC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,UAAmC,CAAC,GAAG;AACjD,SAAK,UAAU,QAAQ,YAAY;AACnC,SAAK,mBAAmB,QAAQ,qBAAqB;AACrD,SAAK,oBAAoB,QAAQ,qBAAqB;AAEtD,QAAI,QAAQ,QAAQ;AAClB,WAAK,SAAS,QAAQ;AAAA,IACxB,OAAO;AAEL,WAAK,SAAS,IAAI,eAAe,QAAQ,YAAY,MAAM;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,IAAI,OAAuB;AACzB,QAAI,CAAC,KAAK,QAAS;AAEnB,UAAM,QAAQ,KAAK,SAAS,MAAM,KAAK;AACvC,UAAM,aAAa,KAAK,gBAAgB,KAAK;AAG7C,SAAK,OAAO,KAAK,EAAE,MAAM,SAAS,UAAU;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,OAAmC;AAClD,UAAM,UAA8C;AAAA,MAClD,OAAO;AAAA,MACP,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA;AAAA,IACV;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAA0C;AAChE,UAAM,aAAsC,CAAC;AAG7C,QAAI,KAAK,kBAAkB;AACzB,iBAAW,WAAW,IAAI,MAAM;AAAA,IAClC;AAGA,QAAI,KAAK,mBAAmB;AAC1B,iBAAW,YAAY,IAAI,MAAM;AAAA,IACnC;AAGA,QAAI,MAAM,SAAS;AACjB,aAAO,OAAO,YAAY,MAAM,OAAO;AAAA,IACzC;AAGA,QAAI,MAAM,OAAO;AACf,iBAAW,OAAO,IAAI;AAAA,QACpB,MAAM,MAAM,MAAM;AAAA,QAClB,SAAS,MAAM,MAAM;AAAA,QACrB,OAAO,MAAM,MAAM;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,SAAS,uBACd,SACkB;AAClB,SAAO,IAAI,iBAAiB,OAAO;AACrC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/runtime.ts","../../src/env.ts","../../src/transports/console.ts","../../src/transports/axiom.ts","../../src/transports/sentry.ts","../../src/transports/logtape.ts"],"sourcesContent":["/**\n * @module\n * Runtime detection for Node.js, Deno, Bun, and Cloudflare Workers.\n * Detects the current JavaScript runtime and provides helper functions.\n *\n * @example\n * ```typescript\n * import { runtime, isNode, isDeno, isBun, isCloudflare, getRuntimeVersion } from '@parsrun/core';\n *\n * console.log(`Running on ${runtime}`); // \"node\", \"deno\", \"bun\", \"cloudflare\", etc.\n *\n * if (isNode()) {\n * // Node.js specific code\n * } else if (isCloudflare()) {\n * // Cloudflare Workers specific code\n * }\n *\n * console.log(getRuntimeVersion()); // \"Node.js 20.0.0\"\n * ```\n */\n\nexport type Runtime = \"node\" | \"deno\" | \"bun\" | \"cloudflare\" | \"edge\" | \"browser\" | \"unknown\";\n\n/**\n * Detect the current JavaScript runtime\n */\nexport function detectRuntime(): Runtime {\n // Bun check (must be before Node since Bun also has process)\n if (typeof globalThis !== \"undefined\" && \"Bun\" in globalThis) {\n return \"bun\";\n }\n\n // Deno check\n if (typeof globalThis !== \"undefined\" && \"Deno\" in globalThis) {\n return \"deno\";\n }\n\n // Cloudflare Workers check (has caches but no process)\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).caches !== \"undefined\" &&\n typeof (globalThis as any).process === \"undefined\"\n ) {\n return \"cloudflare\";\n }\n\n // Generic Edge runtime check (Vercel Edge, etc.)\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).EdgeRuntime !== \"undefined\"\n ) {\n return \"edge\";\n }\n\n // Browser check\n if (\n typeof globalThis !== \"undefined\" &&\n typeof (globalThis as any).window !== \"undefined\" &&\n typeof (globalThis as any).document !== \"undefined\"\n ) {\n return \"browser\";\n }\n\n // Node.js check\n if (\n typeof process !== \"undefined\" &&\n process.versions &&\n process.versions.node\n ) {\n return \"node\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Current runtime (cached)\n */\nexport const runtime = detectRuntime();\n\n/**\n * Runtime information helpers\n */\nexport const runtimeInfo = {\n runtime,\n isNode: runtime === \"node\",\n isDeno: runtime === \"deno\",\n isBun: runtime === \"bun\",\n isCloudflare: runtime === \"cloudflare\",\n isEdge: runtime === \"cloudflare\" || runtime === \"edge\" || runtime === \"deno\",\n isBrowser: runtime === \"browser\",\n isServer: runtime !== \"browser\",\n supportsWebCrypto: typeof globalThis.crypto?.subtle !== \"undefined\",\n supportsStreams: typeof globalThis.ReadableStream !== \"undefined\",\n} as const;\n\n/**\n * Check if running in Node.js\n */\nexport function isNode(): boolean {\n return runtime === \"node\";\n}\n\n/**\n * Check if running in Deno\n */\nexport function isDeno(): boolean {\n return runtime === \"deno\";\n}\n\n/**\n * Check if running in Bun\n */\nexport function isBun(): boolean {\n return runtime === \"bun\";\n}\n\n/**\n * Check if running in Cloudflare Workers\n */\nexport function isCloudflare(): boolean {\n return runtime === \"cloudflare\";\n}\n\n/**\n * Check if running in any edge environment\n */\nexport function isEdge(): boolean {\n return runtimeInfo.isEdge;\n}\n\n/**\n * Check if running in browser\n */\nexport function isBrowser(): boolean {\n return runtime === \"browser\";\n}\n\n/**\n * Check if running on server (not browser)\n */\nexport function isServer(): boolean {\n return runtimeInfo.isServer;\n}\n\n/**\n * Get runtime version string\n */\nexport function getRuntimeVersion(): string {\n switch (runtime) {\n case \"node\":\n return `Node.js ${process?.versions?.node ?? \"unknown\"}`;\n case \"bun\":\n return `Bun ${(globalThis as any).Bun.version}`;\n case \"deno\":\n return `Deno ${(globalThis as any).Deno.version.deno}`;\n case \"cloudflare\":\n return \"Cloudflare Workers\";\n case \"edge\":\n return \"Edge Runtime\";\n case \"browser\":\n return typeof navigator !== \"undefined\" ? navigator.userAgent : \"Browser\";\n default:\n return \"Unknown\";\n }\n}\n","/**\n * @module\n * Runtime-agnostic environment variable access.\n * Works in Node.js, Deno, Bun, and Cloudflare Workers.\n *\n * @example\n * ```typescript\n * import { getEnv, requireEnv, getEnvNumber, isDevelopment } from '@parsrun/core';\n *\n * // Get optional env var with default\n * const port = getEnvNumber('PORT', 3000);\n *\n * // Get required env var (throws if missing)\n * const apiKey = requireEnv('API_KEY');\n *\n * // Check environment\n * if (isDevelopment()) {\n * console.log('Running in development mode');\n * }\n * ```\n */\n\nimport { runtime } from \"./runtime.js\";\n\n/**\n * Environment variable store for edge runtimes\n * Must be set from the request handler's env parameter\n */\nlet edgeEnvStore: Record<string, string | undefined> = {};\n\n/**\n * Set environment variables for edge runtimes (Cloudflare Workers, etc.)\n * Call this from your worker's fetch handler with the env parameter\n *\n * @example\n * ```typescript\n * export default {\n * async fetch(request, env) {\n * setEdgeEnv(env);\n * // ... rest of handler\n * }\n * }\n * ```\n */\nexport function setEdgeEnv(env: Record<string, string | undefined>): void {\n edgeEnvStore = { ...edgeEnvStore, ...env };\n}\n\n/**\n * Clear edge environment store\n */\nexport function clearEdgeEnv(): void {\n edgeEnvStore = {};\n}\n\n/**\n * Get an environment variable value\n * Works across all runtimes\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n // Edge runtimes (Cloudflare Workers, etc.)\n if (runtime === \"cloudflare\" || runtime === \"edge\") {\n return edgeEnvStore[key] ?? defaultValue;\n }\n\n // Deno\n if (runtime === \"deno\") {\n try {\n return (globalThis as any).Deno.env.get(key) ?? defaultValue;\n } catch {\n return defaultValue;\n }\n }\n\n // Node.js / Bun (both use process.env)\n if (typeof process !== \"undefined\" && process.env) {\n return process.env[key] ?? defaultValue;\n }\n\n // Browser - check for injected env\n if (runtime === \"browser\" && typeof (globalThis as any).__ENV__ !== \"undefined\") {\n return (globalThis as any).__ENV__[key] ?? defaultValue;\n }\n\n return defaultValue;\n}\n\n/**\n * Get an environment variable, throwing if not found\n */\nexport function requireEnv(key: string): string {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n throw new Error(`Required environment variable \"${key}\" is not set`);\n }\n return value;\n}\n\n/**\n * Get an environment variable as a number\n */\nexport function getEnvNumber(key: string, defaultValue?: number): number | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get an environment variable as a float\n */\nexport function getEnvFloat(key: string, defaultValue?: number): number | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n const parsed = parseFloat(value);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n\n/**\n * Get an environment variable as a boolean\n */\nexport function getEnvBoolean(key: string, defaultValue: boolean = false): boolean {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n return value === \"true\" || value === \"1\" || value === \"yes\";\n}\n\n/**\n * Get an environment variable as an array (comma-separated)\n */\nexport function getEnvArray(key: string, defaultValue: string[] = []): string[] {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n return value.split(\",\").map((s) => s.trim()).filter(Boolean);\n}\n\n/**\n * Get an environment variable as JSON\n */\nexport function getEnvJson<T>(key: string, defaultValue?: T): T | undefined {\n const value = getEnv(key);\n if (value === undefined || value === \"\") {\n return defaultValue;\n }\n try {\n return JSON.parse(value) as T;\n } catch {\n return defaultValue;\n }\n}\n\n/**\n * Check if running in development mode\n */\nexport function isDevelopment(): boolean {\n const env = getEnv(\"NODE_ENV\");\n return env === \"development\" || env === undefined;\n}\n\n/**\n * Check if running in production mode\n */\nexport function isProduction(): boolean {\n return getEnv(\"NODE_ENV\") === \"production\";\n}\n\n/**\n * Check if running in test mode\n */\nexport function isTest(): boolean {\n return getEnv(\"NODE_ENV\") === \"test\";\n}\n\n/**\n * Environment mode\n */\nexport type EnvMode = \"development\" | \"production\" | \"test\";\n\n/**\n * Get current environment mode\n */\nexport function getEnvMode(): EnvMode {\n const env = getEnv(\"NODE_ENV\");\n if (env === \"production\") return \"production\";\n if (env === \"test\") return \"test\";\n return \"development\";\n}\n\n/**\n * Create a typed environment configuration object\n *\n * @example\n * ```typescript\n * const env = createEnvConfig({\n * DATABASE_URL: { required: true },\n * PORT: { type: 'number', default: 3000 },\n * DEBUG: { type: 'boolean', default: false },\n * });\n *\n * env.DATABASE_URL // string\n * env.PORT // number\n * env.DEBUG // boolean\n * ```\n */\nexport function createEnvConfig<T extends EnvSchema>(schema: T): EnvResult<T> {\n const result: Record<string, unknown> = {};\n\n for (const [key, config] of Object.entries(schema)) {\n const envConfig = config as EnvConfigItem;\n let value: unknown;\n\n switch (envConfig.type) {\n case \"number\":\n value = getEnvNumber(key, envConfig.default as number | undefined);\n break;\n case \"boolean\":\n value = getEnvBoolean(key, envConfig.default as boolean | undefined);\n break;\n case \"array\":\n value = getEnvArray(key, envConfig.default as string[] | undefined);\n break;\n case \"json\":\n value = getEnvJson(key, envConfig.default);\n break;\n default:\n value = getEnv(key, envConfig.default as string | undefined);\n }\n\n if (envConfig.required && (value === undefined || value === \"\")) {\n throw new Error(`Required environment variable \"${key}\" is not set`);\n }\n\n result[key] = value;\n }\n\n return result as EnvResult<T>;\n}\n\n// Type helpers for createEnvConfig\ntype EnvConfigItem = {\n type?: \"string\" | \"number\" | \"boolean\" | \"array\" | \"json\";\n required?: boolean;\n default?: unknown;\n};\n\ntype EnvSchema = Record<string, EnvConfigItem>;\n\ntype EnvResult<T extends EnvSchema> = {\n [K in keyof T]: T[K][\"type\"] extends \"number\"\n ? number\n : T[K][\"type\"] extends \"boolean\"\n ? boolean\n : T[K][\"type\"] extends \"array\"\n ? string[]\n : T[K][\"type\"] extends \"json\"\n ? unknown\n : string;\n};\n","/**\n * @parsrun/core - Console Transport\n *\n * Default log transport that outputs to the console.\n * Supports pretty printing with colors for development and JSON output for production.\n *\n * @example\n * ```typescript\n * import { ConsoleTransport } from '@parsrun/core';\n *\n * const transport = new ConsoleTransport({\n * pretty: true, // Enable colored, human-readable output\n * colors: true // Enable ANSI colors\n * });\n * ```\n */\n\nimport { runtime } from \"../runtime.js\";\nimport { isDevelopment } from \"../env.js\";\nimport type { LogEntry, LogLevelName } from \"../logger.js\";\nimport type { LogTransport } from \"./types.js\";\n\n/**\n * Console transport options\n */\nexport interface ConsoleTransportOptions {\n /** Enable pretty printing (default: true in development) */\n pretty?: boolean;\n /** Enable ANSI colors (default: true in Node/Bun) */\n colors?: boolean;\n}\n\n/**\n * Console transport\n * Outputs logs to console with optional pretty printing and colors\n */\nexport class ConsoleTransport implements LogTransport {\n readonly name = \"console\";\n\n private pretty: boolean;\n private colors: boolean;\n\n constructor(options: ConsoleTransportOptions = {}) {\n this.pretty = options.pretty ?? isDevelopment();\n this.colors = options.colors ?? (runtime === \"node\" || runtime === \"bun\");\n }\n\n log(entry: LogEntry): void {\n if (this.pretty) {\n this.logPretty(entry);\n } else {\n this.logJson(entry);\n }\n }\n\n private logJson(entry: LogEntry): void {\n const { level, message, timestamp, context, error } = entry;\n\n const output: Record<string, unknown> = {\n level,\n time: timestamp,\n msg: message,\n };\n\n if (context && Object.keys(context).length > 0) {\n Object.assign(output, context);\n }\n\n if (error) {\n output[\"err\"] = error;\n }\n\n console.log(JSON.stringify(output));\n }\n\n private logPretty(entry: LogEntry): void {\n const { level, message, timestamp, context, error } = entry;\n\n const levelColors: Record<LogLevelName, string> = {\n TRACE: \"\\x1b[90m\", // Gray\n DEBUG: \"\\x1b[36m\", // Cyan\n INFO: \"\\x1b[32m\", // Green\n WARN: \"\\x1b[33m\", // Yellow\n ERROR: \"\\x1b[31m\", // Red\n FATAL: \"\\x1b[35m\", // Magenta\n SILENT: \"\",\n };\n\n const reset = \"\\x1b[0m\";\n const color = this.colors ? levelColors[level] : \"\";\n const resetCode = this.colors ? reset : \"\";\n\n // Extract time part from ISO timestamp\n const timePart = timestamp.split(\"T\")[1];\n const time = timePart ? timePart.slice(0, 8) : timestamp;\n\n let output = `${color}[${time}] ${level.padEnd(5)}${resetCode} ${message}`;\n\n if (context && Object.keys(context).length > 0) {\n output += ` ${JSON.stringify(context)}`;\n }\n\n // Route to appropriate console method\n if (level === \"ERROR\" || level === \"FATAL\") {\n console.error(output);\n if (error?.stack) {\n console.error(error.stack);\n }\n } else if (level === \"WARN\") {\n console.warn(output);\n } else if (level === \"DEBUG\" || level === \"TRACE\") {\n console.debug(output);\n } else {\n console.log(output);\n }\n }\n}\n","/**\n * @parsrun/core - Axiom Transport\n *\n * Log ingestion transport for Axiom (axiom.co).\n * Uses native fetch - works on all runtimes (Node, Deno, Bun, Workers).\n *\n * @example\n * ```typescript\n * import { createLogger, AxiomTransport } from '@parsrun/core';\n *\n * const axiom = new AxiomTransport({\n * token: process.env.AXIOM_TOKEN!,\n * dataset: 'my-app-logs',\n * batchSize: 100, // Send every 100 logs\n * flushInterval: 5000, // Or every 5 seconds\n * });\n *\n * const logger = createLogger({\n * transports: [axiom]\n * });\n * ```\n */\n\nimport type { LogEntry } from \"../logger.js\";\nimport type { LogTransport, BatchTransportOptions } from \"./types.js\";\n\n/**\n * Axiom transport options\n */\nexport interface AxiomTransportOptions extends BatchTransportOptions {\n /** Axiom API token */\n token: string;\n /** Dataset name to ingest logs into */\n dataset: string;\n /** Organization ID (optional, for personal tokens) */\n orgId?: string;\n /** Custom Axiom API URL (default: https://api.axiom.co) */\n apiUrl?: string;\n /** Callback for errors during ingestion */\n onError?: (error: Error, droppedCount: number) => void;\n}\n\n/**\n * Axiom log event structure\n */\ninterface AxiomEvent {\n _time: string;\n level: string;\n message: string;\n [key: string]: unknown;\n}\n\n/**\n * Axiom Transport\n * Batches logs and sends them to Axiom's ingest API\n */\nexport class AxiomTransport implements LogTransport {\n readonly name = \"axiom\";\n\n private buffer: AxiomEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private isFlushing = false;\n private readonly options: Required<\n Pick<AxiomTransportOptions, \"batchSize\" | \"flushInterval\" | \"apiUrl\">\n > &\n AxiomTransportOptions;\n\n constructor(options: AxiomTransportOptions) {\n this.options = {\n batchSize: 100,\n flushInterval: 5000,\n apiUrl: \"https://api.axiom.co\",\n ...options,\n };\n\n // Start flush interval if enabled\n if (this.options.flushInterval > 0) {\n this.flushTimer = setInterval(\n () => this.flush(),\n this.options.flushInterval\n );\n }\n }\n\n log(entry: LogEntry): void {\n if (this.options.enabled === false) return;\n\n const event: AxiomEvent = {\n _time: entry.timestamp,\n level: entry.level,\n message: entry.message,\n };\n\n // Add context fields\n if (entry.context) {\n Object.assign(event, entry.context);\n }\n\n // Add error fields\n if (entry.error) {\n event[\"error.name\"] = entry.error.name;\n event[\"error.message\"] = entry.error.message;\n event[\"error.stack\"] = entry.error.stack;\n }\n\n this.buffer.push(event);\n\n // Flush if buffer is full\n if (this.buffer.length >= this.options.batchSize) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n // Prevent concurrent flushes\n if (this.isFlushing || this.buffer.length === 0) return;\n\n this.isFlushing = true;\n const events = this.buffer;\n this.buffer = [];\n\n try {\n const response = await fetch(\n `${this.options.apiUrl}/v1/datasets/${this.options.dataset}/ingest`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.token}`,\n \"Content-Type\": \"application/json\",\n ...(this.options.orgId && {\n \"X-Axiom-Org-Id\": this.options.orgId,\n }),\n },\n body: JSON.stringify(events),\n }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Axiom ingest failed: ${response.status} ${errorText}`);\n }\n } catch (error) {\n // Call error handler if provided\n if (this.options.onError) {\n this.options.onError(\n error instanceof Error ? error : new Error(String(error)),\n events.length\n );\n } else {\n // Silent fail by default - don't crash the app for logging failures\n console.error(\"[Axiom] Failed to send logs:\", error);\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n async close(): Promise<void> {\n // Stop flush timer\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Final flush\n await this.flush();\n }\n}\n\n/**\n * Create an Axiom transport instance.\n *\n * @param options - Axiom transport configuration\n * @returns A new AxiomTransport instance\n *\n * @example\n * ```typescript\n * const axiom = createAxiomTransport({\n * token: 'xaat-xxx',\n * dataset: 'logs'\n * });\n * ```\n */\nexport function createAxiomTransport(\n options: AxiomTransportOptions\n): AxiomTransport {\n return new AxiomTransport(options);\n}\n","/**\n * @parsrun/core - Sentry Transport\n * Error tracking transport for Sentry\n *\n * Supports two modes:\n * 1. HTTP API mode (default) - Zero dependency, works on all runtimes\n * 2. SDK mode (BYOS) - Full features with user-provided Sentry SDK\n *\n * @example HTTP API mode (simple, universal)\n * ```typescript\n * const sentry = new SentryTransport({\n * dsn: 'https://xxx@sentry.io/123',\n * environment: 'production',\n * });\n * ```\n *\n * @example SDK mode (full features)\n * ```typescript\n * import * as Sentry from '@sentry/cloudflare'; // or @sentry/node\n *\n * Sentry.init({ dsn: '...' });\n *\n * const sentry = new SentryTransport({\n * client: Sentry,\n * });\n * ```\n */\n\nimport type { LogEntry } from \"../logger.js\";\nimport type {\n LogTransport,\n ErrorTransport,\n ErrorContext,\n ErrorUser,\n Breadcrumb,\n BaseTransportOptions,\n} from \"./types.js\";\n\n/**\n * Sentry SDK interface (minimal interface for BYOS)\n * Compatible with @sentry/node, @sentry/cloudflare, @sentry/browser, etc.\n */\nexport interface SentryClient {\n captureException(error: Error, hint?: unknown): string;\n captureMessage(message: string, level?: string): string;\n withScope(callback: (scope: SentryScope) => void): void;\n flush?(timeout?: number): Promise<boolean>;\n}\n\nexport interface SentryScope {\n setTag(key: string, value: string): void;\n setUser(user: { id: string; email?: string; [key: string]: unknown } | null): void;\n setExtra(key: string, value: unknown): void;\n setExtras(extras: Record<string, unknown>): void;\n setLevel(level: string): void;\n addBreadcrumb(breadcrumb: unknown): void;\n}\n\n/**\n * Parsed DSN components\n */\ninterface ParsedDSN {\n protocol: string;\n publicKey: string;\n host: string;\n projectId: string;\n}\n\n/**\n * Sentry transport options\n */\nexport interface SentryTransportOptions extends BaseTransportOptions {\n /**\n * Sentry DSN (required for HTTP mode)\n * Format: https://{publicKey}@{host}/{projectId}\n */\n dsn?: string;\n\n /**\n * Sentry SDK client (for BYOS mode)\n * Pass your initialized Sentry client for full SDK features\n */\n client?: SentryClient;\n\n /** Environment name (e.g., 'production', 'staging') */\n environment?: string;\n\n /** Release version */\n release?: string;\n\n /** Server name */\n serverName?: string;\n\n /** Sample rate for error events (0.0 to 1.0) */\n sampleRate?: number;\n\n /** Additional tags to add to all events */\n tags?: Record<string, string>;\n\n /** Callback before sending (return null to drop event) */\n beforeSend?: (event: SentryEvent) => SentryEvent | null;\n\n /** Callback for transport errors */\n onError?: (error: Error) => void;\n}\n\n/**\n * Sentry event structure (simplified)\n */\nexport interface SentryEvent {\n event_id: string;\n timestamp: string;\n platform: string;\n level: \"fatal\" | \"error\" | \"warning\" | \"info\" | \"debug\";\n logger?: string;\n transaction?: string;\n server_name?: string;\n release?: string;\n environment?: string;\n message?: { formatted: string };\n exception?: {\n values: Array<{\n type: string;\n value: string;\n stacktrace?: {\n frames: Array<{\n filename?: string;\n function?: string;\n lineno?: number;\n colno?: number;\n in_app?: boolean;\n }>;\n };\n }>;\n };\n tags?: Record<string, string>;\n extra?: Record<string, unknown>;\n user?: {\n id?: string;\n email?: string;\n username?: string;\n [key: string]: unknown;\n };\n breadcrumbs?: Array<{\n type?: string;\n category?: string;\n message?: string;\n data?: Record<string, unknown>;\n level?: string;\n timestamp?: number;\n }>;\n contexts?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Sentry Transport\n * Implements both LogTransport and ErrorTransport\n */\nexport class SentryTransport implements LogTransport, ErrorTransport {\n readonly name = \"sentry\";\n\n private readonly client?: SentryClient;\n private readonly dsn?: ParsedDSN;\n private readonly options: SentryTransportOptions;\n private user: ErrorUser | null = null;\n private contexts: Map<string, Record<string, unknown>> = new Map();\n private breadcrumbs: Breadcrumb[] = [];\n private readonly maxBreadcrumbs = 100;\n\n constructor(options: SentryTransportOptions) {\n this.options = {\n sampleRate: 1.0,\n ...options,\n };\n\n if (options.client) {\n this.client = options.client;\n } else if (options.dsn) {\n this.dsn = this.parseDSN(options.dsn);\n } else {\n throw new Error(\"SentryTransport requires either 'dsn' or 'client' option\");\n }\n }\n\n /**\n * Parse Sentry DSN\n */\n private parseDSN(dsn: string): ParsedDSN {\n const match = dsn.match(/^(https?):\\/\\/([^@]+)@([^/]+)\\/(.+)$/);\n if (!match || !match[1] || !match[2] || !match[3] || !match[4]) {\n throw new Error(`Invalid Sentry DSN: ${dsn}`);\n }\n return {\n protocol: match[1],\n publicKey: match[2],\n host: match[3],\n projectId: match[4],\n };\n }\n\n /**\n * LogTransport implementation\n * Only sends ERROR and FATAL level logs\n */\n log(entry: LogEntry): void {\n if (this.options.enabled === false) return;\n\n // Only capture errors\n if (entry.levelValue < 50) return; // ERROR = 50, FATAL = 60\n\n if (entry.error) {\n const error = new Error(entry.error.message);\n error.name = entry.error.name;\n if (entry.error.stack) {\n error.stack = entry.error.stack;\n }\n\n this.captureException(\n error,\n entry.context ? { extra: entry.context } : undefined\n );\n } else {\n this.captureMessage(\n entry.message,\n entry.level === \"FATAL\" ? \"error\" : \"warning\",\n entry.context ? { extra: entry.context } : undefined\n );\n }\n }\n\n /**\n * Capture an exception\n */\n captureException(error: Error, context?: ErrorContext): void {\n if (this.options.enabled === false) return;\n if (!this.shouldSample()) return;\n\n if (this.client) {\n this.captureWithSdk(error, context);\n } else {\n this.captureWithHttp(error, context);\n }\n }\n\n /**\n * Capture a message\n */\n captureMessage(\n message: string,\n level: \"info\" | \"warning\" | \"error\",\n context?: ErrorContext\n ): void {\n if (this.options.enabled === false) return;\n if (!this.shouldSample()) return;\n\n if (this.client) {\n this.client.withScope((scope) => {\n this.applyContext(scope, context);\n scope.setLevel(level);\n this.client!.captureMessage(message, level);\n });\n } else {\n this.sendHttpEvent({\n level: level === \"warning\" ? \"warning\" : level === \"info\" ? \"info\" : \"error\",\n message: { formatted: message },\n ...this.buildEventContext(context),\n });\n }\n }\n\n /**\n * Set user context\n */\n setUser(user: ErrorUser | null): void {\n this.user = user;\n }\n\n /**\n * Set custom context\n */\n setContext(name: string, context: Record<string, unknown>): void {\n this.contexts.set(name, context);\n }\n\n /**\n * Add breadcrumb\n */\n addBreadcrumb(breadcrumb: Breadcrumb): void {\n this.breadcrumbs.push({\n ...breadcrumb,\n timestamp: breadcrumb.timestamp ?? Date.now() / 1000,\n });\n\n // Keep only last N breadcrumbs\n if (this.breadcrumbs.length > this.maxBreadcrumbs) {\n this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);\n }\n }\n\n /**\n * Flush pending events\n */\n async flush(): Promise<void> {\n if (this.client?.flush) {\n await this.client.flush(2000);\n }\n }\n\n // ============================================================================\n // Private Methods\n // ============================================================================\n\n private shouldSample(): boolean {\n const rate = this.options.sampleRate ?? 1.0;\n return Math.random() < rate;\n }\n\n /**\n * Capture with SDK (BYOS mode)\n */\n private captureWithSdk(error: Error, context?: ErrorContext): void {\n this.client!.withScope((scope) => {\n this.applyContext(scope, context);\n this.client!.captureException(error);\n });\n }\n\n /**\n * Apply context to SDK scope\n */\n private applyContext(scope: SentryScope, context?: ErrorContext): void {\n // User\n if (this.user) {\n scope.setUser(this.user);\n } else if (context?.userId) {\n scope.setUser({ id: context.userId });\n }\n\n // Tags\n if (this.options.tags) {\n for (const [key, value] of Object.entries(this.options.tags)) {\n scope.setTag(key, value);\n }\n }\n if (context?.tags) {\n for (const [key, value] of Object.entries(context.tags)) {\n scope.setTag(key, value);\n }\n }\n if (context?.requestId) {\n scope.setTag(\"requestId\", context.requestId);\n }\n if (context?.tenantId) {\n scope.setTag(\"tenantId\", context.tenantId);\n }\n\n // Extra\n if (context?.extra) {\n scope.setExtras(context.extra);\n }\n\n // Breadcrumbs\n for (const bc of this.breadcrumbs) {\n scope.addBreadcrumb(bc);\n }\n }\n\n /**\n * Capture with HTTP API (default mode)\n */\n private captureWithHttp(error: Error, context?: ErrorContext): void {\n const stacktrace = this.parseStackTrace(error.stack);\n const exceptionValue: {\n type: string;\n value: string;\n stacktrace?: { frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> };\n } = {\n type: error.name,\n value: error.message,\n };\n\n if (stacktrace) {\n exceptionValue.stacktrace = stacktrace;\n }\n\n const event: Partial<SentryEvent> = {\n level: \"error\",\n exception: {\n values: [exceptionValue],\n },\n ...this.buildEventContext(context),\n };\n\n this.sendHttpEvent(event);\n }\n\n /**\n * Build event context for HTTP API\n */\n private buildEventContext(context?: ErrorContext): Partial<SentryEvent> {\n const event: Partial<SentryEvent> = {};\n\n // Environment & Release\n if (this.options.environment) {\n event.environment = this.options.environment;\n }\n if (this.options.release) {\n event.release = this.options.release;\n }\n if (this.options.serverName) {\n event.server_name = this.options.serverName;\n }\n\n // Tags\n const tags: Record<string, string> = { ...this.options.tags };\n if (context?.tags) {\n Object.assign(tags, context.tags);\n }\n if (context?.requestId) {\n tags[\"requestId\"] = context.requestId;\n }\n if (context?.tenantId) {\n tags[\"tenantId\"] = context.tenantId;\n }\n if (Object.keys(tags).length > 0) {\n event.tags = tags;\n }\n\n // Extra\n if (context?.extra) {\n event.extra = context.extra;\n }\n\n // User\n if (this.user) {\n event.user = this.user;\n } else if (context?.userId) {\n event.user = { id: context.userId };\n }\n\n // Breadcrumbs\n if (this.breadcrumbs.length > 0) {\n event.breadcrumbs = this.breadcrumbs.map((bc) => {\n const crumb: {\n type?: string;\n category?: string;\n message?: string;\n data?: Record<string, unknown>;\n level?: string;\n timestamp?: number;\n } = {};\n if (bc.type) crumb.type = bc.type;\n if (bc.category) crumb.category = bc.category;\n if (bc.message) crumb.message = bc.message;\n if (bc.data) crumb.data = bc.data;\n if (bc.level) crumb.level = bc.level;\n if (bc.timestamp !== undefined) crumb.timestamp = bc.timestamp;\n return crumb;\n });\n }\n\n // Contexts\n if (this.contexts.size > 0) {\n event.contexts = Object.fromEntries(this.contexts);\n }\n\n return event;\n }\n\n /**\n * Parse error stack trace into Sentry format\n */\n private parseStackTrace(\n stack?: string\n ): { frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> } | undefined {\n if (!stack) return undefined;\n\n const lines = stack.split(\"\\n\").slice(1); // Skip first line (error message)\n const frames: Array<{ filename?: string; function?: string; lineno?: number; colno?: number }> = [];\n\n for (const line of lines) {\n // Parse V8 stack trace format\n const match = line.match(/^\\s*at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/);\n if (match && match[3] && match[4]) {\n const frame: { filename?: string; function?: string; lineno?: number; colno?: number } = {\n function: match[1] || \"<anonymous>\",\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n };\n if (match[2]) {\n frame.filename = match[2];\n }\n frames.push(frame);\n }\n }\n\n // Sentry expects oldest frame first\n frames.reverse();\n\n return frames.length > 0 ? { frames } : undefined;\n }\n\n /**\n * Generate event ID\n */\n private generateEventId(): string {\n // 32 character hex string\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n }\n\n /**\n * Send event via HTTP API\n */\n private async sendHttpEvent(eventData: Partial<SentryEvent>): Promise<void> {\n if (!this.dsn) return;\n\n const event: SentryEvent = {\n event_id: this.generateEventId(),\n timestamp: new Date().toISOString(),\n platform: \"javascript\",\n level: \"error\",\n ...eventData,\n };\n\n // Apply beforeSend hook\n if (this.options.beforeSend) {\n const result = this.options.beforeSend(event);\n if (result === null) return;\n }\n\n const url = `${this.dsn.protocol}://${this.dsn.host}/api/${this.dsn.projectId}/store/`;\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Sentry-Auth\": [\n \"Sentry sentry_version=7\",\n `sentry_client=pars-sentry/1.0.0`,\n `sentry_key=${this.dsn.publicKey}`,\n ].join(\", \"),\n },\n body: JSON.stringify(event),\n });\n\n if (!response.ok) {\n throw new Error(`Sentry API error: ${response.status}`);\n }\n } catch (error) {\n if (this.options.onError) {\n this.options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n // Silent fail - don't crash the app for logging failures\n }\n }\n}\n\n/**\n * Create a Sentry transport instance.\n *\n * @param options - Sentry transport configuration\n * @returns A new SentryTransport instance\n *\n * @example\n * ```typescript\n * const sentry = createSentryTransport({\n * dsn: 'https://xxx@sentry.io/123',\n * environment: 'production'\n * });\n * ```\n */\nexport function createSentryTransport(options: SentryTransportOptions): SentryTransport {\n return new SentryTransport(options);\n}\n","/**\n * @parsrun/core - Logtape Transport\n * Structured logging transport for Logtape (@logtape/logtape)\n *\n * Logtape is a TypeScript-first structured logging library.\n * This transport bridges Pars Logger to Logtape for advanced logging scenarios.\n *\n * @example BYOS (Bring Your Own SDK)\n * ```typescript\n * import { getLogger, configure } from '@logtape/logtape';\n *\n * // Configure Logtape\n * await configure({\n * sinks: { console: consoleSink() },\n * loggers: [{ category: 'pars', sinks: ['console'], level: 'info' }],\n * });\n *\n * const logtapeLogger = getLogger('pars');\n * const transport = new LogtapeTransport({ logger: logtapeLogger });\n * ```\n *\n * @example Simple mode (creates internal logger)\n * ```typescript\n * const transport = new LogtapeTransport({\n * category: 'my-app',\n * });\n * ```\n */\n\nimport type { LogEntry, LogLevelName } from \"../logger.js\";\nimport type { LogTransport, BaseTransportOptions } from \"./types.js\";\n\n/**\n * Logtape Logger interface (minimal interface for BYOS)\n * Compatible with @logtape/logtape getLogger() return type\n */\nexport interface LogtapeLogger {\n debug(message: string, properties?: Record<string, unknown>): void;\n info(message: string, properties?: Record<string, unknown>): void;\n warn(message: string, properties?: Record<string, unknown>): void;\n warning(message: string, properties?: Record<string, unknown>): void;\n error(message: string, properties?: Record<string, unknown>): void;\n fatal(message: string, properties?: Record<string, unknown>): void;\n}\n\n/**\n * Level mapping from Pars to Logtape\n */\ntype LogtapeLevel = \"debug\" | \"info\" | \"warning\" | \"error\" | \"fatal\";\n\n/**\n * Logtape transport options\n */\nexport interface LogtapeTransportOptions extends BaseTransportOptions {\n /**\n * Logtape logger instance (for BYOS mode)\n * Get this from @logtape/logtape's getLogger()\n */\n logger?: LogtapeLogger;\n\n /**\n * Category name for the logger\n * Only used if logger is not provided (creates a simple fallback logger)\n */\n category?: string;\n\n /**\n * Include timestamp in properties\n * @default true\n */\n includeTimestamp?: boolean;\n\n /**\n * Include level value in properties\n * @default false\n */\n includeLevelValue?: boolean;\n}\n\n/**\n * Simple fallback logger when no Logtape instance is provided\n * Just outputs structured JSON - users should use BYOS for full features\n */\nclass FallbackLogger implements LogtapeLogger {\n constructor(private category: string) {}\n\n private log(level: string, message: string, properties?: Record<string, unknown>): void {\n const entry = {\n level,\n category: this.category,\n msg: message,\n time: new Date().toISOString(),\n ...properties,\n };\n console.log(JSON.stringify(entry));\n }\n\n debug(message: string, properties?: Record<string, unknown>): void {\n this.log(\"debug\", message, properties);\n }\n\n info(message: string, properties?: Record<string, unknown>): void {\n this.log(\"info\", message, properties);\n }\n\n warn(message: string, properties?: Record<string, unknown>): void {\n this.log(\"warn\", message, properties);\n }\n\n warning(message: string, properties?: Record<string, unknown>): void {\n this.log(\"warning\", message, properties);\n }\n\n error(message: string, properties?: Record<string, unknown>): void {\n this.log(\"error\", message, properties);\n }\n\n fatal(message: string, properties?: Record<string, unknown>): void {\n this.log(\"fatal\", message, properties);\n }\n}\n\n/**\n * Logtape Transport\n * Bridges Pars Logger to Logtape\n */\nexport class LogtapeTransport implements LogTransport {\n readonly name = \"logtape\";\n\n private readonly logger: LogtapeLogger;\n private readonly includeTimestamp: boolean;\n private readonly includeLevelValue: boolean;\n private readonly enabled: boolean;\n\n constructor(options: LogtapeTransportOptions = {}) {\n this.enabled = options.enabled !== false;\n this.includeTimestamp = options.includeTimestamp !== false;\n this.includeLevelValue = options.includeLevelValue ?? false;\n\n if (options.logger) {\n this.logger = options.logger;\n } else {\n // Create fallback logger\n this.logger = new FallbackLogger(options.category ?? \"pars\");\n }\n }\n\n log(entry: LogEntry): void {\n if (!this.enabled) return;\n\n const level = this.mapLevel(entry.level);\n const properties = this.buildProperties(entry);\n\n // Call the appropriate log method\n this.logger[level](entry.message, properties);\n }\n\n /**\n * Map Pars log level to Logtape level\n */\n private mapLevel(level: LogLevelName): LogtapeLevel {\n const mapping: Record<LogLevelName, LogtapeLevel> = {\n TRACE: \"debug\",\n DEBUG: \"debug\",\n INFO: \"info\",\n WARN: \"warning\",\n ERROR: \"error\",\n FATAL: \"fatal\",\n SILENT: \"debug\", // Should never be logged\n };\n return mapping[level];\n }\n\n /**\n * Build properties object for Logtape\n */\n private buildProperties(entry: LogEntry): Record<string, unknown> {\n const properties: Record<string, unknown> = {};\n\n // Add timestamp if enabled\n if (this.includeTimestamp) {\n properties[\"timestamp\"] = entry.timestamp;\n }\n\n // Add level value if enabled\n if (this.includeLevelValue) {\n properties[\"levelValue\"] = entry.levelValue;\n }\n\n // Add context\n if (entry.context) {\n Object.assign(properties, entry.context);\n }\n\n // Add error info\n if (entry.error) {\n properties[\"error\"] = {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n };\n }\n\n return properties;\n }\n}\n\n/**\n * Create a Logtape transport instance.\n *\n * @param options - Logtape transport configuration\n * @returns A new LogtapeTransport instance\n *\n * @example\n * ```typescript\n * import { getLogger } from '@logtape/logtape';\n *\n * const transport = createLogtapeTransport({\n * logger: getLogger('my-app')\n * });\n * ```\n */\nexport function createLogtapeTransport(\n options?: LogtapeTransportOptions\n): LogtapeTransport {\n return new LogtapeTransport(options);\n}\n"],"mappings":"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","names":[]}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @parsrun/core - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core type definitions for multi-tenant applications including users,
|
|
5
|
+
* sessions, tenants, memberships, and common utility types.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Represents a tenant (organization/workspace) in a multi-tenant application.
|
|
9
|
+
* Each tenant has its own isolated data and can have multiple users.
|
|
3
10
|
*/
|
|
4
11
|
interface Tenant {
|
|
5
12
|
id: string;
|
|
@@ -11,7 +18,18 @@ interface Tenant {
|
|
|
11
18
|
createdAt: Date;
|
|
12
19
|
updatedAt: Date;
|
|
13
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Possible states for a tenant.
|
|
23
|
+
* - `active`: Tenant is fully operational
|
|
24
|
+
* - `suspended`: Tenant access is temporarily disabled
|
|
25
|
+
* - `pending`: Tenant is awaiting activation or verification
|
|
26
|
+
* - `deleted`: Tenant has been soft-deleted
|
|
27
|
+
*/
|
|
14
28
|
type TenantStatus = "active" | "suspended" | "pending" | "deleted";
|
|
29
|
+
/**
|
|
30
|
+
* Represents a user in the system.
|
|
31
|
+
* Users can belong to multiple tenants through memberships.
|
|
32
|
+
*/
|
|
15
33
|
interface User {
|
|
16
34
|
id: string;
|
|
17
35
|
email: string;
|
|
@@ -22,6 +40,10 @@ interface User {
|
|
|
22
40
|
createdAt: Date;
|
|
23
41
|
updatedAt: Date;
|
|
24
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Represents an authenticated user session.
|
|
45
|
+
* Sessions are scoped to a specific user and tenant.
|
|
46
|
+
*/
|
|
25
47
|
interface Session {
|
|
26
48
|
id: string;
|
|
27
49
|
userId: string;
|
|
@@ -31,6 +53,10 @@ interface Session {
|
|
|
31
53
|
userAgent?: string;
|
|
32
54
|
createdAt: Date;
|
|
33
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Represents a user's membership in a tenant.
|
|
58
|
+
* Contains role, permissions, and access restrictions.
|
|
59
|
+
*/
|
|
34
60
|
interface TenantMembership {
|
|
35
61
|
id: string;
|
|
36
62
|
userId: string;
|
|
@@ -49,39 +75,106 @@ interface TenantMembership {
|
|
|
49
75
|
createdAt: Date;
|
|
50
76
|
updatedAt: Date;
|
|
51
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Possible states for a tenant membership.
|
|
80
|
+
* - `pending`: Invitation sent, awaiting acceptance
|
|
81
|
+
* - `active`: Membership is active and user has access
|
|
82
|
+
* - `suspended`: Membership temporarily disabled
|
|
83
|
+
* - `expired`: Membership has passed its expiration date
|
|
84
|
+
* - `revoked`: Membership has been permanently revoked
|
|
85
|
+
*/
|
|
52
86
|
type MembershipStatus = "pending" | "active" | "suspended" | "expired" | "revoked";
|
|
87
|
+
/**
|
|
88
|
+
* Access level for a membership.
|
|
89
|
+
* - `full`: Complete access to all resources
|
|
90
|
+
* - `limited`: Restricted access based on permissions
|
|
91
|
+
* - `readonly`: Can view but not modify resources
|
|
92
|
+
* - `custom`: Custom permission set defined by permissions field
|
|
93
|
+
*/
|
|
53
94
|
type AccessLevel = "full" | "limited" | "readonly" | "custom";
|
|
95
|
+
/**
|
|
96
|
+
* Maps resources to allowed actions.
|
|
97
|
+
* Keys are resource names, values are arrays of permitted actions.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const permissions: MembershipPermissions = {
|
|
102
|
+
* users: ['read', 'create'],
|
|
103
|
+
* settings: ['read']
|
|
104
|
+
* };
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
54
107
|
interface MembershipPermissions {
|
|
55
108
|
[resource: string]: string[];
|
|
56
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Restricts access to specific resources within a tenant.
|
|
112
|
+
* Can limit access by location, department, project, or custom dimensions.
|
|
113
|
+
*/
|
|
57
114
|
interface ResourceRestrictions {
|
|
115
|
+
/** Allowed location IDs */
|
|
58
116
|
locations?: string[];
|
|
117
|
+
/** Allowed department IDs */
|
|
59
118
|
departments?: string[];
|
|
119
|
+
/** Allowed project IDs */
|
|
60
120
|
projects?: string[];
|
|
121
|
+
/** Custom resource restrictions */
|
|
61
122
|
[key: string]: string[] | undefined;
|
|
62
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* IP-based access restrictions for a membership.
|
|
126
|
+
* Can whitelist or blacklist specific IPs or CIDR ranges.
|
|
127
|
+
*/
|
|
63
128
|
interface IpRestrictions {
|
|
129
|
+
/** Specific IP addresses that are allowed */
|
|
64
130
|
allowedIps?: string[];
|
|
131
|
+
/** CIDR ranges that are allowed (e.g., "192.168.1.0/24") */
|
|
65
132
|
allowedCidrs?: string[];
|
|
133
|
+
/** Specific IP addresses that are denied */
|
|
66
134
|
deniedIps?: string[];
|
|
67
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Time-based access restrictions for a membership.
|
|
138
|
+
* Can limit access to specific days and hours.
|
|
139
|
+
*/
|
|
68
140
|
interface TimeRestrictions {
|
|
141
|
+
/** Timezone for time calculations (e.g., "America/New_York") */
|
|
69
142
|
timezone?: string;
|
|
143
|
+
/** Days of the week when access is allowed (0-6, Sunday = 0) */
|
|
70
144
|
allowedDays?: number[];
|
|
145
|
+
/** Hour when access begins (0-23) */
|
|
71
146
|
allowedHoursStart?: number;
|
|
147
|
+
/** Hour when access ends (0-23) */
|
|
72
148
|
allowedHoursEnd?: number;
|
|
73
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Complete authentication context for a request.
|
|
152
|
+
* Contains the authenticated user, their session, tenant, and membership.
|
|
153
|
+
*/
|
|
74
154
|
interface AuthContext {
|
|
75
155
|
user: User;
|
|
76
156
|
session: Session;
|
|
77
157
|
tenant: Tenant;
|
|
78
158
|
membership: TenantMembership;
|
|
79
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Parameters for paginated queries.
|
|
162
|
+
* Supports both offset-based and cursor-based pagination.
|
|
163
|
+
*/
|
|
80
164
|
interface PaginationParams {
|
|
165
|
+
/** Page number (1-indexed) for offset pagination */
|
|
81
166
|
page?: number;
|
|
167
|
+
/** Number of items per page */
|
|
82
168
|
limit?: number;
|
|
169
|
+
/** Cursor for cursor-based pagination */
|
|
83
170
|
cursor?: string;
|
|
84
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Result of a paginated query.
|
|
174
|
+
* Contains the data array and pagination metadata.
|
|
175
|
+
*
|
|
176
|
+
* @typeParam T - The type of items in the result
|
|
177
|
+
*/
|
|
85
178
|
interface PaginatedResult<T> {
|
|
86
179
|
data: T[];
|
|
87
180
|
pagination: {
|
|
@@ -94,6 +187,28 @@ interface PaginatedResult<T> {
|
|
|
94
187
|
nextCursor?: string;
|
|
95
188
|
};
|
|
96
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* A discriminated union type representing either success or failure.
|
|
192
|
+
* Use pattern matching to safely handle both cases.
|
|
193
|
+
*
|
|
194
|
+
* @typeParam T - The success value type
|
|
195
|
+
* @typeParam E - The error type (defaults to Error)
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* function divide(a: number, b: number): Result<number, string> {
|
|
200
|
+
* if (b === 0) return err('Division by zero');
|
|
201
|
+
* return ok(a / b);
|
|
202
|
+
* }
|
|
203
|
+
*
|
|
204
|
+
* const result = divide(10, 2);
|
|
205
|
+
* if (result.success) {
|
|
206
|
+
* console.log(result.data); // 5
|
|
207
|
+
* } else {
|
|
208
|
+
* console.error(result.error);
|
|
209
|
+
* }
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
97
212
|
type Result<T, E = Error> = {
|
|
98
213
|
success: true;
|
|
99
214
|
data: T;
|
|
@@ -101,14 +216,75 @@ type Result<T, E = Error> = {
|
|
|
101
216
|
success: false;
|
|
102
217
|
error: E;
|
|
103
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* Create a successful Result.
|
|
221
|
+
*
|
|
222
|
+
* @param data - The success value
|
|
223
|
+
* @returns A Result indicating success with the provided data
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* return ok({ id: 1, name: 'John' });
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
104
230
|
declare function ok<T>(data: T): Result<T, never>;
|
|
231
|
+
/**
|
|
232
|
+
* Create a failed Result.
|
|
233
|
+
*
|
|
234
|
+
* @param error - The error value
|
|
235
|
+
* @returns A Result indicating failure with the provided error
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* return err(new Error('User not found'));
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
105
242
|
declare function err<E>(error: E): Result<never, E>;
|
|
243
|
+
/**
|
|
244
|
+
* Flattens complex intersection types for better IDE display.
|
|
245
|
+
* Use this to make hover information more readable.
|
|
246
|
+
*
|
|
247
|
+
* @typeParam T - The type to prettify
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* type Ugly = { a: string } & { b: number };
|
|
252
|
+
* type Pretty = Prettify<Ugly>; // { a: string; b: number }
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
106
255
|
type Prettify<T> = {
|
|
107
256
|
[K in keyof T]: T[K];
|
|
108
257
|
} & {};
|
|
258
|
+
/**
|
|
259
|
+
* Makes all properties in T optional recursively.
|
|
260
|
+
* Useful for partial updates or patch operations.
|
|
261
|
+
*
|
|
262
|
+
* @typeParam T - The type to make deeply partial
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* interface User { name: string; address: { city: string } }
|
|
267
|
+
* type PartialUser = DeepPartial<User>;
|
|
268
|
+
* // { name?: string; address?: { city?: string } }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
109
271
|
type DeepPartial<T> = {
|
|
110
272
|
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
111
273
|
};
|
|
274
|
+
/**
|
|
275
|
+
* Requires at least one of the specified keys to be present.
|
|
276
|
+
* Useful for APIs that need at least one identifier.
|
|
277
|
+
*
|
|
278
|
+
* @typeParam T - The base type
|
|
279
|
+
* @typeParam Keys - The keys of which at least one must be present
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* interface Query { id?: string; email?: string; name?: string }
|
|
284
|
+
* type UserQuery = RequireAtLeastOne<Query, 'id' | 'email'>;
|
|
285
|
+
* // Must have either id or email (or both)
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
112
288
|
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
|
|
113
289
|
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
|
|
114
290
|
}[Keys];
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * @parsrun/core - Type Definitions\n */\n\n// ============================================\n// TENANT TYPES\n// ============================================\n\nexport interface Tenant {\n id: string;\n name: string;\n slug: string;\n status: TenantStatus;\n plan?: string;\n settings?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport type TenantStatus = \"active\" | \"suspended\" | \"pending\" | \"deleted\";\n\n// ============================================\n// USER TYPES\n// ============================================\n\nexport interface User {\n id: string;\n email: string;\n emailVerified: boolean;\n name?: string;\n avatarUrl?: string;\n twoFactorEnabled: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Session {\n id: string;\n userId: string;\n tenantId: string;\n expiresAt: Date;\n ipAddress?: string;\n userAgent?: string;\n createdAt: Date;\n}\n\n// ============================================\n// MEMBERSHIP TYPES\n// ============================================\n\nexport interface TenantMembership {\n id: string;\n userId: string;\n tenantId: string;\n roleId?: string;\n permissions: MembershipPermissions;\n accessLevel: AccessLevel;\n resourceRestrictions: ResourceRestrictions;\n ipRestrictions?: IpRestrictions;\n timeRestrictions?: TimeRestrictions;\n status: MembershipStatus;\n expiresAt?: Date;\n invitedBy?: string;\n invitedAt?: Date;\n joinedAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport type MembershipStatus = \"pending\" | \"active\" | \"suspended\" | \"expired\" | \"revoked\";\n\nexport type AccessLevel = \"full\" | \"limited\" | \"readonly\" | \"custom\";\n\nexport interface MembershipPermissions {\n [resource: string]: string[];\n}\n\nexport interface ResourceRestrictions {\n locations?: string[];\n departments?: string[];\n projects?: string[];\n [key: string]: string[] | undefined;\n}\n\nexport interface IpRestrictions {\n allowedIps?: string[];\n allowedCidrs?: string[];\n deniedIps?: string[];\n}\n\nexport interface TimeRestrictions {\n timezone?: string;\n
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * @parsrun/core - Type Definitions\n *\n * Core type definitions for multi-tenant applications including users,\n * sessions, tenants, memberships, and common utility types.\n */\n\n// ============================================\n// TENANT TYPES\n// ============================================\n\n/**\n * Represents a tenant (organization/workspace) in a multi-tenant application.\n * Each tenant has its own isolated data and can have multiple users.\n */\nexport interface Tenant {\n id: string;\n name: string;\n slug: string;\n status: TenantStatus;\n plan?: string;\n settings?: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/**\n * Possible states for a tenant.\n * - `active`: Tenant is fully operational\n * - `suspended`: Tenant access is temporarily disabled\n * - `pending`: Tenant is awaiting activation or verification\n * - `deleted`: Tenant has been soft-deleted\n */\nexport type TenantStatus = \"active\" | \"suspended\" | \"pending\" | \"deleted\";\n\n// ============================================\n// USER TYPES\n// ============================================\n\n/**\n * Represents a user in the system.\n * Users can belong to multiple tenants through memberships.\n */\nexport interface User {\n id: string;\n email: string;\n emailVerified: boolean;\n name?: string;\n avatarUrl?: string;\n twoFactorEnabled: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/**\n * Represents an authenticated user session.\n * Sessions are scoped to a specific user and tenant.\n */\nexport interface Session {\n id: string;\n userId: string;\n tenantId: string;\n expiresAt: Date;\n ipAddress?: string;\n userAgent?: string;\n createdAt: Date;\n}\n\n// ============================================\n// MEMBERSHIP TYPES\n// ============================================\n\n/**\n * Represents a user's membership in a tenant.\n * Contains role, permissions, and access restrictions.\n */\nexport interface TenantMembership {\n id: string;\n userId: string;\n tenantId: string;\n roleId?: string;\n permissions: MembershipPermissions;\n accessLevel: AccessLevel;\n resourceRestrictions: ResourceRestrictions;\n ipRestrictions?: IpRestrictions;\n timeRestrictions?: TimeRestrictions;\n status: MembershipStatus;\n expiresAt?: Date;\n invitedBy?: string;\n invitedAt?: Date;\n joinedAt?: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/**\n * Possible states for a tenant membership.\n * - `pending`: Invitation sent, awaiting acceptance\n * - `active`: Membership is active and user has access\n * - `suspended`: Membership temporarily disabled\n * - `expired`: Membership has passed its expiration date\n * - `revoked`: Membership has been permanently revoked\n */\nexport type MembershipStatus = \"pending\" | \"active\" | \"suspended\" | \"expired\" | \"revoked\";\n\n/**\n * Access level for a membership.\n * - `full`: Complete access to all resources\n * - `limited`: Restricted access based on permissions\n * - `readonly`: Can view but not modify resources\n * - `custom`: Custom permission set defined by permissions field\n */\nexport type AccessLevel = \"full\" | \"limited\" | \"readonly\" | \"custom\";\n\n/**\n * Maps resources to allowed actions.\n * Keys are resource names, values are arrays of permitted actions.\n *\n * @example\n * ```typescript\n * const permissions: MembershipPermissions = {\n * users: ['read', 'create'],\n * settings: ['read']\n * };\n * ```\n */\nexport interface MembershipPermissions {\n [resource: string]: string[];\n}\n\n/**\n * Restricts access to specific resources within a tenant.\n * Can limit access by location, department, project, or custom dimensions.\n */\nexport interface ResourceRestrictions {\n /** Allowed location IDs */\n locations?: string[];\n /** Allowed department IDs */\n departments?: string[];\n /** Allowed project IDs */\n projects?: string[];\n /** Custom resource restrictions */\n [key: string]: string[] | undefined;\n}\n\n/**\n * IP-based access restrictions for a membership.\n * Can whitelist or blacklist specific IPs or CIDR ranges.\n */\nexport interface IpRestrictions {\n /** Specific IP addresses that are allowed */\n allowedIps?: string[];\n /** CIDR ranges that are allowed (e.g., \"192.168.1.0/24\") */\n allowedCidrs?: string[];\n /** Specific IP addresses that are denied */\n deniedIps?: string[];\n}\n\n/**\n * Time-based access restrictions for a membership.\n * Can limit access to specific days and hours.\n */\nexport interface TimeRestrictions {\n /** Timezone for time calculations (e.g., \"America/New_York\") */\n timezone?: string;\n /** Days of the week when access is allowed (0-6, Sunday = 0) */\n allowedDays?: number[];\n /** Hour when access begins (0-23) */\n allowedHoursStart?: number;\n /** Hour when access ends (0-23) */\n allowedHoursEnd?: number;\n}\n\n// ============================================\n// AUTH CONTEXT\n// ============================================\n\n/**\n * Complete authentication context for a request.\n * Contains the authenticated user, their session, tenant, and membership.\n */\nexport interface AuthContext {\n user: User;\n session: Session;\n tenant: Tenant;\n membership: TenantMembership;\n}\n\n// ============================================\n// PAGINATION\n// ============================================\n\n/**\n * Parameters for paginated queries.\n * Supports both offset-based and cursor-based pagination.\n */\nexport interface PaginationParams {\n /** Page number (1-indexed) for offset pagination */\n page?: number;\n /** Number of items per page */\n limit?: number;\n /** Cursor for cursor-based pagination */\n cursor?: string;\n}\n\n/**\n * Result of a paginated query.\n * Contains the data array and pagination metadata.\n *\n * @typeParam T - The type of items in the result\n */\nexport interface PaginatedResult<T> {\n data: T[];\n pagination: {\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n hasNext: boolean;\n hasPrev: boolean;\n nextCursor?: string;\n };\n}\n\n// ============================================\n// RESULT TYPES\n// ============================================\n\n/**\n * A discriminated union type representing either success or failure.\n * Use pattern matching to safely handle both cases.\n *\n * @typeParam T - The success value type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * ```typescript\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) return err('Division by zero');\n * return ok(a / b);\n * }\n *\n * const result = divide(10, 2);\n * if (result.success) {\n * console.log(result.data); // 5\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport type Result<T, E = Error> =\n | { success: true; data: T }\n | { success: false; error: E };\n\n/**\n * Create a successful Result.\n *\n * @param data - The success value\n * @returns A Result indicating success with the provided data\n *\n * @example\n * ```typescript\n * return ok({ id: 1, name: 'John' });\n * ```\n */\nexport function ok<T>(data: T): Result<T, never> {\n return { success: true, data };\n}\n\n/**\n * Create a failed Result.\n *\n * @param error - The error value\n * @returns A Result indicating failure with the provided error\n *\n * @example\n * ```typescript\n * return err(new Error('User not found'));\n * ```\n */\nexport function err<E>(error: E): Result<never, E> {\n return { success: false, error };\n}\n\n// ============================================\n// UTILITY TYPES\n// ============================================\n\n/**\n * Flattens complex intersection types for better IDE display.\n * Use this to make hover information more readable.\n *\n * @typeParam T - The type to prettify\n *\n * @example\n * ```typescript\n * type Ugly = { a: string } & { b: number };\n * type Pretty = Prettify<Ugly>; // { a: string; b: number }\n * ```\n */\nexport type Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\n/**\n * Makes all properties in T optional recursively.\n * Useful for partial updates or patch operations.\n *\n * @typeParam T - The type to make deeply partial\n *\n * @example\n * ```typescript\n * interface User { name: string; address: { city: string } }\n * type PartialUser = DeepPartial<User>;\n * // { name?: string; address?: { city?: string } }\n * ```\n */\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n/**\n * Requires at least one of the specified keys to be present.\n * Useful for APIs that need at least one identifier.\n *\n * @typeParam T - The base type\n * @typeParam Keys - The keys of which at least one must be present\n *\n * @example\n * ```typescript\n * interface Query { id?: string; email?: string; name?: string }\n * type UserQuery = RequireAtLeastOne<Query, 'id' | 'email'>;\n * // Must have either id or email (or both)\n * ```\n */\nexport type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<\n T,\n Exclude<keyof T, Keys>\n> &\n {\n [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;\n }[Keys];\n"],"mappings":";AAyQO,SAAS,GAAM,MAA2B;AAC/C,SAAO,EAAE,SAAS,MAAM,KAAK;AAC/B;AAaO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,SAAS,OAAO,MAAM;AACjC;","names":[]}
|