@tillstack/electron 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +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"]}
1
+ {"version":3,"sources":["../../scrub-core/src/rules.ts","../../scrub-core/src/scrub.ts","../src/scrub.ts","../src/queue.ts","../src/index.ts"],"names":["scrubEvent","path","fs","randomBytes","createCipheriv","createDecipheriv","safeStorage","app","crashReporter","classifyIngestResponse","release","os"],"mappings":"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","file":"index.js","sourcesContent":["// @tillstack/scrub-core — the canonical PII scrub rules for the whole suite.\n//\n// This is the SINGLE SOURCE OF TRUTH. The SDK on-device scrubbers\n// (packages/sdk-*/src/scrub.ts), the server \"second line of defence\"\n// (apps/event-processor), and TillArk's clone masker all key off these rules so\n// they can never drift apart. Import-free on purpose: nothing here touches Node,\n// the DOM, or React Native, so any runtime can consume it.\n//\n// Rule order matters: more-specific patterns must run before more-general ones,\n// otherwise a permissive pattern (e.g. phone) consumes the digits a stricter\n// pattern (e.g. card or NIN) was looking for.\n\nexport interface ScrubRule {\n pattern: RegExp\n replacement: string\n /** Stable label for the class of data this rule neutralises (used by the\n * masking planner to classify columns by the shape of their sample values). */\n label: ScrubLabel\n /** Skips UUIDs and hex ids, which would otherwise read as phone or card digits. */\n outsideIds?: true\n}\n\nexport type ScrubLabel =\n | 'private_key'\n | 'aws_key'\n | 'aws_secret'\n | 'gcp_key'\n | 'github_token'\n | 'gitlab_token'\n | 'slack_token'\n | 'webhook_url'\n | 'jwt'\n | 'bearer'\n | 'apikey'\n | 'package_token'\n | 'ai_key'\n | 'service_token'\n | 'tilldev_token'\n | 'db_url'\n | 'email'\n | 'mpesa_tx'\n | 'card'\n | 'id_number'\n | 'phone'\n | 'ip'\n\n/** The kinds of data a policy can decide about; every label belongs to one. */\nexport const DATA_CLASSES = ['credential', 'contact', 'financial', 'government_id', 'network'] as const\nexport type DataClass = (typeof DATA_CLASSES)[number]\n\nexport const LABEL_CLASS: Record<ScrubLabel, DataClass> = {\n private_key: 'credential',\n aws_key: 'credential',\n aws_secret: 'credential',\n gcp_key: 'credential',\n github_token: 'credential',\n gitlab_token: 'credential',\n slack_token: 'credential',\n webhook_url: 'credential',\n jwt: 'credential',\n bearer: 'credential',\n apikey: 'credential',\n package_token: 'credential',\n ai_key: 'credential',\n service_token: 'credential',\n tilldev_token: 'credential',\n db_url: 'credential',\n email: 'contact',\n phone: 'contact',\n mpesa_tx: 'financial',\n card: 'financial',\n id_number: 'government_id',\n ip: 'network',\n}\n\nexport const SCRUB_RULES: ScrubRule[] = [\n // ── Credentials (highest priority — long, distinctive patterns) ──\n\n { label: 'private_key', pattern: /-----BEGIN [A-Z0-9 ]{0,40}PRIVATE KEY(?: BLOCK)?-----(?:[^-\"\\\\]|\\\\[^\"]|-Type|-Info)*(?:-----END [A-Z0-9 ]{0,40}PRIVATE KEY(?: BLOCK)?-----)?/g, replacement: '[private_key]' },\n { label: 'private_key', pattern: /\\bAGE-SECRET-KEY-1[0-9A-Z]{58}/g, replacement: '[private_key]' },\n\n { label: 'aws_key', pattern: /\\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\\b/g, replacement: '[aws_key]' },\n // 40-char base64 following common context words\n { label: 'aws_secret', pattern: /(?:aws_secret|secret_access_key|AWS_SECRET)[^A-Za-z0-9+/]*([A-Za-z0-9+/]{40})\\b/gi, replacement: '[aws_secret]' },\n\n { label: 'gcp_key', pattern: /\\bAIza[0-9A-Za-z_-]{35}\\b/g, replacement: '[gcp_key]' },\n { label: 'gcp_key', pattern: /\\bGOCSPX-[0-9A-Za-z_-]{28}/g, replacement: '[gcp_key]' },\n\n { label: 'github_token', pattern: /\\bgh[pousr]_[A-Za-z0-9]{36,}\\b/g, replacement: '[github_token]' },\n { label: 'github_token', pattern: /\\bgithub_pat_[A-Za-z0-9_]{82}\\b/g, replacement: '[github_token]' },\n { label: 'gitlab_token', pattern: /\\bgl(?:pat|rt|ptt|ft|dt|soat|imt|cbt|agent)-[0-9A-Za-z_-]{20,}/g, replacement: '[gitlab_token]' },\n\n { label: 'slack_token', pattern: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { label: 'slack_token', pattern: /\\bxapp-\\d-[A-Za-z0-9-]{10,}\\b/g, replacement: '[slack_token]' },\n { label: 'webhook_url', pattern: /https:\\/\\/hooks\\.slack\\.com\\/services\\/[0-9A-Za-z_/]+/g, replacement: '[webhook_url]' },\n { label: 'webhook_url', pattern: /https:\\/\\/(?:canary\\.|ptb\\.)?discord(?:app)?\\.com\\/api\\/webhooks\\/[0-9]+\\/[0-9A-Za-z_-]+/g, replacement: '[webhook_url]' },\n\n // Three base64url segments separated by dots (eyJ header is canonical)\n { label: 'jwt', pattern: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+/g, replacement: '[jwt]' },\n\n { label: 'bearer', pattern: /Bearer\\s+[A-Za-z0-9\\-._~+/]+=*/gi, replacement: 'Bearer [token]' },\n { label: 'apikey', pattern: /\\b(?:sk|pk|ak|rk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g, replacement: '[apikey]' },\n { label: 'apikey', pattern: /\\bwhsec_[0-9A-Za-z]{24,}/g, replacement: '[apikey]' },\n\n { label: 'package_token', pattern: /\\b(?:npm_[0-9A-Za-z]{36}|pypi-[0-9A-Za-z_-]{50,}|rubygems_[0-9a-f]{48}|dckr_pat_[0-9A-Za-z_-]{20,})/g, replacement: '[package_token]' },\n { label: 'ai_key', pattern: /\\b(?:sk-(?:or-v1-[0-9a-f]{64}|ant-[0-9A-Za-z_-]{32,}|proj-[0-9A-Za-z_-]{32,})|hf_[0-9A-Za-z]{34,})/g, replacement: '[ai_key]' },\n { label: 'ai_key', pattern: /\\bsk-[A-Za-z0-9]{48}\\b/g, replacement: '[ai_key]' },\n { label: 'service_token', pattern: /\\b(?:SG\\.[0-9A-Za-z_-]{22}\\.[0-9A-Za-z_-]{43}|do[oprt]_v1_[0-9a-f]{64}|hv[sb]\\.[A-Za-z0-9_-]{24,}|lin_api_[0-9A-Za-z]{40}|shp(?:at|ss|pa|ca)_[0-9a-fA-F]{32}|sntry[su]_[0-9A-Za-z_-]{40,})/g, replacement: '[service_token]' },\n { label: 'tilldev_token', pattern: /\\b(?:(?:tp|tc|ts|ta|tsc|tse|tark|tnot|tfp|tfd|tfr|tfb)_[0-9a-f]{32}\\b|tfn_[0-9a-f]{64}\\b|tg_secret_[0-9a-f]{40}\\b|(?:tausec|tau_rt|tau_sso|tau_ssg|scim)_[A-Za-z0-9_-]{43}|tau_inv_[A-Za-z0-9_-]{38}|tdr_[A-Za-z0-9_-]{32})/g, replacement: '[tilldev_token]' },\n\n // The user and password of a connection string; the host stays.\n { label: 'db_url', pattern: /\\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\\+srv)?|rediss?|amqps?):\\/\\/[^:@/\\s\"']*:[^@/\\s\"']+@/g, replacement: '[db_credentials]@' },\n\n // ── Personal data ──\n\n { label: 'email', pattern: /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g, replacement: '[email]' },\n\n // M-Pesa transaction IDs (10 alphanumeric, common Kenyan format)\n { label: 'mpesa_tx', pattern: /\\b[A-Z0-9]{10}\\b(?=\\s*confirmed|\\s*transaction|\\s*M-?Pesa)/gi, replacement: '[mpesa_tx]' },\n\n // 13–19 digits; before phone, which would eat the first 11 digits.\n { label: 'card', pattern: /\\b(?:\\d[ -]*?){13,19}\\b/g, replacement: '[card]', outsideIds: true },\n\n // South African ID (13) and Nigerian NIN / BVN (11); before phone.\n { label: 'id_number', pattern: /\\b\\d{13}\\b/g, replacement: '[id_number]', outsideIds: true },\n { label: 'id_number', pattern: /\\b\\d{11}\\b/g, replacement: '[id_number]', outsideIds: true },\n\n // E.164-ish, 7–15 digits with optional + and separators\n { label: 'phone', pattern: /\\+?\\d{1,3}[\\s.-]?\\(?\\d{2,4}\\)?[\\s.-]?\\d{3,4}[\\s.-]?\\d{3,4}\\b/g, replacement: '[phone]', outsideIds: true },\n\n // IPv6 with at least three groups, so C++ scopes and clock times stay; then IPv4.\n { label: 'ip', pattern: /\\b(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}\\b|\\b(?:[0-9A-Fa-f]{1,4}:){2,6}:[0-9A-Fa-f]{1,4}\\b|\\b[0-9A-Fa-f]{1,4}::(?:[0-9A-Fa-f]{1,4}:){1,5}[0-9A-Fa-f]{1,4}\\b/g, replacement: '[ip]', outsideIds: true },\n { label: 'ip', pattern: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g, replacement: '[ip]', outsideIds: true },\n]\n\n// Key-name redaction. The value-pattern rules above catch data that *looks*\n// like a secret, but they miss a secret that carries no distinctive shape —\n// e.g. `\"password\": \"correct horse\"` or `\"authorization\": \"opaque\"`. Any value\n// whose key matches a sensitive name is replaced wholesale, regardless of the\n// value's format. Runs before the pattern pass so redacted values don't get\n// double-processed.\nexport const 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\nexport const ID_TOKEN = /\\b[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\b|\\b[0-9A-Fa-f]{16,}\\b/g\nconst HEX_LETTER = /[A-Fa-f]/\n\n/** Applies one rule; an `outsideIds` rule leaves UUIDs and hex ids (those with a letter) alone. */\nexport function applyRule(rule: ScrubRule, text: string, onHit?: () => void): string {\n const rep = onHit\n ? () => {\n onHit()\n return rule.replacement\n }\n : () => rule.replacement\n if (!rule.outsideIds) return text.replace(rule.pattern, rep)\n let out = ''\n let last = 0\n for (const m of text.matchAll(ID_TOKEN)) {\n if (!HEX_LETTER.test(m[0])) continue\n out += text.slice(last, m.index).replace(rule.pattern, rep) + m[0]\n last = m.index + m[0].length\n }\n return out + text.slice(last).replace(rule.pattern, rep)\n}\n","// @tillstack/scrub-core — free-text / event scrubbing (import-free, portable).\n//\n// `scrubEvent` is the drop-in the SDKs and the event-processor use: it walks an\n// arbitrary object, redacts sensitive KEYS wholesale, then runs the value-pattern\n// rules over the serialised form. It NEVER throws and NEVER drops the event —\n// scrubbing must not be able to lose data — so on any internal failure it returns\n// the input unchanged (which, for the server, is at worst the SDK-already-scrubbed\n// payload). See rules.ts for the canonical rule set.\n\nimport { SCRUB_RULES, SENSITIVE_KEY, applyRule } from './rules.js'\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\n/** Scrub PII/secrets from an arbitrary event object. Never throws, never drops. */\nexport function scrubEvent<T>(event: T): T {\n try {\n let json = JSON.stringify(redactByKey(event))\n for (const rule of SCRUB_RULES) json = applyRule(rule, json)\n return JSON.parse(json) as T\n } catch {\n // Scrubbing can never be the reason an event is lost.\n return event\n }\n}\n\n/** Scrub a single free-text string with the value-pattern rules only (no key\n * redaction — there are no keys). Used to mask free-text columns in a clone. */\nexport function scrubText(text: string): string {\n let out = text\n for (const rule of SCRUB_RULES) out = applyRule(rule, out)\n return out\n}\n","// On-device scrub with the suite's shared rules, bundled in at build time.\nimport { scrubEvent as scrub } from '@tillstack/scrub-core'\n\n/** A scrubbed deep copy of `event`; on any failure the input, so an event is never dropped. */\nexport function scrubEvent<T>(event: T): T {\n return scrub(event)\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport interface QItem {\n id: string\n event_type: string\n payload: string\n attempts: number\n next: number\n created: number\n}\n\nexport interface Codec {\n encode(plain: string): Buffer\n decode(data: Buffer): string\n}\n\nexport interface KeyWrap {\n wrap(plain: string): Buffer\n unwrap(data: Buffer): string\n}\n\nconst KEY_FILE = 'tillpulse-queue.key'\nconst AAD = Buffer.from('tillpulse.queue.v1')\nconst VERSION = 1\n\n/** AES-256-GCM over the queue; the OS key store (via `kw`) protects only the data key. */\nexport function gcmCodec(dir: string, kw: KeyWrap): Codec {\n let key: Buffer | null = null\n const keyFile = path.join(dir, KEY_FILE)\n const load = (): Buffer => {\n if (key) return key\n try {\n const k = Buffer.from(kw.unwrap(fs.readFileSync(keyFile)), 'base64')\n if (k.length === 32) return (key = k)\n } catch {}\n const fresh = randomBytes(32)\n const tmp = `${keyFile}.${process.pid}.tmp`\n fs.writeFileSync(tmp, kw.wrap(fresh.toString('base64')), { mode: 0o600 })\n fs.renameSync(tmp, keyFile)\n return (key = fresh)\n }\n return {\n encode(plain) {\n const iv = randomBytes(12)\n const c = createCipheriv('aes-256-gcm', load(), iv)\n c.setAAD(AAD)\n const ct = Buffer.concat([c.update(plain, 'utf8'), c.final()])\n return Buffer.concat([Buffer.from([VERSION]), iv, ct, c.getAuthTag()])\n },\n decode(data) {\n if (data.length < 29 || data[0] !== VERSION) throw new Error('bad blob')\n const d = createDecipheriv('aes-256-gcm', load(), data.subarray(1, 13))\n d.setAAD(AAD)\n d.setAuthTag(data.subarray(data.length - 16))\n return Buffer.concat([d.update(data.subarray(13, data.length - 16)), d.final()]).toString('utf8')\n },\n }\n}\n\nconst FILE = 'tillpulse-queue.bin'\nconst LEGACY_FILE = 'tillpulse-queue.json'\nconst MAX_ITEMS = 500\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000\nconst LEASE_MS = 20_000\n\nexport class FileQueue {\n private items: QItem[] = []\n private codec: Codec | null = null\n private dir: string | null = null\n onTamper: (() => void) | null = null\n\n attach(dir: string, codec: Codec | null): void {\n this.dir = dir\n this.codec = codec\n const now = Date.now()\n const legacy = path.join(dir, LEGACY_FILE)\n try {\n if (fs.existsSync(legacy)) {\n this.adopt(JSON.parse(fs.readFileSync(legacy, 'utf8')), now)\n fs.rmSync(legacy, { force: true })\n }\n } catch {\n fs.rmSync(legacy, { force: true })\n }\n const file = path.join(dir, FILE)\n if (codec && fs.existsSync(file)) {\n try {\n this.adopt(JSON.parse(codec.decode(fs.readFileSync(file))), now)\n } catch {\n fs.rmSync(file, { force: true })\n this.onTamper?.()\n }\n } else if (!codec) {\n fs.rmSync(file, { force: true })\n }\n this.persist()\n }\n\n private adopt(list: unknown, now: number): void {\n if (!Array.isArray(list)) return\n const known = new Set(this.items.map((i) => i.id))\n for (const raw of list as Partial<QItem>[]) {\n if (!raw || typeof raw.id !== 'string' || typeof raw.payload !== 'string' || typeof raw.event_type !== 'string') continue\n if (known.has(raw.id)) continue\n const created = typeof raw.created === 'number' ? raw.created : now\n if (now - created > MAX_AGE_MS) continue\n this.items.push({ id: raw.id, event_type: raw.event_type, payload: raw.payload, attempts: Number(raw.attempts) || 0, next: 0, created })\n }\n if (this.items.length > MAX_ITEMS) this.items = this.items.slice(this.items.length - MAX_ITEMS)\n }\n\n private persist(): void {\n if (!this.dir || !this.codec) return\n const file = path.join(this.dir, FILE)\n try {\n if (this.items.length === 0) {\n fs.rmSync(file, { force: true })\n return\n }\n const tmp = `${file}.${process.pid}.tmp`\n fs.writeFileSync(tmp, this.codec.encode(JSON.stringify(this.items)), { mode: 0o600 })\n fs.renameSync(tmp, file)\n } catch {}\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 const now = Date.now()\n this.items.push({ id, event_type: eventType, payload, attempts: 0, next: 0, created: now })\n if (this.items.length > MAX_ITEMS) this.items = this.items.slice(this.items.length - MAX_ITEMS)\n this.persist()\n }\n\n due(limit: number): QItem[] {\n const now = Date.now()\n this.items = this.items.filter((i) => now - i.created <= MAX_AGE_MS)\n const out = this.items.filter((i) => i.next <= now).slice(0, limit)\n for (const i of out) i.next = now + LEASE_MS\n return out\n }\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\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\n clear(): void {\n this.items = []\n if (this.dir) {\n fs.rmSync(path.join(this.dir, FILE), { force: true })\n fs.rmSync(path.join(this.dir, LEGACY_FILE), { force: true })\n }\n }\n\n size(): number {\n return this.items.length\n }\n}\n","import type { TillPulseEvent, Severity, DeviceContext, Breadcrumb, BreadcrumbType } from '@tillstack/types'\nimport { classifyIngestResponse } from '@tillstack/types'\nimport { app, crashReporter, safeStorage } from 'electron'\nimport * as os from 'node:os'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { scrubEvent } from './scrub'\nimport { FileQueue, gcmCodec, type Codec } from './queue'\n\nconst SDK_NAME = '@tillstack/electron'\ndeclare const __SDK_VERSION__: string | undefined\nconst SDK_VERSION = typeof __SDK_VERSION__ === 'string' ? __SDK_VERSION__ : '0.0.0-dev'\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 /** Keep the offline queue on disk unencrypted where the OS offers no keychain (Linux without a secret store). Default false: memory only. */\n allowPlaintextQueue?: boolean\n beforeSend?: (e: TillPulseEvent) => TillPulseEvent | null | Promise<TillPulseEvent | null>\n initialTags?: Record<string, string>\n flushIntervalMs?: number\n debug?: boolean\n}\n\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\nfunction osCodec(dir: string, allowPlaintext: boolean): Codec | null {\n try {\n const backend = process.platform === 'linux' ? safeStorage.getSelectedStorageBackend?.() : undefined\n if (safeStorage.isEncryptionAvailable() && backend !== 'basic_text' && backend !== 'unknown') {\n return gcmCodec(dir, { wrap: (t) => safeStorage.encryptString(t), unwrap: (b) => safeStorage.decryptString(b) })\n }\n } catch {}\n return allowPlaintext ? { encode: (t) => Buffer.from(t, 'utf8'), decode: (b) => b.toString('utf8') } : null\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()\n this.queue.onTamper = () => {\n if (!this.opts?.enableThreatDetection) return\n this.captureSecurityEvent({\n securityType: 'storage_tamper',\n severity: 'medium',\n confidence: 0.6,\n message: 'Stored TillPulse events failed their integrity check and were discarded',\n })\n }\n const attach = () => this.queue?.attach(dir, osCodec(dir, options.allowPlaintextQueue === true))\n if (app.isReady()) attach()\n else void app.whenReady().then(attach)\n\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 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 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 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 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 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 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 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 clearQueue(): void {\n this.queue?.clear()\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 const verdict = classifyIngestResponse({\n status: res.status,\n contentType: res.headers.get('content-type'),\n })\n if (verdict.disposition === 'retry') {\n this.queue.ackFail(ids)\n return false\n }\n this.queue.ackOk(ids)\n return true\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 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 ;(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\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 CHANGED
@@ -1,35 +1,71 @@
1
- import { app, crashReporter } from 'electron';
1
+ import { classifyIngestResponse } from '@tillstack/types';
2
+ import { app, crashReporter, safeStorage } from 'electron';
2
3
  import * as os from 'os';
4
+ import { createDecipheriv, randomBytes, createCipheriv } from 'crypto';
3
5
  import * as fs from 'fs';
4
6
  import * as path from 'path';
5
7
 
6
8
  // src/index.ts
7
9
 
8
- // src/scrub.ts
10
+ // ../scrub-core/dist/rules.js
9
11
  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]" }
12
+ { label: "private_key", pattern: /-----BEGIN [A-Z0-9 ]{0,40}PRIVATE KEY(?: BLOCK)?-----(?:[^-"\\]|\\[^"]|-Type|-Info)*(?:-----END [A-Z0-9 ]{0,40}PRIVATE KEY(?: BLOCK)?-----)?/g, replacement: "[private_key]" },
13
+ { label: "private_key", pattern: /\bAGE-SECRET-KEY-1[0-9A-Z]{58}/g, replacement: "[private_key]" },
14
+ { label: "aws_key", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g, replacement: "[aws_key]" },
15
+ { label: "aws_secret", pattern: /(?:aws_secret|secret_access_key|AWS_SECRET)[^A-Za-z0-9+/]*([A-Za-z0-9+/]{40})\b/gi, replacement: "[aws_secret]" },
16
+ { label: "gcp_key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g, replacement: "[gcp_key]" },
17
+ { label: "gcp_key", pattern: /\bGOCSPX-[0-9A-Za-z_-]{28}/g, replacement: "[gcp_key]" },
18
+ { label: "github_token", pattern: /\bgh[pousr]_[A-Za-z0-9]{36,}\b/g, replacement: "[github_token]" },
19
+ { label: "github_token", pattern: /\bgithub_pat_[A-Za-z0-9_]{82}\b/g, replacement: "[github_token]" },
20
+ { label: "gitlab_token", pattern: /\bgl(?:pat|rt|ptt|ft|dt|soat|imt|cbt|agent)-[0-9A-Za-z_-]{20,}/g, replacement: "[gitlab_token]" },
21
+ { label: "slack_token", pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, replacement: "[slack_token]" },
22
+ { label: "slack_token", pattern: /\bxapp-\d-[A-Za-z0-9-]{10,}\b/g, replacement: "[slack_token]" },
23
+ { label: "webhook_url", pattern: /https:\/\/hooks\.slack\.com\/services\/[0-9A-Za-z_/]+/g, replacement: "[webhook_url]" },
24
+ { label: "webhook_url", pattern: /https:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[0-9A-Za-z_-]+/g, replacement: "[webhook_url]" },
25
+ { label: "jwt", pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, replacement: "[jwt]" },
26
+ { label: "bearer", pattern: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, replacement: "Bearer [token]" },
27
+ { label: "apikey", pattern: /\b(?:sk|pk|ak|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replacement: "[apikey]" },
28
+ { label: "apikey", pattern: /\bwhsec_[0-9A-Za-z]{24,}/g, replacement: "[apikey]" },
29
+ { label: "package_token", pattern: /\b(?:npm_[0-9A-Za-z]{36}|pypi-[0-9A-Za-z_-]{50,}|rubygems_[0-9a-f]{48}|dckr_pat_[0-9A-Za-z_-]{20,})/g, replacement: "[package_token]" },
30
+ { label: "ai_key", pattern: /\b(?:sk-(?:or-v1-[0-9a-f]{64}|ant-[0-9A-Za-z_-]{32,}|proj-[0-9A-Za-z_-]{32,})|hf_[0-9A-Za-z]{34,})/g, replacement: "[ai_key]" },
31
+ { label: "ai_key", pattern: /\bsk-[A-Za-z0-9]{48}\b/g, replacement: "[ai_key]" },
32
+ { label: "service_token", pattern: /\b(?:SG\.[0-9A-Za-z_-]{22}\.[0-9A-Za-z_-]{43}|do[oprt]_v1_[0-9a-f]{64}|hv[sb]\.[A-Za-z0-9_-]{24,}|lin_api_[0-9A-Za-z]{40}|shp(?:at|ss|pa|ca)_[0-9a-fA-F]{32}|sntry[su]_[0-9A-Za-z_-]{40,})/g, replacement: "[service_token]" },
33
+ { label: "tilldev_token", pattern: /\b(?:(?:tp|tc|ts|ta|tsc|tse|tark|tnot|tfp|tfd|tfr|tfb)_[0-9a-f]{32}\b|tfn_[0-9a-f]{64}\b|tg_secret_[0-9a-f]{40}\b|(?:tausec|tau_rt|tau_sso|tau_ssg|scim)_[A-Za-z0-9_-]{43}|tau_inv_[A-Za-z0-9_-]{38}|tdr_[A-Za-z0-9_-]{32})/g, replacement: "[tilldev_token]" },
34
+ { label: "db_url", pattern: /\b(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^:@/\s"']*:[^@/\s"']+@/g, replacement: "[db_credentials]@" },
35
+ { label: "email", pattern: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g, replacement: "[email]" },
36
+ { label: "mpesa_tx", pattern: /\b[A-Z0-9]{10}\b(?=\s*confirmed|\s*transaction|\s*M-?Pesa)/gi, replacement: "[mpesa_tx]" },
37
+ { label: "card", pattern: /\b(?:\d[ -]*?){13,19}\b/g, replacement: "[card]", outsideIds: true },
38
+ { label: "id_number", pattern: /\b\d{13}\b/g, replacement: "[id_number]", outsideIds: true },
39
+ { label: "id_number", pattern: /\b\d{11}\b/g, replacement: "[id_number]", outsideIds: true },
40
+ { label: "phone", pattern: /\+?\d{1,3}[\s.-]?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{3,4}\b/g, replacement: "[phone]", outsideIds: true },
41
+ { label: "ip", pattern: /\b(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}\b|\b(?:[0-9A-Fa-f]{1,4}:){2,6}:[0-9A-Fa-f]{1,4}\b|\b[0-9A-Fa-f]{1,4}::(?:[0-9A-Fa-f]{1,4}:){1,5}[0-9A-Fa-f]{1,4}\b/g, replacement: "[ip]", outsideIds: true },
42
+ { label: "ip", pattern: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g, replacement: "[ip]", outsideIds: true }
27
43
  ];
28
44
  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;
45
+ var ID_TOKEN = /\b[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\b|\b[0-9A-Fa-f]{16,}\b/g;
46
+ var HEX_LETTER = /[A-Fa-f]/;
47
+ function applyRule(rule, text, onHit) {
48
+ const rep = () => rule.replacement;
49
+ if (!rule.outsideIds)
50
+ return text.replace(rule.pattern, rep);
51
+ let out = "";
52
+ let last = 0;
53
+ for (const m of text.matchAll(ID_TOKEN)) {
54
+ if (!HEX_LETTER.test(m[0]))
55
+ continue;
56
+ out += text.slice(last, m.index).replace(rule.pattern, rep) + m[0];
57
+ last = m.index + m[0].length;
58
+ }
59
+ return out + text.slice(last).replace(rule.pattern, rep);
60
+ }
61
+
62
+ // ../scrub-core/dist/scrub.js
29
63
  var MAX_REDACT_DEPTH = 24;
30
64
  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));
65
+ if (depth > MAX_REDACT_DEPTH || value === null || typeof value !== "object")
66
+ return value;
67
+ if (Array.isArray(value))
68
+ return value.map((v) => redactByKey(v, depth + 1));
33
69
  const out = {};
34
70
  for (const [k, v] of Object.entries(value)) {
35
71
  out[k] = SENSITIVE_KEY.test(k) ? "[redacted]" : redactByKey(v, depth + 1);
@@ -39,46 +75,115 @@ function redactByKey(value, depth = 0) {
39
75
  function scrubEvent(event) {
40
76
  try {
41
77
  let json = JSON.stringify(redactByKey(event));
42
- for (const { pattern, replacement } of SCRUB_RULES) json = json.replace(pattern, replacement);
78
+ for (const rule of SCRUB_RULES)
79
+ json = applyRule(rule, json);
43
80
  return JSON.parse(json);
44
81
  } catch {
45
82
  return event;
46
83
  }
47
84
  }
48
85
 
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 };
86
+ // src/scrub.ts
87
+ function scrubEvent2(event) {
88
+ return scrubEvent(event);
62
89
  }
63
- function generateId() {
64
- return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
90
+ var KEY_FILE = "tillpulse-queue.key";
91
+ var AAD = Buffer.from("tillpulse.queue.v1");
92
+ var VERSION = 1;
93
+ function gcmCodec(dir, kw) {
94
+ let key = null;
95
+ const keyFile = path.join(dir, KEY_FILE);
96
+ const load = () => {
97
+ if (key) return key;
98
+ try {
99
+ const k = Buffer.from(kw.unwrap(fs.readFileSync(keyFile)), "base64");
100
+ if (k.length === 32) return key = k;
101
+ } catch {
102
+ }
103
+ const fresh = randomBytes(32);
104
+ const tmp = `${keyFile}.${process.pid}.tmp`;
105
+ fs.writeFileSync(tmp, kw.wrap(fresh.toString("base64")), { mode: 384 });
106
+ fs.renameSync(tmp, keyFile);
107
+ return key = fresh;
108
+ };
109
+ return {
110
+ encode(plain) {
111
+ const iv = randomBytes(12);
112
+ const c = createCipheriv("aes-256-gcm", load(), iv);
113
+ c.setAAD(AAD);
114
+ const ct = Buffer.concat([c.update(plain, "utf8"), c.final()]);
115
+ return Buffer.concat([Buffer.from([VERSION]), iv, ct, c.getAuthTag()]);
116
+ },
117
+ decode(data) {
118
+ if (data.length < 29 || data[0] !== VERSION) throw new Error("bad blob");
119
+ const d = createDecipheriv("aes-256-gcm", load(), data.subarray(1, 13));
120
+ d.setAAD(AAD);
121
+ d.setAuthTag(data.subarray(data.length - 16));
122
+ return Buffer.concat([d.update(data.subarray(13, data.length - 16)), d.final()]).toString("utf8");
123
+ }
124
+ };
65
125
  }
126
+ var FILE = "tillpulse-queue.bin";
127
+ var LEGACY_FILE = "tillpulse-queue.json";
128
+ var MAX_ITEMS = 500;
129
+ var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
130
+ var LEASE_MS = 2e4;
66
131
  var FileQueue = class {
67
- constructor(dir) {
132
+ constructor() {
68
133
  this.items = [];
69
- this.max = 500;
70
- this.file = path.join(dir, "tillpulse-queue.json");
134
+ this.codec = null;
135
+ this.dir = null;
136
+ this.onTamper = null;
137
+ }
138
+ attach(dir, codec) {
139
+ this.dir = dir;
140
+ this.codec = codec;
141
+ const now = Date.now();
142
+ const legacy = path.join(dir, LEGACY_FILE);
71
143
  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;
144
+ if (fs.existsSync(legacy)) {
145
+ this.adopt(JSON.parse(fs.readFileSync(legacy, "utf8")), now);
146
+ fs.rmSync(legacy, { force: true });
75
147
  }
76
148
  } catch {
149
+ fs.rmSync(legacy, { force: true });
150
+ }
151
+ const file = path.join(dir, FILE);
152
+ if (codec && fs.existsSync(file)) {
153
+ try {
154
+ this.adopt(JSON.parse(codec.decode(fs.readFileSync(file))), now);
155
+ } catch {
156
+ fs.rmSync(file, { force: true });
157
+ this.onTamper?.();
158
+ }
159
+ } else if (!codec) {
160
+ fs.rmSync(file, { force: true });
161
+ }
162
+ this.persist();
163
+ }
164
+ adopt(list, now) {
165
+ if (!Array.isArray(list)) return;
166
+ const known = new Set(this.items.map((i) => i.id));
167
+ for (const raw of list) {
168
+ if (!raw || typeof raw.id !== "string" || typeof raw.payload !== "string" || typeof raw.event_type !== "string") continue;
169
+ if (known.has(raw.id)) continue;
170
+ const created = typeof raw.created === "number" ? raw.created : now;
171
+ if (now - created > MAX_AGE_MS) continue;
172
+ this.items.push({ id: raw.id, event_type: raw.event_type, payload: raw.payload, attempts: Number(raw.attempts) || 0, next: 0, created });
77
173
  }
174
+ if (this.items.length > MAX_ITEMS) this.items = this.items.slice(this.items.length - MAX_ITEMS);
78
175
  }
79
176
  persist() {
177
+ if (!this.dir || !this.codec) return;
178
+ const file = path.join(this.dir, FILE);
80
179
  try {
81
- fs.writeFileSync(this.file, JSON.stringify(this.items));
180
+ if (this.items.length === 0) {
181
+ fs.rmSync(file, { force: true });
182
+ return;
183
+ }
184
+ const tmp = `${file}.${process.pid}.tmp`;
185
+ fs.writeFileSync(tmp, this.codec.encode(JSON.stringify(this.items)), { mode: 384 });
186
+ fs.renameSync(tmp, file);
82
187
  } catch {
83
188
  }
84
189
  }
@@ -89,13 +194,17 @@ var FileQueue = class {
89
194
  } catch {
90
195
  return;
91
196
  }
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);
197
+ const now = Date.now();
198
+ this.items.push({ id, event_type: eventType, payload, attempts: 0, next: 0, created: now });
199
+ if (this.items.length > MAX_ITEMS) this.items = this.items.slice(this.items.length - MAX_ITEMS);
94
200
  this.persist();
95
201
  }
96
202
  due(limit) {
97
203
  const now = Date.now();
98
- return this.items.filter((i) => i.next <= now).slice(0, limit);
204
+ this.items = this.items.filter((i) => now - i.created <= MAX_AGE_MS);
205
+ const out = this.items.filter((i) => i.next <= now).slice(0, limit);
206
+ for (const i of out) i.next = now + LEASE_MS;
207
+ return out;
99
208
  }
100
209
  ackOk(ids) {
101
210
  const s = new Set(ids);
@@ -113,10 +222,45 @@ var FileQueue = class {
113
222
  this.items = this.items.filter((i) => i.attempts <= 10);
114
223
  this.persist();
115
224
  }
225
+ clear() {
226
+ this.items = [];
227
+ if (this.dir) {
228
+ fs.rmSync(path.join(this.dir, FILE), { force: true });
229
+ fs.rmSync(path.join(this.dir, LEGACY_FILE), { force: true });
230
+ }
231
+ }
116
232
  size() {
117
233
  return this.items.length;
118
234
  }
119
235
  };
236
+
237
+ // src/index.ts
238
+ var SDK_NAME = "@tillstack/electron";
239
+ var SDK_VERSION = "0.4.0" ;
240
+ function parseDsn(dsn) {
241
+ const m = dsn.match(/^(https?):\/\/([^@]+)@([^/]+)\/(.+)$/);
242
+ if (!m) throw new Error(`TillPulse: invalid DSN: ${dsn}`);
243
+ const scheme = m[1];
244
+ const host = m[3];
245
+ const local = /^(localhost|127\.0\.0\.1|::1)(:\d+)?$/i.test(host);
246
+ if (scheme !== "https" && !local) {
247
+ throw new Error("TillPulse: DSN must use https:// (refusing to send telemetry over cleartext http)");
248
+ }
249
+ return { scheme, host };
250
+ }
251
+ function generateId() {
252
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
253
+ }
254
+ function osCodec(dir, allowPlaintext) {
255
+ try {
256
+ const backend = process.platform === "linux" ? safeStorage.getSelectedStorageBackend?.() : void 0;
257
+ if (safeStorage.isEncryptionAvailable() && backend !== "basic_text" && backend !== "unknown") {
258
+ return gcmCodec(dir, { wrap: (t) => safeStorage.encryptString(t), unwrap: (b) => safeStorage.decryptString(b) });
259
+ }
260
+ } catch {
261
+ }
262
+ return allowPlaintext ? { encode: (t) => Buffer.from(t, "utf8"), decode: (b) => b.toString("utf8") } : null;
263
+ }
120
264
  function osName() {
121
265
  if (process.platform === "darwin") return "macOS";
122
266
  if (process.platform === "win32") return "Windows";
@@ -156,7 +300,19 @@ var TillPulseElectronSDK = class {
156
300
  dir = app.getPath("userData");
157
301
  } catch {
158
302
  }
159
- this.queue = new FileQueue(dir);
303
+ this.queue = new FileQueue();
304
+ this.queue.onTamper = () => {
305
+ if (!this.opts?.enableThreatDetection) return;
306
+ this.captureSecurityEvent({
307
+ securityType: "storage_tamper",
308
+ severity: "medium",
309
+ confidence: 0.6,
310
+ message: "Stored TillPulse events failed their integrity check and were discarded"
311
+ });
312
+ };
313
+ const attach = () => this.queue?.attach(dir, osCodec(dir, options.allowPlaintextQueue === true));
314
+ if (app.isReady()) attach();
315
+ else void app.whenReady().then(attach);
160
316
  if (options.enableNativeCrashReporter !== false) {
161
317
  try {
162
318
  crashReporter.start({
@@ -257,14 +413,12 @@ var TillPulseElectronSDK = class {
257
413
  if (this.timer.unref) this.timer.unref();
258
414
  if (options.debug) console.log("[TillPulse] electron SDK initialized", { release: options.release });
259
415
  }
260
- // ── Public API ──
261
416
  captureException(error) {
262
417
  this._captureError(error, "high", "captureException");
263
418
  }
264
419
  captureMessage(message, level = "info") {
265
420
  this._enqueue(this._base("error", level, { exception: { type: "Message", value: message } }));
266
421
  }
267
- // Report a security signal to TillShield (same shape as the other SDKs).
268
422
  captureSecurityEvent(input) {
269
423
  this._enqueue(
270
424
  this._base("security", input.severity ?? "high", {
@@ -295,6 +449,9 @@ var TillPulseElectronSDK = class {
295
449
  getQueueStats() {
296
450
  return { size: this.queue?.size() ?? 0 };
297
451
  }
452
+ clearQueue() {
453
+ this.queue?.clear();
454
+ }
298
455
  async flush(useKeepalive = false) {
299
456
  if (!this.queue || !this.opts) return false;
300
457
  const items = this.queue.due(50);
@@ -314,12 +471,16 @@ var TillPulseElectronSDK = class {
314
471
  headers: { "Content-Type": "application/x-sentry-envelope", Authorization: `DSN ${this.opts.dsn}` },
315
472
  body: lines.join("\n")
316
473
  });
317
- if (res.ok || res.status >= 400 && res.status < 500 && res.status !== 429) {
318
- this.queue.ackOk(ids);
319
- return true;
474
+ const verdict = classifyIngestResponse({
475
+ status: res.status,
476
+ contentType: res.headers.get("content-type")
477
+ });
478
+ if (verdict.disposition === "retry") {
479
+ this.queue.ackFail(ids);
480
+ return false;
320
481
  }
321
- this.queue.ackFail(ids);
322
- return false;
482
+ this.queue.ackOk(ids);
483
+ return true;
323
484
  } catch {
324
485
  this.queue.ackFail(ids);
325
486
  return false;
@@ -334,7 +495,6 @@ var TillPulseElectronSDK = class {
334
495
  void this.flush(true);
335
496
  this.initialized = false;
336
497
  }
337
- // ── Internals ──
338
498
  _device() {
339
499
  let total = 0;
340
500
  let free = 0;
@@ -436,7 +596,7 @@ var TillPulseElectronSDK = class {
436
596
  }
437
597
  }
438
598
  if (!final) return;
439
- if (!o.disableDefaultPiiScrubbing) final = scrubEvent(final);
599
+ if (!o.disableDefaultPiiScrubbing) final = scrubEvent2(final);
440
600
  this.queue.enqueue(final.event_id, final.event_type, final);
441
601
  }
442
602
  };