@agent-native/core 0.114.9 → 0.114.11
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +19 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/corpus/core/src/agent/engine/anthropic-engine.ts +20 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +56 -9
- package/corpus/core/src/agent/engine/first-event-timeout.ts +64 -0
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +7 -3
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +1 -1
- package/corpus/core/src/extensions/actions.ts +219 -0
- package/corpus/core/src/integrations/computer-supervision-store.ts +4 -4
- package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
- package/corpus/core/src/integrations/remote-commands-store.ts +5 -5
- package/corpus/core/src/integrations/remote-devices-store.ts +3 -3
- package/corpus/core/src/integrations/remote-push-store.ts +3 -3
- package/corpus/core/src/observability/traces.ts +38 -1
- package/corpus/core/src/server/action-routes.ts +23 -1
- package/corpus/core/src/server/http-response-telemetry.ts +6 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/corpus/templates/analytics/actions/account-deep-dive.ts +41 -6
- package/corpus/templates/analytics/changelog/2026-07-20-account-deep-dives-finish-with-partial-data-when-hubspot-lo.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-20-dashboard-email-reports-now-include-an-image-when-a-single-p.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +11 -3
- package/corpus/templates/calendar/actions/create-event.ts +5 -0
- package/corpus/templates/calendar/app/components/ThemeToggle.tsx +4 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +0 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +309 -421
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +349 -0
- package/corpus/templates/calendar/app/i18n-data.ts +110 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +6 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +3 -0
- package/corpus/templates/calendar/app/root.tsx +4 -2
- package/corpus/templates/calendar/changelog/2026-07-20-events-can-be-updated-inline-with-date-time-timezone-and-rep.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-20-public-booking-pages-no-longer-hydrate-with-a-theme-mismatch.md +6 -0
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/clips/AGENTS.md +7 -0
- package/corpus/templates/clips/actions/create-recording.ts +51 -10
- package/corpus/templates/clips/actions/get-recording-player-data.ts +34 -5
- package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
- package/corpus/templates/clips/actions/list-recordings.ts +15 -4
- package/corpus/templates/clips/actions/regenerate-summary.ts +32 -0
- package/corpus/templates/clips/actions/search-recordings.ts +28 -6
- package/corpus/templates/clips/actions/view-screen.ts +54 -47
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -5
- package/corpus/templates/clips/app/components/library/recording-card.tsx +29 -32
- package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +13 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +48 -0
- package/corpus/templates/clips/app/lib/fmp4.ts +26 -4
- package/corpus/templates/clips/app/lib/mse-video-loader.ts +13 -2
- package/corpus/templates/clips/changelog/2026-07-20-agent-summaries-use-bounded-transcript-context.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clip-summaries-now-finish-without-sending-a-long-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clips-agents-only-discover-recordings-you-own-or-have-alread.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-hosted-recordings-no-longer-start-with-an-impossible-buffered-upload.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-meeting-recordings-now-offer-a-cleaner-transcript-view-with-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-rewind-capture-status.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-shared-clips-can-be-shared-with-limited-access-and-cards-sho.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +81 -71
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +12 -8
- package/corpus/templates/clips/desktop/src/lib/rewind-status.ts +103 -0
- package/corpus/templates/clips/desktop/src/overlays/pill-logo.tsx +2 -15
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +107 -340
- package/corpus/templates/clips/desktop/src/styles.css +37 -145
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +6 -7
- package/corpus/templates/clips/server/lib/agent-recording-access.ts +73 -0
- package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +18 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +10 -11
- package/corpus/templates/plan/changelog/2026-07-20-bridged-local-plans-now-load-comments-from-the-local-workspace.md +6 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +27 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +19 -4
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +40 -7
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/first-event-timeout.d.ts +25 -0
- package/dist/agent/engine/first-event-timeout.d.ts.map +1 -0
- package/dist/agent/engine/first-event-timeout.js +49 -0
- package/dist/agent/engine/first-event-timeout.js.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -2
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +1 -1
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +193 -1
- package/dist/extensions/actions.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/computer-supervision-store.js +4 -4
- package/dist/integrations/computer-supervision-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.js +1 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.js +5 -5
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.js +3 -3
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.js +3 -3
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +23 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +23 -1
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/http-response-telemetry.d.ts +2 -0
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +5 -1
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/package.json +2 -2
- package/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/src/agent/engine/anthropic-engine.ts +20 -3
- package/src/agent/engine/builder-engine.ts +56 -9
- package/src/agent/engine/first-event-timeout.ts +64 -0
- package/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/src/client/AssistantChat.tsx +7 -3
- package/src/client/resources/ResourcesPanel.tsx +1 -1
- package/src/extensions/actions.ts +219 -0
- package/src/integrations/computer-supervision-store.ts +4 -4
- package/src/integrations/pending-tasks-store.ts +1 -1
- package/src/integrations/remote-commands-store.ts +5 -5
- package/src/integrations/remote-devices-store.ts +3 -3
- package/src/integrations/remote-push-store.ts +3 -3
- package/src/observability/traces.ts +38 -1
- package/src/server/action-routes.ts +23 -1
- package/src/server/http-response-telemetry.ts +6 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-devices-store.js","sourceRoot":"","sources":["../../src/integrations/remote-devices-store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n intType,\n isPostgres,\n retryOnDdlRace,\n} from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport type {\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteDevice,\n RemoteDeviceMetadata,\n} from \"./remote-types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS integration_remote_devices (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n label TEXT NOT NULL,\n platform TEXT,\n app_version TEXT,\n host_name TEXT,\n metadata_json TEXT,\n device_token_hash TEXT NOT NULL,\n last_seen_at ${intType()},\n status TEXT NOT NULL,\n revoked_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `;\n\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"integration_remote_devices\", createSql);\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"platform\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS platform TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"app_version\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS app_version TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"host_name\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS host_name TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"metadata_json\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS metadata_json TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"revoked_at\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS revoked_at ${intType()}`,\n );\n await ensureIndexExists(\n \"idx_remote_devices_token_hash\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_devices_token_hash ON integration_remote_devices(device_token_hash)`,\n );\n await ensureIndexExists(\n \"idx_remote_devices_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_devices_owner ON integration_remote_devices(owner_email, org_id)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior verbatim\n await retryOnDdlRace(() => client.execute(createSql));\n await addColumnIfMissing(\"platform\", \"TEXT\");\n await addColumnIfMissing(\"app_version\", \"TEXT\");\n await addColumnIfMissing(\"host_name\", \"TEXT\");\n await addColumnIfMissing(\"metadata_json\", \"TEXT\");\n await addColumnIfMissing(\"revoked_at\", intType());\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_devices_token_hash ON integration_remote_devices(device_token_hash)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_devices_owner ON integration_remote_devices(owner_email, org_id)`,\n ),\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function addColumnIfMissing(\n name: string,\n definition: string,\n): Promise<void> {\n const sql = isPostgres()\n ? `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS ${name} ${definition}`\n : `ALTER TABLE integration_remote_devices ADD COLUMN ${name} ${definition}`;\n try {\n await retryOnDdlRace(() => getDbExec().execute(sql));\n } catch (err) {\n if (isDuplicateColumnError(err)) return;\n throw err;\n }\n}\n\nfunction isDuplicateColumnError(err: unknown): boolean {\n const code = String((err as { code?: unknown })?.code ?? \"\");\n const message = String((err as { message?: unknown })?.message ?? err)\n .toLowerCase()\n .trim();\n return (\n code === \"42701\" ||\n message.includes(\"duplicate column\") ||\n message.includes(\"already exists\")\n );\n}\n\nfunction rowToDevice(row: Record<string, unknown>): RemoteDevice {\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n label: row.label as string,\n platform: (row.platform as string | null) ?? null,\n appVersion: (row.app_version as string | null) ?? null,\n hostName: (row.host_name as string | null) ?? null,\n metadata: parseJson(row.metadata_json, null) as RemoteDeviceMetadata | null,\n deviceTokenHash: row.device_token_hash as string,\n lastSeenAt:\n row.last_seen_at == null ? null : Number(row.last_seen_at as number),\n status: row.status as RemoteDevice[\"status\"],\n revokedAt: row.revoked_at == null ? null : Number(row.revoked_at as number),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function toPublicRemoteDevice(device: RemoteDevice): PublicRemoteDevice {\n return {\n id: device.id,\n ownerEmail: device.ownerEmail,\n orgId: device.orgId,\n label: device.label,\n platform: device.platform,\n appVersion: device.appVersion,\n hostName: device.hostName,\n metadata: device.metadata,\n lastSeenAt: device.lastSeenAt,\n status: device.status,\n revokedAt: device.revokedAt,\n createdAt: device.createdAt,\n updatedAt: device.updatedAt,\n };\n}\n\nexport function getRemoteComputerCapabilities(\n device: Pick<RemoteDevice, \"metadata\">,\n): RemoteComputerCapabilities | null {\n const value = device.metadata?.computerCapabilities;\n if (!value || typeof value !== \"object\") return null;\n return normalizeComputerCapabilities(value);\n}\n\nexport async function createRemoteDevice(input: {\n ownerEmail: string;\n orgId?: string | null;\n label: string;\n platform?: string | null;\n appVersion?: string | null;\n hostName?: string | null;\n metadata?: RemoteDeviceMetadata | null;\n}): Promise<{ device: RemoteDevice; token: string }> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const id = `remote-device-${now}-${randomHex(8)}`;\n const token = `anr_${randomHex(32)}`;\n const tokenHash = await hashRemoteDeviceToken(token);\n\n await client.execute({\n sql: `INSERT INTO integration_remote_devices\n (id, owner_email, org_id, label, platform, app_version, host_name, metadata_json,\n device_token_hash, last_seen_at, status, revoked_at, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n input.ownerEmail,\n input.orgId ?? null,\n input.label.trim() || \"Remote device\",\n sanitizeOptionalString(input.platform, 80),\n sanitizeOptionalString(input.appVersion, 120),\n sanitizeOptionalString(input.hostName, 200),\n serializeRemoteDeviceMetadata(input.metadata),\n tokenHash,\n now,\n \"active\",\n null,\n now,\n now,\n ],\n });\n\n const device = await getRemoteDevice(id);\n if (!device) throw new Error(\"remote device insert failed\");\n return { device, token };\n}\n\nexport async function getRemoteDevice(\n id: string,\n): Promise<RemoteDevice | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices WHERE id = ? LIMIT 1`,\n args: [id],\n });\n return rows[0] ? rowToDevice(rows[0] as Record<string, unknown>) : null;\n}\n\nexport async function getRemoteDeviceForOwner(input: {\n id: string;\n ownerEmail: string;\n orgId?: string | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE id = ?\n AND owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)\n LIMIT 1`,\n args: [\n input.id,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ],\n });\n return rows[0] ? rowToDevice(rows[0] as Record<string, unknown>) : null;\n}\n\nexport async function listRemoteDevicesForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n status?: RemoteDevice[\"status\"];\n limit?: number;\n}): Promise<RemoteDevice[]> {\n await ensureTable();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.status ? \" AND status = ?\" : \"\";\n if (!Object.prototype.hasOwnProperty.call(input, \"orgId\")) {\n const args: Array<string | number> = [input.ownerEmail];\n if (input.status) args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE owner_email = ?${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToDevice(row as Record<string, unknown>));\n }\n const args: Array<string | number | null> = [\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ];\n if (input.status) args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToDevice(row as Record<string, unknown>));\n}\n\nexport async function authenticateRemoteDeviceToken(\n rawToken: string | null | undefined,\n): Promise<RemoteDevice | null> {\n if (!rawToken) return null;\n await ensureTable();\n const tokenHash = await hashRemoteDeviceToken(rawToken);\n const now = Date.now();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE device_token_hash = ? AND status = 'active'\n LIMIT 1`,\n args: [tokenHash],\n });\n if (!rows[0]) return null;\n const device = rowToDevice(rows[0] as Record<string, unknown>);\n await client.execute({\n sql: `UPDATE integration_remote_devices\n SET last_seen_at = ?, updated_at = ?\n WHERE id = ?`,\n args: [now, now, device.id],\n });\n return { ...device, lastSeenAt: now, updatedAt: now };\n}\n\nexport async function updateRemoteDeviceDetails(input: {\n id: string;\n label?: string | null;\n platform?: string | null;\n appVersion?: string | null;\n hostName?: string | null;\n metadata?: RemoteDeviceMetadata | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const now = Date.now();\n const updates: string[] = [];\n const args: Array<string | number | null> = [];\n if (input.label !== undefined) {\n updates.push(\"label = ?\");\n args.push(sanitizeOptionalString(input.label, 200) ?? \"Remote device\");\n }\n if (input.platform !== undefined) {\n updates.push(\"platform = ?\");\n args.push(sanitizeOptionalString(input.platform, 80));\n }\n if (input.appVersion !== undefined) {\n updates.push(\"app_version = ?\");\n args.push(sanitizeOptionalString(input.appVersion, 120));\n }\n if (input.hostName !== undefined) {\n updates.push(\"host_name = ?\");\n args.push(sanitizeOptionalString(input.hostName, 200));\n }\n if (input.metadata !== undefined) {\n updates.push(\"metadata_json = ?\");\n args.push(serializeRemoteDeviceMetadata(input.metadata));\n }\n if (updates.length === 0) return getRemoteDevice(input.id);\n\n updates.push(\"updated_at = ?\");\n args.push(now, input.id);\n await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET ${updates.join(\", \")}\n WHERE id = ? AND status = 'active'`,\n args,\n });\n return getRemoteDevice(input.id);\n}\n\nexport async function revokeRemoteDeviceForOwner(input: {\n id: string;\n ownerEmail: string;\n orgId?: string | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const now = Date.now();\n await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET status = 'inactive',\n revoked_at = COALESCE(revoked_at, ?),\n updated_at = ?\n WHERE id = ?\n AND owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)`,\n args: [\n now,\n now,\n input.id,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ],\n });\n return getRemoteDeviceForOwner(input);\n}\n\nexport async function unregisterRemoteDevice(id: string): Promise<boolean> {\n await ensureTable();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET status = 'inactive',\n revoked_at = COALESCE(revoked_at, ?),\n updated_at = ?\n WHERE id = ? AND status = 'active'`,\n args: [now, now, id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) > 0;\n}\n\nexport async function hashRemoteDeviceToken(token: string): Promise<string> {\n const bytes = new TextEncoder().encode(token);\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n return bytesToHex(new Uint8Array(digest));\n}\n\nfunction sanitizeOptionalString(\n value: string | null | undefined,\n max: number,\n): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.slice(0, max) : null;\n}\n\nfunction parseJson(value: unknown, fallback: unknown): unknown {\n if (value == null) return fallback;\n try {\n return JSON.parse(String(value));\n } catch {\n return fallback;\n }\n}\n\nfunction serializeRemoteDeviceMetadata(\n metadata: RemoteDeviceMetadata | null | undefined,\n): string | null {\n if (!metadata) return null;\n const normalized: RemoteDeviceMetadata = { ...metadata };\n if (metadata.computerCapabilities !== undefined) {\n normalized.computerCapabilities = normalizeComputerCapabilities(\n metadata.computerCapabilities,\n );\n }\n const json = JSON.stringify(normalized);\n if (new TextEncoder().encode(json).byteLength > 32_768) {\n throw new Error(\"Remote device metadata exceeds 32 KiB\");\n }\n if (/\"data:[^\"]*\"/i.test(json) || looksLikeLargeBase64(json)) {\n throw new Error(\"Remote device metadata cannot contain binary payloads\");\n }\n return json;\n}\n\nfunction normalizeComputerCapabilities(\n value: RemoteComputerCapabilities,\n): RemoteComputerCapabilities {\n const result: RemoteComputerCapabilities = {};\n if (value.browser && typeof value.browser === \"object\") {\n result.browser = {\n observe: value.browser.observe === true,\n control: value.browser.control === true,\n provider: sanitizeOptionalString(value.browser.provider, 120),\n version: sanitizeOptionalString(value.browser.version, 120),\n };\n }\n if (value.desktop && typeof value.desktop === \"object\") {\n result.desktop = {\n observe: value.desktop.observe === true,\n control: value.desktop.control === true,\n accessibility: value.desktop.accessibility === true,\n screenCapture: value.desktop.screenCapture === true,\n provider: sanitizeOptionalString(value.desktop.provider, 120),\n version: sanitizeOptionalString(value.desktop.version, 120),\n };\n }\n return result;\n}\n\nfunction looksLikeLargeBase64(value: string): boolean {\n return value.length > 4_096 && /[A-Za-z0-9+/]{4_096,}={0,2}/.test(value);\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n globalThis.crypto.getRandomValues(bytes);\n return bytesToHex(bytes);\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-devices-store.js","sourceRoot":"","sources":["../../src/integrations/remote-devices-store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n intType,\n isPostgres,\n retryOnDdlRace,\n} from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport type {\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteDevice,\n RemoteDeviceMetadata,\n} from \"./remote-types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS integration_remote_devices (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n label TEXT NOT NULL,\n platform TEXT,\n app_version TEXT,\n host_name TEXT,\n metadata_json TEXT,\n device_token_hash TEXT NOT NULL,\n last_seen_at ${intType()},\n status TEXT NOT NULL,\n revoked_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `;\n\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\"integration_remote_devices\", createSql);\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"platform\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS platform TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"app_version\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS app_version TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"host_name\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS host_name TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"metadata_json\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS metadata_json TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_devices\",\n \"revoked_at\",\n `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS revoked_at ${intType()}`,\n );\n await ensureIndexExists(\n \"idx_remote_devices_token_hash\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_devices_token_hash ON integration_remote_devices(device_token_hash)`,\n );\n await ensureIndexExists(\n \"idx_remote_devices_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_devices_owner ON integration_remote_devices(owner_email, org_id)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior verbatim\n await retryOnDdlRace(() => client.execute(createSql));\n await addColumnIfMissing(\"platform\", \"TEXT\");\n await addColumnIfMissing(\"app_version\", \"TEXT\");\n await addColumnIfMissing(\"host_name\", \"TEXT\");\n await addColumnIfMissing(\"metadata_json\", \"TEXT\");\n await addColumnIfMissing(\"revoked_at\", intType());\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_devices_token_hash ON integration_remote_devices(device_token_hash)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_devices_owner ON integration_remote_devices(owner_email, org_id)`,\n ),\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function addColumnIfMissing(\n name: string,\n definition: string,\n): Promise<void> {\n const sql = isPostgres()\n ? `ALTER TABLE integration_remote_devices ADD COLUMN IF NOT EXISTS ${name} ${definition}`\n : `ALTER TABLE integration_remote_devices ADD COLUMN ${name} ${definition}`;\n try {\n await retryOnDdlRace(() => getDbExec().execute(sql));\n } catch (err) {\n if (isDuplicateColumnError(err)) return;\n throw err;\n }\n}\n\nfunction isDuplicateColumnError(err: unknown): boolean {\n const code = String((err as { code?: unknown })?.code ?? \"\");\n const message = String((err as { message?: unknown })?.message ?? err)\n .toLowerCase()\n .trim();\n return (\n code === \"42701\" ||\n message.includes(\"duplicate column\") ||\n message.includes(\"already exists\")\n );\n}\n\nfunction rowToDevice(row: Record<string, unknown>): RemoteDevice {\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n label: row.label as string,\n platform: (row.platform as string | null) ?? null,\n appVersion: (row.app_version as string | null) ?? null,\n hostName: (row.host_name as string | null) ?? null,\n metadata: parseJson(row.metadata_json, null) as RemoteDeviceMetadata | null,\n deviceTokenHash: row.device_token_hash as string,\n lastSeenAt:\n row.last_seen_at == null ? null : Number(row.last_seen_at as number),\n status: row.status as RemoteDevice[\"status\"],\n revokedAt: row.revoked_at == null ? null : Number(row.revoked_at as number),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function toPublicRemoteDevice(device: RemoteDevice): PublicRemoteDevice {\n return {\n id: device.id,\n ownerEmail: device.ownerEmail,\n orgId: device.orgId,\n label: device.label,\n platform: device.platform,\n appVersion: device.appVersion,\n hostName: device.hostName,\n metadata: device.metadata,\n lastSeenAt: device.lastSeenAt,\n status: device.status,\n revokedAt: device.revokedAt,\n createdAt: device.createdAt,\n updatedAt: device.updatedAt,\n };\n}\n\nexport function getRemoteComputerCapabilities(\n device: Pick<RemoteDevice, \"metadata\">,\n): RemoteComputerCapabilities | null {\n const value = device.metadata?.computerCapabilities;\n if (!value || typeof value !== \"object\") return null;\n return normalizeComputerCapabilities(value);\n}\n\nexport async function createRemoteDevice(input: {\n ownerEmail: string;\n orgId?: string | null;\n label: string;\n platform?: string | null;\n appVersion?: string | null;\n hostName?: string | null;\n metadata?: RemoteDeviceMetadata | null;\n}): Promise<{ device: RemoteDevice; token: string }> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const id = `remote-device-${now}-${randomHex(8)}`;\n const token = `anr_${randomHex(32)}`;\n const tokenHash = await hashRemoteDeviceToken(token);\n\n await client.execute({\n sql: `INSERT INTO integration_remote_devices\n (id, owner_email, org_id, label, platform, app_version, host_name, metadata_json,\n device_token_hash, last_seen_at, status, revoked_at, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n input.ownerEmail,\n input.orgId ?? null,\n input.label.trim() || \"Remote device\",\n sanitizeOptionalString(input.platform, 80),\n sanitizeOptionalString(input.appVersion, 120),\n sanitizeOptionalString(input.hostName, 200),\n serializeRemoteDeviceMetadata(input.metadata),\n tokenHash,\n now,\n \"active\",\n null,\n now,\n now,\n ],\n });\n\n const device = await getRemoteDevice(id);\n if (!device) throw new Error(\"remote device insert failed\");\n return { device, token };\n}\n\nexport async function getRemoteDevice(\n id: string,\n): Promise<RemoteDevice | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices WHERE id = ? LIMIT 1`,\n args: [id],\n });\n return rows[0] ? rowToDevice(rows[0] as Record<string, unknown>) : null;\n}\n\nexport async function getRemoteDeviceForOwner(input: {\n id: string;\n ownerEmail: string;\n orgId?: string | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE id = ?\n AND owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)\n LIMIT 1`,\n args: [\n input.id,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ],\n });\n return rows[0] ? rowToDevice(rows[0] as Record<string, unknown>) : null;\n}\n\nexport async function listRemoteDevicesForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n status?: RemoteDevice[\"status\"];\n limit?: number;\n}): Promise<RemoteDevice[]> {\n await ensureTable();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.status ? \" AND status = ?\" : \"\";\n if (!Object.prototype.hasOwnProperty.call(input, \"orgId\")) {\n const args: Array<string | number> = [input.ownerEmail];\n if (input.status) args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE owner_email = ?${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToDevice(row as Record<string, unknown>));\n }\n const args: Array<string | number | null> = [\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ];\n if (input.status) args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToDevice(row as Record<string, unknown>));\n}\n\nexport async function authenticateRemoteDeviceToken(\n rawToken: string | null | undefined,\n): Promise<RemoteDevice | null> {\n if (!rawToken) return null;\n await ensureTable();\n const tokenHash = await hashRemoteDeviceToken(rawToken);\n const now = Date.now();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM integration_remote_devices\n WHERE device_token_hash = ? AND status = 'active'\n LIMIT 1`,\n args: [tokenHash],\n });\n if (!rows[0]) return null;\n const device = rowToDevice(rows[0] as Record<string, unknown>);\n await client.execute({\n sql: `UPDATE integration_remote_devices\n SET last_seen_at = ?, updated_at = ?\n WHERE id = ?`,\n args: [now, now, device.id],\n });\n return { ...device, lastSeenAt: now, updatedAt: now };\n}\n\nexport async function updateRemoteDeviceDetails(input: {\n id: string;\n label?: string | null;\n platform?: string | null;\n appVersion?: string | null;\n hostName?: string | null;\n metadata?: RemoteDeviceMetadata | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const now = Date.now();\n const updates: string[] = [];\n const args: Array<string | number | null> = [];\n if (input.label !== undefined) {\n updates.push(\"label = ?\");\n args.push(sanitizeOptionalString(input.label, 200) ?? \"Remote device\");\n }\n if (input.platform !== undefined) {\n updates.push(\"platform = ?\");\n args.push(sanitizeOptionalString(input.platform, 80));\n }\n if (input.appVersion !== undefined) {\n updates.push(\"app_version = ?\");\n args.push(sanitizeOptionalString(input.appVersion, 120));\n }\n if (input.hostName !== undefined) {\n updates.push(\"host_name = ?\");\n args.push(sanitizeOptionalString(input.hostName, 200));\n }\n if (input.metadata !== undefined) {\n updates.push(\"metadata_json = ?\");\n args.push(serializeRemoteDeviceMetadata(input.metadata));\n }\n if (updates.length === 0) return getRemoteDevice(input.id);\n\n updates.push(\"updated_at = ?\");\n args.push(now, input.id);\n await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET ${updates.join(\", \")}\n WHERE id = ? AND status = 'active'`,\n args,\n });\n return getRemoteDevice(input.id);\n}\n\nexport async function revokeRemoteDeviceForOwner(input: {\n id: string;\n ownerEmail: string;\n orgId?: string | null;\n}): Promise<RemoteDevice | null> {\n await ensureTable();\n const now = Date.now();\n await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET status = 'inactive',\n revoked_at = COALESCE(revoked_at, ?),\n updated_at = ?\n WHERE id = ?\n AND owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)`,\n args: [\n now,\n now,\n input.id,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ],\n });\n return getRemoteDeviceForOwner(input);\n}\n\nexport async function unregisterRemoteDevice(id: string): Promise<boolean> {\n await ensureTable();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_devices\n SET status = 'inactive',\n revoked_at = COALESCE(revoked_at, ?),\n updated_at = ?\n WHERE id = ? AND status = 'active'`,\n args: [now, now, id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) > 0;\n}\n\nexport async function hashRemoteDeviceToken(token: string): Promise<string> {\n const bytes = new TextEncoder().encode(token);\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n return bytesToHex(new Uint8Array(digest));\n}\n\nfunction sanitizeOptionalString(\n value: string | null | undefined,\n max: number,\n): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.slice(0, max) : null;\n}\n\nfunction parseJson(value: unknown, fallback: unknown): unknown {\n if (value == null) return fallback;\n try {\n return JSON.parse(String(value));\n } catch {\n return fallback;\n }\n}\n\nfunction serializeRemoteDeviceMetadata(\n metadata: RemoteDeviceMetadata | null | undefined,\n): string | null {\n if (!metadata) return null;\n const normalized: RemoteDeviceMetadata = { ...metadata };\n if (metadata.computerCapabilities !== undefined) {\n normalized.computerCapabilities = normalizeComputerCapabilities(\n metadata.computerCapabilities,\n );\n }\n const json = JSON.stringify(normalized);\n if (new TextEncoder().encode(json).byteLength > 32_768) {\n throw new Error(\"Remote device metadata exceeds 32 KiB\");\n }\n if (/\"data:[^\"]*\"/i.test(json) || looksLikeLargeBase64(json)) {\n throw new Error(\"Remote device metadata cannot contain binary payloads\");\n }\n return json;\n}\n\nfunction normalizeComputerCapabilities(\n value: RemoteComputerCapabilities,\n): RemoteComputerCapabilities {\n const result: RemoteComputerCapabilities = {};\n if (value.browser && typeof value.browser === \"object\") {\n result.browser = {\n observe: value.browser.observe === true,\n control: value.browser.control === true,\n provider: sanitizeOptionalString(value.browser.provider, 120),\n version: sanitizeOptionalString(value.browser.version, 120),\n };\n }\n if (value.desktop && typeof value.desktop === \"object\") {\n result.desktop = {\n observe: value.desktop.observe === true,\n control: value.desktop.control === true,\n accessibility: value.desktop.accessibility === true,\n screenCapture: value.desktop.screenCapture === true,\n provider: sanitizeOptionalString(value.desktop.provider, 120),\n version: sanitizeOptionalString(value.desktop.version, 120),\n };\n }\n return result;\n}\n\nfunction looksLikeLargeBase64(value: string): boolean {\n return value.length > 4_096 && /[A-Za-z0-9+/]{4_096,}={0,2}/.test(value);\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n globalThis.crypto.getRandomValues(bytes);\n return bytesToHex(bytes);\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n"]}
|
|
@@ -226,7 +226,7 @@ export async function listRemotePushRegistrationsForOwner(input) {
|
|
|
226
226
|
const { rows } = await getDbExec().execute({
|
|
227
227
|
sql: `SELECT * FROM integration_remote_push_registrations
|
|
228
228
|
WHERE owner_email = ?
|
|
229
|
-
AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}
|
|
229
|
+
AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}
|
|
230
230
|
ORDER BY COALESCE(last_seen_at, updated_at) DESC
|
|
231
231
|
LIMIT ?`,
|
|
232
232
|
args: [input.ownerEmail, input.orgId ?? null, input.orgId ?? null, limit],
|
|
@@ -243,7 +243,7 @@ export async function unregisterRemotePushRegistrationForOwner(input) {
|
|
|
243
243
|
sql: `UPDATE integration_remote_push_registrations
|
|
244
244
|
SET status = 'inactive', updated_at = ?
|
|
245
245
|
WHERE owner_email = ?
|
|
246
|
-
AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
|
|
246
|
+
AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
|
|
247
247
|
AND (${input.id ? "id = ?" : "0 = 1"} OR ${tokenHash ? "token_hash = ?" : "0 = 1"})`,
|
|
248
248
|
args: [
|
|
249
249
|
now,
|
|
@@ -309,7 +309,7 @@ export async function listRemotePushNotificationsForOwner(input) {
|
|
|
309
309
|
const { rows } = await getDbExec().execute({
|
|
310
310
|
sql: `SELECT * FROM integration_remote_push_notifications
|
|
311
311
|
WHERE owner_email = ?
|
|
312
|
-
AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}
|
|
312
|
+
AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}
|
|
313
313
|
ORDER BY created_at DESC
|
|
314
314
|
LIMIT ?`,
|
|
315
315
|
args,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-push-store.js","sourceRoot":"","sources":["../../src/integrations/remote-push-store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n isPostgres,\n intType,\n retryOnDdlRace,\n} from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport { isDuplicateColumnError } from \"../db/migrations.js\";\nimport type {\n PublicRemotePushRegistration,\n RemotePushNotification,\n RemotePushRegistration,\n} from \"./remote-types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Build the CREATE SQL lazily (not at module scope) so intType() runs at\n// RUNTIME, not import time — a module-scope call breaks any consumer whose\n// db/client mock doesn't stub intType (e.g. db-admin specs).\nfunction buildCreateRegistrationsSql(): string {\n return `\n CREATE TABLE IF NOT EXISTS integration_remote_push_registrations (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n provider TEXT NOT NULL,\n platform TEXT,\n client_device_id TEXT,\n label TEXT,\n token TEXT NOT NULL,\n token_hash TEXT NOT NULL,\n status TEXT NOT NULL,\n last_seen_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n`;\n}\n\nfunction buildCreateNotificationsSql(): string {\n return `\n CREATE TABLE IF NOT EXISTS integration_remote_push_notifications (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n registration_id TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n status TEXT NOT NULL,\n attempts ${intType()} NOT NULL DEFAULT 0,\n provider_ticket_id TEXT,\n next_attempt_at ${intType()} NOT NULL DEFAULT 0,\n last_error TEXT,\n delivered_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n`;\n}\n\nasync function ensureTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createRegistrationsSql = buildCreateRegistrationsSql();\n const createNotificationsSql = buildCreateNotificationsSql();\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\n \"integration_remote_push_registrations\",\n createRegistrationsSql,\n );\n await ensureIndexExists(\n \"idx_remote_push_token_hash\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_push_token_hash ON integration_remote_push_registrations(token_hash)`,\n );\n await ensureIndexExists(\n \"idx_remote_push_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_owner ON integration_remote_push_registrations(owner_email, org_id, status)`,\n );\n await ensureTableExists(\n \"integration_remote_push_notifications\",\n createNotificationsSql,\n );\n await ensureIndexExists(\n \"idx_remote_push_notifications_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_owner ON integration_remote_push_notifications(owner_email, org_id, status, created_at)`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"provider_ticket_id\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS provider_ticket_id TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"next_attempt_at\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS next_attempt_at ${intType()} NOT NULL DEFAULT 0`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"last_error\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS last_error TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"delivered_at\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS delivered_at ${intType()}`,\n );\n await ensureIndexExists(\n \"idx_remote_push_notifications_delivery\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_delivery ON integration_remote_push_notifications(status, next_attempt_at, updated_at)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior\n await retryOnDdlRace(() => client.execute(createRegistrationsSql));\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_push_token_hash ON integration_remote_push_registrations(token_hash)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_owner ON integration_remote_push_registrations(owner_email, org_id, status)`,\n ),\n );\n\n await retryOnDdlRace(() => client.execute(createNotificationsSql));\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_owner ON integration_remote_push_notifications(owner_email, org_id, status, created_at)`,\n ),\n );\n await addNotificationColumnIfMissing(\"provider_ticket_id\", \"TEXT\");\n await addNotificationColumnIfMissing(\n \"next_attempt_at\",\n `${intType()} NOT NULL DEFAULT 0`,\n );\n await addNotificationColumnIfMissing(\"last_error\", \"TEXT\");\n await addNotificationColumnIfMissing(\"delivered_at\", intType());\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_delivery ON integration_remote_push_notifications(status, next_attempt_at, updated_at)`,\n ),\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function addNotificationColumnIfMissing(\n name: string,\n definition: string,\n): Promise<void> {\n try {\n await retryOnDdlRace(() =>\n getDbExec().execute(\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN ${name} ${definition}`,\n ),\n );\n } catch (error) {\n if (!isDuplicateColumnError(error)) throw error;\n }\n}\n\nfunction rowToRegistration(\n row: Record<string, unknown>,\n): RemotePushRegistration {\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n provider: row.provider as string,\n platform: (row.platform as string | null) ?? null,\n clientDeviceId: (row.client_device_id as string | null) ?? null,\n label: (row.label as string | null) ?? null,\n token: row.token as string,\n tokenHash: row.token_hash as string,\n status: row.status as RemotePushRegistration[\"status\"],\n lastSeenAt:\n row.last_seen_at == null ? null : Number(row.last_seen_at as number),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nfunction rowToNotification(\n row: Record<string, unknown>,\n): RemotePushNotification {\n const storedStatus = String(row.status ?? \"pending\");\n const status =\n storedStatus === \"delivered\" || storedStatus === \"failed\"\n ? storedStatus\n : \"pending\";\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n registrationId: row.registration_id as string,\n payload: parseJson(row.payload_json, null),\n status,\n attempts: Number(row.attempts ?? 0),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function toPublicRemotePushRegistration(\n registration: RemotePushRegistration,\n): PublicRemotePushRegistration {\n return {\n id: registration.id,\n ownerEmail: registration.ownerEmail,\n orgId: registration.orgId,\n provider: registration.provider,\n platform: registration.platform,\n clientDeviceId: registration.clientDeviceId,\n label: registration.label,\n status: registration.status,\n lastSeenAt: registration.lastSeenAt,\n createdAt: registration.createdAt,\n updatedAt: registration.updatedAt,\n };\n}\n\nexport async function upsertRemotePushRegistration(input: {\n ownerEmail: string;\n orgId?: string | null;\n provider: string;\n token: string;\n platform?: string | null;\n clientDeviceId?: string | null;\n label?: string | null;\n}): Promise<RemotePushRegistration> {\n await ensureTables();\n const client = getDbExec();\n const now = Date.now();\n const tokenHash = await hashToken(input.token);\n const provider = sanitizeString(input.provider, 80) ?? \"unknown\";\n const platform = sanitizeString(input.platform, 80);\n const clientDeviceId = sanitizeString(input.clientDeviceId, 200);\n const label = sanitizeString(input.label, 200);\n\n const existing = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (existing) {\n await client.execute({\n sql: `UPDATE integration_remote_push_registrations\n SET owner_email = ?,\n org_id = ?,\n provider = ?,\n platform = ?,\n client_device_id = ?,\n label = ?,\n token = ?,\n status = 'active',\n last_seen_at = ?,\n updated_at = ?\n WHERE token_hash = ?`,\n args: [\n input.ownerEmail,\n input.orgId ?? null,\n provider,\n platform,\n clientDeviceId,\n label,\n input.token,\n now,\n now,\n tokenHash,\n ],\n });\n const updated = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (!updated) throw new Error(\"remote push registration update failed\");\n return updated;\n }\n\n const id = `remote-push-${now}-${randomHex(8)}`;\n await client.execute({\n sql: `INSERT INTO integration_remote_push_registrations\n (id, owner_email, org_id, provider, platform, client_device_id, label,\n token, token_hash, status, last_seen_at, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n input.ownerEmail,\n input.orgId ?? null,\n provider,\n platform,\n clientDeviceId,\n label,\n input.token,\n tokenHash,\n \"active\",\n now,\n now,\n now,\n ],\n });\n const registration = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (!registration) throw new Error(\"remote push registration insert failed\");\n return registration;\n}\n\nexport async function listRemotePushRegistrationsForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n includeInactive?: boolean;\n limit?: number;\n}): Promise<RemotePushRegistration[]> {\n await ensureTables();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.includeInactive ? \"\" : \" AND status = 'active'\";\n if (!Object.prototype.hasOwnProperty.call(input, \"orgId\")) {\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE owner_email = ?${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args: [input.ownerEmail, limit],\n });\n return rows.map((row) => rowToRegistration(row as Record<string, unknown>));\n }\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args: [input.ownerEmail, input.orgId ?? null, input.orgId ?? null, limit],\n });\n return rows.map((row) => rowToRegistration(row as Record<string, unknown>));\n}\n\nexport async function unregisterRemotePushRegistrationForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n id?: string | null;\n token?: string | null;\n}): Promise<boolean> {\n await ensureTables();\n const tokenHash = input.token ? await hashToken(input.token) : null;\n if (!input.id && !tokenHash) return false;\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_registrations\n SET status = 'inactive', updated_at = ?\n WHERE owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)\n AND (${input.id ? \"id = ?\" : \"0 = 1\"} OR ${\n tokenHash ? \"token_hash = ?\" : \"0 = 1\"\n })`,\n args: [\n now,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ...(input.id ? [input.id] : []),\n ...(tokenHash ? [tokenHash] : []),\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) > 0;\n}\n\nexport async function queueRemotePushNotifications(input: {\n ownerEmail: string;\n orgId?: string | null;\n payload: unknown;\n}): Promise<{ queued: number }> {\n await ensureTables();\n const registrations = await listRemotePushRegistrationsForOwner({\n ownerEmail: input.ownerEmail,\n orgId: input.orgId ?? null,\n limit: 100,\n });\n if (registrations.length === 0) return { queued: 0 };\n\n const client = getDbExec();\n const now = Date.now();\n const payload = JSON.stringify(input.payload ?? null);\n const values = registrations.map(\n () => \"(?, ?, ?, ?, ?, 'pending', 0, ?, ?, ?)\",\n );\n const args = registrations.flatMap((registration) => [\n `remote-push-notification-${now}-${randomHex(8)}`,\n input.ownerEmail,\n input.orgId ?? null,\n registration.id,\n payload,\n now,\n now,\n now,\n ]);\n const result = await client.execute({\n sql: `INSERT INTO integration_remote_push_notifications\n (id, owner_email, org_id, registration_id, payload_json, status,\n attempts, next_attempt_at, created_at, updated_at)\n VALUES ${values.join(\", \")}`,\n args,\n });\n return {\n queued: result.rowsAffected ?? (result as any).rowCount ?? 0,\n };\n}\n\nexport async function listRemotePushNotificationsForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n status?: RemotePushNotification[\"status\"];\n limit?: number;\n}): Promise<RemotePushNotification[]> {\n await ensureTables();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.status\n ? input.status === \"pending\"\n ? \" AND status IN ('pending', 'sending', 'sent', 'checking')\"\n : \" AND status = ?\"\n : \"\";\n const args: Array<string | number | null> = [\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ];\n if (input.status && input.status !== \"pending\") args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_notifications\n WHERE owner_email = ?\n AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}\n ORDER BY created_at DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToNotification(row as Record<string, unknown>));\n}\n\nexport interface ClaimedRemotePushDelivery {\n id: string;\n registrationId: string;\n provider: string;\n token: string;\n payload: unknown;\n phase: \"send\" | \"receipt\";\n providerTicketId: string | null;\n attempts: number;\n}\n\nexport async function claimNextRemotePushDelivery(input?: {\n now?: number;\n staleAfterMs?: number;\n}): Promise<ClaimedRemotePushDelivery | null> {\n await ensureTables();\n const client = getDbExec();\n const now = input?.now ?? Date.now();\n const staleBefore = now - Math.max(30_000, input?.staleAfterMs ?? 120_000);\n const { rows } = await client.execute({\n sql: `SELECT n.id, n.registration_id, n.payload_json, n.status, n.attempts,\n n.provider_ticket_id, n.updated_at, r.provider, r.token\n FROM integration_remote_push_notifications n\n INNER JOIN integration_remote_push_registrations r\n ON r.id = n.registration_id\n WHERE r.status = 'active'\n AND (\n (n.status IN ('pending', 'sent') AND n.next_attempt_at <= ?)\n OR (n.status IN ('sending', 'checking') AND n.updated_at <= ?)\n )\n ORDER BY n.created_at ASC\n LIMIT 1`,\n args: [now, staleBefore],\n });\n const row = rows[0] as Record<string, unknown> | undefined;\n if (!row) return null;\n\n const previousStatus = String(row.status ?? \"pending\");\n const providerTicketId = sanitizeString(\n row.provider_ticket_id as string | null,\n 200,\n );\n const phase =\n (previousStatus === \"sent\" || previousStatus === \"checking\") &&\n providerTicketId\n ? \"receipt\"\n : \"send\";\n const claimedStatus = phase === \"receipt\" ? \"checking\" : \"sending\";\n const result = await client.execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = ? AND updated_at = ?`,\n args: [\n claimedStatus,\n now,\n row.id as string,\n previousStatus,\n Number(row.updated_at),\n ],\n });\n if ((result.rowsAffected ?? (result as any).rowCount ?? 0) !== 1) return null;\n\n return {\n id: row.id as string,\n registrationId: row.registration_id as string,\n provider: String(row.provider ?? \"unknown\"),\n token: row.token as string,\n payload: parseJson(row.payload_json, null),\n phase,\n providerTicketId,\n attempts: Number(row.attempts ?? 0) + 1,\n };\n}\n\nexport async function markRemotePushTicketAccepted(input: {\n id: string;\n providerTicketId: string;\n checkAfter: number;\n}): Promise<boolean> {\n await ensureTables();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'sent', provider_ticket_id = ?, next_attempt_at = ?,\n last_error = NULL, updated_at = ?\n WHERE id = ? AND status = 'sending'`,\n args: [input.providerTicketId, input.checkAfter, now, input.id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function markRemotePushDelivered(id: string): Promise<boolean> {\n await ensureTables();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'delivered', last_error = NULL, delivered_at = ?,\n updated_at = ?\n WHERE id = ? AND status = 'checking'`,\n args: [now, now, id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function retryRemotePushDelivery(input: {\n id: string;\n phase: \"send\" | \"receipt\";\n retryAt: number;\n errorCode: string;\n resend?: boolean;\n}): Promise<boolean> {\n await ensureTables();\n const currentStatus = input.phase === \"receipt\" ? \"checking\" : \"sending\";\n const retryStatus =\n input.phase === \"receipt\" && !input.resend ? \"sent\" : \"pending\";\n const clearTicketClause = input.resend ? \", provider_ticket_id = NULL\" : \"\";\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = ?, next_attempt_at = ?, last_error = ?${clearTicketClause},\n updated_at = ?\n WHERE id = ? AND status = ?`,\n args: [\n retryStatus,\n input.retryAt,\n sanitizeString(input.errorCode, 160) ?? \"temporary_error\",\n Date.now(),\n input.id,\n currentStatus,\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function failRemotePushDelivery(input: {\n id: string;\n phase: \"send\" | \"receipt\";\n errorCode: string;\n}): Promise<boolean> {\n await ensureTables();\n const currentStatus = input.phase === \"receipt\" ? \"checking\" : \"sending\";\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'failed', last_error = ?, updated_at = ?\n WHERE id = ? AND status = ?`,\n args: [\n sanitizeString(input.errorCode, 160) ?? \"delivery_failed\",\n Date.now(),\n input.id,\n currentStatus,\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function deactivateRemotePushRegistration(\n registrationId: string,\n): Promise<boolean> {\n await ensureTables();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE integration_remote_push_registrations\n SET status = 'inactive', updated_at = ?\n WHERE id = ? AND status = 'active'`,\n args: [now, registrationId],\n });\n await client.execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'failed', last_error = 'registration_inactive',\n updated_at = ?\n WHERE registration_id = ?\n AND status IN ('pending', 'sending', 'sent', 'checking')`,\n args: [now, registrationId],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nasync function getRemotePushRegistrationByTokenHash(\n tokenHash: string,\n): Promise<RemotePushRegistration | null> {\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE token_hash = ?\n LIMIT 1`,\n args: [tokenHash],\n });\n return rows[0] ? rowToRegistration(rows[0] as Record<string, unknown>) : null;\n}\n\nasync function hashToken(token: string): Promise<string> {\n const bytes = new TextEncoder().encode(token);\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n globalThis.crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction sanitizeString(\n value: string | null | undefined,\n max: number,\n): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.slice(0, max) : null;\n}\n\nfunction parseJson(value: unknown, fallback: unknown): unknown {\n if (value == null) return fallback;\n try {\n return JSON.parse(String(value));\n } catch {\n return fallback;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-push-store.js","sourceRoot":"","sources":["../../src/integrations/remote-push-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,UAAU,EACV,OAAO,EACP,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAO7D,IAAI,YAAuC,CAAC;AAE5C,yEAAyE;AACzE,2EAA2E;AAC3E,6DAA6D;AAC7D,SAAS,2BAA2B;IAClC,OAAO;;;;;;;;;;;;mBAYU,OAAO,EAAE;iBACX,OAAO,EAAE;iBACT,OAAO,EAAE;;CAEzB,CAAC;AACF,CAAC;AAED,SAAS,2BAA2B;IAClC,OAAO;;;;;;;;eAQM,OAAO,EAAE;;sBAEF,OAAO,EAAE;;mBAEZ,OAAO,EAAE;iBACX,OAAO,EAAE;iBACT,OAAO,EAAE;;CAEzB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,MAAM,sBAAsB,GAAG,2BAA2B,EAAE,CAAC;YAC7D,MAAM,sBAAsB,GAAG,2BAA2B,EAAE,CAAC;YAC7D,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,0FAA0F;gBAC1F,MAAM,iBAAiB,CACrB,uCAAuC,EACvC,sBAAsB,CACvB,CAAC;gBACF,MAAM,iBAAiB,CACrB,4BAA4B,EAC5B,mHAAmH,CACpH,CAAC;gBACF,MAAM,iBAAiB,CACrB,uBAAuB,EACvB,wHAAwH,CACzH,CAAC;gBACF,MAAM,iBAAiB,CACrB,uCAAuC,EACvC,sBAAsB,CACvB,CAAC;gBACF,MAAM,iBAAiB,CACrB,qCAAqC,EACrC,kJAAkJ,CACnJ,CAAC;gBACF,MAAM,kBAAkB,CACtB,uCAAuC,EACvC,oBAAoB,EACpB,oGAAoG,CACrG,CAAC;gBACF,MAAM,kBAAkB,CACtB,uCAAuC,EACvC,iBAAiB,EACjB,8FAA8F,OAAO,EAAE,qBAAqB,CAC7H,CAAC;gBACF,MAAM,kBAAkB,CACtB,uCAAuC,EACvC,YAAY,EACZ,4FAA4F,CAC7F,CAAC;gBACF,MAAM,kBAAkB,CACtB,uCAAuC,EACvC,cAAc,EACd,2FAA2F,OAAO,EAAE,EAAE,CACvG,CAAC;gBACF,MAAM,iBAAiB,CACrB,wCAAwC,EACxC,iJAAiJ,CAClJ,CAAC;gBACF,OAAO;YACT,CAAC;YACD,6CAA6C;YAC7C,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACnE,MAAM,cAAc,CAAC,GAAG,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,mHAAmH,CACpH,CACF,CAAC;YACF,MAAM,cAAc,CAAC,GAAG,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,wHAAwH,CACzH,CACF,CAAC;YAEF,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACnE,MAAM,cAAc,CAAC,GAAG,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,kJAAkJ,CACnJ,CACF,CAAC;YACF,MAAM,8BAA8B,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACnE,MAAM,8BAA8B,CAClC,iBAAiB,EACjB,GAAG,OAAO,EAAE,qBAAqB,CAClC,CAAC;YACF,MAAM,8BAA8B,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC3D,MAAM,8BAA8B,CAAC,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAChE,MAAM,cAAc,CAAC,GAAG,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,iJAAiJ,CAClJ,CACF,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACjB,sDAAsD;YACtD,YAAY,GAAG,SAAS,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,8BAA8B,CAC3C,IAAY,EACZ,UAAkB;IAElB,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,GAAG,EAAE,CACxB,SAAS,EAAE,CAAC,OAAO,CACjB,gEAAgE,IAAI,IAAI,UAAU,EAAE,CACrF,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;IAClD,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CACxB,GAA4B;IAE5B,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,UAAU,EAAE,GAAG,CAAC,WAAqB;QACrC,KAAK,EAAG,GAAG,CAAC,MAAwB,IAAI,IAAI;QAC5C,QAAQ,EAAE,GAAG,CAAC,QAAkB;QAChC,QAAQ,EAAG,GAAG,CAAC,QAA0B,IAAI,IAAI;QACjD,cAAc,EAAG,GAAG,CAAC,gBAAkC,IAAI,IAAI;QAC/D,KAAK,EAAG,GAAG,CAAC,KAAuB,IAAI,IAAI;QAC3C,KAAK,EAAE,GAAG,CAAC,KAAe;QAC1B,SAAS,EAAE,GAAG,CAAC,UAAoB;QACnC,MAAM,EAAE,GAAG,CAAC,MAA0C;QACtD,UAAU,EACR,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAsB,CAAC;QACtE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;QACtC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,GAA4B;IAE5B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;IACrD,MAAM,MAAM,GACV,YAAY,KAAK,WAAW,IAAI,YAAY,KAAK,QAAQ;QACvD,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,SAAS,CAAC;IAChB,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,UAAU,EAAE,GAAG,CAAC,WAAqB;QACrC,KAAK,EAAG,GAAG,CAAC,MAAwB,IAAI,IAAI;QAC5C,cAAc,EAAE,GAAG,CAAC,eAAyB;QAC7C,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC;QAC1C,MAAM;QACN,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;QACtC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,YAAoC;IAEpC,OAAO;QACL,EAAE,EAAE,YAAY,CAAC,EAAE;QACnB,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,QAAQ,EAAE,YAAY,CAAC,QAAQ;QAC/B,QAAQ,EAAE,YAAY,CAAC,QAAQ;QAC/B,cAAc,EAAE,YAAY,CAAC,cAAc;QAC3C,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,MAAM,EAAE,YAAY,CAAC,MAAM;QAC3B,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,SAAS,EAAE,YAAY,CAAC,SAAS;QACjC,SAAS,EAAE,YAAY,CAAC,SAAS;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,KAQlD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;IACjE,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACpD,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC;IACvE,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,OAAO,CAAC;YACnB,GAAG,EAAE;;;;;;;;;;;iCAWsB;YAC3B,IAAI,EAAE;gBACJ,KAAK,CAAC,UAAU;gBAChB,KAAK,CAAC,KAAK,IAAI,IAAI;gBACnB,QAAQ;gBACR,QAAQ;gBACR,cAAc;gBACd,KAAK;gBACL,KAAK,CAAC,KAAK;gBACX,GAAG;gBACH,GAAG;gBACH,SAAS;aACV;SACF,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE;;;qDAG4C;QACjD,IAAI,EAAE;YACJ,EAAE;YACF,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,KAAK,IAAI,IAAI;YACnB,QAAQ;YACR,QAAQ;YACR,cAAc;YACd,KAAK;YACL,KAAK,CAAC,KAAK;YACX,SAAS;YACT,QAAQ;YACR,GAAG;YACH,GAAG;YACH,GAAG;SACJ;KACF,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,MAAM,oCAAoC,CAAC,SAAS,CAAC,CAAC;IAC3E,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC7E,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CAAC,KAKzD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC;IAC3E,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;YACzC,GAAG,EAAE;mCACwB,YAAY;;oBAE3B;YACd,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC;SAChC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAA8B,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACzC,GAAG,EAAE;;8EAEqE,YAAY;;kBAExE;QACd,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,KAAK,CAAC;KAC1E,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAA8B,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAAC,KAK9D;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;;;mBAIU,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,OAClC,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OACjC,GAAG;QACX,IAAI,EAAE;YACJ,GAAG;YACH,KAAK,CAAC,UAAU;YAChB,KAAK,CAAC,KAAK,IAAI,IAAI;YACnB,KAAK,CAAC,KAAK,IAAI,IAAI;YACnB,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAClC;KACF,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,KAIlD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,aAAa,GAAG,MAAM,mCAAmC,CAAC;QAC9D,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;QAC1B,KAAK,EAAE,GAAG;KACX,CAAC,CAAC;IACH,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAErD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAC9B,GAAG,EAAE,CAAC,wCAAwC,CAC/C,CAAC;IACF,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;QACnD,4BAA4B,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE;QACjD,KAAK,CAAC,UAAU;QAChB,KAAK,CAAC,KAAK,IAAI,IAAI;QACnB,YAAY,CAAC,EAAE;QACf,OAAO;QACP,GAAG;QACH,GAAG;QACH,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;iBAGQ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChC,IAAI;KACL,CAAC,CAAC;IACH,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CAAC,KAKzD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM;QAC/B,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;YAC1B,CAAC,CAAC,2DAA2D;YAC7D,CAAC,CAAC,iBAAiB;QACrB,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,GAAkC;QAC1C,KAAK,CAAC,UAAU;QAChB,KAAK,CAAC,KAAK,IAAI,IAAI;QACnB,KAAK,CAAC,KAAK,IAAI,IAAI;KACpB,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACzC,GAAG,EAAE;;8EAEqE,YAAY;;kBAExE;QACd,IAAI;KACL,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAA8B,CAAC,CAAC,CAAC;AAC9E,CAAC;AAaD,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAGjD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,KAAK,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACrC,MAAM,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,IAAI,OAAO,CAAC,CAAC;IAC3E,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE;;;;;;;;;;;kBAWS;QACd,IAAI,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC;KACzB,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAwC,CAAC;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;IACvD,MAAM,gBAAgB,GAAG,cAAc,CACrC,GAAG,CAAC,kBAAmC,EACvC,GAAG,CACJ,CAAC;IACF,MAAM,KAAK,GACT,CAAC,cAAc,KAAK,MAAM,IAAI,cAAc,KAAK,UAAU,CAAC;QAC5D,gBAAgB;QACd,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,CAAC;IACb,MAAM,aAAa,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;yDAEgD;QACrD,IAAI,EAAE;YACJ,aAAa;YACb,GAAG;YACH,GAAG,CAAC,EAAY;YAChB,cAAc;YACd,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;SACvB;KACF,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9E,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,cAAc,EAAE,GAAG,CAAC,eAAyB;QAC7C,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,SAAS,CAAC;QAC3C,KAAK,EAAE,GAAG,CAAC,KAAe;QAC1B,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC;QAC1C,KAAK;QACL,gBAAgB;QAChB,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,KAIlD;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;;8CAGqC;QAC1C,IAAI,EAAE,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;KAChE,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAAU;IACtD,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;;+CAGsC;QAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;KACrB,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAM7C;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,MAAM,WAAW,GACf,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,MAAM,iBAAiB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;+DACsD,iBAAiB;;sCAE1C;QAClC,IAAI,EAAE;YACJ,WAAW;YACX,KAAK,CAAC,OAAO;YACb,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,iBAAiB;YACzD,IAAI,CAAC,GAAG,EAAE;YACV,KAAK,CAAC,EAAE;YACR,aAAa;SACd;KACF,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAI5C;IACC,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACvC,GAAG,EAAE;;sCAE6B;QAClC,IAAI,EAAE;YACJ,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,iBAAiB;YACzD,IAAI,CAAC,GAAG,EAAE;YACV,KAAK,CAAC,EAAE;YACR,aAAa;SACd;KACF,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,cAAsB;IAEtB,MAAM,YAAY,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;6CAEoC;QACzC,IAAI,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC;KAC5B,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE;;;;qEAI4D;QACjE,IAAI,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC;KAC5B,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,YAAY,IAAK,MAAc,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC;AAED,KAAK,UAAU,oCAAoC,CACjD,SAAiB;IAEjB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC;QACzC,GAAG,EAAE;;kBAES;QACd,IAAI,EAAE,CAAC,SAAS,CAAC;KAClB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,KAAa;IACpC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACvE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,UAAkB;IACnC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CACrB,KAAgC,EAChC,GAAW;IAEX,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,SAAS,SAAS,CAAC,KAAc,EAAE,QAAiB;IAClD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC","sourcesContent":["import {\n getDbExec,\n isPostgres,\n intType,\n retryOnDdlRace,\n} from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport { isDuplicateColumnError } from \"../db/migrations.js\";\nimport type {\n PublicRemotePushRegistration,\n RemotePushNotification,\n RemotePushRegistration,\n} from \"./remote-types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Build the CREATE SQL lazily (not at module scope) so intType() runs at\n// RUNTIME, not import time — a module-scope call breaks any consumer whose\n// db/client mock doesn't stub intType (e.g. db-admin specs).\nfunction buildCreateRegistrationsSql(): string {\n return `\n CREATE TABLE IF NOT EXISTS integration_remote_push_registrations (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n provider TEXT NOT NULL,\n platform TEXT,\n client_device_id TEXT,\n label TEXT,\n token TEXT NOT NULL,\n token_hash TEXT NOT NULL,\n status TEXT NOT NULL,\n last_seen_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n`;\n}\n\nfunction buildCreateNotificationsSql(): string {\n return `\n CREATE TABLE IF NOT EXISTS integration_remote_push_notifications (\n id TEXT PRIMARY KEY,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n registration_id TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n status TEXT NOT NULL,\n attempts ${intType()} NOT NULL DEFAULT 0,\n provider_ticket_id TEXT,\n next_attempt_at ${intType()} NOT NULL DEFAULT 0,\n last_error TEXT,\n delivered_at ${intType()},\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n`;\n}\n\nasync function ensureTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createRegistrationsSql = buildCreateRegistrationsSql();\n const createNotificationsSql = buildCreateNotificationsSql();\n if (isPostgres()) {\n // PG guard: probe via information_schema, only issue DDL if missing, bounded lock_timeout\n await ensureTableExists(\n \"integration_remote_push_registrations\",\n createRegistrationsSql,\n );\n await ensureIndexExists(\n \"idx_remote_push_token_hash\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_push_token_hash ON integration_remote_push_registrations(token_hash)`,\n );\n await ensureIndexExists(\n \"idx_remote_push_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_owner ON integration_remote_push_registrations(owner_email, org_id, status)`,\n );\n await ensureTableExists(\n \"integration_remote_push_notifications\",\n createNotificationsSql,\n );\n await ensureIndexExists(\n \"idx_remote_push_notifications_owner\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_owner ON integration_remote_push_notifications(owner_email, org_id, status, created_at)`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"provider_ticket_id\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS provider_ticket_id TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"next_attempt_at\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS next_attempt_at ${intType()} NOT NULL DEFAULT 0`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"last_error\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS last_error TEXT`,\n );\n await ensureColumnExists(\n \"integration_remote_push_notifications\",\n \"delivered_at\",\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN IF NOT EXISTS delivered_at ${intType()}`,\n );\n await ensureIndexExists(\n \"idx_remote_push_notifications_delivery\",\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_delivery ON integration_remote_push_notifications(status, next_attempt_at, updated_at)`,\n );\n return;\n }\n // SQLite (local dev): keep existing behavior\n await retryOnDdlRace(() => client.execute(createRegistrationsSql));\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_remote_push_token_hash ON integration_remote_push_registrations(token_hash)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_owner ON integration_remote_push_registrations(owner_email, org_id, status)`,\n ),\n );\n\n await retryOnDdlRace(() => client.execute(createNotificationsSql));\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_owner ON integration_remote_push_notifications(owner_email, org_id, status, created_at)`,\n ),\n );\n await addNotificationColumnIfMissing(\"provider_ticket_id\", \"TEXT\");\n await addNotificationColumnIfMissing(\n \"next_attempt_at\",\n `${intType()} NOT NULL DEFAULT 0`,\n );\n await addNotificationColumnIfMissing(\"last_error\", \"TEXT\");\n await addNotificationColumnIfMissing(\"delivered_at\", intType());\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_remote_push_notifications_delivery ON integration_remote_push_notifications(status, next_attempt_at, updated_at)`,\n ),\n );\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nasync function addNotificationColumnIfMissing(\n name: string,\n definition: string,\n): Promise<void> {\n try {\n await retryOnDdlRace(() =>\n getDbExec().execute(\n `ALTER TABLE integration_remote_push_notifications ADD COLUMN ${name} ${definition}`,\n ),\n );\n } catch (error) {\n if (!isDuplicateColumnError(error)) throw error;\n }\n}\n\nfunction rowToRegistration(\n row: Record<string, unknown>,\n): RemotePushRegistration {\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n provider: row.provider as string,\n platform: (row.platform as string | null) ?? null,\n clientDeviceId: (row.client_device_id as string | null) ?? null,\n label: (row.label as string | null) ?? null,\n token: row.token as string,\n tokenHash: row.token_hash as string,\n status: row.status as RemotePushRegistration[\"status\"],\n lastSeenAt:\n row.last_seen_at == null ? null : Number(row.last_seen_at as number),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nfunction rowToNotification(\n row: Record<string, unknown>,\n): RemotePushNotification {\n const storedStatus = String(row.status ?? \"pending\");\n const status =\n storedStatus === \"delivered\" || storedStatus === \"failed\"\n ? storedStatus\n : \"pending\";\n return {\n id: row.id as string,\n ownerEmail: row.owner_email as string,\n orgId: (row.org_id as string | null) ?? null,\n registrationId: row.registration_id as string,\n payload: parseJson(row.payload_json, null),\n status,\n attempts: Number(row.attempts ?? 0),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport function toPublicRemotePushRegistration(\n registration: RemotePushRegistration,\n): PublicRemotePushRegistration {\n return {\n id: registration.id,\n ownerEmail: registration.ownerEmail,\n orgId: registration.orgId,\n provider: registration.provider,\n platform: registration.platform,\n clientDeviceId: registration.clientDeviceId,\n label: registration.label,\n status: registration.status,\n lastSeenAt: registration.lastSeenAt,\n createdAt: registration.createdAt,\n updatedAt: registration.updatedAt,\n };\n}\n\nexport async function upsertRemotePushRegistration(input: {\n ownerEmail: string;\n orgId?: string | null;\n provider: string;\n token: string;\n platform?: string | null;\n clientDeviceId?: string | null;\n label?: string | null;\n}): Promise<RemotePushRegistration> {\n await ensureTables();\n const client = getDbExec();\n const now = Date.now();\n const tokenHash = await hashToken(input.token);\n const provider = sanitizeString(input.provider, 80) ?? \"unknown\";\n const platform = sanitizeString(input.platform, 80);\n const clientDeviceId = sanitizeString(input.clientDeviceId, 200);\n const label = sanitizeString(input.label, 200);\n\n const existing = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (existing) {\n await client.execute({\n sql: `UPDATE integration_remote_push_registrations\n SET owner_email = ?,\n org_id = ?,\n provider = ?,\n platform = ?,\n client_device_id = ?,\n label = ?,\n token = ?,\n status = 'active',\n last_seen_at = ?,\n updated_at = ?\n WHERE token_hash = ?`,\n args: [\n input.ownerEmail,\n input.orgId ?? null,\n provider,\n platform,\n clientDeviceId,\n label,\n input.token,\n now,\n now,\n tokenHash,\n ],\n });\n const updated = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (!updated) throw new Error(\"remote push registration update failed\");\n return updated;\n }\n\n const id = `remote-push-${now}-${randomHex(8)}`;\n await client.execute({\n sql: `INSERT INTO integration_remote_push_registrations\n (id, owner_email, org_id, provider, platform, client_device_id, label,\n token, token_hash, status, last_seen_at, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n input.ownerEmail,\n input.orgId ?? null,\n provider,\n platform,\n clientDeviceId,\n label,\n input.token,\n tokenHash,\n \"active\",\n now,\n now,\n now,\n ],\n });\n const registration = await getRemotePushRegistrationByTokenHash(tokenHash);\n if (!registration) throw new Error(\"remote push registration insert failed\");\n return registration;\n}\n\nexport async function listRemotePushRegistrationsForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n includeInactive?: boolean;\n limit?: number;\n}): Promise<RemotePushRegistration[]> {\n await ensureTables();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.includeInactive ? \"\" : \" AND status = 'active'\";\n if (!Object.prototype.hasOwnProperty.call(input, \"orgId\")) {\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE owner_email = ?${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args: [input.ownerEmail, limit],\n });\n return rows.map((row) => rowToRegistration(row as Record<string, unknown>));\n }\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}\n ORDER BY COALESCE(last_seen_at, updated_at) DESC\n LIMIT ?`,\n args: [input.ownerEmail, input.orgId ?? null, input.orgId ?? null, limit],\n });\n return rows.map((row) => rowToRegistration(row as Record<string, unknown>));\n}\n\nexport async function unregisterRemotePushRegistrationForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n id?: string | null;\n token?: string | null;\n}): Promise<boolean> {\n await ensureTables();\n const tokenHash = input.token ? await hashToken(input.token) : null;\n if (!input.id && !tokenHash) return false;\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_registrations\n SET status = 'inactive', updated_at = ?\n WHERE owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)\n AND (${input.id ? \"id = ?\" : \"0 = 1\"} OR ${\n tokenHash ? \"token_hash = ?\" : \"0 = 1\"\n })`,\n args: [\n now,\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ...(input.id ? [input.id] : []),\n ...(tokenHash ? [tokenHash] : []),\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) > 0;\n}\n\nexport async function queueRemotePushNotifications(input: {\n ownerEmail: string;\n orgId?: string | null;\n payload: unknown;\n}): Promise<{ queued: number }> {\n await ensureTables();\n const registrations = await listRemotePushRegistrationsForOwner({\n ownerEmail: input.ownerEmail,\n orgId: input.orgId ?? null,\n limit: 100,\n });\n if (registrations.length === 0) return { queued: 0 };\n\n const client = getDbExec();\n const now = Date.now();\n const payload = JSON.stringify(input.payload ?? null);\n const values = registrations.map(\n () => \"(?, ?, ?, ?, ?, 'pending', 0, ?, ?, ?)\",\n );\n const args = registrations.flatMap((registration) => [\n `remote-push-notification-${now}-${randomHex(8)}`,\n input.ownerEmail,\n input.orgId ?? null,\n registration.id,\n payload,\n now,\n now,\n now,\n ]);\n const result = await client.execute({\n sql: `INSERT INTO integration_remote_push_notifications\n (id, owner_email, org_id, registration_id, payload_json, status,\n attempts, next_attempt_at, created_at, updated_at)\n VALUES ${values.join(\", \")}`,\n args,\n });\n return {\n queued: result.rowsAffected ?? (result as any).rowCount ?? 0,\n };\n}\n\nexport async function listRemotePushNotificationsForOwner(input: {\n ownerEmail: string;\n orgId?: string | null;\n status?: RemotePushNotification[\"status\"];\n limit?: number;\n}): Promise<RemotePushNotification[]> {\n await ensureTables();\n const limit = Math.max(1, Math.min(input.limit ?? 50, 100));\n const statusClause = input.status\n ? input.status === \"pending\"\n ? \" AND status IN ('pending', 'sending', 'sent', 'checking')\"\n : \" AND status = ?\"\n : \"\";\n const args: Array<string | number | null> = [\n input.ownerEmail,\n input.orgId ?? null,\n input.orgId ?? null,\n ];\n if (input.status && input.status !== \"pending\") args.push(input.status);\n args.push(limit);\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_notifications\n WHERE owner_email = ?\n AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}\n ORDER BY created_at DESC\n LIMIT ?`,\n args,\n });\n return rows.map((row) => rowToNotification(row as Record<string, unknown>));\n}\n\nexport interface ClaimedRemotePushDelivery {\n id: string;\n registrationId: string;\n provider: string;\n token: string;\n payload: unknown;\n phase: \"send\" | \"receipt\";\n providerTicketId: string | null;\n attempts: number;\n}\n\nexport async function claimNextRemotePushDelivery(input?: {\n now?: number;\n staleAfterMs?: number;\n}): Promise<ClaimedRemotePushDelivery | null> {\n await ensureTables();\n const client = getDbExec();\n const now = input?.now ?? Date.now();\n const staleBefore = now - Math.max(30_000, input?.staleAfterMs ?? 120_000);\n const { rows } = await client.execute({\n sql: `SELECT n.id, n.registration_id, n.payload_json, n.status, n.attempts,\n n.provider_ticket_id, n.updated_at, r.provider, r.token\n FROM integration_remote_push_notifications n\n INNER JOIN integration_remote_push_registrations r\n ON r.id = n.registration_id\n WHERE r.status = 'active'\n AND (\n (n.status IN ('pending', 'sent') AND n.next_attempt_at <= ?)\n OR (n.status IN ('sending', 'checking') AND n.updated_at <= ?)\n )\n ORDER BY n.created_at ASC\n LIMIT 1`,\n args: [now, staleBefore],\n });\n const row = rows[0] as Record<string, unknown> | undefined;\n if (!row) return null;\n\n const previousStatus = String(row.status ?? \"pending\");\n const providerTicketId = sanitizeString(\n row.provider_ticket_id as string | null,\n 200,\n );\n const phase =\n (previousStatus === \"sent\" || previousStatus === \"checking\") &&\n providerTicketId\n ? \"receipt\"\n : \"send\";\n const claimedStatus = phase === \"receipt\" ? \"checking\" : \"sending\";\n const result = await client.execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = ?, attempts = attempts + 1, updated_at = ?\n WHERE id = ? AND status = ? AND updated_at = ?`,\n args: [\n claimedStatus,\n now,\n row.id as string,\n previousStatus,\n Number(row.updated_at),\n ],\n });\n if ((result.rowsAffected ?? (result as any).rowCount ?? 0) !== 1) return null;\n\n return {\n id: row.id as string,\n registrationId: row.registration_id as string,\n provider: String(row.provider ?? \"unknown\"),\n token: row.token as string,\n payload: parseJson(row.payload_json, null),\n phase,\n providerTicketId,\n attempts: Number(row.attempts ?? 0) + 1,\n };\n}\n\nexport async function markRemotePushTicketAccepted(input: {\n id: string;\n providerTicketId: string;\n checkAfter: number;\n}): Promise<boolean> {\n await ensureTables();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'sent', provider_ticket_id = ?, next_attempt_at = ?,\n last_error = NULL, updated_at = ?\n WHERE id = ? AND status = 'sending'`,\n args: [input.providerTicketId, input.checkAfter, now, input.id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function markRemotePushDelivered(id: string): Promise<boolean> {\n await ensureTables();\n const now = Date.now();\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'delivered', last_error = NULL, delivered_at = ?,\n updated_at = ?\n WHERE id = ? AND status = 'checking'`,\n args: [now, now, id],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function retryRemotePushDelivery(input: {\n id: string;\n phase: \"send\" | \"receipt\";\n retryAt: number;\n errorCode: string;\n resend?: boolean;\n}): Promise<boolean> {\n await ensureTables();\n const currentStatus = input.phase === \"receipt\" ? \"checking\" : \"sending\";\n const retryStatus =\n input.phase === \"receipt\" && !input.resend ? \"sent\" : \"pending\";\n const clearTicketClause = input.resend ? \", provider_ticket_id = NULL\" : \"\";\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = ?, next_attempt_at = ?, last_error = ?${clearTicketClause},\n updated_at = ?\n WHERE id = ? AND status = ?`,\n args: [\n retryStatus,\n input.retryAt,\n sanitizeString(input.errorCode, 160) ?? \"temporary_error\",\n Date.now(),\n input.id,\n currentStatus,\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function failRemotePushDelivery(input: {\n id: string;\n phase: \"send\" | \"receipt\";\n errorCode: string;\n}): Promise<boolean> {\n await ensureTables();\n const currentStatus = input.phase === \"receipt\" ? \"checking\" : \"sending\";\n const result = await getDbExec().execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'failed', last_error = ?, updated_at = ?\n WHERE id = ? AND status = ?`,\n args: [\n sanitizeString(input.errorCode, 160) ?? \"delivery_failed\",\n Date.now(),\n input.id,\n currentStatus,\n ],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nexport async function deactivateRemotePushRegistration(\n registrationId: string,\n): Promise<boolean> {\n await ensureTables();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE integration_remote_push_registrations\n SET status = 'inactive', updated_at = ?\n WHERE id = ? AND status = 'active'`,\n args: [now, registrationId],\n });\n await client.execute({\n sql: `UPDATE integration_remote_push_notifications\n SET status = 'failed', last_error = 'registration_inactive',\n updated_at = ?\n WHERE registration_id = ?\n AND status IN ('pending', 'sending', 'sent', 'checking')`,\n args: [now, registrationId],\n });\n return (result.rowsAffected ?? (result as any).rowCount ?? 0) === 1;\n}\n\nasync function getRemotePushRegistrationByTokenHash(\n tokenHash: string,\n): Promise<RemotePushRegistration | null> {\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM integration_remote_push_registrations\n WHERE token_hash = ?\n LIMIT 1`,\n args: [tokenHash],\n });\n return rows[0] ? rowToRegistration(rows[0] as Record<string, unknown>) : null;\n}\n\nasync function hashToken(token: string): Promise<string> {\n const bytes = new TextEncoder().encode(token);\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n globalThis.crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction sanitizeString(\n value: string | null | undefined,\n max: number,\n): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return trimmed ? trimmed.slice(0, max) : null;\n}\n\nfunction parseJson(value: unknown, fallback: unknown): unknown {\n if (value == null) return fallback;\n try {\n return JSON.parse(String(value));\n } catch {\n return fallback;\n }\n}\n"]}
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
14
|
count: number;
|
|
15
15
|
updated?: undefined;
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error?: undefined;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
count?: undefined;
|
|
20
20
|
updated: number;
|
|
21
|
-
ok?: undefined;
|
|
22
21
|
error?: undefined;
|
|
22
|
+
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
25
25
|
updated?: undefined;
|
|
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
45
|
summary: import("./types.js").TraceSummary;
|
|
47
46
|
spans: import("./types.js").TraceSpan[];
|
|
48
47
|
id?: undefined;
|
|
48
|
+
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
summary?: undefined;
|
|
53
52
|
spans?: undefined;
|
|
54
53
|
id: string;
|
|
55
|
-
} | {
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../src/observability/traces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,mBAAmB,CAAC;AAGxE,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"traces.d.ts","sourceRoot":"","sources":["../../src/observability/traces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,mBAAmB,CAAC;AAGxE,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAiM/E;;;wEAGwE;AACxE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE7D;AAoBD,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAc3E;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,YAAY,EAAE,CAAC,QAAQ,EAAE;QACvB,MAAM,EAAE,GAAG,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,GAAG,EAAE,CAAC;QACb,QAAQ,EAAE,GAAG,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;QACtC,MAAM,EAAE,WAAW,CAAC;QACpB,eAAe,CAAC,EAAE,GAAG,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9B,QAAQ,EAAE;QACR,MAAM,EAAE,GAAG,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,GAAG,EAAE,CAAC;QACb,QAAQ,EAAE,GAAG,EAAE,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;QACtC,MAAM,EAAE,WAAW,CAAC;QACpB,eAAe,CAAC,EAAE,GAAG,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;iBAGa;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,qBAAqB,CAAC,EAAE,KAAK,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE;QACX,QAAQ,EAAE,KAAK,GAAG,KAAK,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAC3B;QACE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;KAC3C,GACD,IAAI,GACJ,SAAS,CAAC;CACf,GAAG,OAAO,CAAC,cAAc,CAAC,CAigB1B"}
|
|
@@ -22,6 +22,22 @@ function costUsdFromCenticents(value) {
|
|
|
22
22
|
return Math.round((value / 10_000) * 1_000_000) / 1_000_000;
|
|
23
23
|
}
|
|
24
24
|
const MAX_TRACKED_GENERATION_TOOL_CALLS = 50;
|
|
25
|
+
const MAX_TOOL_ERROR_MESSAGE_LENGTH = 500;
|
|
26
|
+
const STANDALONE_API_KEY_PATTERN = /\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{8,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}|AIza[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,})\b/g;
|
|
27
|
+
function truncateToolErrorMessage(value) {
|
|
28
|
+
return value.length > MAX_TOOL_ERROR_MESSAGE_LENGTH
|
|
29
|
+
? `${value.slice(0, MAX_TOOL_ERROR_MESSAGE_LENGTH)}…`
|
|
30
|
+
: value;
|
|
31
|
+
}
|
|
32
|
+
function redactToolErrorMessage(value) {
|
|
33
|
+
const credentialName = "authorization|cookie|api[_ -]?key|password|secret|token|access[_ -]?token|refresh[_ -]?token";
|
|
34
|
+
const labeledCredential = `(["']?\\b(?:${credentialName})\\b["']?\\s*[:=]\\s*["']?)`;
|
|
35
|
+
return value
|
|
36
|
+
.replace(new RegExp(`${labeledCredential}(?:Bearer|Basic)\\s+[^"'\\s,;)}\\]]+`, "gi"), "$1[REDACTED]")
|
|
37
|
+
.replace(new RegExp(`${labeledCredential}[^"'\\s,;)}\\[\\]]+`, "gi"), "$1[REDACTED]")
|
|
38
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "[REDACTED]")
|
|
39
|
+
.replace(STANDALONE_API_KEY_PATTERN, "[REDACTED]");
|
|
40
|
+
}
|
|
25
41
|
function emitLlmGenerationTrackingEvent(args) {
|
|
26
42
|
const provider = llmProviderFromEngine(args.engineName, args.model);
|
|
27
43
|
const costUsd = costUsdFromCenticents(args.costCentsX100);
|
|
@@ -297,6 +313,9 @@ export async function instrumentAgentLoop(opts) {
|
|
|
297
313
|
: explicitError
|
|
298
314
|
? "tool_error"
|
|
299
315
|
: "legacy_inferred_error",
|
|
316
|
+
error_message: isError && config.captureToolResults
|
|
317
|
+
? truncateToolErrorMessage(redactToolErrorMessage(event.result))
|
|
318
|
+
: undefined,
|
|
300
319
|
});
|
|
301
320
|
}
|
|
302
321
|
// Finalize the OTel tool span. If the span promise hasn't resolved yet
|
|
@@ -385,6 +404,7 @@ export async function instrumentAgentLoop(opts) {
|
|
|
385
404
|
for (const [counter, pending] of pendingTools) {
|
|
386
405
|
toolCallCount += 1;
|
|
387
406
|
failedTools += 1;
|
|
407
|
+
const interruptedMessage = "Tool call interrupted before completion";
|
|
388
408
|
if (counter < MAX_TRACKED_GENERATION_TOOL_CALLS) {
|
|
389
409
|
generationToolCalls.set(counter, {
|
|
390
410
|
name: pending.toolName,
|
|
@@ -392,9 +412,11 @@ export async function instrumentAgentLoop(opts) {
|
|
|
392
412
|
duration_ms: Math.max(0, runEnd - pending.startMs),
|
|
393
413
|
status: "error",
|
|
394
414
|
error_class: "interrupted",
|
|
415
|
+
error_message: config.captureToolResults
|
|
416
|
+
? interruptedMessage
|
|
417
|
+
: undefined,
|
|
395
418
|
});
|
|
396
419
|
}
|
|
397
|
-
const interruptedMessage = "Tool call interrupted before completion";
|
|
398
420
|
if (pending.otelSpan) {
|
|
399
421
|
openOtelToolSpans.delete(pending.otelSpan);
|
|
400
422
|
endAgentSpan(pending.otelSpan, {
|