@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.
- package/README.md +48 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +492 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +465 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
|
@@ -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"]}
|