@tillstack/electron 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scrub.ts","../src/index.ts"],"names":["path","fs","app","crashReporter","release","os"],"mappings":"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","file":"index.js","sourcesContent":["// On-device PII scrubbing — mirror of the browser/RN/server scrub. Runs over\n// every event before it is queued, so secrets never leave the machine.\n\nconst SCRUB_RULES: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /\\bAKIA[0-9A-Z]{16}\\b/g, replacement: '[aws_key]' },\n { pattern: /(?:aws_secret|secret_access_key|AWS_SECRET)[^A-Za-z0-9+/]*([A-Za-z0-9+/]{40})\\b/gi, replacement: '[aws_secret]' },\n { pattern: /\\bAIza[0-9A-Za-z_-]{35}\\b/g, replacement: '[gcp_key]' },\n { pattern: /\\b(?:ghp|gho|ghs|ghr)_[A-Za-z0-9]{36,}\\b/g, replacement: '[github_token]' },\n { pattern: /\\bgithub_pat_[A-Za-z0-9_]{82}\\b/g, replacement: '[github_token]' },\n { pattern: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { pattern: /\\bxapp-\\d-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { pattern: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/g, replacement: '[jwt]' },\n { pattern: /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/gi, replacement: 'Bearer [token]' },\n { pattern: /\\b(?:sk|pk|ak|rk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g, replacement: '[apikey]' },\n { pattern: /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g, replacement: '[email]' },\n { pattern: /\\b[A-Z0-9]{10}\\b(?=\\s*confirmed|\\s*transaction|\\s*M-?Pesa)/gi, replacement: '[mpesa_tx]' },\n { pattern: /\\b(?:\\d[ -]*?){13,19}\\b/g, replacement: '[card]' },\n { pattern: /\\b\\d{13}\\b/g, replacement: '[id_number]' },\n { pattern: /\\b\\d{11}\\b/g, replacement: '[id_number]' },\n { pattern: /\\+?\\d{1,3}[\\s.-]?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}\\b/g, replacement: '[phone]' },\n { pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g, replacement: '[ip]' },\n]\n\nconst SENSITIVE_KEY =\n /^(?:password|passwd|pwd|secret|client[_-]?secret|token|access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|apikey|authorization|auth|session|cookie|set[_-]?cookie|private[_-]?key|credit[_-]?card|card[_-]?number|cvv|cvc|ssn|pin|otp|mfa|totp)$/i\n\nconst MAX_REDACT_DEPTH = 24\n\nfunction redactByKey(value: unknown, depth = 0): unknown {\n if (depth > MAX_REDACT_DEPTH || value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map((v) => redactByKey(v, depth + 1))\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = SENSITIVE_KEY.test(k) ? '[redacted]' : redactByKey(v, depth + 1)\n }\n return out\n}\n\nexport function scrubEvent<T>(event: T): T {\n try {\n let json = JSON.stringify(redactByKey(event))\n for (const { pattern, replacement } of SCRUB_RULES) json = json.replace(pattern, replacement)\n return JSON.parse(json) as T\n } catch {\n return event\n }\n}\n","import type { TillPulseEvent, Severity, DeviceContext, Breadcrumb, BreadcrumbType } from '@tillstack/types'\nimport { app, crashReporter } from 'electron'\nimport * as os from 'node:os'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { scrubEvent } from './scrub'\n\nconst SDK_NAME = '@tillstack/electron'\nconst SDK_VERSION = '0.1.0'\n\nexport interface TillPulseElectronOptions {\n dsn: string\n release: string\n environment?: string\n dist?: string\n /** Start Electron's native crashReporter (writes minidumps for C++/V8 crashes the JS layer can't catch). Default true. */\n enableNativeCrashReporter?: boolean\n /** Upload native minidumps to `<dsn-host>/api/v1/minidump`. Default false (dumps kept locally + a metadata event is sent). */\n uploadMinidumps?: boolean\n /** Capture renderer/GPU/child-process-gone as crashes. Default true. */\n enableProcessMonitoring?: boolean\n /** Treat renderer `unresponsive` as an app-hang event. Default true. */\n enableHangDetection?: boolean\n /** Emit session events for crash-free sessions. Default true. */\n enableSessionTracking?: boolean\n /** Opt in to TillShield desktop threat detection (DevTools-open on renderers). Off by default. */\n enableThreatDetection?: boolean\n disableDefaultPiiScrubbing?: boolean\n beforeSend?: (e: TillPulseEvent) => TillPulseEvent | null | Promise<TillPulseEvent | null>\n initialTags?: Record<string, string>\n flushIntervalMs?: number\n debug?: boolean\n}\n\n// ── DSN ──\nfunction parseDsn(dsn: string): { scheme: string; host: string } {\n const m = dsn.match(/^(https?):\\/\\/([^@]+)@([^/]+)\\/(.+)$/)\n if (!m) throw new Error(`TillPulse: invalid DSN: ${dsn}`)\n const scheme = m[1]!\n const host = m[3]!\n const local = /^(localhost|127\\.0\\.0\\.1|::1)(:\\d+)?$/i.test(host)\n if (scheme !== 'https' && !local) {\n throw new Error('TillPulse: DSN must use https:// (refusing to send telemetry over cleartext http)')\n }\n return { scheme, host }\n}\n\nfunction generateId(): string {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n}\n\n// ── File-backed queue (survives an app restart after a crash) ──\ninterface QItem { id: string; event_type: string; payload: string; attempts: number; next: number }\n\nclass FileQueue {\n private items: QItem[] = []\n private file: string\n private max = 500\n\n constructor(dir: string) {\n this.file = path.join(dir, 'tillpulse-queue.json')\n try {\n if (fs.existsSync(this.file)) {\n const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8')) as QItem[]\n if (Array.isArray(parsed)) this.items = parsed\n }\n } catch {\n /* corrupt — start clean */\n }\n }\n private persist(): void {\n try {\n fs.writeFileSync(this.file, JSON.stringify(this.items))\n } catch {\n /* non-fatal */\n }\n }\n enqueue(id: string, eventType: string, obj: unknown): void {\n let payload: string\n try {\n payload = JSON.stringify(obj)\n } catch {\n return\n }\n this.items.push({ id, event_type: eventType, payload, attempts: 0, next: 0 })\n if (this.items.length > this.max) this.items = this.items.slice(this.items.length - this.max)\n this.persist()\n }\n due(limit: number): QItem[] {\n const now = Date.now()\n return this.items.filter((i) => i.next <= now).slice(0, limit)\n }\n ackOk(ids: string[]): void {\n const s = new Set(ids)\n this.items = this.items.filter((i) => !s.has(i.id))\n this.persist()\n }\n ackFail(ids: string[]): void {\n const s = new Set(ids)\n const now = Date.now()\n for (const i of this.items) {\n if (!s.has(i.id)) continue\n i.attempts += 1\n i.next = now + Math.min(2000 * 2 ** (i.attempts - 1), 5 * 60_000)\n }\n this.items = this.items.filter((i) => i.attempts <= 10)\n this.persist()\n }\n size(): number {\n return this.items.length\n }\n}\n\nfunction osName(): DeviceContext['os_name'] {\n if (process.platform === 'darwin') return 'macOS'\n if (process.platform === 'win32') return 'Windows'\n return 'Linux'\n}\n\nclass TillPulseElectronSDK {\n private opts: TillPulseElectronOptions | null = null\n private queue: FileQueue | null = null\n private endpoint = ''\n private sessionId = generateId()\n private timer: ReturnType<typeof setInterval> | null = null\n private initialized = false\n private crumbs: Breadcrumb[] = []\n private tags: Record<string, string> = {}\n private user: { id?: string; segment?: string } | null = null\n private sessionStartedAt = 0\n private sessionEnded = false\n\n init(options: TillPulseElectronOptions): void {\n if (this.initialized) return\n let host: string\n let scheme: string\n try {\n ;({ host, scheme } = parseDsn(options.dsn))\n } catch (err) {\n if (options.debug) console.error('[TillPulse]', err)\n return\n }\n this.initialized = true\n this.opts = options\n this.endpoint = `${scheme}://${host}/api/v1/envelope`\n this.tags = { ...options.initialTags }\n\n let dir = '.'\n try {\n dir = app.getPath('userData')\n } catch {\n /* app not ready yet in some flows — cwd fallback */\n }\n this.queue = new FileQueue(dir)\n\n // 1. Native crash reporter — captures C++/V8 minidumps the JS layer can't.\n if (options.enableNativeCrashReporter !== false) {\n try {\n crashReporter.start({\n productName: (() => {\n try {\n return app.getName()\n } catch {\n return 'app'\n }\n })(),\n submitURL: options.uploadMinidumps ? `${scheme}://${host}/api/v1/minidump` : undefined,\n uploadToServer: options.uploadMinidumps === true,\n compress: true,\n globalExtra: { tp_release: options.release, tp_env: options.environment ?? 'production' },\n })\n } catch (err) {\n if (options.debug) console.warn('[TillPulse] crashReporter.start failed:', err)\n }\n }\n\n // 2. Main-process JS crashes.\n process.on('uncaughtException', (...args: unknown[]) => {\n const err = args[0]\n this._captureError(err, 'critical', 'uncaughtException')\n this._markSession('crashed')\n void this.flush(true)\n })\n process.on('unhandledRejection', (...args: unknown[]) => {\n this._captureError(args[0], 'high', 'unhandledRejection')\n })\n\n // 3. Renderer / GPU / child-process crashes + hangs.\n if (options.enableProcessMonitoring !== false) {\n app.on('render-process-gone', (...a: unknown[]) => {\n const details = (a[2] ?? a[1]) as { reason?: string; exitCode?: number } | undefined\n const reason = details?.reason ?? 'unknown'\n const crashed = reason === 'crashed' || reason === 'oom' || reason === 'abnormal-exit'\n this._captureSynthetic('RenderProcessGone', `Renderer process gone: ${reason}`, crashed ? 'critical' : 'medium', {\n reason,\n exit_code: details?.exitCode,\n })\n if (crashed) this._markSession('crashed')\n })\n app.on('child-process-gone', (...a: unknown[]) => {\n const d = a[1] as { type?: string; reason?: string; exitCode?: number } | undefined\n this._captureSynthetic('ChildProcessGone', `Child process (${d?.type}) gone: ${d?.reason}`, 'medium', {\n type: d?.type,\n reason: d?.reason,\n exit_code: d?.exitCode,\n })\n })\n }\n\n if (options.enableHangDetection !== false) {\n app.on('web-contents-created', (...a: unknown[]) => {\n const contents = a[1] as { on(ev: string, cb: (...x: unknown[]) => void): void } | undefined\n if (!contents || typeof contents.on !== 'function') return\n let hangStart = 0\n contents.on('unresponsive', () => {\n hangStart = Date.now()\n this.addBreadcrumb({ type: 'device', category: 'app.hang', level: 'warning', message: 'renderer unresponsive' })\n })\n contents.on('responsive', () => {\n const dur = hangStart ? Date.now() - hangStart : 0\n if (dur > 0) {\n this._captureSynthetic('AppHang', `Renderer was unresponsive for ${dur}ms`, 'medium', { duration_ms: dur })\n }\n hangStart = 0\n })\n })\n }\n\n // 3b. TillShield threat detection — opt-in. DevTools opening on a renderer\n // is a real tamper/inspection signal for a desktop app (e.g. someone poking\n // at your production build). Reported as a security event.\n if (options.enableThreatDetection) {\n app.on('web-contents-created', (...a: unknown[]) => {\n const contents = a[1] as { on(ev: string, cb: (...x: unknown[]) => void): void; getURL?(): string } | undefined\n if (!contents || typeof contents.on !== 'function') return\n contents.on('devtools-opened', () => {\n this.captureSecurityEvent({\n securityType: 'devtools_open',\n severity: 'low',\n confidence: 0.7,\n message: 'DevTools opened on a renderer',\n evidence: { url: typeof contents.getURL === 'function' ? contents.getURL() : undefined },\n })\n })\n })\n }\n\n // 4. Session lifecycle.\n if (options.enableSessionTracking !== false) {\n const start = () => this._startSession()\n try {\n if (app.isReady()) start()\n else void app.whenReady().then(start)\n } catch {\n start()\n }\n app.on('before-quit', () => {\n this._markSession('exited')\n void this.flush(true)\n })\n app.on('window-all-closed', () => this._markSession('exited'))\n }\n\n // 5. Flush loop.\n const flushMs = options.flushIntervalMs ?? 10_000\n this.timer = setInterval(() => void this.flush(), flushMs)\n if (this.timer.unref) this.timer.unref()\n\n if (options.debug) console.log('[TillPulse] electron SDK initialized', { release: options.release })\n }\n\n // ── Public API ──\n captureException(error: unknown): void {\n this._captureError(error, 'high', 'captureException')\n }\n captureMessage(message: string, level: Severity = 'info'): void {\n this._enqueue(this._base('error', level, { exception: { type: 'Message', value: message } }))\n }\n\n // Report a security signal to TillShield (same shape as the other SDKs).\n captureSecurityEvent(input: {\n securityType: string\n severity?: Severity\n confidence?: number\n evidence?: Record<string, unknown>\n message?: string\n }): void {\n this._enqueue(\n this._base('security', input.severity ?? 'high', {\n security_type: input.securityType,\n security_confidence: input.confidence ?? 0.8,\n security_evidence: input.evidence ?? {},\n exception: { type: `security:${input.securityType}`, value: input.message ?? input.securityType },\n }),\n )\n }\n addBreadcrumb(input: { type: BreadcrumbType; category: string; message: string; level?: Breadcrumb['level']; data?: Record<string, unknown> }): void {\n this.crumbs.push({\n type: input.type,\n category: input.category,\n message: input.message,\n level: input.level ?? 'info',\n data: input.data,\n timestamp: new Date().toISOString(),\n })\n if (this.crumbs.length > 100) this.crumbs = this.crumbs.slice(this.crumbs.length - 100)\n }\n setUser(user: { id?: string; segment?: string } | null): void {\n this.user = user\n }\n setTag(key: string, value: string): void {\n this.tags[key] = value\n }\n getQueueStats(): { size: number } {\n return { size: this.queue?.size() ?? 0 }\n }\n\n async flush(useKeepalive = false): Promise<boolean> {\n void useKeepalive\n if (!this.queue || !this.opts) return false\n const items = this.queue.due(50)\n if (items.length === 0) return true\n const lines: string[] = [\n JSON.stringify({ dsn: this.opts.dsn, sdk: { name: SDK_NAME, version: SDK_VERSION }, sent_at: new Date().toISOString() }),\n ]\n const ids: string[] = []\n for (const it of items) {\n lines.push(JSON.stringify({ type: it.event_type, content_type: 'application/json', length: it.payload.length }))\n lines.push(it.payload)\n ids.push(it.id)\n }\n try {\n const res = await fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-sentry-envelope', Authorization: `DSN ${this.opts.dsn}` },\n body: lines.join('\\n'),\n })\n if (res.ok || (res.status >= 400 && res.status < 500 && res.status !== 429)) {\n this.queue.ackOk(ids)\n return true\n }\n this.queue.ackFail(ids)\n return false\n } catch {\n this.queue.ackFail(ids)\n return false\n }\n }\n\n close(): void {\n this._markSession('exited')\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n void this.flush(true)\n this.initialized = false\n }\n\n // ── Internals ──\n private _device(): DeviceContext {\n let total = 0\n let free = 0\n let release = ''\n try {\n total = os.totalmem()\n free = os.freemem()\n release = os.release()\n } catch {\n /* ignore */\n }\n return {\n device_model: `Electron ${process.versions['electron'] ?? ''}`.trim(),\n device_manufacturer: process.platform,\n device_family: 'desktop',\n os_name: osName(),\n os_version: release,\n cpu_architecture: process.arch,\n total_ram_mb: total ? Math.round(total / (1024 * 1024)) : undefined,\n available_ram_mb: free ? Math.round(free / (1024 * 1024)) : undefined,\n locale: process.env['LANG'],\n }\n }\n\n private _base(eventType: TillPulseEvent['event_type'], severity: Severity, extra: Partial<TillPulseEvent>): TillPulseEvent {\n const o = this.opts!\n const now = new Date().toISOString()\n const event: TillPulseEvent = {\n event_id: generateId(),\n project_id: '',\n event_type: eventType,\n severity,\n environment: o.environment ?? 'production',\n release_version: o.release,\n dist: o.dist,\n timestamp: now,\n client_timestamp: now,\n breadcrumbs: this.crumbs.slice(),\n device: this._device(),\n network: { network_type: 'unknown' },\n app_state: { app_foreground: true },\n user: { user_id: this.user?.id, user_segment: this.user?.segment, session_id: this.sessionId },\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n ...extra,\n }\n if (Object.keys(this.tags).length) {\n ;(event as TillPulseEvent & { tags?: Record<string, string> }).tags = { ...this.tags }\n }\n // Runtime versions are always useful desktop context.\n ;(event as TillPulseEvent & { extra?: Record<string, unknown> }).extra = {\n chrome: process.versions['chrome'],\n node: process.versions['node'],\n v8: process.versions['v8'],\n }\n return event\n }\n\n private _captureError(thrown: unknown, severity: Severity, mechanism: string): void {\n if (!this.opts) return\n const err = thrown instanceof Error ? thrown : null\n this._enqueue(\n this._base('error', severity, {\n exception: {\n type: err?.name ?? 'Error',\n value: err?.message ?? String(thrown),\n stacktrace: err?.stack ? { frames: parseNodeStack(err.stack) } : undefined,\n },\n fingerprint: `electron:${mechanism}`,\n }),\n )\n }\n\n private _captureSynthetic(type: string, message: string, severity: Severity, data?: Record<string, unknown>): void {\n if (!this.opts) return\n const ev = this._base('error', severity, { exception: { type, value: message } })\n ;(ev as TillPulseEvent & { extra?: Record<string, unknown> }).extra = {\n ...(ev as TillPulseEvent & { extra?: Record<string, unknown> }).extra,\n ...data,\n }\n this._enqueue(ev)\n }\n\n private _startSession(): void {\n this.sessionStartedAt = Date.now()\n this.sessionEnded = false\n this._emitSession('ok')\n }\n private _markSession(status: 'exited' | 'crashed' | 'errored'): void {\n if (this.sessionEnded || this.sessionStartedAt === 0) return\n this.sessionEnded = true\n this._emitSession(status, Date.now() - this.sessionStartedAt)\n }\n private _emitSession(status: 'ok' | 'exited' | 'crashed' | 'errored', durationMs?: number): void {\n if (!this.opts) return\n this._enqueue(this._base('session', 'info', { session_status: status, ...(durationMs !== undefined ? { session_duration_ms: durationMs } : {}) }))\n }\n\n private async _enqueue(event: TillPulseEvent): Promise<void> {\n const o = this.opts\n if (!o || !this.queue) return\n let final: TillPulseEvent | null = event\n if (o.beforeSend) {\n try {\n final = await o.beforeSend(event)\n } catch {\n /* ignore */\n }\n }\n if (!final) return\n if (!o.disableDefaultPiiScrubbing) final = scrubEvent(final)\n this.queue.enqueue(final.event_id, final.event_type, final)\n }\n}\n\n// Minimal V8 stack parser: \" at fn (/abs/file.js:12:34)\" / \" at /abs/file.js:12:34\".\nfunction parseNodeStack(stack: string): Array<{ filename: string; function: string; lineno?: number; colno?: number; in_app: boolean }> {\n const frames: Array<{ filename: string; function: string; lineno?: number; colno?: number; in_app: boolean }> = []\n for (const raw of stack.split('\\n')) {\n const line = raw.trim()\n const m = line.match(/^at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/) || line.match(/^at\\s+()(.+?):(\\d+):(\\d+)$/)\n if (!m) continue\n const file = m[2]!\n frames.push({\n filename: file,\n function: m[1] || '<anonymous>',\n lineno: Number(m[3]),\n colno: Number(m[4]),\n in_app: !file.includes('node_modules') && !file.startsWith('node:'),\n })\n }\n return frames\n}\n\nexport const TillPulse = new TillPulseElectronSDK()\nexport default TillPulse\nexport type { TillPulseEvent } from '@tillstack/types'\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,465 @@
1
+ import { app, crashReporter } from 'electron';
2
+ import * as os from 'os';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+
6
+ // src/index.ts
7
+
8
+ // src/scrub.ts
9
+ var SCRUB_RULES = [
10
+ { pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[aws_key]" },
11
+ { pattern: /(?:aws_secret|secret_access_key|AWS_SECRET)[^A-Za-z0-9+/]*([A-Za-z0-9+/]{40})\b/gi, replacement: "[aws_secret]" },
12
+ { pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g, replacement: "[gcp_key]" },
13
+ { pattern: /\b(?:ghp|gho|ghs|ghr)_[A-Za-z0-9]{36,}\b/g, replacement: "[github_token]" },
14
+ { pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g, replacement: "[github_token]" },
15
+ { pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, replacement: "[slack_token]" },
16
+ { pattern: /\bxapp-\d-[A-Za-z0-9-]{10,}\b/g, replacement: "[slack_token]" },
17
+ { pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, replacement: "[jwt]" },
18
+ { pattern: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, replacement: "Bearer [token]" },
19
+ { pattern: /\b(?:sk|pk|ak|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replacement: "[apikey]" },
20
+ { pattern: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g, replacement: "[email]" },
21
+ { pattern: /\b[A-Z0-9]{10}\b(?=\s*confirmed|\s*transaction|\s*M-?Pesa)/gi, replacement: "[mpesa_tx]" },
22
+ { pattern: /\b(?:\d[ -]*?){13,19}\b/g, replacement: "[card]" },
23
+ { pattern: /\b\d{13}\b/g, replacement: "[id_number]" },
24
+ { pattern: /\b\d{11}\b/g, replacement: "[id_number]" },
25
+ { pattern: /\+?\d{1,3}[\s.-]?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{3,4}\b/g, replacement: "[phone]" },
26
+ { pattern: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g, replacement: "[ip]" }
27
+ ];
28
+ var SENSITIVE_KEY = /^(?:password|passwd|pwd|secret|client[_-]?secret|token|access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|apikey|authorization|auth|session|cookie|set[_-]?cookie|private[_-]?key|credit[_-]?card|card[_-]?number|cvv|cvc|ssn|pin|otp|mfa|totp)$/i;
29
+ var MAX_REDACT_DEPTH = 24;
30
+ function redactByKey(value, depth = 0) {
31
+ if (depth > MAX_REDACT_DEPTH || value === null || typeof value !== "object") return value;
32
+ if (Array.isArray(value)) return value.map((v) => redactByKey(v, depth + 1));
33
+ const out = {};
34
+ for (const [k, v] of Object.entries(value)) {
35
+ out[k] = SENSITIVE_KEY.test(k) ? "[redacted]" : redactByKey(v, depth + 1);
36
+ }
37
+ return out;
38
+ }
39
+ function scrubEvent(event) {
40
+ try {
41
+ let json = JSON.stringify(redactByKey(event));
42
+ for (const { pattern, replacement } of SCRUB_RULES) json = json.replace(pattern, replacement);
43
+ return JSON.parse(json);
44
+ } catch {
45
+ return event;
46
+ }
47
+ }
48
+
49
+ // src/index.ts
50
+ var SDK_NAME = "@tillstack/electron";
51
+ var SDK_VERSION = "0.1.0";
52
+ function parseDsn(dsn) {
53
+ const m = dsn.match(/^(https?):\/\/([^@]+)@([^/]+)\/(.+)$/);
54
+ if (!m) throw new Error(`TillPulse: invalid DSN: ${dsn}`);
55
+ const scheme = m[1];
56
+ const host = m[3];
57
+ const local = /^(localhost|127\.0\.0\.1|::1)(:\d+)?$/i.test(host);
58
+ if (scheme !== "https" && !local) {
59
+ throw new Error("TillPulse: DSN must use https:// (refusing to send telemetry over cleartext http)");
60
+ }
61
+ return { scheme, host };
62
+ }
63
+ function generateId() {
64
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
65
+ }
66
+ var FileQueue = class {
67
+ constructor(dir) {
68
+ this.items = [];
69
+ this.max = 500;
70
+ this.file = path.join(dir, "tillpulse-queue.json");
71
+ try {
72
+ if (fs.existsSync(this.file)) {
73
+ const parsed = JSON.parse(fs.readFileSync(this.file, "utf8"));
74
+ if (Array.isArray(parsed)) this.items = parsed;
75
+ }
76
+ } catch {
77
+ }
78
+ }
79
+ persist() {
80
+ try {
81
+ fs.writeFileSync(this.file, JSON.stringify(this.items));
82
+ } catch {
83
+ }
84
+ }
85
+ enqueue(id, eventType, obj) {
86
+ let payload;
87
+ try {
88
+ payload = JSON.stringify(obj);
89
+ } catch {
90
+ return;
91
+ }
92
+ this.items.push({ id, event_type: eventType, payload, attempts: 0, next: 0 });
93
+ if (this.items.length > this.max) this.items = this.items.slice(this.items.length - this.max);
94
+ this.persist();
95
+ }
96
+ due(limit) {
97
+ const now = Date.now();
98
+ return this.items.filter((i) => i.next <= now).slice(0, limit);
99
+ }
100
+ ackOk(ids) {
101
+ const s = new Set(ids);
102
+ this.items = this.items.filter((i) => !s.has(i.id));
103
+ this.persist();
104
+ }
105
+ ackFail(ids) {
106
+ const s = new Set(ids);
107
+ const now = Date.now();
108
+ for (const i of this.items) {
109
+ if (!s.has(i.id)) continue;
110
+ i.attempts += 1;
111
+ i.next = now + Math.min(2e3 * 2 ** (i.attempts - 1), 5 * 6e4);
112
+ }
113
+ this.items = this.items.filter((i) => i.attempts <= 10);
114
+ this.persist();
115
+ }
116
+ size() {
117
+ return this.items.length;
118
+ }
119
+ };
120
+ function osName() {
121
+ if (process.platform === "darwin") return "macOS";
122
+ if (process.platform === "win32") return "Windows";
123
+ return "Linux";
124
+ }
125
+ var TillPulseElectronSDK = class {
126
+ constructor() {
127
+ this.opts = null;
128
+ this.queue = null;
129
+ this.endpoint = "";
130
+ this.sessionId = generateId();
131
+ this.timer = null;
132
+ this.initialized = false;
133
+ this.crumbs = [];
134
+ this.tags = {};
135
+ this.user = null;
136
+ this.sessionStartedAt = 0;
137
+ this.sessionEnded = false;
138
+ }
139
+ init(options) {
140
+ if (this.initialized) return;
141
+ let host;
142
+ let scheme;
143
+ try {
144
+ ;
145
+ ({ host, scheme } = parseDsn(options.dsn));
146
+ } catch (err) {
147
+ if (options.debug) console.error("[TillPulse]", err);
148
+ return;
149
+ }
150
+ this.initialized = true;
151
+ this.opts = options;
152
+ this.endpoint = `${scheme}://${host}/api/v1/envelope`;
153
+ this.tags = { ...options.initialTags };
154
+ let dir = ".";
155
+ try {
156
+ dir = app.getPath("userData");
157
+ } catch {
158
+ }
159
+ this.queue = new FileQueue(dir);
160
+ if (options.enableNativeCrashReporter !== false) {
161
+ try {
162
+ crashReporter.start({
163
+ productName: (() => {
164
+ try {
165
+ return app.getName();
166
+ } catch {
167
+ return "app";
168
+ }
169
+ })(),
170
+ submitURL: options.uploadMinidumps ? `${scheme}://${host}/api/v1/minidump` : void 0,
171
+ uploadToServer: options.uploadMinidumps === true,
172
+ compress: true,
173
+ globalExtra: { tp_release: options.release, tp_env: options.environment ?? "production" }
174
+ });
175
+ } catch (err) {
176
+ if (options.debug) console.warn("[TillPulse] crashReporter.start failed:", err);
177
+ }
178
+ }
179
+ process.on("uncaughtException", (...args) => {
180
+ const err = args[0];
181
+ this._captureError(err, "critical", "uncaughtException");
182
+ this._markSession("crashed");
183
+ void this.flush(true);
184
+ });
185
+ process.on("unhandledRejection", (...args) => {
186
+ this._captureError(args[0], "high", "unhandledRejection");
187
+ });
188
+ if (options.enableProcessMonitoring !== false) {
189
+ app.on("render-process-gone", (...a) => {
190
+ const details = a[2] ?? a[1];
191
+ const reason = details?.reason ?? "unknown";
192
+ const crashed = reason === "crashed" || reason === "oom" || reason === "abnormal-exit";
193
+ this._captureSynthetic("RenderProcessGone", `Renderer process gone: ${reason}`, crashed ? "critical" : "medium", {
194
+ reason,
195
+ exit_code: details?.exitCode
196
+ });
197
+ if (crashed) this._markSession("crashed");
198
+ });
199
+ app.on("child-process-gone", (...a) => {
200
+ const d = a[1];
201
+ this._captureSynthetic("ChildProcessGone", `Child process (${d?.type}) gone: ${d?.reason}`, "medium", {
202
+ type: d?.type,
203
+ reason: d?.reason,
204
+ exit_code: d?.exitCode
205
+ });
206
+ });
207
+ }
208
+ if (options.enableHangDetection !== false) {
209
+ app.on("web-contents-created", (...a) => {
210
+ const contents = a[1];
211
+ if (!contents || typeof contents.on !== "function") return;
212
+ let hangStart = 0;
213
+ contents.on("unresponsive", () => {
214
+ hangStart = Date.now();
215
+ this.addBreadcrumb({ type: "device", category: "app.hang", level: "warning", message: "renderer unresponsive" });
216
+ });
217
+ contents.on("responsive", () => {
218
+ const dur = hangStart ? Date.now() - hangStart : 0;
219
+ if (dur > 0) {
220
+ this._captureSynthetic("AppHang", `Renderer was unresponsive for ${dur}ms`, "medium", { duration_ms: dur });
221
+ }
222
+ hangStart = 0;
223
+ });
224
+ });
225
+ }
226
+ if (options.enableThreatDetection) {
227
+ app.on("web-contents-created", (...a) => {
228
+ const contents = a[1];
229
+ if (!contents || typeof contents.on !== "function") return;
230
+ contents.on("devtools-opened", () => {
231
+ this.captureSecurityEvent({
232
+ securityType: "devtools_open",
233
+ severity: "low",
234
+ confidence: 0.7,
235
+ message: "DevTools opened on a renderer",
236
+ evidence: { url: typeof contents.getURL === "function" ? contents.getURL() : void 0 }
237
+ });
238
+ });
239
+ });
240
+ }
241
+ if (options.enableSessionTracking !== false) {
242
+ const start = () => this._startSession();
243
+ try {
244
+ if (app.isReady()) start();
245
+ else void app.whenReady().then(start);
246
+ } catch {
247
+ start();
248
+ }
249
+ app.on("before-quit", () => {
250
+ this._markSession("exited");
251
+ void this.flush(true);
252
+ });
253
+ app.on("window-all-closed", () => this._markSession("exited"));
254
+ }
255
+ const flushMs = options.flushIntervalMs ?? 1e4;
256
+ this.timer = setInterval(() => void this.flush(), flushMs);
257
+ if (this.timer.unref) this.timer.unref();
258
+ if (options.debug) console.log("[TillPulse] electron SDK initialized", { release: options.release });
259
+ }
260
+ // ── Public API ──
261
+ captureException(error) {
262
+ this._captureError(error, "high", "captureException");
263
+ }
264
+ captureMessage(message, level = "info") {
265
+ this._enqueue(this._base("error", level, { exception: { type: "Message", value: message } }));
266
+ }
267
+ // Report a security signal to TillShield (same shape as the other SDKs).
268
+ captureSecurityEvent(input) {
269
+ this._enqueue(
270
+ this._base("security", input.severity ?? "high", {
271
+ security_type: input.securityType,
272
+ security_confidence: input.confidence ?? 0.8,
273
+ security_evidence: input.evidence ?? {},
274
+ exception: { type: `security:${input.securityType}`, value: input.message ?? input.securityType }
275
+ })
276
+ );
277
+ }
278
+ addBreadcrumb(input) {
279
+ this.crumbs.push({
280
+ type: input.type,
281
+ category: input.category,
282
+ message: input.message,
283
+ level: input.level ?? "info",
284
+ data: input.data,
285
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
286
+ });
287
+ if (this.crumbs.length > 100) this.crumbs = this.crumbs.slice(this.crumbs.length - 100);
288
+ }
289
+ setUser(user) {
290
+ this.user = user;
291
+ }
292
+ setTag(key, value) {
293
+ this.tags[key] = value;
294
+ }
295
+ getQueueStats() {
296
+ return { size: this.queue?.size() ?? 0 };
297
+ }
298
+ async flush(useKeepalive = false) {
299
+ if (!this.queue || !this.opts) return false;
300
+ const items = this.queue.due(50);
301
+ if (items.length === 0) return true;
302
+ const lines = [
303
+ JSON.stringify({ dsn: this.opts.dsn, sdk: { name: SDK_NAME, version: SDK_VERSION }, sent_at: (/* @__PURE__ */ new Date()).toISOString() })
304
+ ];
305
+ const ids = [];
306
+ for (const it of items) {
307
+ lines.push(JSON.stringify({ type: it.event_type, content_type: "application/json", length: it.payload.length }));
308
+ lines.push(it.payload);
309
+ ids.push(it.id);
310
+ }
311
+ try {
312
+ const res = await fetch(this.endpoint, {
313
+ method: "POST",
314
+ headers: { "Content-Type": "application/x-sentry-envelope", Authorization: `DSN ${this.opts.dsn}` },
315
+ body: lines.join("\n")
316
+ });
317
+ if (res.ok || res.status >= 400 && res.status < 500 && res.status !== 429) {
318
+ this.queue.ackOk(ids);
319
+ return true;
320
+ }
321
+ this.queue.ackFail(ids);
322
+ return false;
323
+ } catch {
324
+ this.queue.ackFail(ids);
325
+ return false;
326
+ }
327
+ }
328
+ close() {
329
+ this._markSession("exited");
330
+ if (this.timer) {
331
+ clearInterval(this.timer);
332
+ this.timer = null;
333
+ }
334
+ void this.flush(true);
335
+ this.initialized = false;
336
+ }
337
+ // ── Internals ──
338
+ _device() {
339
+ let total = 0;
340
+ let free = 0;
341
+ let release2 = "";
342
+ try {
343
+ total = os.totalmem();
344
+ free = os.freemem();
345
+ release2 = os.release();
346
+ } catch {
347
+ }
348
+ return {
349
+ device_model: `Electron ${process.versions["electron"] ?? ""}`.trim(),
350
+ device_manufacturer: process.platform,
351
+ device_family: "desktop",
352
+ os_name: osName(),
353
+ os_version: release2,
354
+ cpu_architecture: process.arch,
355
+ total_ram_mb: total ? Math.round(total / (1024 * 1024)) : void 0,
356
+ available_ram_mb: free ? Math.round(free / (1024 * 1024)) : void 0,
357
+ locale: process.env["LANG"]
358
+ };
359
+ }
360
+ _base(eventType, severity, extra) {
361
+ const o = this.opts;
362
+ const now = (/* @__PURE__ */ new Date()).toISOString();
363
+ const event = {
364
+ event_id: generateId(),
365
+ project_id: "",
366
+ event_type: eventType,
367
+ severity,
368
+ environment: o.environment ?? "production",
369
+ release_version: o.release,
370
+ dist: o.dist,
371
+ timestamp: now,
372
+ client_timestamp: now,
373
+ breadcrumbs: this.crumbs.slice(),
374
+ device: this._device(),
375
+ network: { network_type: "unknown" },
376
+ app_state: { app_foreground: true },
377
+ user: { user_id: this.user?.id, user_segment: this.user?.segment, session_id: this.sessionId },
378
+ sdk: { name: SDK_NAME, version: SDK_VERSION },
379
+ ...extra
380
+ };
381
+ if (Object.keys(this.tags).length) {
382
+ event.tags = { ...this.tags };
383
+ }
384
+ event.extra = {
385
+ chrome: process.versions["chrome"],
386
+ node: process.versions["node"],
387
+ v8: process.versions["v8"]
388
+ };
389
+ return event;
390
+ }
391
+ _captureError(thrown, severity, mechanism) {
392
+ if (!this.opts) return;
393
+ const err = thrown instanceof Error ? thrown : null;
394
+ this._enqueue(
395
+ this._base("error", severity, {
396
+ exception: {
397
+ type: err?.name ?? "Error",
398
+ value: err?.message ?? String(thrown),
399
+ stacktrace: err?.stack ? { frames: parseNodeStack(err.stack) } : void 0
400
+ },
401
+ fingerprint: `electron:${mechanism}`
402
+ })
403
+ );
404
+ }
405
+ _captureSynthetic(type, message, severity, data) {
406
+ if (!this.opts) return;
407
+ const ev = this._base("error", severity, { exception: { type, value: message } });
408
+ ev.extra = {
409
+ ...ev.extra,
410
+ ...data
411
+ };
412
+ this._enqueue(ev);
413
+ }
414
+ _startSession() {
415
+ this.sessionStartedAt = Date.now();
416
+ this.sessionEnded = false;
417
+ this._emitSession("ok");
418
+ }
419
+ _markSession(status) {
420
+ if (this.sessionEnded || this.sessionStartedAt === 0) return;
421
+ this.sessionEnded = true;
422
+ this._emitSession(status, Date.now() - this.sessionStartedAt);
423
+ }
424
+ _emitSession(status, durationMs) {
425
+ if (!this.opts) return;
426
+ this._enqueue(this._base("session", "info", { session_status: status, ...durationMs !== void 0 ? { session_duration_ms: durationMs } : {} }));
427
+ }
428
+ async _enqueue(event) {
429
+ const o = this.opts;
430
+ if (!o || !this.queue) return;
431
+ let final = event;
432
+ if (o.beforeSend) {
433
+ try {
434
+ final = await o.beforeSend(event);
435
+ } catch {
436
+ }
437
+ }
438
+ if (!final) return;
439
+ if (!o.disableDefaultPiiScrubbing) final = scrubEvent(final);
440
+ this.queue.enqueue(final.event_id, final.event_type, final);
441
+ }
442
+ };
443
+ function parseNodeStack(stack) {
444
+ const frames = [];
445
+ for (const raw of stack.split("\n")) {
446
+ const line = raw.trim();
447
+ const m = line.match(/^at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)$/) || line.match(/^at\s+()(.+?):(\d+):(\d+)$/);
448
+ if (!m) continue;
449
+ const file = m[2];
450
+ frames.push({
451
+ filename: file,
452
+ function: m[1] || "<anonymous>",
453
+ lineno: Number(m[3]),
454
+ colno: Number(m[4]),
455
+ in_app: !file.includes("node_modules") && !file.startsWith("node:")
456
+ });
457
+ }
458
+ return frames;
459
+ }
460
+ var TillPulse = new TillPulseElectronSDK();
461
+ var src_default = TillPulse;
462
+
463
+ export { TillPulse, src_default as default };
464
+ //# sourceMappingURL=index.mjs.map
465
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scrub.ts","../src/index.ts"],"names":["release"],"mappings":"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","file":"index.mjs","sourcesContent":["// On-device PII scrubbing — mirror of the browser/RN/server scrub. Runs over\n// every event before it is queued, so secrets never leave the machine.\n\nconst SCRUB_RULES: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /\\bAKIA[0-9A-Z]{16}\\b/g, replacement: '[aws_key]' },\n { pattern: /(?:aws_secret|secret_access_key|AWS_SECRET)[^A-Za-z0-9+/]*([A-Za-z0-9+/]{40})\\b/gi, replacement: '[aws_secret]' },\n { pattern: /\\bAIza[0-9A-Za-z_-]{35}\\b/g, replacement: '[gcp_key]' },\n { pattern: /\\b(?:ghp|gho|ghs|ghr)_[A-Za-z0-9]{36,}\\b/g, replacement: '[github_token]' },\n { pattern: /\\bgithub_pat_[A-Za-z0-9_]{82}\\b/g, replacement: '[github_token]' },\n { pattern: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { pattern: /\\bxapp-\\d-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { pattern: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/g, replacement: '[jwt]' },\n { pattern: /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/gi, replacement: 'Bearer [token]' },\n { pattern: /\\b(?:sk|pk|ak|rk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g, replacement: '[apikey]' },\n { pattern: /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g, replacement: '[email]' },\n { pattern: /\\b[A-Z0-9]{10}\\b(?=\\s*confirmed|\\s*transaction|\\s*M-?Pesa)/gi, replacement: '[mpesa_tx]' },\n { pattern: /\\b(?:\\d[ -]*?){13,19}\\b/g, replacement: '[card]' },\n { pattern: /\\b\\d{13}\\b/g, replacement: '[id_number]' },\n { pattern: /\\b\\d{11}\\b/g, replacement: '[id_number]' },\n { pattern: /\\+?\\d{1,3}[\\s.-]?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}\\b/g, replacement: '[phone]' },\n { pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g, replacement: '[ip]' },\n]\n\nconst SENSITIVE_KEY =\n /^(?:password|passwd|pwd|secret|client[_-]?secret|token|access[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|apikey|authorization|auth|session|cookie|set[_-]?cookie|private[_-]?key|credit[_-]?card|card[_-]?number|cvv|cvc|ssn|pin|otp|mfa|totp)$/i\n\nconst MAX_REDACT_DEPTH = 24\n\nfunction redactByKey(value: unknown, depth = 0): unknown {\n if (depth > MAX_REDACT_DEPTH || value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map((v) => redactByKey(v, depth + 1))\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = SENSITIVE_KEY.test(k) ? '[redacted]' : redactByKey(v, depth + 1)\n }\n return out\n}\n\nexport function scrubEvent<T>(event: T): T {\n try {\n let json = JSON.stringify(redactByKey(event))\n for (const { pattern, replacement } of SCRUB_RULES) json = json.replace(pattern, replacement)\n return JSON.parse(json) as T\n } catch {\n return event\n }\n}\n","import type { TillPulseEvent, Severity, DeviceContext, Breadcrumb, BreadcrumbType } from '@tillstack/types'\nimport { app, crashReporter } from 'electron'\nimport * as os from 'node:os'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { scrubEvent } from './scrub'\n\nconst SDK_NAME = '@tillstack/electron'\nconst SDK_VERSION = '0.1.0'\n\nexport interface TillPulseElectronOptions {\n dsn: string\n release: string\n environment?: string\n dist?: string\n /** Start Electron's native crashReporter (writes minidumps for C++/V8 crashes the JS layer can't catch). Default true. */\n enableNativeCrashReporter?: boolean\n /** Upload native minidumps to `<dsn-host>/api/v1/minidump`. Default false (dumps kept locally + a metadata event is sent). */\n uploadMinidumps?: boolean\n /** Capture renderer/GPU/child-process-gone as crashes. Default true. */\n enableProcessMonitoring?: boolean\n /** Treat renderer `unresponsive` as an app-hang event. Default true. */\n enableHangDetection?: boolean\n /** Emit session events for crash-free sessions. Default true. */\n enableSessionTracking?: boolean\n /** Opt in to TillShield desktop threat detection (DevTools-open on renderers). Off by default. */\n enableThreatDetection?: boolean\n disableDefaultPiiScrubbing?: boolean\n beforeSend?: (e: TillPulseEvent) => TillPulseEvent | null | Promise<TillPulseEvent | null>\n initialTags?: Record<string, string>\n flushIntervalMs?: number\n debug?: boolean\n}\n\n// ── DSN ──\nfunction parseDsn(dsn: string): { scheme: string; host: string } {\n const m = dsn.match(/^(https?):\\/\\/([^@]+)@([^/]+)\\/(.+)$/)\n if (!m) throw new Error(`TillPulse: invalid DSN: ${dsn}`)\n const scheme = m[1]!\n const host = m[3]!\n const local = /^(localhost|127\\.0\\.0\\.1|::1)(:\\d+)?$/i.test(host)\n if (scheme !== 'https' && !local) {\n throw new Error('TillPulse: DSN must use https:// (refusing to send telemetry over cleartext http)')\n }\n return { scheme, host }\n}\n\nfunction generateId(): string {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n}\n\n// ── File-backed queue (survives an app restart after a crash) ──\ninterface QItem { id: string; event_type: string; payload: string; attempts: number; next: number }\n\nclass FileQueue {\n private items: QItem[] = []\n private file: string\n private max = 500\n\n constructor(dir: string) {\n this.file = path.join(dir, 'tillpulse-queue.json')\n try {\n if (fs.existsSync(this.file)) {\n const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8')) as QItem[]\n if (Array.isArray(parsed)) this.items = parsed\n }\n } catch {\n /* corrupt — start clean */\n }\n }\n private persist(): void {\n try {\n fs.writeFileSync(this.file, JSON.stringify(this.items))\n } catch {\n /* non-fatal */\n }\n }\n enqueue(id: string, eventType: string, obj: unknown): void {\n let payload: string\n try {\n payload = JSON.stringify(obj)\n } catch {\n return\n }\n this.items.push({ id, event_type: eventType, payload, attempts: 0, next: 0 })\n if (this.items.length > this.max) this.items = this.items.slice(this.items.length - this.max)\n this.persist()\n }\n due(limit: number): QItem[] {\n const now = Date.now()\n return this.items.filter((i) => i.next <= now).slice(0, limit)\n }\n ackOk(ids: string[]): void {\n const s = new Set(ids)\n this.items = this.items.filter((i) => !s.has(i.id))\n this.persist()\n }\n ackFail(ids: string[]): void {\n const s = new Set(ids)\n const now = Date.now()\n for (const i of this.items) {\n if (!s.has(i.id)) continue\n i.attempts += 1\n i.next = now + Math.min(2000 * 2 ** (i.attempts - 1), 5 * 60_000)\n }\n this.items = this.items.filter((i) => i.attempts <= 10)\n this.persist()\n }\n size(): number {\n return this.items.length\n }\n}\n\nfunction osName(): DeviceContext['os_name'] {\n if (process.platform === 'darwin') return 'macOS'\n if (process.platform === 'win32') return 'Windows'\n return 'Linux'\n}\n\nclass TillPulseElectronSDK {\n private opts: TillPulseElectronOptions | null = null\n private queue: FileQueue | null = null\n private endpoint = ''\n private sessionId = generateId()\n private timer: ReturnType<typeof setInterval> | null = null\n private initialized = false\n private crumbs: Breadcrumb[] = []\n private tags: Record<string, string> = {}\n private user: { id?: string; segment?: string } | null = null\n private sessionStartedAt = 0\n private sessionEnded = false\n\n init(options: TillPulseElectronOptions): void {\n if (this.initialized) return\n let host: string\n let scheme: string\n try {\n ;({ host, scheme } = parseDsn(options.dsn))\n } catch (err) {\n if (options.debug) console.error('[TillPulse]', err)\n return\n }\n this.initialized = true\n this.opts = options\n this.endpoint = `${scheme}://${host}/api/v1/envelope`\n this.tags = { ...options.initialTags }\n\n let dir = '.'\n try {\n dir = app.getPath('userData')\n } catch {\n /* app not ready yet in some flows — cwd fallback */\n }\n this.queue = new FileQueue(dir)\n\n // 1. Native crash reporter — captures C++/V8 minidumps the JS layer can't.\n if (options.enableNativeCrashReporter !== false) {\n try {\n crashReporter.start({\n productName: (() => {\n try {\n return app.getName()\n } catch {\n return 'app'\n }\n })(),\n submitURL: options.uploadMinidumps ? `${scheme}://${host}/api/v1/minidump` : undefined,\n uploadToServer: options.uploadMinidumps === true,\n compress: true,\n globalExtra: { tp_release: options.release, tp_env: options.environment ?? 'production' },\n })\n } catch (err) {\n if (options.debug) console.warn('[TillPulse] crashReporter.start failed:', err)\n }\n }\n\n // 2. Main-process JS crashes.\n process.on('uncaughtException', (...args: unknown[]) => {\n const err = args[0]\n this._captureError(err, 'critical', 'uncaughtException')\n this._markSession('crashed')\n void this.flush(true)\n })\n process.on('unhandledRejection', (...args: unknown[]) => {\n this._captureError(args[0], 'high', 'unhandledRejection')\n })\n\n // 3. Renderer / GPU / child-process crashes + hangs.\n if (options.enableProcessMonitoring !== false) {\n app.on('render-process-gone', (...a: unknown[]) => {\n const details = (a[2] ?? a[1]) as { reason?: string; exitCode?: number } | undefined\n const reason = details?.reason ?? 'unknown'\n const crashed = reason === 'crashed' || reason === 'oom' || reason === 'abnormal-exit'\n this._captureSynthetic('RenderProcessGone', `Renderer process gone: ${reason}`, crashed ? 'critical' : 'medium', {\n reason,\n exit_code: details?.exitCode,\n })\n if (crashed) this._markSession('crashed')\n })\n app.on('child-process-gone', (...a: unknown[]) => {\n const d = a[1] as { type?: string; reason?: string; exitCode?: number } | undefined\n this._captureSynthetic('ChildProcessGone', `Child process (${d?.type}) gone: ${d?.reason}`, 'medium', {\n type: d?.type,\n reason: d?.reason,\n exit_code: d?.exitCode,\n })\n })\n }\n\n if (options.enableHangDetection !== false) {\n app.on('web-contents-created', (...a: unknown[]) => {\n const contents = a[1] as { on(ev: string, cb: (...x: unknown[]) => void): void } | undefined\n if (!contents || typeof contents.on !== 'function') return\n let hangStart = 0\n contents.on('unresponsive', () => {\n hangStart = Date.now()\n this.addBreadcrumb({ type: 'device', category: 'app.hang', level: 'warning', message: 'renderer unresponsive' })\n })\n contents.on('responsive', () => {\n const dur = hangStart ? Date.now() - hangStart : 0\n if (dur > 0) {\n this._captureSynthetic('AppHang', `Renderer was unresponsive for ${dur}ms`, 'medium', { duration_ms: dur })\n }\n hangStart = 0\n })\n })\n }\n\n // 3b. TillShield threat detection — opt-in. DevTools opening on a renderer\n // is a real tamper/inspection signal for a desktop app (e.g. someone poking\n // at your production build). Reported as a security event.\n if (options.enableThreatDetection) {\n app.on('web-contents-created', (...a: unknown[]) => {\n const contents = a[1] as { on(ev: string, cb: (...x: unknown[]) => void): void; getURL?(): string } | undefined\n if (!contents || typeof contents.on !== 'function') return\n contents.on('devtools-opened', () => {\n this.captureSecurityEvent({\n securityType: 'devtools_open',\n severity: 'low',\n confidence: 0.7,\n message: 'DevTools opened on a renderer',\n evidence: { url: typeof contents.getURL === 'function' ? contents.getURL() : undefined },\n })\n })\n })\n }\n\n // 4. Session lifecycle.\n if (options.enableSessionTracking !== false) {\n const start = () => this._startSession()\n try {\n if (app.isReady()) start()\n else void app.whenReady().then(start)\n } catch {\n start()\n }\n app.on('before-quit', () => {\n this._markSession('exited')\n void this.flush(true)\n })\n app.on('window-all-closed', () => this._markSession('exited'))\n }\n\n // 5. Flush loop.\n const flushMs = options.flushIntervalMs ?? 10_000\n this.timer = setInterval(() => void this.flush(), flushMs)\n if (this.timer.unref) this.timer.unref()\n\n if (options.debug) console.log('[TillPulse] electron SDK initialized', { release: options.release })\n }\n\n // ── Public API ──\n captureException(error: unknown): void {\n this._captureError(error, 'high', 'captureException')\n }\n captureMessage(message: string, level: Severity = 'info'): void {\n this._enqueue(this._base('error', level, { exception: { type: 'Message', value: message } }))\n }\n\n // Report a security signal to TillShield (same shape as the other SDKs).\n captureSecurityEvent(input: {\n securityType: string\n severity?: Severity\n confidence?: number\n evidence?: Record<string, unknown>\n message?: string\n }): void {\n this._enqueue(\n this._base('security', input.severity ?? 'high', {\n security_type: input.securityType,\n security_confidence: input.confidence ?? 0.8,\n security_evidence: input.evidence ?? {},\n exception: { type: `security:${input.securityType}`, value: input.message ?? input.securityType },\n }),\n )\n }\n addBreadcrumb(input: { type: BreadcrumbType; category: string; message: string; level?: Breadcrumb['level']; data?: Record<string, unknown> }): void {\n this.crumbs.push({\n type: input.type,\n category: input.category,\n message: input.message,\n level: input.level ?? 'info',\n data: input.data,\n timestamp: new Date().toISOString(),\n })\n if (this.crumbs.length > 100) this.crumbs = this.crumbs.slice(this.crumbs.length - 100)\n }\n setUser(user: { id?: string; segment?: string } | null): void {\n this.user = user\n }\n setTag(key: string, value: string): void {\n this.tags[key] = value\n }\n getQueueStats(): { size: number } {\n return { size: this.queue?.size() ?? 0 }\n }\n\n async flush(useKeepalive = false): Promise<boolean> {\n void useKeepalive\n if (!this.queue || !this.opts) return false\n const items = this.queue.due(50)\n if (items.length === 0) return true\n const lines: string[] = [\n JSON.stringify({ dsn: this.opts.dsn, sdk: { name: SDK_NAME, version: SDK_VERSION }, sent_at: new Date().toISOString() }),\n ]\n const ids: string[] = []\n for (const it of items) {\n lines.push(JSON.stringify({ type: it.event_type, content_type: 'application/json', length: it.payload.length }))\n lines.push(it.payload)\n ids.push(it.id)\n }\n try {\n const res = await fetch(this.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-sentry-envelope', Authorization: `DSN ${this.opts.dsn}` },\n body: lines.join('\\n'),\n })\n if (res.ok || (res.status >= 400 && res.status < 500 && res.status !== 429)) {\n this.queue.ackOk(ids)\n return true\n }\n this.queue.ackFail(ids)\n return false\n } catch {\n this.queue.ackFail(ids)\n return false\n }\n }\n\n close(): void {\n this._markSession('exited')\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n void this.flush(true)\n this.initialized = false\n }\n\n // ── Internals ──\n private _device(): DeviceContext {\n let total = 0\n let free = 0\n let release = ''\n try {\n total = os.totalmem()\n free = os.freemem()\n release = os.release()\n } catch {\n /* ignore */\n }\n return {\n device_model: `Electron ${process.versions['electron'] ?? ''}`.trim(),\n device_manufacturer: process.platform,\n device_family: 'desktop',\n os_name: osName(),\n os_version: release,\n cpu_architecture: process.arch,\n total_ram_mb: total ? Math.round(total / (1024 * 1024)) : undefined,\n available_ram_mb: free ? Math.round(free / (1024 * 1024)) : undefined,\n locale: process.env['LANG'],\n }\n }\n\n private _base(eventType: TillPulseEvent['event_type'], severity: Severity, extra: Partial<TillPulseEvent>): TillPulseEvent {\n const o = this.opts!\n const now = new Date().toISOString()\n const event: TillPulseEvent = {\n event_id: generateId(),\n project_id: '',\n event_type: eventType,\n severity,\n environment: o.environment ?? 'production',\n release_version: o.release,\n dist: o.dist,\n timestamp: now,\n client_timestamp: now,\n breadcrumbs: this.crumbs.slice(),\n device: this._device(),\n network: { network_type: 'unknown' },\n app_state: { app_foreground: true },\n user: { user_id: this.user?.id, user_segment: this.user?.segment, session_id: this.sessionId },\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n ...extra,\n }\n if (Object.keys(this.tags).length) {\n ;(event as TillPulseEvent & { tags?: Record<string, string> }).tags = { ...this.tags }\n }\n // Runtime versions are always useful desktop context.\n ;(event as TillPulseEvent & { extra?: Record<string, unknown> }).extra = {\n chrome: process.versions['chrome'],\n node: process.versions['node'],\n v8: process.versions['v8'],\n }\n return event\n }\n\n private _captureError(thrown: unknown, severity: Severity, mechanism: string): void {\n if (!this.opts) return\n const err = thrown instanceof Error ? thrown : null\n this._enqueue(\n this._base('error', severity, {\n exception: {\n type: err?.name ?? 'Error',\n value: err?.message ?? String(thrown),\n stacktrace: err?.stack ? { frames: parseNodeStack(err.stack) } : undefined,\n },\n fingerprint: `electron:${mechanism}`,\n }),\n )\n }\n\n private _captureSynthetic(type: string, message: string, severity: Severity, data?: Record<string, unknown>): void {\n if (!this.opts) return\n const ev = this._base('error', severity, { exception: { type, value: message } })\n ;(ev as TillPulseEvent & { extra?: Record<string, unknown> }).extra = {\n ...(ev as TillPulseEvent & { extra?: Record<string, unknown> }).extra,\n ...data,\n }\n this._enqueue(ev)\n }\n\n private _startSession(): void {\n this.sessionStartedAt = Date.now()\n this.sessionEnded = false\n this._emitSession('ok')\n }\n private _markSession(status: 'exited' | 'crashed' | 'errored'): void {\n if (this.sessionEnded || this.sessionStartedAt === 0) return\n this.sessionEnded = true\n this._emitSession(status, Date.now() - this.sessionStartedAt)\n }\n private _emitSession(status: 'ok' | 'exited' | 'crashed' | 'errored', durationMs?: number): void {\n if (!this.opts) return\n this._enqueue(this._base('session', 'info', { session_status: status, ...(durationMs !== undefined ? { session_duration_ms: durationMs } : {}) }))\n }\n\n private async _enqueue(event: TillPulseEvent): Promise<void> {\n const o = this.opts\n if (!o || !this.queue) return\n let final: TillPulseEvent | null = event\n if (o.beforeSend) {\n try {\n final = await o.beforeSend(event)\n } catch {\n /* ignore */\n }\n }\n if (!final) return\n if (!o.disableDefaultPiiScrubbing) final = scrubEvent(final)\n this.queue.enqueue(final.event_id, final.event_type, final)\n }\n}\n\n// Minimal V8 stack parser: \" at fn (/abs/file.js:12:34)\" / \" at /abs/file.js:12:34\".\nfunction parseNodeStack(stack: string): Array<{ filename: string; function: string; lineno?: number; colno?: number; in_app: boolean }> {\n const frames: Array<{ filename: string; function: string; lineno?: number; colno?: number; in_app: boolean }> = []\n for (const raw of stack.split('\\n')) {\n const line = raw.trim()\n const m = line.match(/^at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/) || line.match(/^at\\s+()(.+?):(\\d+):(\\d+)$/)\n if (!m) continue\n const file = m[2]!\n frames.push({\n filename: file,\n function: m[1] || '<anonymous>',\n lineno: Number(m[3]),\n colno: Number(m[4]),\n in_app: !file.includes('node_modules') && !file.startsWith('node:'),\n })\n }\n return frames\n}\n\nexport const TillPulse = new TillPulseElectronSDK()\nexport default TillPulse\nexport type { TillPulseEvent } from '@tillstack/types'\n"]}