@hipnation-truth/sdk 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +103 -1
- package/dist/index.d.ts +103 -1
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -0
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.ts +24 -1
- package/dist/react.js +35 -0
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/resources/appointments.ts","../src/resources/attachments.ts","../src/resources/dialpad.ts","../src/resources/ehr.ts","../src/resources/notes.ts","../src/resources/notifications.ts","../src/resources/patient-details.ts","../src/resources/patients.ts","../src/resources/physicians.ts","../src/resources/reminders.ts","../src/resources/tasks.ts","../src/resources/translation.ts","../src/tracking/tracker.ts","../src/web-push.ts","../src/tracking/events.ts","../src/types/config.ts"],"sourcesContent":["/**\n * TruthClient -- main entry point for the @hipnation-truth/sdk package.\n *\n * Provides:\n * - `.patients` Resource-based patient data access (Convex-backed)\n * - `.appointments` Resource-based appointment data access (Convex-backed)\n * - `.ehr` EHR proxy access (Elation, Hint)\n * - `.messages` Messaging proxy access (Dialpad)\n * - `.track()` Fire-and-forget event tracking (batched HTTP -> Truth API)\n * - `.identify()` Set default actor context for subsequent events\n * - `.flush()` Force flush of buffered events (for graceful shutdown)\n */\n\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { AppointmentResource } from \"./resources/appointments\";\nimport { AttachmentsResource } from \"./resources/attachments\";\nimport { MessagesResource } from \"./resources/dialpad\";\nimport { EhrResource } from \"./resources/ehr\";\nimport { NotesResource } from \"./resources/notes\";\nimport { NotificationsResource } from \"./resources/notifications\";\nimport { PatientDetailsResource } from \"./resources/patient-details\";\nimport { PatientResource } from \"./resources/patients\";\nimport { PhysiciansResource } from \"./resources/physicians\";\nimport { RemindersResource } from \"./resources/reminders\";\nimport { TasksResource } from \"./resources/tasks\";\nimport { TranslationResource } from \"./resources/translation\";\nimport type {\n EventPayloadMap,\n EventType,\n TrackOptions,\n} from \"./tracking/events\";\nimport {\n DEFAULT_BATCH_SIZE,\n DEFAULT_FLUSH_INTERVAL_MS,\n Tracker,\n} from \"./tracking/tracker\";\nimport type { ActorContext, TruthClientConfig } from \"./types/config\";\nimport {\n isWebPushSupported,\n registerServiceWorker,\n subscribeToPush,\n subscriptionToJSON,\n} from \"./web-push\";\n\n// ---------------------------------------------------------------------------\n// Environment -> Convex URL mapping\n// ---------------------------------------------------------------------------\n\n// Environment → Convex deployment URL.\n//\n// Topology mirrors API_URLS in tracker.ts:\n// local / staging / stg / sandbox → sandbox Convex (courteous-duck-623)\n// uat / production → production Convex (gallant-gecko-217)\n//\n// UAT shares production resources. Staging is the isolated sandbox env.\nconst CONVEX_URLS: Record<string, string> = {\n local: \"https://courteous-duck-623.convex.cloud\",\n staging: \"https://courteous-duck-623.convex.cloud\",\n stg: \"https://courteous-duck-623.convex.cloud\",\n sandbox: \"https://courteous-duck-623.convex.cloud\",\n uat: \"https://gallant-gecko-217.convex.cloud\",\n production: \"https://gallant-gecko-217.convex.cloud\",\n};\n\n// ---------------------------------------------------------------------------\n// TruthClient\n// ---------------------------------------------------------------------------\n\nclass TruthClient {\n /** Patient data resource (Convex-backed) */\n readonly patients: PatientResource;\n\n /** Appointment data resource (Convex-backed) */\n readonly appointments: AppointmentResource;\n\n /** EHR proxy — typed access to Elation and Hint APIs through Truth */\n readonly ehr: EhrResource;\n\n /** Messaging proxy — typed access to Dialpad APIs through Truth */\n readonly messages: MessagesResource;\n\n /** Conversation reminders (Convex-backed, durable scheduler) */\n readonly reminders: RemindersResource;\n\n /** Translation (Azure Translator proxy) */\n readonly translation: TranslationResource;\n\n /** Tasks (Convex-backed) */\n readonly tasks: TasksResource;\n\n /** Patient details — merged Hint + Elation lookups via Truth API */\n readonly patientDetails: PatientDetailsResource;\n\n /** Attachments — presigned S3 upload/download + Convex metadata */\n readonly attachments: AttachmentsResource;\n\n /** Notes — push formatted notes to Elation */\n readonly notes: NotesResource;\n\n /** Physicians (Convex-backed cache from Elation) */\n readonly physicians: PhysiciansResource;\n\n /** Push / web notifications (AWS End User Messaging) */\n readonly notifications: NotificationsResource;\n\n private readonly convex: ConvexHttpClient;\n private readonly tracker: Tracker;\n private _vapidPublicKey: string | null = null;\n private _webPushReady: Promise<void> | null = null;\n private readonly _serviceWorkerPath: string;\n\n constructor(config: TruthClientConfig) {\n // Resolve Convex URL\n const convexUrl =\n config.convexUrl ?? CONVEX_URLS[config.environment] ?? CONVEX_URLS.local;\n\n // Initialize Convex HTTP client for data access\n this.convex = new ConvexHttpClient(convexUrl);\n\n // Initialize event tracker\n this.tracker = new Tracker({\n apiKey: config.apiKey,\n environment: config.environment,\n source: config.source ?? \"unknown\",\n sourceVersion: config.sourceVersion ?? \"unknown\",\n tenantId: config.tenantId ?? \"\",\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n apiBaseUrl: config.apiBaseUrl,\n });\n\n const apiUrl = this.tracker.apiUrl;\n\n // Initialize resources\n this.patients = new PatientResource(this.convex);\n this.appointments = new AppointmentResource(this.convex);\n this.ehr = new EhrResource(apiUrl);\n this.messages = new MessagesResource(apiUrl, config.apiKey);\n this.reminders = new RemindersResource(this.convex);\n this.translation = new TranslationResource(apiUrl, config.apiKey);\n this.tasks = new TasksResource(this.convex);\n this.patientDetails = new PatientDetailsResource(apiUrl, config.apiKey);\n this.attachments = new AttachmentsResource(\n apiUrl,\n config.apiKey,\n this.convex,\n );\n this.notes = new NotesResource(apiUrl, config.apiKey);\n this.physicians = new PhysiciansResource(this.convex);\n this.notifications = new NotificationsResource(apiUrl, config.apiKey);\n this._serviceWorkerPath = config.serviceWorkerPath ?? \"/truth-sw.js\";\n\n if (\n typeof window !== \"undefined\" &&\n isWebPushSupported() &&\n config.autoInitServiceWorker !== false\n ) {\n this._webPushReady = this.initWebPush();\n }\n }\n\n get vapidPublicKey(): string | null {\n return this._vapidPublicKey;\n }\n\n get webPushReady(): Promise<void> | null {\n return this._webPushReady;\n }\n\n private async initWebPush(): Promise<void> {\n try {\n const key = await this.notifications.getVapidKey();\n if (!key) {\n return;\n }\n this._vapidPublicKey = key;\n\n const registration = await registerServiceWorker(this._serviceWorkerPath);\n await navigator.serviceWorker.ready;\n const subscription = await subscribeToPush(registration, key);\n const subJSON = subscriptionToJSON(subscription);\n\n await this.notifications.registerDevice({\n userId: \"__pending__\",\n platform: \"web\",\n webPushSubscription: subJSON,\n locale: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n } catch {\n // Web push init is best-effort — don't break the client.\n }\n }\n\n /**\n * The resolved Truth API base URL for this environment.\n * Use this when making HTTP calls to Truth's proxy endpoints\n * (e.g., EHR proxy, messages proxy).\n *\n * @example\n * ```ts\n * const url = `${truth.apiBaseUrl}/api/ehr/elation/patients/123`;\n * ```\n */\n get apiBaseUrl(): string {\n return this.tracker.apiUrl;\n }\n\n /**\n * Track an event. Fire-and-forget -- the event is buffered internally\n * and flushed in batches to the Truth API.\n *\n * @example\n * ```ts\n * truth.track('conversation.message_sent.v1', {\n * channel: 'sms',\n * direction: 'outbound',\n * message_chars: 140,\n * has_attachment: false,\n * provider_system: 'dialpad',\n * });\n * ```\n */\n track<T extends EventType>(\n eventType: T,\n payload: EventPayloadMap[T],\n options?: TrackOptions,\n ): void {\n this.tracker.track(eventType, payload, options);\n }\n\n /**\n * Set the default actor context for all subsequent tracked events.\n * Can be overridden per-event via TrackOptions.\n *\n * @example\n * ```ts\n * truth.identify('user_123', 'user');\n * ```\n */\n identify(actorId: string, actorType: ActorContext[\"actorType\"]): void {\n this.tracker.setActor({ actorId, actorType });\n }\n\n /**\n * Flush all buffered events immediately. Returns a Promise that resolves\n * when the flush completes. Use this for graceful shutdown.\n *\n * @example\n * ```ts\n * process.on('SIGTERM', async () => {\n * await truth.flush();\n * process.exit(0);\n * });\n * ```\n */\n async flush(): Promise<void> {\n await this.tracker.flush();\n }\n\n /**\n * Gracefully shut down the client. Flushes all pending events and\n * releases resources.\n */\n async destroy(): Promise<void> {\n await this.tracker.shutdown();\n }\n}\n\nexport { TruthClient };\n","/**\n * AppointmentResource provides data access to normalized appointment records\n * backed by Convex.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type { Appointment, AppointmentListOptions } from \"../types/appointment\";\nimport type { PaginatedResult } from \"../types/config\";\n\nclass AppointmentResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Get an appointment by its Truth platform ID.\n */\n async get(id: string): Promise<Appointment | null> {\n try {\n const result = await this.convex.query(\n \"appointments:getById\" as never,\n { id } as never,\n );\n return (result as Appointment) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * List appointments with optional filters, pagination, and limit.\n */\n async list(\n options?: AppointmentListOptions,\n ): Promise<PaginatedResult<Appointment>> {\n try {\n const result = await this.convex.query(\n \"appointments:list\" as never,\n {\n patientId: options?.patientId,\n startDate: options?.startDate,\n endDate: options?.endDate,\n status: options?.status,\n limit: options?.limit,\n cursor: options?.cursor,\n } as never,\n );\n\n const typed = result as PaginatedResult<Appointment> | undefined;\n\n return typed ?? { data: [], cursor: null, hasMore: false };\n } catch {\n return { data: [], cursor: null, hasMore: false };\n }\n }\n}\n\nexport { AppointmentResource };\n","/**\n * AttachmentsResource — presigned S3 upload/download + Convex metadata.\n *\n * Replaces CommHub's base64-in-Postgres attachment flow:\n * 1. client.attachments.createUploadUrl(...) → presigned PUT URL\n * 2. Client PUTs bytes directly to S3\n * 3. client.attachments.record(...) → writes Convex metadata\n * 4. client.attachments.getDownloadUrl(s3Key) → presigned GET URL\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\n\nexport interface CreateUploadUrlInput {\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n}\n\nexport interface CreateUploadUrlResult {\n uploadUrl: string;\n s3Key: string;\n expiresIn: number;\n applicationId: string | null;\n}\n\nexport interface GetDownloadUrlResult {\n url: string;\n expiresIn: number;\n}\n\nexport interface RecordAttachmentInput {\n s3Key: string;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n}\n\nexport interface Attachment {\n _id: string;\n s3Key: string;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n createdAt: string;\n}\n\nclass AttachmentsError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Attachment ${operation} failed (HTTP ${status})`);\n this.name = \"AttachmentsError\";\n this.status = status;\n }\n}\n\nclass AttachmentsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly convex: ConvexHttpClient;\n\n constructor(\n apiBaseUrl: string,\n apiKey: string,\n convexClient: ConvexHttpClient,\n ) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n this.convex = convexClient;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new AttachmentsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async createUploadUrl(\n input: CreateUploadUrlInput,\n ): Promise<CreateUploadUrlResult> {\n return await this.post<CreateUploadUrlResult>(\n \"/attachments/upload-url\",\n input,\n );\n }\n\n async getDownloadUrl(\n s3Key: string,\n expiresIn?: number,\n ): Promise<GetDownloadUrlResult> {\n return await this.post<GetDownloadUrlResult>(\"/attachments/download-url\", {\n s3Key,\n expiresIn,\n });\n }\n\n async record(\n input: RecordAttachmentInput,\n ): Promise<{ attachmentId: string; s3Key: string }> {\n return (await this.convex.mutation(\n \"attachments:record\" as never,\n input as never,\n )) as { attachmentId: string; s3Key: string };\n }\n\n async get(attachmentId: string): Promise<Attachment | null> {\n try {\n const row = (await this.convex.query(\n \"attachments:getById\" as never,\n { attachmentId } as never,\n )) as Attachment | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByConversation(conversationId: string): Promise<Attachment[]> {\n try {\n const rows = (await this.convex.query(\n \"attachments:listByConversation\" as never,\n { conversationId } as never,\n )) as Attachment[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * One-shot upload: presign → PUT to S3 → record in Convex → return a\n * 7-day signed download URL ready to embed in an outbound SMS. Caller\n * passes the resulting `downloadUrl` to `messages.dialpad.sendSms` (or\n * the new `messages.sendAttachmentMessage`) to actually deliver it.\n *\n * Replaces CommHub's NestJS `/send-attachment` controller in a single\n * SDK call — no base64 round-trip, no legacy `/attachments/:id/download`\n * REST endpoint required.\n */\n async upload(input: {\n file: Blob | ArrayBuffer | Uint8Array;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n /** Download URL TTL in seconds. Default 7 days. */\n downloadExpiresIn?: number;\n }): Promise<{\n attachmentId: string;\n s3Key: string;\n downloadUrl: string;\n }> {\n const presigned = await this.createUploadUrl({\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n conversationId: input.conversationId,\n });\n\n // Cast through `BodyInit` — Uint8Array is accepted by undici/fetch\n // at runtime but the lib.dom types only declare BufferSource so TS\n // doesn't see the overload. Same trick the AttachmentsResource\n // already uses elsewhere.\n const body: BodyInit =\n input.file instanceof Blob\n ? input.file\n : input.file instanceof Uint8Array\n ? (input.file as unknown as BodyInit)\n : (new Uint8Array(input.file) as unknown as BodyInit);\n\n // 30s timeout matches the legacy NestJS handler so a stuck S3 PUT\n // can't hang the calling UI thread indefinitely.\n const abort = new AbortController();\n const timer = setTimeout(() => abort.abort(), 30_000);\n let putRes: Response;\n try {\n putRes = await fetch(presigned.uploadUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": input.mimeType },\n body,\n signal: abort.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (!putRes.ok) {\n throw new AttachmentsError(\n \"s3-put\",\n putRes.status,\n `S3 PUT ${putRes.status} for ${presigned.s3Key}`,\n );\n }\n\n const recorded = await this.record({\n s3Key: presigned.s3Key,\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n conversationId: input.conversationId,\n uploadedBy: input.uploadedBy,\n });\n\n const signed = await this.getDownloadUrl(\n presigned.s3Key,\n input.downloadExpiresIn ?? 7 * 24 * 3600,\n );\n\n return {\n attachmentId: recorded.attachmentId,\n s3Key: presigned.s3Key,\n downloadUrl: signed.url,\n };\n }\n}\n\nexport { AttachmentsResource, AttachmentsError };\n","/**\n * Dialpad resource — typed access to Truth's Dialpad proxy endpoints.\n *\n * @example\n * ```ts\n * const sms = await truth.messages.dialpad.sendSms({ from_number, to_number, message });\n * const call = await truth.messages.dialpad.initiateCall(userId, phoneNumber);\n * await truth.messages.dialpad.hangupCall(callId);\n * const url = await truth.messages.dialpad.authenticateVoicemail(voicemailLink);\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface SendSmsParams {\n from_number: string;\n to_number: string;\n message?: string;\n media?: string | string[];\n}\n\ninterface SendSmsResponse {\n id: string;\n message_status: string;\n [key: string]: unknown;\n}\n\ninterface InitiateCallResponse {\n call_id: number;\n [key: string]: unknown;\n}\n\ninterface CallStatusResponse {\n state: string;\n [key: string]: unknown;\n}\n\ninterface DialpadUser {\n id: number;\n emails: string[];\n first_name?: string;\n last_name?: string;\n [key: string]: unknown;\n}\n\ninterface DialpadUserListResponse {\n items: DialpadUser[];\n}\n\ninterface DialpadNumberInfo {\n user_id?: number;\n type?: string;\n [key: string]: unknown;\n}\n\ninterface VoicemailAuthResponse {\n success: boolean;\n authenticated_url: string | null;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Dialpad Resource\n// ---------------------------------------------------------------------------\n\nclass DialpadResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /**\n * Send an SMS or MMS message via Dialpad.\n */\n async sendSms(params: SendSmsParams): Promise<SendSmsResponse> {\n const body = {\n to_numbers: [params.to_number],\n from_number: params.from_number,\n infer_country_code: false,\n ...(params.message ? { text: params.message } : {}),\n ...(params.media ? { media: params.media } : {}),\n };\n\n return this.post<SendSmsResponse>(\"/sms\", body);\n }\n\n /**\n * Initiate an outbound call from a Dialpad user to a phone number.\n */\n async initiateCall(\n userId: number,\n phoneNumber: string,\n ): Promise<InitiateCallResponse> {\n return this.post<InitiateCallResponse>(`/users/${userId}/initiate_call`, {\n phone_number: phoneNumber,\n });\n }\n\n /**\n * Hang up an active call.\n */\n async hangupCall(callId: number): Promise<void> {\n await this.put(`/call/${callId}/actions/hangup`);\n }\n\n /**\n * Alias for `hangupCall` — mirrors the CommHub `endCall` action name so\n * the SDK swap is a direct rename.\n */\n async endCall(callId: number): Promise<void> {\n await this.hangupCall(callId);\n }\n\n /**\n * Send an MMS with a pre-uploaded attachment. Takes the S3 key returned\n * by `client.attachments.createUploadUrl(...)` + PUT, fetches a short-\n * lived download URL, and hands it to Dialpad as `media[]`.\n *\n * Replaces CommHub's `sendAttachment` Hasura Action (which stored bytes\n * as base64 in Postgres and served them through a GET endpoint).\n */\n async sendAttachmentWithUrl(params: {\n from_number: string;\n to_number: string;\n mediaUrl: string;\n message?: string;\n }): Promise<SendSmsResponse> {\n return this.sendSms({\n from_number: params.from_number,\n to_number: params.to_number,\n message: params.message,\n media: [params.mediaUrl],\n });\n }\n\n /**\n * Get the status of a call.\n */\n async getCallStatus(callId: number): Promise<CallStatusResponse | null> {\n try {\n return await this.get<CallStatusResponse>(`/call/${callId}`);\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Get a Dialpad user by their user ID.\n */\n async getUser(userId: string | number): Promise<DialpadUser | null> {\n try {\n return await this.get<DialpadUser>(`/users/${userId}`);\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Find a Dialpad user by email.\n */\n async getUserByEmail(email: string): Promise<DialpadUser | null> {\n const result = await this.get<DialpadUserListResponse>(\"/users\", {\n email,\n });\n return result.items?.[0] ?? null;\n }\n\n /**\n * Find a Dialpad user by phone number.\n */\n async getUserByPhoneNumber(phoneNumber: string): Promise<DialpadUser | null> {\n const result = await this.get<DialpadUserListResponse>(\"/users\", {\n number: phoneNumber,\n });\n return result.items?.[0] ?? null;\n }\n\n /**\n * Get information about a Dialpad phone number.\n */\n async getNumberInfo(phoneNumber: string): Promise<DialpadNumberInfo | null> {\n try {\n const cleanNumber = phoneNumber.replace(/[^\\d+]/g, \"\");\n return await this.get<DialpadNumberInfo>(\n `/numbers/${encodeURIComponent(cleanNumber)}`,\n );\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Authenticate a voicemail download URL. Truth appends the Dialpad API key,\n * follows redirects, and returns the clean URL for client-side playback.\n */\n async authenticateVoicemail(voicemailLink: string): Promise<string | null> {\n const url = `${this.baseUrl}/api/messages/dialpad/voicemail/authenticate`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify({ voicemail_link: voicemailLink }),\n });\n\n const result = (await response.json()) as VoicemailAuthResponse;\n\n if (!response.ok || !result.success) {\n return null;\n }\n\n return result.authenticated_url;\n }\n\n // -----------------------------------------------------------------------\n // Internal HTTP helpers\n // -----------------------------------------------------------------------\n\n private async get<T>(\n path: string,\n params?: Record<string, unknown>,\n ): Promise<T> {\n const url = new URL(`/api/messages/dialpad${path}`, this.baseUrl);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"GET\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n private async post<T>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/messages/dialpad${path}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"POST\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n private async put<T = void>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/messages/dialpad${path}`;\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"PUT\", path, response.status);\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n return (await response.json()) as T;\n }\n\n return undefined as T;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Messages Resource — namespace for messaging providers\n// ---------------------------------------------------------------------------\n\nclass MessagesResource {\n readonly dialpad: DialpadResource;\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.dialpad = new DialpadResource(apiBaseUrl, apiKey);\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /**\n * End a Dialpad call via the typed oRPC procedure (POST hangup, with\n * 404→`alreadyEnded` so the UI doesn't flash an error on a natural\n * race). Replaces CommHub's `useEndCallMutation` Hasura action.\n *\n * Prefer this over `messages.dialpad.endCall` which goes through the\n * generic proxy (PUT, no 404 handling).\n */\n async endCall(callId: number | string): Promise<{\n ok: true;\n alreadyEnded: boolean;\n }> {\n const res = await fetch(`${this.baseUrl}/api/conversations/calls/end`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify({ callId }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `messages.endCall failed (HTTP ${res.status}): ${text.slice(0, 200)}`,\n );\n }\n return (await res.json()) as { ok: true; alreadyEnded: boolean };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nclass DialpadProxyError extends Error {\n readonly method: string;\n readonly path: string;\n readonly status: number;\n\n constructor(method: string, path: string, status: number) {\n super(\n `Dialpad proxy error: ${method} /api/messages/dialpad${path} returned ${status}`,\n );\n this.name = \"DialpadProxyError\";\n this.method = method;\n this.path = path;\n this.status = status;\n }\n}\n\nexport { DialpadResource, DialpadProxyError, MessagesResource };\nexport type {\n SendSmsParams,\n SendSmsResponse,\n InitiateCallResponse,\n CallStatusResponse,\n DialpadUser,\n DialpadNumberInfo,\n VoicemailAuthResponse,\n};\n","/**\n * EHR proxy resource — typed access to Truth's EHR proxy endpoints.\n *\n * Provides per-provider proxy methods so consumers never construct\n * proxy URLs manually.\n *\n * @example\n * ```ts\n * const patient = await truth.ehr.elation.get('/patients/123/');\n * const notes = await truth.ehr.elation.post('/non_visit_notes/', noteData);\n * const hintPatient = await truth.ehr.hint.get('/provider/patients/456');\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Provider proxy\n// ---------------------------------------------------------------------------\n\nclass EhrProviderProxy {\n private readonly baseUrl: string;\n private readonly provider: string;\n\n constructor(apiBaseUrl: string, provider: string) {\n this.baseUrl = apiBaseUrl;\n this.provider = provider;\n }\n\n /**\n * GET request to the EHR proxy.\n * @param path — path relative to the provider root (e.g., \"/patients/123/\")\n * @param params — optional query parameters\n */\n async get<T = unknown>(\n path: string,\n params?: Record<string, unknown>,\n ): Promise<T> {\n const url = new URL(`/api/ehr/${this.provider}${path}`, this.baseUrl);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"GET\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * POST request to the EHR proxy.\n */\n async post<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"POST\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * PUT request to the EHR proxy.\n */\n async put<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"PUT\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * PATCH request to the EHR proxy.\n */\n async patch<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"PATCH\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * DELETE request to the EHR proxy.\n */\n async delete<T = unknown>(path: string): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"DELETE\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n}\n\n// ---------------------------------------------------------------------------\n// EHR Resource — exposes per-provider proxies\n// ---------------------------------------------------------------------------\n\nclass EhrResource {\n /** Elation EHR proxy */\n readonly elation: EhrProviderProxy;\n\n /** Hint Health proxy */\n readonly hint: EhrProviderProxy;\n\n constructor(apiBaseUrl: string) {\n this.elation = new EhrProviderProxy(apiBaseUrl, \"elation\");\n this.hint = new EhrProviderProxy(apiBaseUrl, \"hint\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nclass EhrProxyError extends Error {\n readonly provider: string;\n readonly method: string;\n readonly path: string;\n readonly status: number;\n\n constructor(provider: string, method: string, path: string, status: number) {\n super(\n `EHR proxy error: ${method} /api/ehr/${provider}${path} returned ${status}`,\n );\n this.name = \"EhrProxyError\";\n this.provider = provider;\n this.method = method;\n this.path = path;\n this.status = status;\n }\n}\n\nexport { EhrResource, EhrProviderProxy, EhrProxyError };\n","/**\n * NotesResource — push formatted notes to Elation.\n *\n * Caller assembles the note body (text + bullets). Truth owns the Elation\n * OAuth token and the HTTP call so applications can drop Elation\n * credentials from their own config.\n */\n\nexport interface NonVisitNoteBullet {\n text: string;\n category?: string;\n}\n\nexport interface PushNoteToElationInput {\n patient: number;\n physician: number;\n practice: number;\n note: { text: string; type?: string };\n document_date?: string;\n chart_date?: string;\n bullets?: NonVisitNoteBullet[];\n}\n\nexport interface PushNoteToElationResult {\n success: boolean;\n elationNoteId: number | null;\n}\n\nclass NotesError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Notes ${operation} failed (HTTP ${status})`);\n this.name = \"NotesError\";\n this.status = status;\n }\n}\n\nexport interface PushConversationToElationInput {\n /** One-of: conversationId (Convex) or phonePair. */\n conversationId?: string;\n phonePair?: string;\n /** Actor user id — recorded on the audit row. */\n initiatedBy: string;\n /** Pre-assembled note body (transcript-style text). */\n noteText: string;\n bullets?: NonVisitNoteBullet[];\n notesPushed?: number;\n messagesPushed?: number;\n tasksPushed?: number;\n}\n\nexport interface PushConversationToElationResult {\n success: true;\n batchId: string;\n elationNoteId: number | null;\n notesPushed: number;\n messagesPushed: number;\n tasksPushed: number;\n}\n\nclass NotesResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /** 30s upstream timeout — Elation API has occasional slow hops; we\n * don't want a pending mutation to hold the UI thread indefinitely. */\n private static readonly REQUEST_TIMEOUT_MS = 30_000;\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const controller = new AbortController();\n const timeout = setTimeout(\n () => controller.abort(),\n NotesResource.REQUEST_TIMEOUT_MS,\n );\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n // Transport failures (DNS, TCP, TLS, AbortError) become typed\n // NotesError so consumers don't have to special-case raw fetch\n // exceptions. status=0 signals \"never made it to the server\".\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n const message = isAbort\n ? `Notes ${path} timed out after ${NotesResource.REQUEST_TIMEOUT_MS}ms`\n : err instanceof Error\n ? err.message\n : \"Notes request failed before response\";\n throw new NotesError(path, 0, message);\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotesError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n /**\n * Low-level — caller has already resolved Elation patient / physician\n * / practice. Use `pushConversationToElation` if you only have a\n * conversation handle.\n */\n async pushToElation(\n input: PushNoteToElationInput,\n ): Promise<PushNoteToElationResult> {\n return await this.post<PushNoteToElationResult>(\n \"/notes/push-to-elation\",\n input,\n );\n }\n\n /**\n * Orchestrator — pass a conversation handle + pre-assembled note text.\n * Truth resolves the linked patient via Convex, looks up\n * physician/practice in Elation, posts the note, and writes an audit\n * row to `elationSyncEvents`. Replaces CommHub's `pushNotesToElation`\n * Hasura action.\n */\n async pushConversationToElation(\n input: PushConversationToElationInput,\n ): Promise<PushConversationToElationResult> {\n return await this.post<PushConversationToElationResult>(\n \"/notes/push-conversation-to-elation\",\n input,\n );\n }\n}\n\nexport { NotesResource, NotesError };\n","/**\n * NotificationsResource — wraps Truth's /api/notifications/* endpoints.\n *\n * Server-side use (for example from a CommHub backend job or\n * another service): `truth.notifications.send({ userId, title, body })`.\n *\n * Client-side React usage lives in `@hipnation-truth/sdk/react` via\n * the `useNotifications` hook which mirrors `expo-notifications`.\n */\n\nexport type NotificationPlatform = \"ios\" | \"android\" | \"web\";\n\nexport interface RegisterDeviceInput {\n userId: string;\n platform: NotificationPlatform;\n nativeToken?: string;\n webPushSubscription?: {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n };\n appVersion?: string;\n osVersion?: string;\n locale?: string;\n timezone?: string;\n /**\n * iOS only — which APNs endpoint the `nativeToken` was issued\n * against. The token bytes don't carry this; it's determined by\n * the build's `aps-environment` entitlement\n * (`development` ⇒ sandbox, `production` ⇒ production). When the\n * consumer is an Expo app, detect via\n * `Application.getIosPushNotificationServiceEnvironmentAsync()`\n * from `expo-application` and pass the normalised value here.\n * Omitting this falls back to the application's default\n * `pushConfig.ios.environment` server-side.\n */\n apnsEnvironment?: \"sandbox\" | \"production\";\n}\n\nexport interface RegisterDeviceResult {\n deviceId: string;\n action: \"inserted\" | \"updated\";\n snsEndpointArn?: string;\n}\n\nexport interface UnregisterDeviceInput {\n nativeToken?: string;\n deviceId?: string;\n}\n\nexport interface SendNotificationInput {\n userId: string;\n title: string;\n body: string;\n data?: Record<string, unknown>;\n badge?: number;\n sound?: string;\n}\n\nexport interface SendNotificationResult {\n delivered: number;\n failed?: number;\n suppressed?: boolean;\n suppressionReason?: string;\n}\n\nexport interface NotificationPreferences {\n channels: { sms: boolean; push: boolean; email: boolean; inApp: boolean };\n quietHours?: {\n enabled: boolean;\n start: string;\n end: string;\n timezone: string;\n };\n doNotDisturbUntil?: string;\n updatedAt: string;\n}\n\nexport interface UpdatePreferencesInput {\n userId: string;\n channels?: NotificationPreferences[\"channels\"];\n quietHours?: NotificationPreferences[\"quietHours\"];\n doNotDisturbUntil?: string | null;\n}\n\nexport interface ScheduleNotificationInput extends SendNotificationInput {\n /** ISO 8601 timestamp; must be strictly in the future. */\n scheduledAt: string;\n}\n\nexport interface ScheduleNotificationResult {\n jobId: string;\n scheduledAt: string;\n}\n\nexport interface CancelScheduledNotificationResult {\n cancelled: boolean;\n reason?: string;\n status?: string;\n}\n\nexport type ScheduledJobStatus =\n | \"pending\"\n | \"executed\"\n | \"cancelled\"\n | \"failed\";\n\nexport interface ScheduledNotification {\n jobId: string;\n userId: string;\n title: string;\n body: string;\n data?: unknown;\n badge?: number;\n sound?: string;\n scheduledAt: string;\n status: ScheduledJobStatus;\n resultHistoryId?: string;\n errorMessage?: string;\n createdAt: string;\n}\n\nexport interface PushEventPayload {\n title: string;\n body: string;\n data?: unknown;\n}\n\nclass NotificationsError extends Error {\n readonly status: number;\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Notifications ${operation} failed (HTTP ${status})`);\n this.name = \"NotificationsError\";\n this.status = status;\n }\n}\n\nclass NotificationsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n private async get<T>(\n path: string,\n params: Record<string, string>,\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}/api${path}`);\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n private async delete<T>(path: string): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"DELETE\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n /**\n * Register a device (or refresh its metadata) for push delivery.\n * Safe to call repeatedly — the server dedupes by native token.\n */\n async registerDevice(\n input: RegisterDeviceInput,\n ): Promise<RegisterDeviceResult> {\n return this.post(\"/notifications/devices/register\", input);\n }\n\n /** Revoke a device — on sign-out or when the OS reports an invalid token. */\n async unregisterDevice(\n input: UnregisterDeviceInput,\n ): Promise<{ revoked: boolean }> {\n return this.post(\"/notifications/devices/unregister\", input);\n }\n\n /**\n * Send a push notification to every active device belonging to\n * `userId`. Honors the user's notificationPreferences (quiet hours,\n * DND, channel off) before publishing.\n */\n async send(input: SendNotificationInput): Promise<SendNotificationResult> {\n return this.post(\"/notifications/send\", input);\n }\n\n /** Read a user's notification preferences. Returns defaults when no row exists. */\n async getPreferences(userId: string): Promise<NotificationPreferences> {\n return this.get(\"/notifications/preferences\", { userId });\n }\n\n async updatePreferences(\n input: UpdatePreferencesInput,\n ): Promise<{ ok: boolean }> {\n return this.post(\"/notifications/preferences\", input);\n }\n\n /**\n * Schedule a future push notification. Convex's native scheduler\n * fires the send at `scheduledAt` and runs the same delivery\n * pipeline as `send()` (preferences, devices, history audit).\n *\n * Throws `NotificationsError` with status 400 if `scheduledAt` is\n * not strictly in the future.\n */\n async schedule(\n input: ScheduleNotificationInput,\n ): Promise<ScheduleNotificationResult> {\n return this.post(\"/notifications/schedule\", input);\n }\n\n /**\n * Cancel a pending scheduled notification. Returns `cancelled: false`\n * (no error) if the job has already executed, was previously\n * cancelled, or no longer exists — `reason` describes which case.\n */\n async cancelScheduled(\n jobId: string,\n ): Promise<CancelScheduledNotificationResult> {\n return this.delete(`/notifications/schedule/${encodeURIComponent(jobId)}`);\n }\n\n /**\n * List scheduled notifications for a user — pending, executed,\n * cancelled, or failed. Most-recent first. Default limit 100.\n */\n async listScheduled(\n userId: string,\n options?: { limit?: number },\n ): Promise<ScheduledNotification[]> {\n const params: Record<string, string> = { userId };\n if (options?.limit !== undefined) {\n params.limit = String(options.limit);\n }\n return this.get(\"/notifications/schedule\", params);\n }\n\n async getVapidKey(): Promise<string | null> {\n try {\n const result = await this.get<{ vapidPublicKey: string | null }>(\n \"/notifications/vapid-key\",\n {},\n );\n return result.vapidPublicKey;\n } catch {\n return null;\n }\n }\n\n onPushReceived(callback: (payload: PushEventPayload) => void): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === \"TRUTH_PUSH_RECEIVED\") {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () =>\n navigator.serviceWorker.removeEventListener(\"message\", handler);\n }\n\n onPushTapped(callback: (payload: PushEventPayload) => void): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === \"TRUTH_PUSH_TAPPED\") {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () =>\n navigator.serviceWorker.removeEventListener(\"message\", handler);\n }\n}\n\nexport { NotificationsResource, NotificationsError };\n","/**\n * PatientDetailsResource — merged Hint + Elation patient lookups.\n *\n * Backed by the Truth API at /api/patients/details/*, authenticated with\n * X-API-Key. Replaces CommHub's getPatientDetails / getPatientBasicDetails\n * / getPatientMedicalDetails actions.\n */\n\nexport interface PatientDetailsInput {\n hintId?: string;\n elationId?: string;\n}\n\nexport interface PatientDetailsResult {\n hint: unknown | null;\n elation: unknown | null;\n resolvedElationId: string | null;\n}\n\nexport interface PatientBasicDetailsResult {\n hint: unknown | null;\n elationId: string | null;\n}\n\nexport interface PatientMedicalDetailsResult {\n problems: unknown | null;\n medications: unknown | null;\n allergies: unknown | null;\n appointments: unknown | null;\n}\n\nclass PatientDetailsError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Patient ${operation} failed (HTTP ${status})`);\n this.name = \"PatientDetailsError\";\n this.status = status;\n }\n}\n\nclass PatientDetailsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new PatientDetailsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async get(input: PatientDetailsInput): Promise<PatientDetailsResult> {\n return await this.post<PatientDetailsResult>(\"/patients/details\", input);\n }\n\n async getBasic(\n input: PatientDetailsInput,\n ): Promise<PatientBasicDetailsResult> {\n return await this.post<PatientBasicDetailsResult>(\n \"/patients/details/basic\",\n input,\n );\n }\n\n async getMedical(elationId: string): Promise<PatientMedicalDetailsResult> {\n return await this.post<PatientMedicalDetailsResult>(\n \"/patients/details/medical\",\n { elationId },\n );\n }\n\n /**\n * Trigger a server-side refresh of the patient's Elation medical\n * records (medications, problems, allergies, appointments) into the\n * Convex cache. Fire-and-forget — the UI should read via the Convex-\n * reactive `usePatientMedical` hook.\n */\n async refreshMedical(elationId: number): Promise<{\n totals: {\n medications: number;\n problems: number;\n allergies: number;\n appointments: number;\n };\n }> {\n return await this.post<{\n totals: {\n medications: number;\n problems: number;\n allergies: number;\n appointments: number;\n };\n }>(\"/patients/medical/refresh\", { elationId });\n }\n}\n\nexport { PatientDetailsResource, PatientDetailsError };\n","/**\n * PatientResource provides data access to normalized patient records\n * backed by Convex.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type { PaginatedResult } from \"../types/config\";\nimport type { Patient, PatientListOptions } from \"../types/patient\";\n\nclass PatientResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Get a patient by their Truth platform ID.\n */\n async get(id: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getById\" as never,\n {\n id,\n } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Get a patient by their Elation EHR ID.\n */\n async getByElationId(elationId: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getByElationId\" as never,\n { elationId } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Get a patient by their Hint EHR ID.\n */\n async getByHintId(hintId: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getByHintId\" as never,\n {\n hintId,\n } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * List patients with optional search, pagination, and limit.\n */\n async list(options?: PatientListOptions): Promise<PaginatedResult<Patient>> {\n try {\n const result = await this.convex.query(\n \"patients:list\" as never,\n {\n search: options?.search,\n limit: options?.limit,\n cursor: options?.cursor,\n } as never,\n );\n\n const typed = result as PaginatedResult<Patient> | undefined;\n\n return typed ?? { data: [], cursor: null, hasMore: false };\n } catch {\n return { data: [], cursor: null, hasMore: false };\n }\n }\n}\n\nexport { PatientResource };\n","/**\n * PhysiciansResource — Convex-backed physician lookups.\n *\n * Populated from Elation by Truth's daily PhysiciansBackfillCron. Replaces\n * per-physician Elation HTTP hops with a single Convex batch query.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\n\nexport interface Physician {\n _id: string;\n elationId: number;\n firstName?: string;\n lastName?: string;\n npi?: string;\n credentials?: string;\n specialties?: string[];\n practice?: number;\n email?: string;\n phone?: string;\n lastSyncedAt: string;\n}\n\nclass PhysiciansResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convex: ConvexHttpClient) {\n this.convex = convex;\n }\n\n /**\n * Resolve a batch of physicians by Elation IDs. Missing ids are dropped.\n */\n async getByElationIds(ids: number[]): Promise<Physician[]> {\n if (ids.length === 0) {\n return [];\n }\n try {\n const rows = (await this.convex.query(\n \"physicians:getByElationIds\" as never,\n { ids } as never,\n )) as Physician[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n async getByElationId(id: number): Promise<Physician | null> {\n try {\n const row = (await this.convex.query(\n \"physicians:getByElationId\" as never,\n { id } as never,\n )) as Physician | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByPractice(practice: number, limit?: number): Promise<Physician[]> {\n try {\n const rows = (await this.convex.query(\n \"physicians:listByPractice\" as never,\n { practice, limit } as never,\n )) as Physician[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { PhysiciansResource };\n","/**\n * RemindersResource — schedule, cancel, and list conversation reminders.\n *\n * Backed by Convex mutations/queries (durable scheduled functions) — replaces\n * the in-memory setTimeout scheduler that used to live in CommHub's NestJS\n * backend.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n Reminder,\n ScheduleReminderInput,\n ScheduleReminderResult,\n} from \"../types/reminder\";\n\nclass RemindersResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Schedule a reminder to fire at `remindAt`. Returns the reminder id,\n * which callers should store if they may want to cancel it later.\n */\n async schedule(\n input: ScheduleReminderInput,\n ): Promise<ScheduleReminderResult> {\n const remindAt =\n input.remindAt instanceof Date\n ? input.remindAt.toISOString()\n : input.remindAt;\n\n const result = (await this.convex.mutation(\n \"reminders:schedule\" as never,\n {\n conversationId: input.conversationId,\n remindAt,\n note: input.note,\n createdBy: input.createdBy,\n } as never,\n )) as ScheduleReminderResult;\n\n return result;\n }\n\n /**\n * Cancel a pending reminder. No-op if the reminder has already fired or\n * been cancelled.\n */\n async cancel(\n reminderId: string,\n cancelledBy: string,\n ): Promise<{ reminderId: string; status: string }> {\n return (await this.convex.mutation(\n \"reminders:cancel\" as never,\n { reminderId, cancelledBy } as never,\n )) as { reminderId: string; status: string };\n }\n\n /**\n * List reminders for a conversation (most recent first).\n */\n async listByConversation(conversationId: string): Promise<Reminder[]> {\n try {\n const rows = (await this.convex.query(\n \"reminders:listByConversation\" as never,\n { conversationId } as never,\n )) as Reminder[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { RemindersResource };\n","/**\n * TasksResource — create, update status, list, get tasks.\n *\n * Backed by Convex. Replaces CommHub's createTaskWithNotification +\n * updateTaskStatusWithNotification actions. Push notification side-effect\n * is emitted downstream via Kinesis.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n CreateTaskInput,\n Task,\n TaskStatus,\n UpdateTaskStatusInput,\n} from \"../types/task\";\n\nclass TasksResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n async create(\n input: CreateTaskInput,\n ): Promise<{ taskId: string; status: TaskStatus }> {\n return (await this.convex.mutation(\n \"tasks:create\" as never,\n input as never,\n )) as { taskId: string; status: TaskStatus };\n }\n\n async updateStatus(input: UpdateTaskStatusInput): Promise<{\n taskId: string;\n status: TaskStatus;\n previousStatus: TaskStatus;\n }> {\n return (await this.convex.mutation(\n \"tasks:updateStatus\" as never,\n input as never,\n )) as {\n taskId: string;\n status: TaskStatus;\n previousStatus: TaskStatus;\n };\n }\n\n async get(taskId: string): Promise<Task | null> {\n try {\n const row = (await this.convex.query(\n \"tasks:get\" as never,\n { taskId } as never,\n )) as Task | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByAssignee(\n assignedTo: string,\n options?: { status?: TaskStatus; limit?: number },\n ): Promise<Task[]> {\n try {\n const rows = (await this.convex.query(\n \"tasks:listByAssignee\" as never,\n {\n assignedTo,\n status: options?.status,\n limit: options?.limit,\n } as never,\n )) as Task[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n async listOpen(limit?: number): Promise<Task[]> {\n try {\n const rows = (await this.convex.query(\n \"tasks:listOpen\" as never,\n { limit } as never,\n )) as Task[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { TasksResource };\n","/**\n * TranslationResource — Azure Translator proxy through Truth API.\n *\n * All three operations are HTTP calls to Truth's oRPC endpoints at\n * `/api/translation/*`, authenticated with the same X-API-Key as the\n * event tracker.\n */\n\nimport type {\n DetectionResult,\n TranslateBatchInput,\n TranslateTextInput,\n TranslationResult,\n} from \"../types/translation\";\n\nclass TranslationError extends Error {\n readonly status: number;\n readonly operation: string;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Translation ${operation} failed (HTTP ${status})`);\n this.name = \"TranslationError\";\n this.status = status;\n this.operation = operation;\n }\n}\n\nclass TranslationResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}/api${path}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new TranslationError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async translate(input: TranslateTextInput): Promise<TranslationResult> {\n return await this.post<TranslationResult>(\"/translation/translate\", input);\n }\n\n async translateBatch(\n input: TranslateBatchInput,\n ): Promise<TranslationResult[]> {\n const response = await this.post<{ results: TranslationResult[] }>(\n \"/translation/translate-batch\",\n input,\n );\n return response.results;\n }\n\n async detect(text: string): Promise<DetectionResult> {\n return await this.post<DetectionResult>(\"/translation/detect\", { text });\n }\n}\n\nexport { TranslationResource, TranslationError };\n","/**\n * Event tracker with batching, retry, and flush support.\n *\n * Buffers events in an internal queue and flushes them to the Truth API\n * endpoint. Flushes occur when the buffer reaches `batchSize` or every\n * `flushIntervalMs` milliseconds, whichever comes first.\n */\n\nimport type { ActorContext } from \"../types/config\";\nimport type {\n EventEnvelope,\n EventPayloadMap,\n EventType,\n TrackOptions,\n} from \"./events\";\n\n// ---------------------------------------------------------------------------\n// UUID v7 helper (no external dependency)\n// ---------------------------------------------------------------------------\n\n/**\n * Generates a UUID v7 string. Uses crypto.randomUUID where available,\n * falling back to a timestamp + random bytes implementation.\n *\n * UUID v7 layout (RFC 9562):\n * 48 bits - unix timestamp (ms)\n * 4 bits - version (0b0111)\n * 12 bits - random\n * 2 bits - variant (0b10)\n * 62 bits - random\n */\nfunction generateUuidV7(): string {\n const now = Date.now();\n\n // 6 bytes of timestamp\n const timeBytes = new Uint8Array(6);\n let ts = now;\n for (let i = 5; i >= 0; i--) {\n timeBytes[i] = ts & 0xff;\n ts = Math.floor(ts / 256);\n }\n\n // 10 bytes of random\n const randomBytes = new Uint8Array(10);\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n globalThis.crypto.getRandomValues\n ) {\n globalThis.crypto.getRandomValues(randomBytes);\n } else {\n for (let i = 0; i < 10; i++) {\n randomBytes[i] = Math.floor(Math.random() * 256);\n }\n }\n\n // Assemble 16 bytes\n const bytes = new Uint8Array(16);\n bytes.set(timeBytes, 0);\n bytes.set(randomBytes, 6);\n\n // Set version (bits 48-51 to 0b0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70;\n\n // Set variant (bits 64-65 to 0b10)\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n // Format as hex string with dashes\n const hex = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20, 32),\n ].join(\"-\");\n}\n\n// ---------------------------------------------------------------------------\n// Environment-based API URL resolution\n// ---------------------------------------------------------------------------\n\n// Environment → Truth API base URL.\n//\n// Topology:\n// local / staging / stg / sandbox → sandbox Truth (app.sandbox.*)\n// uat / production → production Truth (app.truth.*)\n//\n// UAT shares production resources (same Fly Postgres + Hasura + Truth +\n// Convex + EHR tokens as prod). Staging is the isolated sandbox env.\nconst API_URLS: Record<string, string> = {\n local: \"http://localhost:3000\",\n staging: \"https://app.sandbox.communication-hub.com\",\n stg: \"https://app.sandbox.communication-hub.com\",\n sandbox: \"https://app.sandbox.communication-hub.com\",\n uat: \"https://app.truth.communication-hub.com\",\n production: \"https://app.truth.communication-hub.com\",\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_BATCH_SIZE = 25;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\nconst MAX_RETRIES = 3;\nconst BASE_RETRY_DELAY_MS = 500;\n\n// ---------------------------------------------------------------------------\n// Tracker configuration\n// ---------------------------------------------------------------------------\n\ninterface TrackerConfig {\n apiKey: string;\n environment: string;\n source: string;\n sourceVersion: string;\n tenantId: string;\n batchSize: number;\n flushIntervalMs: number;\n apiBaseUrl?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Tracker class\n// ---------------------------------------------------------------------------\n\nclass Tracker {\n private readonly config: TrackerConfig;\n readonly apiUrl: string;\n private queue: EventEnvelope[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private defaultActor: ActorContext | undefined;\n private isFlushing = false;\n private isShutdown = false;\n\n constructor(config: TrackerConfig) {\n this.config = config;\n this.apiUrl =\n config.apiBaseUrl ?? API_URLS[config.environment] ?? API_URLS.local;\n\n this.startFlushInterval();\n this.registerShutdownHooks();\n }\n\n /**\n * Set the default actor context for subsequent events.\n */\n setActor(actor: ActorContext): void {\n this.defaultActor = actor;\n }\n\n /**\n * Enqueue a typed event for delivery. This is fire-and-forget from\n * the caller's perspective -- events are buffered and flushed in batches.\n */\n track<T extends EventType>(\n eventType: T,\n payload: EventPayloadMap[T],\n options?: TrackOptions,\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const now = new Date().toISOString();\n\n const envelope: EventEnvelope = {\n event_id: generateUuidV7(),\n event_type: eventType,\n schema_version: 1,\n occurred_at: options?.occurredAt ?? now,\n received_at: now,\n source: this.config.source,\n source_version: this.config.sourceVersion,\n tenant_id: options?.tenantId ?? this.config.tenantId,\n actor:\n options?.actor ??\n (this.defaultActor\n ? {\n actor_id: this.defaultActor.actorId,\n actor_type: this.defaultActor.actorType,\n }\n : undefined),\n subject: options?.subject,\n compliance: options?.compliance,\n payload: payload as unknown as Record<string, unknown>,\n };\n\n this.queue.push(envelope);\n\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Force an immediate flush of all buffered events.\n * Returns a promise that resolves when the flush completes.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0 || this.isFlushing) {\n return;\n }\n\n this.isFlushing = true;\n const batch = this.queue.splice(0, this.config.batchSize);\n\n try {\n await this.sendBatch(batch);\n } catch {\n // Re-queue events that failed to send (prepend to maintain ordering)\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n\n // If there are still events in the queue, flush again\n if (this.queue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Gracefully shut down the tracker. Flushes remaining events and\n * clears the flush interval.\n */\n async shutdown(): Promise<void> {\n this.isShutdown = true;\n this.stopFlushInterval();\n await this.flush();\n }\n\n /**\n * Send a batch of events to the Truth API with exponential backoff retry.\n */\n private async sendBatch(batch: EventEnvelope[]): Promise<void> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(`${this.apiUrl}/api/events/ingest`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n },\n body: JSON.stringify({ events: batch }),\n });\n\n if (response.ok) {\n return;\n }\n\n // Don't retry 4xx client errors (except 429)\n if (\n response.status >= 400 &&\n response.status < 500 &&\n response.status !== 429\n ) {\n return;\n }\n\n lastError = new Error(\n `HTTP ${response.status}: ${response.statusText}`,\n );\n } catch (error) {\n lastError = error;\n }\n\n // Exponential backoff with jitter\n if (attempt < MAX_RETRIES) {\n const delay = BASE_RETRY_DELAY_MS * 2 ** attempt;\n const jitter = Math.random() * delay * 0.5;\n await sleep(delay + jitter);\n }\n }\n\n throw lastError;\n }\n\n private startFlushInterval(): void {\n if (this.config.flushIntervalMs > 0) {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n // Unref the timer so it doesn't prevent Node.js from exiting\n if (typeof this.flushTimer === \"object\" && \"unref\" in this.flushTimer) {\n this.flushTimer.unref();\n }\n }\n }\n\n private stopFlushInterval(): void {\n if (this.flushTimer !== null) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n private registerShutdownHooks(): void {\n // Only register in Node.js environments\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.on) {\n const shutdownHandler = () => {\n void this.shutdown();\n };\n\n globalThis.process.on(\"beforeExit\", shutdownHandler);\n globalThis.process.on(\"SIGTERM\", shutdownHandler);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport {\n Tracker,\n generateUuidV7,\n DEFAULT_BATCH_SIZE,\n DEFAULT_FLUSH_INTERVAL_MS,\n};\nexport type { TrackerConfig };\n","/**\n * Web Push helpers for browser environments.\n *\n * Handles service worker registration, VAPID push subscription, and\n * message forwarding from the service worker to the main thread.\n */\n\nexport interface WebPushConfig {\n vapidPublicKey: string;\n serviceWorkerPath?: string;\n}\n\nexport function isWebPushSupported(): boolean {\n return (\n typeof window !== \"undefined\" &&\n \"serviceWorker\" in navigator &&\n \"PushManager\" in window\n );\n}\n\nexport async function registerServiceWorker(\n path = \"/truth-sw.js\",\n): Promise<ServiceWorkerRegistration> {\n return navigator.serviceWorker.register(path);\n}\n\nexport async function subscribeToPush(\n registration: ServiceWorkerRegistration,\n vapidPublicKey: string,\n): Promise<PushSubscription> {\n const existing = await registration.pushManager.getSubscription();\n if (existing) {\n return existing;\n }\n\n return registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(\n vapidPublicKey,\n ) as unknown as ArrayBuffer,\n });\n}\n\nexport function subscriptionToJSON(sub: PushSubscription): {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n} {\n const json = sub.toJSON();\n return {\n endpoint: sub.endpoint,\n keys: {\n p256dh: json.keys?.p256dh ?? \"\",\n auth: json.keys?.auth ?? \"\",\n },\n };\n}\n\nfunction urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = \"=\".repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, \"+\").replace(/_/g, \"/\");\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n\nexport function onServiceWorkerMessage(\n type: string,\n callback: (payload: unknown) => void,\n): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === type) {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () => navigator.serviceWorker.removeEventListener(\"message\", handler);\n}\n","/**\n * Typed event definitions for the Truth Platform event store.\n *\n * All 26 event types from the Communication Hub -> Truth Event Store Contract.\n * Events are grouped by domain and include typed payload interfaces.\n */\n\n// ---------------------------------------------------------------------------\n// Event type constants\n// ---------------------------------------------------------------------------\n\nconst CONVERSATION_EVENTS = {\n created: \"conversation.created.v1\",\n messageSent: \"conversation.message_sent.v1\",\n messageReceived: \"conversation.message_received.v1\",\n markedRead: \"conversation.marked_read.v1\",\n attachmentUploaded: \"conversation.attachment_uploaded.v1\",\n attachmentDownloaded: \"conversation.attachment_downloaded.v1\",\n} as const;\n\nconst CALL_EVENTS = {\n initiated: \"call.initiated.v1\",\n connected: \"call.connected.v1\",\n ended: \"call.ended.v1\",\n missed: \"call.missed.v1\",\n} as const;\n\nconst TASK_EVENTS = {\n created: \"task.created.v1\",\n assigned: \"task.assigned.v1\",\n statusChanged: \"task.status_changed.v1\",\n} as const;\n\nconst REMINDER_EVENTS = {\n scheduled: \"reminder.scheduled.v1\",\n triggered: \"reminder.triggered.v1\",\n} as const;\n\nconst TRANSLATION_EVENTS = {\n requested: \"translation.requested.v1\",\n completed: \"translation.completed.v1\",\n} as const;\n\nconst NOTIFICATION_EVENTS = {\n sent: \"notification.sent.v1\",\n delivered: \"notification.delivered.v1\",\n opened: \"notification.opened.v1\",\n} as const;\n\nconst PROVIDER_EVENTS = {\n syncStarted: \"provider.sync_started.v1\",\n syncSucceeded: \"provider.sync_succeeded.v1\",\n syncFailed: \"provider.sync_failed.v1\",\n} as const;\n\nconst AUTH_EVENTS = {\n loginSucceeded: \"auth.login_succeeded.v1\",\n loginFailed: \"auth.login_failed.v1\",\n} as const;\n\nconst SECURITY_EVENTS = {\n accessDenied: \"security.access_denied.v1\",\n} as const;\n\n/**\n * All event type constants grouped by domain.\n */\nconst EVENT_TYPES = {\n conversation: CONVERSATION_EVENTS,\n call: CALL_EVENTS,\n task: TASK_EVENTS,\n reminder: REMINDER_EVENTS,\n translation: TRANSLATION_EVENTS,\n notification: NOTIFICATION_EVENTS,\n provider: PROVIDER_EVENTS,\n auth: AUTH_EVENTS,\n security: SECURITY_EVENTS,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Event type union\n// ---------------------------------------------------------------------------\n\ntype ConversationEventType =\n (typeof CONVERSATION_EVENTS)[keyof typeof CONVERSATION_EVENTS];\ntype CallEventType = (typeof CALL_EVENTS)[keyof typeof CALL_EVENTS];\ntype TaskEventType = (typeof TASK_EVENTS)[keyof typeof TASK_EVENTS];\ntype ReminderEventType = (typeof REMINDER_EVENTS)[keyof typeof REMINDER_EVENTS];\ntype TranslationEventType =\n (typeof TRANSLATION_EVENTS)[keyof typeof TRANSLATION_EVENTS];\ntype NotificationEventType =\n (typeof NOTIFICATION_EVENTS)[keyof typeof NOTIFICATION_EVENTS];\ntype ProviderEventType = (typeof PROVIDER_EVENTS)[keyof typeof PROVIDER_EVENTS];\ntype AuthEventType = (typeof AUTH_EVENTS)[keyof typeof AUTH_EVENTS];\ntype SecurityEventType = (typeof SECURITY_EVENTS)[keyof typeof SECURITY_EVENTS];\n\n/**\n * Union of all 26 registered event type strings.\n */\ntype EventType =\n | ConversationEventType\n | CallEventType\n | TaskEventType\n | ReminderEventType\n | TranslationEventType\n | NotificationEventType\n | ProviderEventType\n | AuthEventType\n | SecurityEventType;\n\n// ---------------------------------------------------------------------------\n// Payload interfaces (per the contract doc)\n// ---------------------------------------------------------------------------\n\ninterface ConversationCreatedPayload {\n channel: string;\n origin_system: string;\n participant_count: number;\n}\n\ninterface ConversationMessageSentPayload {\n channel: string;\n direction: string;\n message_chars: number;\n has_attachment: boolean;\n provider_system: string;\n}\n\ninterface ConversationMessageReceivedPayload {\n channel: string;\n direction: string;\n message_chars: number;\n provider_system: string;\n}\n\ninterface ConversationMarkedReadPayload {\n read_by_actor_id: string;\n unread_count_before: number;\n unread_count_after: number;\n}\n\ninterface ConversationAttachmentUploadedPayload {\n attachment_id: string;\n mime_type: string;\n size_bytes: number;\n storage_class: string;\n}\n\ninterface ConversationAttachmentDownloadedPayload {\n attachment_id: string;\n download_actor_type: string;\n access_path: string;\n}\n\ninterface CallInitiatedPayload {\n direction: string;\n provider_system: string;\n from_number_ref: string;\n to_number_ref: string;\n}\n\ninterface CallConnectedPayload {\n provider_system: string;\n ring_duration_ms: number;\n}\n\ninterface CallEndedPayload {\n provider_system: string;\n duration_ms: number;\n end_reason: string;\n disposition: string;\n}\n\ninterface CallMissedPayload {\n provider_system: string;\n miss_reason: string;\n}\n\ninterface TaskCreatedPayload {\n task_id: string;\n created_by: string;\n assigned_to: string;\n priority: string;\n due_at: string;\n}\n\ninterface TaskAssignedPayload {\n task_id: string;\n assigned_to: string;\n assigned_by: string;\n}\n\ninterface TaskStatusChangedPayload {\n task_id: string;\n status_from: string;\n status_to: string;\n changed_by: string;\n}\n\ninterface ReminderScheduledPayload {\n reminder_id: string;\n conversation_id: string;\n scheduled_for: string;\n scheduled_by: string;\n}\n\ninterface ReminderTriggeredPayload {\n reminder_id: string;\n trigger_result: string;\n notification_attempted: boolean;\n}\n\ninterface TranslationRequestedPayload {\n target_language: string;\n source_language: string;\n char_count: number;\n mode: string;\n}\n\ninterface TranslationCompletedPayload {\n target_language: string;\n provider: string;\n latency_ms: number;\n success: boolean;\n error_code?: string;\n}\n\ninterface NotificationSentPayload {\n notification_id: string;\n channel: string;\n platform: string;\n recipient_ref: string;\n success: boolean;\n}\n\ninterface NotificationDeliveredPayload {\n notification_id: string;\n platform: string;\n delivered_at: string;\n}\n\ninterface NotificationOpenedPayload {\n notification_id: string;\n platform: string;\n opened_at: string;\n}\n\ninterface ProviderSyncStartedPayload {\n provider_system: string;\n operation: string;\n scope: string;\n batch_id: string;\n}\n\ninterface ProviderSyncSucceededPayload {\n provider_system: string;\n operation: string;\n batch_id: string;\n records_processed: number;\n duration_ms: number;\n}\n\ninterface ProviderSyncFailedPayload {\n provider_system: string;\n operation: string;\n batch_id: string;\n error_code: string;\n retryable: boolean;\n}\n\ninterface AuthLoginSucceededPayload {\n auth_provider: string;\n platform: string;\n session_ref: string;\n}\n\ninterface AuthLoginFailedPayload {\n auth_provider: string;\n platform: string;\n failure_code: string;\n}\n\ninterface SecurityAccessDeniedPayload {\n resource: string;\n policy: string;\n reason_code: string;\n actor_id: string;\n}\n\n// ---------------------------------------------------------------------------\n// Event type -> payload mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each event type string to its required payload interface.\n */\ninterface EventPayloadMap {\n \"conversation.created.v1\": ConversationCreatedPayload;\n \"conversation.message_sent.v1\": ConversationMessageSentPayload;\n \"conversation.message_received.v1\": ConversationMessageReceivedPayload;\n \"conversation.marked_read.v1\": ConversationMarkedReadPayload;\n \"conversation.attachment_uploaded.v1\": ConversationAttachmentUploadedPayload;\n \"conversation.attachment_downloaded.v1\": ConversationAttachmentDownloadedPayload;\n \"call.initiated.v1\": CallInitiatedPayload;\n \"call.connected.v1\": CallConnectedPayload;\n \"call.ended.v1\": CallEndedPayload;\n \"call.missed.v1\": CallMissedPayload;\n \"task.created.v1\": TaskCreatedPayload;\n \"task.assigned.v1\": TaskAssignedPayload;\n \"task.status_changed.v1\": TaskStatusChangedPayload;\n \"reminder.scheduled.v1\": ReminderScheduledPayload;\n \"reminder.triggered.v1\": ReminderTriggeredPayload;\n \"translation.requested.v1\": TranslationRequestedPayload;\n \"translation.completed.v1\": TranslationCompletedPayload;\n \"notification.sent.v1\": NotificationSentPayload;\n \"notification.delivered.v1\": NotificationDeliveredPayload;\n \"notification.opened.v1\": NotificationOpenedPayload;\n \"provider.sync_started.v1\": ProviderSyncStartedPayload;\n \"provider.sync_succeeded.v1\": ProviderSyncSucceededPayload;\n \"provider.sync_failed.v1\": ProviderSyncFailedPayload;\n \"auth.login_succeeded.v1\": AuthLoginSucceededPayload;\n \"auth.login_failed.v1\": AuthLoginFailedPayload;\n \"security.access_denied.v1\": SecurityAccessDeniedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Event envelope\n// ---------------------------------------------------------------------------\n\n/**\n * Subject references for the event. Uses tokenized references only -- no PHI.\n */\ninterface EventSubject {\n patient_ref?: string;\n conversation_id?: string;\n task_id?: string;\n call_id?: string;\n}\n\n/**\n * Actor who triggered the event.\n */\ninterface EventActor {\n actor_id: string;\n actor_type: \"user\" | \"system\" | \"webhook\";\n}\n\n/**\n * Compliance metadata for the event.\n */\ninterface EventCompliance {\n pii_level: \"none\" | \"limited\" | \"full\";\n contains_phi: boolean;\n consent_context: string;\n retention_class: string;\n}\n\n/**\n * Canonical event envelope. Every tracked event is wrapped in this structure\n * before being sent to the Truth API.\n */\ninterface EventEnvelope {\n event_id: string;\n event_type: string;\n schema_version: number;\n occurred_at: string;\n received_at: string;\n source: string;\n source_version: string;\n tenant_id: string;\n actor?: EventActor;\n subject?: EventSubject;\n compliance?: EventCompliance;\n payload: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Track options (per-call overrides)\n// ---------------------------------------------------------------------------\n\n/**\n * Optional overrides when calling truth.track().\n */\ninterface TrackOptions {\n /** Override the default actor for this event */\n actor?: EventActor;\n\n /** Subject references for this event */\n subject?: EventSubject;\n\n /** Compliance metadata for this event */\n compliance?: EventCompliance;\n\n /** Override the default tenant ID for this event */\n tenantId?: string;\n\n /** Override the occurred_at timestamp (ISO 8601) */\n occurredAt?: string;\n}\n\nexport {\n EVENT_TYPES,\n CONVERSATION_EVENTS,\n CALL_EVENTS,\n TASK_EVENTS,\n REMINDER_EVENTS,\n TRANSLATION_EVENTS,\n NOTIFICATION_EVENTS,\n PROVIDER_EVENTS,\n AUTH_EVENTS,\n SECURITY_EVENTS,\n};\n\nexport type {\n EventType,\n ConversationEventType,\n CallEventType,\n TaskEventType,\n ReminderEventType,\n TranslationEventType,\n NotificationEventType,\n ProviderEventType,\n AuthEventType,\n SecurityEventType,\n EventPayloadMap,\n EventEnvelope,\n EventActor,\n EventSubject,\n EventCompliance,\n TrackOptions,\n ConversationCreatedPayload,\n ConversationMessageSentPayload,\n ConversationMessageReceivedPayload,\n ConversationMarkedReadPayload,\n ConversationAttachmentUploadedPayload,\n ConversationAttachmentDownloadedPayload,\n CallInitiatedPayload,\n CallConnectedPayload,\n CallEndedPayload,\n CallMissedPayload,\n TaskCreatedPayload,\n TaskAssignedPayload,\n TaskStatusChangedPayload,\n ReminderScheduledPayload,\n ReminderTriggeredPayload,\n TranslationRequestedPayload,\n TranslationCompletedPayload,\n NotificationSentPayload,\n NotificationDeliveredPayload,\n NotificationOpenedPayload,\n ProviderSyncStartedPayload,\n ProviderSyncSucceededPayload,\n ProviderSyncFailedPayload,\n AuthLoginSucceededPayload,\n AuthLoginFailedPayload,\n SecurityAccessDeniedPayload,\n};\n","/**\n * Configuration interfaces for the Truth SDK.\n */\n\n/**\n * Environment options for the Truth platform.\n */\nconst ENVIRONMENTS = {\n local: \"local\",\n staging: \"staging\",\n stg: \"stg\",\n sandbox: \"sandbox\",\n uat: \"uat\",\n production: \"production\",\n} as const;\n\ntype Environment = (typeof ENVIRONMENTS)[keyof typeof ENVIRONMENTS];\n\n/**\n * Configuration for initializing a TruthClient.\n */\ninterface TruthClientConfig {\n /** API key for authenticating with the Truth platform (e.g. \"hn_live_...\") */\n apiKey: string;\n\n /** Target environment */\n environment: Environment;\n\n /** Override the default Convex URL for data access */\n convexUrl?: string;\n\n /** Event source identifier (e.g. \"communication-hub.backend\") */\n source?: string;\n\n /** Git SHA or version string for event source versioning */\n sourceVersion?: string;\n\n /** Default tenant (organization) ID for events */\n tenantId?: string;\n\n /** Number of events to buffer before flushing (default: 25) */\n batchSize?: number;\n\n /** Interval in milliseconds between automatic flushes (default: 5000) */\n flushIntervalMs?: number;\n\n /** Base URL for the Truth API (overrides environment-based default) */\n apiBaseUrl?: string;\n\n /**\n * Auto-register the service worker and subscribe to web push on init.\n * Only applies in browser environments with Push API support.\n * Default: true.\n */\n autoInitServiceWorker?: boolean;\n\n /** Path to the service worker file. Default: \"/truth-sw.js\" */\n serviceWorkerPath?: string;\n}\n\n/**\n * Actor context attached to tracked events.\n */\ninterface ActorContext {\n actorId: string;\n actorType: \"user\" | \"system\" | \"webhook\";\n}\n\n/**\n * Paginated result wrapper for list operations.\n */\ninterface PaginatedResult<T> {\n data: T[];\n cursor: string | null;\n hasMore: boolean;\n}\n\nexport { ENVIRONMENTS };\nexport type { Environment, TruthClientConfig, ActorContext, PaginatedResult };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/resources/appointments.ts","../src/resources/attachments.ts","../src/resources/conversations.ts","../src/resources/dialpad.ts","../src/resources/ehr.ts","../src/resources/notes.ts","../src/resources/notifications.ts","../src/resources/patient-details.ts","../src/resources/patients.ts","../src/resources/physicians.ts","../src/resources/reminders.ts","../src/resources/tasks.ts","../src/resources/translation.ts","../src/tracking/tracker.ts","../src/web-push.ts","../src/tracking/events.ts","../src/types/config.ts"],"sourcesContent":["/**\n * TruthClient -- main entry point for the @hipnation-truth/sdk package.\n *\n * Provides:\n * - `.patients` Resource-based patient data access (Convex-backed)\n * - `.appointments` Resource-based appointment data access (Convex-backed)\n * - `.ehr` EHR proxy access (Elation, Hint)\n * - `.messages` Messaging proxy access (Dialpad)\n * - `.track()` Fire-and-forget event tracking (batched HTTP -> Truth API)\n * - `.identify()` Set default actor context for subsequent events\n * - `.flush()` Force flush of buffered events (for graceful shutdown)\n */\n\nimport { ConvexHttpClient } from \"convex/browser\";\nimport { AppointmentResource } from \"./resources/appointments\";\nimport { AttachmentsResource } from \"./resources/attachments\";\nimport { ConversationsResource } from \"./resources/conversations\";\nimport { MessagesResource } from \"./resources/dialpad\";\nimport { EhrResource } from \"./resources/ehr\";\nimport { NotesResource } from \"./resources/notes\";\nimport { NotificationsResource } from \"./resources/notifications\";\nimport { PatientDetailsResource } from \"./resources/patient-details\";\nimport { PatientResource } from \"./resources/patients\";\nimport { PhysiciansResource } from \"./resources/physicians\";\nimport { RemindersResource } from \"./resources/reminders\";\nimport { TasksResource } from \"./resources/tasks\";\nimport { TranslationResource } from \"./resources/translation\";\nimport type {\n EventPayloadMap,\n EventType,\n TrackOptions,\n} from \"./tracking/events\";\nimport {\n DEFAULT_BATCH_SIZE,\n DEFAULT_FLUSH_INTERVAL_MS,\n Tracker,\n} from \"./tracking/tracker\";\nimport type { ActorContext, TruthClientConfig } from \"./types/config\";\nimport {\n isWebPushSupported,\n registerServiceWorker,\n subscribeToPush,\n subscriptionToJSON,\n} from \"./web-push\";\n\n// ---------------------------------------------------------------------------\n// Environment -> Convex URL mapping\n// ---------------------------------------------------------------------------\n\n// Environment → Convex deployment URL.\n//\n// Topology mirrors API_URLS in tracker.ts:\n// local / staging / stg / sandbox → sandbox Convex (courteous-duck-623)\n// uat / production → production Convex (gallant-gecko-217)\n//\n// UAT shares production resources. Staging is the isolated sandbox env.\nconst CONVEX_URLS: Record<string, string> = {\n local: \"https://courteous-duck-623.convex.cloud\",\n staging: \"https://courteous-duck-623.convex.cloud\",\n stg: \"https://courteous-duck-623.convex.cloud\",\n sandbox: \"https://courteous-duck-623.convex.cloud\",\n uat: \"https://gallant-gecko-217.convex.cloud\",\n production: \"https://gallant-gecko-217.convex.cloud\",\n};\n\n// ---------------------------------------------------------------------------\n// TruthClient\n// ---------------------------------------------------------------------------\n\nclass TruthClient {\n /** Patient data resource (Convex-backed) */\n readonly patients: PatientResource;\n\n /** Appointment data resource (Convex-backed) */\n readonly appointments: AppointmentResource;\n\n /** EHR proxy — typed access to Elation and Hint APIs through Truth */\n readonly ehr: EhrResource;\n\n /** Messaging proxy — typed access to Dialpad APIs through Truth */\n readonly messages: MessagesResource;\n\n /** Conversation reminders (Convex-backed, durable scheduler) */\n readonly reminders: RemindersResource;\n\n /** Translation (Azure Translator proxy) */\n readonly translation: TranslationResource;\n\n /** Tasks (Convex-backed) */\n readonly tasks: TasksResource;\n\n /** Patient details — merged Hint + Elation lookups via Truth API */\n readonly patientDetails: PatientDetailsResource;\n\n /** Attachments — presigned S3 upload/download + Convex metadata */\n readonly attachments: AttachmentsResource;\n\n /** Notes — push formatted notes to Elation */\n readonly notes: NotesResource;\n\n /** Physicians (Convex-backed cache from Elation) */\n readonly physicians: PhysiciansResource;\n\n /** Push / web notifications (AWS End User Messaging) */\n readonly notifications: NotificationsResource;\n\n /** Conversation-tied writes — notes, tasks, outbound messages. */\n readonly conversations: ConversationsResource;\n\n private readonly convex: ConvexHttpClient;\n private readonly tracker: Tracker;\n private _vapidPublicKey: string | null = null;\n private _webPushReady: Promise<void> | null = null;\n private readonly _serviceWorkerPath: string;\n\n constructor(config: TruthClientConfig) {\n // Resolve Convex URL\n const convexUrl =\n config.convexUrl ?? CONVEX_URLS[config.environment] ?? CONVEX_URLS.local;\n\n // Initialize Convex HTTP client for data access\n this.convex = new ConvexHttpClient(convexUrl);\n\n // Initialize event tracker\n this.tracker = new Tracker({\n apiKey: config.apiKey,\n environment: config.environment,\n source: config.source ?? \"unknown\",\n sourceVersion: config.sourceVersion ?? \"unknown\",\n tenantId: config.tenantId ?? \"\",\n batchSize: config.batchSize ?? DEFAULT_BATCH_SIZE,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n apiBaseUrl: config.apiBaseUrl,\n });\n\n const apiUrl = this.tracker.apiUrl;\n\n // Initialize resources\n this.patients = new PatientResource(this.convex);\n this.appointments = new AppointmentResource(this.convex);\n this.ehr = new EhrResource(apiUrl);\n this.messages = new MessagesResource(apiUrl, config.apiKey);\n this.reminders = new RemindersResource(this.convex);\n this.translation = new TranslationResource(apiUrl, config.apiKey);\n this.tasks = new TasksResource(this.convex);\n this.patientDetails = new PatientDetailsResource(apiUrl, config.apiKey);\n this.attachments = new AttachmentsResource(\n apiUrl,\n config.apiKey,\n this.convex,\n );\n this.notes = new NotesResource(apiUrl, config.apiKey);\n this.physicians = new PhysiciansResource(this.convex);\n this.notifications = new NotificationsResource(apiUrl, config.apiKey);\n this.conversations = new ConversationsResource(apiUrl, config.apiKey);\n this._serviceWorkerPath = config.serviceWorkerPath ?? \"/truth-sw.js\";\n\n if (\n typeof window !== \"undefined\" &&\n isWebPushSupported() &&\n config.autoInitServiceWorker !== false\n ) {\n this._webPushReady = this.initWebPush();\n }\n }\n\n get vapidPublicKey(): string | null {\n return this._vapidPublicKey;\n }\n\n get webPushReady(): Promise<void> | null {\n return this._webPushReady;\n }\n\n private async initWebPush(): Promise<void> {\n try {\n const key = await this.notifications.getVapidKey();\n if (!key) {\n return;\n }\n this._vapidPublicKey = key;\n\n const registration = await registerServiceWorker(this._serviceWorkerPath);\n await navigator.serviceWorker.ready;\n const subscription = await subscribeToPush(registration, key);\n const subJSON = subscriptionToJSON(subscription);\n\n await this.notifications.registerDevice({\n userId: \"__pending__\",\n platform: \"web\",\n webPushSubscription: subJSON,\n locale: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n } catch {\n // Web push init is best-effort — don't break the client.\n }\n }\n\n /**\n * The resolved Truth API base URL for this environment.\n * Use this when making HTTP calls to Truth's proxy endpoints\n * (e.g., EHR proxy, messages proxy).\n *\n * @example\n * ```ts\n * const url = `${truth.apiBaseUrl}/api/ehr/elation/patients/123`;\n * ```\n */\n get apiBaseUrl(): string {\n return this.tracker.apiUrl;\n }\n\n /**\n * Track an event. Fire-and-forget -- the event is buffered internally\n * and flushed in batches to the Truth API.\n *\n * @example\n * ```ts\n * truth.track('conversation.message_sent.v1', {\n * channel: 'sms',\n * direction: 'outbound',\n * message_chars: 140,\n * has_attachment: false,\n * provider_system: 'dialpad',\n * });\n * ```\n */\n track<T extends EventType>(\n eventType: T,\n payload: EventPayloadMap[T],\n options?: TrackOptions,\n ): void {\n this.tracker.track(eventType, payload, options);\n }\n\n /**\n * Set the default actor context for all subsequent tracked events.\n * Can be overridden per-event via TrackOptions.\n *\n * @example\n * ```ts\n * truth.identify('user_123', 'user');\n * ```\n */\n identify(actorId: string, actorType: ActorContext[\"actorType\"]): void {\n this.tracker.setActor({ actorId, actorType });\n }\n\n /**\n * Flush all buffered events immediately. Returns a Promise that resolves\n * when the flush completes. Use this for graceful shutdown.\n *\n * @example\n * ```ts\n * process.on('SIGTERM', async () => {\n * await truth.flush();\n * process.exit(0);\n * });\n * ```\n */\n async flush(): Promise<void> {\n await this.tracker.flush();\n }\n\n /**\n * Gracefully shut down the client. Flushes all pending events and\n * releases resources.\n */\n async destroy(): Promise<void> {\n await this.tracker.shutdown();\n }\n}\n\nexport { TruthClient };\n","/**\n * AppointmentResource provides data access to normalized appointment records\n * backed by Convex.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type { Appointment, AppointmentListOptions } from \"../types/appointment\";\nimport type { PaginatedResult } from \"../types/config\";\n\nclass AppointmentResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Get an appointment by its Truth platform ID.\n */\n async get(id: string): Promise<Appointment | null> {\n try {\n const result = await this.convex.query(\n \"appointments:getById\" as never,\n { id } as never,\n );\n return (result as Appointment) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * List appointments with optional filters, pagination, and limit.\n */\n async list(\n options?: AppointmentListOptions,\n ): Promise<PaginatedResult<Appointment>> {\n try {\n const result = await this.convex.query(\n \"appointments:list\" as never,\n {\n patientId: options?.patientId,\n startDate: options?.startDate,\n endDate: options?.endDate,\n status: options?.status,\n limit: options?.limit,\n cursor: options?.cursor,\n } as never,\n );\n\n const typed = result as PaginatedResult<Appointment> | undefined;\n\n return typed ?? { data: [], cursor: null, hasMore: false };\n } catch {\n return { data: [], cursor: null, hasMore: false };\n }\n }\n}\n\nexport { AppointmentResource };\n","/**\n * AttachmentsResource — presigned S3 upload/download + Convex metadata.\n *\n * Replaces CommHub's base64-in-Postgres attachment flow:\n * 1. client.attachments.createUploadUrl(...) → presigned PUT URL\n * 2. Client PUTs bytes directly to S3\n * 3. client.attachments.record(...) → writes Convex metadata\n * 4. client.attachments.getDownloadUrl(s3Key) → presigned GET URL\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\n\nexport interface CreateUploadUrlInput {\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n}\n\nexport interface CreateUploadUrlResult {\n uploadUrl: string;\n s3Key: string;\n expiresIn: number;\n applicationId: string | null;\n}\n\nexport interface GetDownloadUrlResult {\n url: string;\n expiresIn: number;\n}\n\nexport interface RecordAttachmentInput {\n s3Key: string;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n}\n\nexport interface Attachment {\n _id: string;\n s3Key: string;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n createdAt: string;\n}\n\nclass AttachmentsError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Attachment ${operation} failed (HTTP ${status})`);\n this.name = \"AttachmentsError\";\n this.status = status;\n }\n}\n\nclass AttachmentsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly convex: ConvexHttpClient;\n\n constructor(\n apiBaseUrl: string,\n apiKey: string,\n convexClient: ConvexHttpClient,\n ) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n this.convex = convexClient;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new AttachmentsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async createUploadUrl(\n input: CreateUploadUrlInput,\n ): Promise<CreateUploadUrlResult> {\n return await this.post<CreateUploadUrlResult>(\n \"/attachments/upload-url\",\n input,\n );\n }\n\n async getDownloadUrl(\n s3Key: string,\n expiresIn?: number,\n ): Promise<GetDownloadUrlResult> {\n return await this.post<GetDownloadUrlResult>(\"/attachments/download-url\", {\n s3Key,\n expiresIn,\n });\n }\n\n async record(\n input: RecordAttachmentInput,\n ): Promise<{ attachmentId: string; s3Key: string }> {\n return (await this.convex.mutation(\n \"attachments:record\" as never,\n input as never,\n )) as { attachmentId: string; s3Key: string };\n }\n\n async get(attachmentId: string): Promise<Attachment | null> {\n try {\n const row = (await this.convex.query(\n \"attachments:getById\" as never,\n { attachmentId } as never,\n )) as Attachment | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByConversation(conversationId: string): Promise<Attachment[]> {\n try {\n const rows = (await this.convex.query(\n \"attachments:listByConversation\" as never,\n { conversationId } as never,\n )) as Attachment[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * One-shot upload: presign → PUT to S3 → record in Convex → return a\n * 7-day signed download URL ready to embed in an outbound SMS. Caller\n * passes the resulting `downloadUrl` to `messages.dialpad.sendSms` (or\n * the new `messages.sendAttachmentMessage`) to actually deliver it.\n *\n * Replaces CommHub's NestJS `/send-attachment` controller in a single\n * SDK call — no base64 round-trip, no legacy `/attachments/:id/download`\n * REST endpoint required.\n */\n async upload(input: {\n file: Blob | ArrayBuffer | Uint8Array;\n fileName: string;\n mimeType: string;\n size: number;\n conversationId?: string;\n uploadedBy: string;\n /** Download URL TTL in seconds. Default 7 days. */\n downloadExpiresIn?: number;\n }): Promise<{\n attachmentId: string;\n s3Key: string;\n downloadUrl: string;\n }> {\n const presigned = await this.createUploadUrl({\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n conversationId: input.conversationId,\n });\n\n // Cast through `BodyInit` — Uint8Array is accepted by undici/fetch\n // at runtime but the lib.dom types only declare BufferSource so TS\n // doesn't see the overload. Same trick the AttachmentsResource\n // already uses elsewhere.\n const body: BodyInit =\n input.file instanceof Blob\n ? input.file\n : input.file instanceof Uint8Array\n ? (input.file as unknown as BodyInit)\n : (new Uint8Array(input.file) as unknown as BodyInit);\n\n // 30s timeout matches the legacy NestJS handler so a stuck S3 PUT\n // can't hang the calling UI thread indefinitely.\n const abort = new AbortController();\n const timer = setTimeout(() => abort.abort(), 30_000);\n let putRes: Response;\n try {\n putRes = await fetch(presigned.uploadUrl, {\n method: \"PUT\",\n headers: { \"Content-Type\": input.mimeType },\n body,\n signal: abort.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (!putRes.ok) {\n throw new AttachmentsError(\n \"s3-put\",\n putRes.status,\n `S3 PUT ${putRes.status} for ${presigned.s3Key}`,\n );\n }\n\n const recorded = await this.record({\n s3Key: presigned.s3Key,\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n conversationId: input.conversationId,\n uploadedBy: input.uploadedBy,\n });\n\n const signed = await this.getDownloadUrl(\n presigned.s3Key,\n input.downloadExpiresIn ?? 7 * 24 * 3600,\n );\n\n return {\n attachmentId: recorded.attachmentId,\n s3Key: presigned.s3Key,\n downloadUrl: signed.url,\n };\n }\n}\n\nexport { AttachmentsResource, AttachmentsError };\n","/**\n * ConversationsResource — write methods that hang off a specific\n * conversation: notes, tasks, outbound messages.\n *\n * Replaces CommHub's `truthConversationApi.ts` raw-fetch wrappers so\n * the frontend goes through a typed SDK surface instead of building\n * URLs by hand.\n *\n * All methods proxy the matching oRPC procedures (`/conversations/*`)\n * via Truth's application-key auth. Errors surface as\n * `ConversationsError` with a status code so callers can distinguish\n * transport failures (status=0) from API rejections (status>=400).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Address a conversation by either Convex `_id` or the phonePair. */\nexport interface ConversationAddress {\n conversationId?: string;\n phonePair?: string;\n}\n\nexport type ConversationTaskStatus = \"pending\" | \"completed\";\nexport type ConversationTaskPriority = \"high\" | \"medium\" | \"low\";\n\nexport interface CreateConversationNoteInput extends ConversationAddress {\n eventId?: string;\n author: string;\n content: string;\n status?: string;\n type?: string;\n}\n\nexport interface CreateConversationTaskInput extends ConversationAddress {\n eventId?: string;\n author: string;\n description: string;\n status?: ConversationTaskStatus;\n priority?: ConversationTaskPriority;\n assignee?: string;\n type?: string;\n}\n\nexport interface SetConversationTaskStatusInput {\n taskId: string;\n status: ConversationTaskStatus;\n resolvedBy?: string;\n}\n\nexport interface SendConversationMessageInput {\n fromNumber: string;\n toNumber: string;\n message?: string;\n media?: string;\n}\n\nexport interface SendConversationMessageResult {\n id: number;\n messageStatus: string;\n direction: string;\n}\n\nexport class ConversationsError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Conversations ${operation} failed (HTTP ${status})`);\n this.name = \"ConversationsError\";\n this.status = status;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Subresources\n// ---------------------------------------------------------------------------\n\nclass ConversationNotesSubresource {\n constructor(\n private readonly post: <T>(path: string, body: unknown) => Promise<T>,\n ) {}\n\n /** Create a note on a conversation (addressed by id or phonePair). */\n async create(input: CreateConversationNoteInput): Promise<{ id: string }> {\n return this.post<{ id: string }>(\"/conversations/notes\", input);\n }\n}\n\nclass ConversationTasksSubresource {\n constructor(\n private readonly post: <T>(path: string, body: unknown) => Promise<T>,\n ) {}\n\n /** Create a task on a conversation. */\n async create(input: CreateConversationTaskInput): Promise<{ id: string }> {\n return this.post<{ id: string }>(\"/conversations/tasks\", input);\n }\n\n /** Mark a task pending or completed. */\n async setStatus(input: SetConversationTaskStatusInput): Promise<{\n ok: true;\n }> {\n return this.post<{ ok: true }>(\n `/conversations/tasks/${encodeURIComponent(input.taskId)}/status`,\n {\n id: input.taskId,\n status: input.status,\n ...(input.resolvedBy ? { resolvedBy: input.resolvedBy } : {}),\n },\n );\n }\n}\n\nclass ConversationMessagesSubresource {\n constructor(\n private readonly post: <T>(path: string, body: unknown) => Promise<T>,\n ) {}\n\n /**\n * Send an outbound SMS / MMS via the typed `sendMessage` oRPC procedure.\n *\n * Prefer this over the generic Dialpad proxy (`messages.dialpad.sendSms`)\n * — this hits the validated Truth route and consistently surfaces\n * `ConversationsError` on failure.\n */\n async send(\n input: SendConversationMessageInput,\n ): Promise<SendConversationMessageResult> {\n if (!input.message && !input.media) {\n throw new ConversationsError(\n \"messages.send\",\n 0,\n \"send requires `message` or `media`\",\n );\n }\n return this.post<SendConversationMessageResult>(\n \"/conversations/messages\",\n input,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// ConversationsResource\n// ---------------------------------------------------------------------------\n\nexport class ConversationsResource {\n readonly notes: ConversationNotesSubresource;\n readonly tasks: ConversationTasksSubresource;\n readonly messages: ConversationMessagesSubresource;\n\n /** 30s upstream timeout — matches NotesResource for consistency. */\n private static readonly REQUEST_TIMEOUT_MS = 30_000;\n\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n const post = <T>(path: string, body: unknown) =>\n this.postRequest<T>(path, body);\n this.notes = new ConversationNotesSubresource(post);\n this.tasks = new ConversationTasksSubresource(post);\n this.messages = new ConversationMessagesSubresource(post);\n }\n\n private async postRequest<T>(path: string, body: unknown): Promise<T> {\n if (!this.apiKey) {\n throw new ConversationsError(\n path,\n 0,\n \"Truth API key not configured — request blocked\",\n );\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(\n () => controller.abort(),\n ConversationsResource.REQUEST_TIMEOUT_MS,\n );\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n const message = isAbort\n ? `Conversations ${path} timed out after ${ConversationsResource.REQUEST_TIMEOUT_MS}ms`\n : err instanceof Error\n ? err.message\n : \"Conversations request failed before response\";\n throw new ConversationsError(path, 0, message);\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new ConversationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n}\n","/**\n * Dialpad resource — typed access to Truth's Dialpad proxy endpoints.\n *\n * @example\n * ```ts\n * const sms = await truth.messages.dialpad.sendSms({ from_number, to_number, message });\n * const call = await truth.messages.dialpad.initiateCall(userId, phoneNumber);\n * await truth.messages.dialpad.hangupCall(callId);\n * const url = await truth.messages.dialpad.authenticateVoicemail(voicemailLink);\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface SendSmsParams {\n from_number: string;\n to_number: string;\n message?: string;\n media?: string | string[];\n}\n\ninterface SendSmsResponse {\n id: string;\n message_status: string;\n [key: string]: unknown;\n}\n\ninterface InitiateCallResponse {\n call_id: number;\n [key: string]: unknown;\n}\n\ninterface CallStatusResponse {\n state: string;\n [key: string]: unknown;\n}\n\ninterface DialpadUser {\n id: number;\n emails: string[];\n first_name?: string;\n last_name?: string;\n [key: string]: unknown;\n}\n\ninterface DialpadUserListResponse {\n items: DialpadUser[];\n}\n\ninterface DialpadNumberInfo {\n user_id?: number;\n type?: string;\n [key: string]: unknown;\n}\n\ninterface VoicemailAuthResponse {\n success: boolean;\n authenticated_url: string | null;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Dialpad Resource\n// ---------------------------------------------------------------------------\n\nclass DialpadResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /**\n * Send an SMS or MMS message via Dialpad.\n */\n async sendSms(params: SendSmsParams): Promise<SendSmsResponse> {\n const body = {\n to_numbers: [params.to_number],\n from_number: params.from_number,\n infer_country_code: false,\n ...(params.message ? { text: params.message } : {}),\n ...(params.media ? { media: params.media } : {}),\n };\n\n return this.post<SendSmsResponse>(\"/sms\", body);\n }\n\n /**\n * Initiate an outbound call from a Dialpad user to a phone number.\n */\n async initiateCall(\n userId: number,\n phoneNumber: string,\n ): Promise<InitiateCallResponse> {\n return this.post<InitiateCallResponse>(`/users/${userId}/initiate_call`, {\n phone_number: phoneNumber,\n });\n }\n\n /**\n * Hang up an active call.\n */\n async hangupCall(callId: number): Promise<void> {\n await this.put(`/call/${callId}/actions/hangup`);\n }\n\n /**\n * Alias for `hangupCall` — mirrors the CommHub `endCall` action name so\n * the SDK swap is a direct rename.\n */\n async endCall(callId: number): Promise<void> {\n await this.hangupCall(callId);\n }\n\n /**\n * Send an MMS with a pre-uploaded attachment. Takes the S3 key returned\n * by `client.attachments.createUploadUrl(...)` + PUT, fetches a short-\n * lived download URL, and hands it to Dialpad as `media[]`.\n *\n * Replaces CommHub's `sendAttachment` Hasura Action (which stored bytes\n * as base64 in Postgres and served them through a GET endpoint).\n */\n async sendAttachmentWithUrl(params: {\n from_number: string;\n to_number: string;\n mediaUrl: string;\n message?: string;\n }): Promise<SendSmsResponse> {\n return this.sendSms({\n from_number: params.from_number,\n to_number: params.to_number,\n message: params.message,\n media: [params.mediaUrl],\n });\n }\n\n /**\n * Get the status of a call.\n */\n async getCallStatus(callId: number): Promise<CallStatusResponse | null> {\n try {\n return await this.get<CallStatusResponse>(`/call/${callId}`);\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Get a Dialpad user by their user ID.\n */\n async getUser(userId: string | number): Promise<DialpadUser | null> {\n try {\n return await this.get<DialpadUser>(`/users/${userId}`);\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Find a Dialpad user by email.\n */\n async getUserByEmail(email: string): Promise<DialpadUser | null> {\n const result = await this.get<DialpadUserListResponse>(\"/users\", {\n email,\n });\n return result.items?.[0] ?? null;\n }\n\n /**\n * Find a Dialpad user by phone number.\n */\n async getUserByPhoneNumber(phoneNumber: string): Promise<DialpadUser | null> {\n const result = await this.get<DialpadUserListResponse>(\"/users\", {\n number: phoneNumber,\n });\n return result.items?.[0] ?? null;\n }\n\n /**\n * Get information about a Dialpad phone number.\n */\n async getNumberInfo(phoneNumber: string): Promise<DialpadNumberInfo | null> {\n try {\n const cleanNumber = phoneNumber.replace(/[^\\d+]/g, \"\");\n return await this.get<DialpadNumberInfo>(\n `/numbers/${encodeURIComponent(cleanNumber)}`,\n );\n } catch (error) {\n if (error instanceof DialpadProxyError && error.status === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Authenticate a voicemail download URL. Truth appends the Dialpad API key,\n * follows redirects, and returns the clean URL for client-side playback.\n */\n async authenticateVoicemail(voicemailLink: string): Promise<string | null> {\n const url = `${this.baseUrl}/api/messages/dialpad/voicemail/authenticate`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify({ voicemail_link: voicemailLink }),\n });\n\n const result = (await response.json()) as VoicemailAuthResponse;\n\n if (!response.ok || !result.success) {\n return null;\n }\n\n return result.authenticated_url;\n }\n\n // -----------------------------------------------------------------------\n // Internal HTTP helpers\n // -----------------------------------------------------------------------\n\n private async get<T>(\n path: string,\n params?: Record<string, unknown>,\n ): Promise<T> {\n const url = new URL(`/api/messages/dialpad${path}`, this.baseUrl);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"GET\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n private async post<T>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/messages/dialpad${path}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"POST\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n private async put<T = void>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/messages/dialpad${path}`;\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new DialpadProxyError(\"PUT\", path, response.status);\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n return (await response.json()) as T;\n }\n\n return undefined as T;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Messages Resource — namespace for messaging providers\n// ---------------------------------------------------------------------------\n\nclass MessagesResource {\n readonly dialpad: DialpadResource;\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.dialpad = new DialpadResource(apiBaseUrl, apiKey);\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /**\n * End a Dialpad call via the typed oRPC procedure (POST hangup, with\n * 404→`alreadyEnded` so the UI doesn't flash an error on a natural\n * race). Replaces CommHub's `useEndCallMutation` Hasura action.\n *\n * Prefer this over `messages.dialpad.endCall` which goes through the\n * generic proxy (PUT, no 404 handling).\n */\n async endCall(callId: number | string): Promise<{\n ok: true;\n alreadyEnded: boolean;\n }> {\n const res = await fetch(`${this.baseUrl}/api/conversations/calls/end`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify({ callId }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `messages.endCall failed (HTTP ${res.status}): ${text.slice(0, 200)}`,\n );\n }\n return (await res.json()) as { ok: true; alreadyEnded: boolean };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nclass DialpadProxyError extends Error {\n readonly method: string;\n readonly path: string;\n readonly status: number;\n\n constructor(method: string, path: string, status: number) {\n super(\n `Dialpad proxy error: ${method} /api/messages/dialpad${path} returned ${status}`,\n );\n this.name = \"DialpadProxyError\";\n this.method = method;\n this.path = path;\n this.status = status;\n }\n}\n\nexport { DialpadResource, DialpadProxyError, MessagesResource };\nexport type {\n SendSmsParams,\n SendSmsResponse,\n InitiateCallResponse,\n CallStatusResponse,\n DialpadUser,\n DialpadNumberInfo,\n VoicemailAuthResponse,\n};\n","/**\n * EHR proxy resource — typed access to Truth's EHR proxy endpoints.\n *\n * Provides per-provider proxy methods so consumers never construct\n * proxy URLs manually.\n *\n * @example\n * ```ts\n * const patient = await truth.ehr.elation.get('/patients/123/');\n * const notes = await truth.ehr.elation.post('/non_visit_notes/', noteData);\n * const hintPatient = await truth.ehr.hint.get('/provider/patients/456');\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Provider proxy\n// ---------------------------------------------------------------------------\n\nclass EhrProviderProxy {\n private readonly baseUrl: string;\n private readonly provider: string;\n\n constructor(apiBaseUrl: string, provider: string) {\n this.baseUrl = apiBaseUrl;\n this.provider = provider;\n }\n\n /**\n * GET request to the EHR proxy.\n * @param path — path relative to the provider root (e.g., \"/patients/123/\")\n * @param params — optional query parameters\n */\n async get<T = unknown>(\n path: string,\n params?: Record<string, unknown>,\n ): Promise<T> {\n const url = new URL(`/api/ehr/${this.provider}${path}`, this.baseUrl);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"GET\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * POST request to the EHR proxy.\n */\n async post<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"POST\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * PUT request to the EHR proxy.\n */\n async put<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"PUT\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * PATCH request to the EHR proxy.\n */\n async patch<T = unknown>(path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"PATCH\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n\n /**\n * DELETE request to the EHR proxy.\n */\n async delete<T = unknown>(path: string): Promise<T> {\n const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n throw new EhrProxyError(this.provider, \"DELETE\", path, response.status);\n }\n\n return (await response.json()) as T;\n }\n}\n\n// ---------------------------------------------------------------------------\n// EHR Resource — exposes per-provider proxies\n// ---------------------------------------------------------------------------\n\nclass EhrResource {\n /** Elation EHR proxy */\n readonly elation: EhrProviderProxy;\n\n /** Hint Health proxy */\n readonly hint: EhrProviderProxy;\n\n constructor(apiBaseUrl: string) {\n this.elation = new EhrProviderProxy(apiBaseUrl, \"elation\");\n this.hint = new EhrProviderProxy(apiBaseUrl, \"hint\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nclass EhrProxyError extends Error {\n readonly provider: string;\n readonly method: string;\n readonly path: string;\n readonly status: number;\n\n constructor(provider: string, method: string, path: string, status: number) {\n super(\n `EHR proxy error: ${method} /api/ehr/${provider}${path} returned ${status}`,\n );\n this.name = \"EhrProxyError\";\n this.provider = provider;\n this.method = method;\n this.path = path;\n this.status = status;\n }\n}\n\nexport { EhrResource, EhrProviderProxy, EhrProxyError };\n","/**\n * NotesResource — push formatted notes to Elation.\n *\n * Caller assembles the note body (text + bullets). Truth owns the Elation\n * OAuth token and the HTTP call so applications can drop Elation\n * credentials from their own config.\n */\n\nexport interface NonVisitNoteBullet {\n text: string;\n category?: string;\n}\n\nexport interface PushNoteToElationInput {\n patient: number;\n physician: number;\n practice: number;\n note: { text: string; type?: string };\n document_date?: string;\n chart_date?: string;\n bullets?: NonVisitNoteBullet[];\n}\n\nexport interface PushNoteToElationResult {\n success: boolean;\n elationNoteId: number | null;\n}\n\nclass NotesError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Notes ${operation} failed (HTTP ${status})`);\n this.name = \"NotesError\";\n this.status = status;\n }\n}\n\nexport interface PushConversationToElationInput {\n /** One-of: conversationId (Convex) or phonePair. */\n conversationId?: string;\n phonePair?: string;\n /** Actor user id — recorded on the audit row. */\n initiatedBy: string;\n /** Pre-assembled note body (transcript-style text). */\n noteText: string;\n bullets?: NonVisitNoteBullet[];\n notesPushed?: number;\n messagesPushed?: number;\n tasksPushed?: number;\n}\n\nexport interface PushConversationToElationResult {\n success: true;\n batchId: string;\n elationNoteId: number | null;\n notesPushed: number;\n messagesPushed: number;\n tasksPushed: number;\n}\n\nclass NotesResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n /** 30s upstream timeout — Elation API has occasional slow hops; we\n * don't want a pending mutation to hold the UI thread indefinitely. */\n private static readonly REQUEST_TIMEOUT_MS = 30_000;\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const controller = new AbortController();\n const timeout = setTimeout(\n () => controller.abort(),\n NotesResource.REQUEST_TIMEOUT_MS,\n );\n\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch (err) {\n // Transport failures (DNS, TCP, TLS, AbortError) become typed\n // NotesError so consumers don't have to special-case raw fetch\n // exceptions. status=0 signals \"never made it to the server\".\n const isAbort =\n err instanceof Error &&\n (err.name === \"AbortError\" || err.name === \"TimeoutError\");\n const message = isAbort\n ? `Notes ${path} timed out after ${NotesResource.REQUEST_TIMEOUT_MS}ms`\n : err instanceof Error\n ? err.message\n : \"Notes request failed before response\";\n throw new NotesError(path, 0, message);\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotesError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n /**\n * Low-level — caller has already resolved Elation patient / physician\n * / practice. Use `pushConversationToElation` if you only have a\n * conversation handle.\n */\n async pushToElation(\n input: PushNoteToElationInput,\n ): Promise<PushNoteToElationResult> {\n return await this.post<PushNoteToElationResult>(\n \"/notes/push-to-elation\",\n input,\n );\n }\n\n /**\n * Orchestrator — pass a conversation handle + pre-assembled note text.\n * Truth resolves the linked patient via Convex, looks up\n * physician/practice in Elation, posts the note, and writes an audit\n * row to `elationSyncEvents`. Replaces CommHub's `pushNotesToElation`\n * Hasura action.\n */\n async pushConversationToElation(\n input: PushConversationToElationInput,\n ): Promise<PushConversationToElationResult> {\n return await this.post<PushConversationToElationResult>(\n \"/notes/push-conversation-to-elation\",\n input,\n );\n }\n}\n\nexport { NotesResource, NotesError };\n","/**\n * NotificationsResource — wraps Truth's /api/notifications/* endpoints.\n *\n * Server-side use (for example from a CommHub backend job or\n * another service): `truth.notifications.send({ userId, title, body })`.\n *\n * Client-side React usage lives in `@hipnation-truth/sdk/react` via\n * the `useNotifications` hook which mirrors `expo-notifications`.\n */\n\nexport type NotificationPlatform = \"ios\" | \"android\" | \"web\";\n\nexport interface RegisterDeviceInput {\n userId: string;\n platform: NotificationPlatform;\n nativeToken?: string;\n webPushSubscription?: {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n };\n appVersion?: string;\n osVersion?: string;\n locale?: string;\n timezone?: string;\n /**\n * iOS only — which APNs endpoint the `nativeToken` was issued\n * against. The token bytes don't carry this; it's determined by\n * the build's `aps-environment` entitlement\n * (`development` ⇒ sandbox, `production` ⇒ production). When the\n * consumer is an Expo app, detect via\n * `Application.getIosPushNotificationServiceEnvironmentAsync()`\n * from `expo-application` and pass the normalised value here.\n * Omitting this falls back to the application's default\n * `pushConfig.ios.environment` server-side.\n */\n apnsEnvironment?: \"sandbox\" | \"production\";\n}\n\nexport interface RegisterDeviceResult {\n deviceId: string;\n action: \"inserted\" | \"updated\";\n snsEndpointArn?: string;\n}\n\nexport interface UnregisterDeviceInput {\n nativeToken?: string;\n deviceId?: string;\n}\n\nexport interface SendNotificationInput {\n userId: string;\n title: string;\n body: string;\n data?: Record<string, unknown>;\n badge?: number;\n sound?: string;\n}\n\nexport interface SendNotificationResult {\n delivered: number;\n failed?: number;\n suppressed?: boolean;\n suppressionReason?: string;\n}\n\nexport interface NotificationPreferences {\n channels: { sms: boolean; push: boolean; email: boolean; inApp: boolean };\n quietHours?: {\n enabled: boolean;\n start: string;\n end: string;\n timezone: string;\n };\n doNotDisturbUntil?: string;\n updatedAt: string;\n}\n\nexport interface UpdatePreferencesInput {\n userId: string;\n channels?: NotificationPreferences[\"channels\"];\n quietHours?: NotificationPreferences[\"quietHours\"];\n doNotDisturbUntil?: string | null;\n}\n\nexport interface ScheduleNotificationInput extends SendNotificationInput {\n /** ISO 8601 timestamp; must be strictly in the future. */\n scheduledAt: string;\n}\n\nexport interface ScheduleNotificationResult {\n jobId: string;\n scheduledAt: string;\n}\n\nexport interface CancelScheduledNotificationResult {\n cancelled: boolean;\n reason?: string;\n status?: string;\n}\n\nexport type ScheduledJobStatus =\n | \"pending\"\n | \"executed\"\n | \"cancelled\"\n | \"failed\";\n\nexport interface ScheduledNotification {\n jobId: string;\n userId: string;\n title: string;\n body: string;\n data?: unknown;\n badge?: number;\n sound?: string;\n scheduledAt: string;\n status: ScheduledJobStatus;\n resultHistoryId?: string;\n errorMessage?: string;\n createdAt: string;\n}\n\nexport interface PushEventPayload {\n title: string;\n body: string;\n data?: unknown;\n}\n\nclass NotificationsError extends Error {\n readonly status: number;\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Notifications ${operation} failed (HTTP ${status})`);\n this.name = \"NotificationsError\";\n this.status = status;\n }\n}\n\nclass NotificationsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n private async get<T>(\n path: string,\n params: Record<string, string>,\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}/api${path}`);\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n private async delete<T>(path: string): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"DELETE\",\n headers: {\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new NotificationsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n /**\n * Register a device (or refresh its metadata) for push delivery.\n * Safe to call repeatedly — the server dedupes by native token.\n */\n async registerDevice(\n input: RegisterDeviceInput,\n ): Promise<RegisterDeviceResult> {\n return this.post(\"/notifications/devices/register\", input);\n }\n\n /** Revoke a device — on sign-out or when the OS reports an invalid token. */\n async unregisterDevice(\n input: UnregisterDeviceInput,\n ): Promise<{ revoked: boolean }> {\n return this.post(\"/notifications/devices/unregister\", input);\n }\n\n /**\n * Send a push notification to every active device belonging to\n * `userId`. Honors the user's notificationPreferences (quiet hours,\n * DND, channel off) before publishing.\n */\n async send(input: SendNotificationInput): Promise<SendNotificationResult> {\n return this.post(\"/notifications/send\", input);\n }\n\n /** Read a user's notification preferences. Returns defaults when no row exists. */\n async getPreferences(userId: string): Promise<NotificationPreferences> {\n return this.get(\"/notifications/preferences\", { userId });\n }\n\n async updatePreferences(\n input: UpdatePreferencesInput,\n ): Promise<{ ok: boolean }> {\n return this.post(\"/notifications/preferences\", input);\n }\n\n /**\n * Schedule a future push notification. Convex's native scheduler\n * fires the send at `scheduledAt` and runs the same delivery\n * pipeline as `send()` (preferences, devices, history audit).\n *\n * Throws `NotificationsError` with status 400 if `scheduledAt` is\n * not strictly in the future.\n */\n async schedule(\n input: ScheduleNotificationInput,\n ): Promise<ScheduleNotificationResult> {\n return this.post(\"/notifications/schedule\", input);\n }\n\n /**\n * Cancel a pending scheduled notification. Returns `cancelled: false`\n * (no error) if the job has already executed, was previously\n * cancelled, or no longer exists — `reason` describes which case.\n */\n async cancelScheduled(\n jobId: string,\n ): Promise<CancelScheduledNotificationResult> {\n return this.delete(`/notifications/schedule/${encodeURIComponent(jobId)}`);\n }\n\n /**\n * List scheduled notifications for a user — pending, executed,\n * cancelled, or failed. Most-recent first. Default limit 100.\n */\n async listScheduled(\n userId: string,\n options?: { limit?: number },\n ): Promise<ScheduledNotification[]> {\n const params: Record<string, string> = { userId };\n if (options?.limit !== undefined) {\n params.limit = String(options.limit);\n }\n return this.get(\"/notifications/schedule\", params);\n }\n\n async getVapidKey(): Promise<string | null> {\n try {\n const result = await this.get<{ vapidPublicKey: string | null }>(\n \"/notifications/vapid-key\",\n {},\n );\n return result.vapidPublicKey;\n } catch {\n return null;\n }\n }\n\n onPushReceived(callback: (payload: PushEventPayload) => void): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === \"TRUTH_PUSH_RECEIVED\") {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () =>\n navigator.serviceWorker.removeEventListener(\"message\", handler);\n }\n\n onPushTapped(callback: (payload: PushEventPayload) => void): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === \"TRUTH_PUSH_TAPPED\") {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () =>\n navigator.serviceWorker.removeEventListener(\"message\", handler);\n }\n}\n\nexport { NotificationsResource, NotificationsError };\n","/**\n * PatientDetailsResource — merged Hint + Elation patient lookups.\n *\n * Backed by the Truth API at /api/patients/details/*, authenticated with\n * X-API-Key. Replaces CommHub's getPatientDetails / getPatientBasicDetails\n * / getPatientMedicalDetails actions.\n */\n\nexport interface PatientDetailsInput {\n hintId?: string;\n elationId?: string;\n}\n\nexport interface PatientDetailsResult {\n hint: unknown | null;\n elation: unknown | null;\n resolvedElationId: string | null;\n}\n\nexport interface PatientBasicDetailsResult {\n hint: unknown | null;\n elationId: string | null;\n}\n\nexport interface PatientMedicalDetailsResult {\n problems: unknown | null;\n medications: unknown | null;\n allergies: unknown | null;\n appointments: unknown | null;\n}\n\nclass PatientDetailsError extends Error {\n readonly status: number;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Patient ${operation} failed (HTTP ${status})`);\n this.name = \"PatientDetailsError\";\n this.status = status;\n }\n}\n\nclass PatientDetailsResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}/api${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new PatientDetailsError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async get(input: PatientDetailsInput): Promise<PatientDetailsResult> {\n return await this.post<PatientDetailsResult>(\"/patients/details\", input);\n }\n\n async getBasic(\n input: PatientDetailsInput,\n ): Promise<PatientBasicDetailsResult> {\n return await this.post<PatientBasicDetailsResult>(\n \"/patients/details/basic\",\n input,\n );\n }\n\n async getMedical(elationId: string): Promise<PatientMedicalDetailsResult> {\n return await this.post<PatientMedicalDetailsResult>(\n \"/patients/details/medical\",\n { elationId },\n );\n }\n\n /**\n * Trigger a server-side refresh of the patient's Elation medical\n * records (medications, problems, allergies, appointments) into the\n * Convex cache. Fire-and-forget — the UI should read via the Convex-\n * reactive `usePatientMedical` hook.\n */\n async refreshMedical(elationId: number): Promise<{\n totals: {\n medications: number;\n problems: number;\n allergies: number;\n appointments: number;\n };\n }> {\n return await this.post<{\n totals: {\n medications: number;\n problems: number;\n allergies: number;\n appointments: number;\n };\n }>(\"/patients/medical/refresh\", { elationId });\n }\n}\n\nexport { PatientDetailsResource, PatientDetailsError };\n","/**\n * PatientResource provides data access to normalized patient records\n * backed by Convex.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type { PaginatedResult } from \"../types/config\";\nimport type { Patient, PatientListOptions } from \"../types/patient\";\n\nclass PatientResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Get a patient by their Truth platform ID.\n */\n async get(id: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getById\" as never,\n {\n id,\n } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Get a patient by their Elation EHR ID.\n */\n async getByElationId(elationId: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getByElationId\" as never,\n { elationId } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Get a patient by their Hint EHR ID.\n */\n async getByHintId(hintId: string): Promise<Patient | null> {\n try {\n const result = await this.convex.query(\n \"patients:getByHintId\" as never,\n {\n hintId,\n } as never,\n );\n return (result as Patient) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * List patients with optional search, pagination, and limit.\n */\n async list(options?: PatientListOptions): Promise<PaginatedResult<Patient>> {\n try {\n const result = await this.convex.query(\n \"patients:list\" as never,\n {\n search: options?.search,\n limit: options?.limit,\n cursor: options?.cursor,\n } as never,\n );\n\n const typed = result as PaginatedResult<Patient> | undefined;\n\n return typed ?? { data: [], cursor: null, hasMore: false };\n } catch {\n return { data: [], cursor: null, hasMore: false };\n }\n }\n}\n\nexport { PatientResource };\n","/**\n * PhysiciansResource — Convex-backed physician lookups.\n *\n * Populated from Elation by Truth's daily PhysiciansBackfillCron. Replaces\n * per-physician Elation HTTP hops with a single Convex batch query.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\n\nexport interface Physician {\n _id: string;\n elationId: number;\n firstName?: string;\n lastName?: string;\n npi?: string;\n credentials?: string;\n specialties?: string[];\n practice?: number;\n email?: string;\n phone?: string;\n lastSyncedAt: string;\n}\n\nclass PhysiciansResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convex: ConvexHttpClient) {\n this.convex = convex;\n }\n\n /**\n * Resolve a batch of physicians by Elation IDs. Missing ids are dropped.\n */\n async getByElationIds(ids: number[]): Promise<Physician[]> {\n if (ids.length === 0) {\n return [];\n }\n try {\n const rows = (await this.convex.query(\n \"physicians:getByElationIds\" as never,\n { ids } as never,\n )) as Physician[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n async getByElationId(id: number): Promise<Physician | null> {\n try {\n const row = (await this.convex.query(\n \"physicians:getByElationId\" as never,\n { id } as never,\n )) as Physician | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByPractice(practice: number, limit?: number): Promise<Physician[]> {\n try {\n const rows = (await this.convex.query(\n \"physicians:listByPractice\" as never,\n { practice, limit } as never,\n )) as Physician[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { PhysiciansResource };\n","/**\n * RemindersResource — schedule, cancel, and list conversation reminders.\n *\n * Backed by Convex mutations/queries (durable scheduled functions) — replaces\n * the in-memory setTimeout scheduler that used to live in CommHub's NestJS\n * backend.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n Reminder,\n ScheduleReminderInput,\n ScheduleReminderResult,\n} from \"../types/reminder\";\n\nclass RemindersResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n /**\n * Schedule a reminder to fire at `remindAt`. Returns the reminder id,\n * which callers should store if they may want to cancel it later.\n */\n async schedule(\n input: ScheduleReminderInput,\n ): Promise<ScheduleReminderResult> {\n const remindAt =\n input.remindAt instanceof Date\n ? input.remindAt.toISOString()\n : input.remindAt;\n\n const result = (await this.convex.mutation(\n \"reminders:schedule\" as never,\n {\n conversationId: input.conversationId,\n remindAt,\n note: input.note,\n createdBy: input.createdBy,\n } as never,\n )) as ScheduleReminderResult;\n\n return result;\n }\n\n /**\n * Cancel a pending reminder. No-op if the reminder has already fired or\n * been cancelled.\n */\n async cancel(\n reminderId: string,\n cancelledBy: string,\n ): Promise<{ reminderId: string; status: string }> {\n return (await this.convex.mutation(\n \"reminders:cancel\" as never,\n { reminderId, cancelledBy } as never,\n )) as { reminderId: string; status: string };\n }\n\n /**\n * List reminders for a conversation (most recent first).\n */\n async listByConversation(conversationId: string): Promise<Reminder[]> {\n try {\n const rows = (await this.convex.query(\n \"reminders:listByConversation\" as never,\n { conversationId } as never,\n )) as Reminder[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { RemindersResource };\n","/**\n * TasksResource — create, update status, list, get tasks.\n *\n * Backed by Convex. Replaces CommHub's createTaskWithNotification +\n * updateTaskStatusWithNotification actions. Push notification side-effect\n * is emitted downstream via Kinesis.\n */\n\nimport type { ConvexHttpClient } from \"convex/browser\";\nimport type {\n CreateTaskInput,\n Task,\n TaskStatus,\n UpdateTaskStatusInput,\n} from \"../types/task\";\n\nclass TasksResource {\n private readonly convex: ConvexHttpClient;\n\n constructor(convexClient: ConvexHttpClient) {\n this.convex = convexClient;\n }\n\n async create(\n input: CreateTaskInput,\n ): Promise<{ taskId: string; status: TaskStatus }> {\n return (await this.convex.mutation(\n \"tasks:create\" as never,\n input as never,\n )) as { taskId: string; status: TaskStatus };\n }\n\n async updateStatus(input: UpdateTaskStatusInput): Promise<{\n taskId: string;\n status: TaskStatus;\n previousStatus: TaskStatus;\n }> {\n return (await this.convex.mutation(\n \"tasks:updateStatus\" as never,\n input as never,\n )) as {\n taskId: string;\n status: TaskStatus;\n previousStatus: TaskStatus;\n };\n }\n\n async get(taskId: string): Promise<Task | null> {\n try {\n const row = (await this.convex.query(\n \"tasks:get\" as never,\n { taskId } as never,\n )) as Task | null;\n return row ?? null;\n } catch {\n return null;\n }\n }\n\n async listByAssignee(\n assignedTo: string,\n options?: { status?: TaskStatus; limit?: number },\n ): Promise<Task[]> {\n try {\n const rows = (await this.convex.query(\n \"tasks:listByAssignee\" as never,\n {\n assignedTo,\n status: options?.status,\n limit: options?.limit,\n } as never,\n )) as Task[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n\n async listOpen(limit?: number): Promise<Task[]> {\n try {\n const rows = (await this.convex.query(\n \"tasks:listOpen\" as never,\n { limit } as never,\n )) as Task[] | null;\n return rows ?? [];\n } catch {\n return [];\n }\n }\n}\n\nexport { TasksResource };\n","/**\n * TranslationResource — Azure Translator proxy through Truth API.\n *\n * All three operations are HTTP calls to Truth's oRPC endpoints at\n * `/api/translation/*`, authenticated with the same X-API-Key as the\n * event tracker.\n */\n\nimport type {\n DetectionResult,\n TranslateBatchInput,\n TranslateTextInput,\n TranslationResult,\n} from \"../types/translation\";\n\nclass TranslationError extends Error {\n readonly status: number;\n readonly operation: string;\n\n constructor(operation: string, status: number, message?: string) {\n super(message ?? `Translation ${operation} failed (HTTP ${status})`);\n this.name = \"TranslationError\";\n this.status = status;\n this.operation = operation;\n }\n}\n\nclass TranslationResource {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiBaseUrl: string, apiKey: string) {\n this.baseUrl = apiBaseUrl;\n this.apiKey = apiKey;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const url = `${this.baseUrl}/api${path}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n \"X-API-Key\": this.apiKey,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new TranslationError(path, res.status, text.slice(0, 200));\n }\n return (await res.json()) as T;\n }\n\n async translate(input: TranslateTextInput): Promise<TranslationResult> {\n return await this.post<TranslationResult>(\"/translation/translate\", input);\n }\n\n async translateBatch(\n input: TranslateBatchInput,\n ): Promise<TranslationResult[]> {\n const response = await this.post<{ results: TranslationResult[] }>(\n \"/translation/translate-batch\",\n input,\n );\n return response.results;\n }\n\n async detect(text: string): Promise<DetectionResult> {\n return await this.post<DetectionResult>(\"/translation/detect\", { text });\n }\n}\n\nexport { TranslationResource, TranslationError };\n","/**\n * Event tracker with batching, retry, and flush support.\n *\n * Buffers events in an internal queue and flushes them to the Truth API\n * endpoint. Flushes occur when the buffer reaches `batchSize` or every\n * `flushIntervalMs` milliseconds, whichever comes first.\n */\n\nimport type { ActorContext } from \"../types/config\";\nimport type {\n EventEnvelope,\n EventPayloadMap,\n EventType,\n TrackOptions,\n} from \"./events\";\n\n// ---------------------------------------------------------------------------\n// UUID v7 helper (no external dependency)\n// ---------------------------------------------------------------------------\n\n/**\n * Generates a UUID v7 string. Uses crypto.randomUUID where available,\n * falling back to a timestamp + random bytes implementation.\n *\n * UUID v7 layout (RFC 9562):\n * 48 bits - unix timestamp (ms)\n * 4 bits - version (0b0111)\n * 12 bits - random\n * 2 bits - variant (0b10)\n * 62 bits - random\n */\nfunction generateUuidV7(): string {\n const now = Date.now();\n\n // 6 bytes of timestamp\n const timeBytes = new Uint8Array(6);\n let ts = now;\n for (let i = 5; i >= 0; i--) {\n timeBytes[i] = ts & 0xff;\n ts = Math.floor(ts / 256);\n }\n\n // 10 bytes of random\n const randomBytes = new Uint8Array(10);\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n globalThis.crypto.getRandomValues\n ) {\n globalThis.crypto.getRandomValues(randomBytes);\n } else {\n for (let i = 0; i < 10; i++) {\n randomBytes[i] = Math.floor(Math.random() * 256);\n }\n }\n\n // Assemble 16 bytes\n const bytes = new Uint8Array(16);\n bytes.set(timeBytes, 0);\n bytes.set(randomBytes, 6);\n\n // Set version (bits 48-51 to 0b0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70;\n\n // Set variant (bits 64-65 to 0b10)\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n\n // Format as hex string with dashes\n const hex = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20, 32),\n ].join(\"-\");\n}\n\n// ---------------------------------------------------------------------------\n// Environment-based API URL resolution\n// ---------------------------------------------------------------------------\n\n// Environment → Truth API base URL.\n//\n// Topology:\n// local / staging / stg / sandbox → sandbox Truth (app.sandbox.*)\n// uat / production → production Truth (app.truth.*)\n//\n// UAT shares production resources (same Fly Postgres + Hasura + Truth +\n// Convex + EHR tokens as prod). Staging is the isolated sandbox env.\nconst API_URLS: Record<string, string> = {\n local: \"http://localhost:3000\",\n staging: \"https://app.sandbox.communication-hub.com\",\n stg: \"https://app.sandbox.communication-hub.com\",\n sandbox: \"https://app.sandbox.communication-hub.com\",\n uat: \"https://app.truth.communication-hub.com\",\n production: \"https://app.truth.communication-hub.com\",\n};\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_BATCH_SIZE = 25;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\nconst MAX_RETRIES = 3;\nconst BASE_RETRY_DELAY_MS = 500;\n\n// ---------------------------------------------------------------------------\n// Tracker configuration\n// ---------------------------------------------------------------------------\n\ninterface TrackerConfig {\n apiKey: string;\n environment: string;\n source: string;\n sourceVersion: string;\n tenantId: string;\n batchSize: number;\n flushIntervalMs: number;\n apiBaseUrl?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Tracker class\n// ---------------------------------------------------------------------------\n\nclass Tracker {\n private readonly config: TrackerConfig;\n readonly apiUrl: string;\n private queue: EventEnvelope[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private defaultActor: ActorContext | undefined;\n private isFlushing = false;\n private isShutdown = false;\n\n constructor(config: TrackerConfig) {\n this.config = config;\n this.apiUrl =\n config.apiBaseUrl ?? API_URLS[config.environment] ?? API_URLS.local;\n\n this.startFlushInterval();\n this.registerShutdownHooks();\n }\n\n /**\n * Set the default actor context for subsequent events.\n */\n setActor(actor: ActorContext): void {\n this.defaultActor = actor;\n }\n\n /**\n * Enqueue a typed event for delivery. This is fire-and-forget from\n * the caller's perspective -- events are buffered and flushed in batches.\n */\n track<T extends EventType>(\n eventType: T,\n payload: EventPayloadMap[T],\n options?: TrackOptions,\n ): void {\n if (this.isShutdown) {\n return;\n }\n\n const now = new Date().toISOString();\n\n const envelope: EventEnvelope = {\n event_id: generateUuidV7(),\n event_type: eventType,\n schema_version: 1,\n occurred_at: options?.occurredAt ?? now,\n received_at: now,\n source: this.config.source,\n source_version: this.config.sourceVersion,\n tenant_id: options?.tenantId ?? this.config.tenantId,\n actor:\n options?.actor ??\n (this.defaultActor\n ? {\n actor_id: this.defaultActor.actorId,\n actor_type: this.defaultActor.actorType,\n }\n : undefined),\n subject: options?.subject,\n compliance: options?.compliance,\n payload: payload as unknown as Record<string, unknown>,\n };\n\n this.queue.push(envelope);\n\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Force an immediate flush of all buffered events.\n * Returns a promise that resolves when the flush completes.\n */\n async flush(): Promise<void> {\n if (this.queue.length === 0 || this.isFlushing) {\n return;\n }\n\n this.isFlushing = true;\n const batch = this.queue.splice(0, this.config.batchSize);\n\n try {\n await this.sendBatch(batch);\n } catch {\n // Re-queue events that failed to send (prepend to maintain ordering)\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n\n // If there are still events in the queue, flush again\n if (this.queue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Gracefully shut down the tracker. Flushes remaining events and\n * clears the flush interval.\n */\n async shutdown(): Promise<void> {\n this.isShutdown = true;\n this.stopFlushInterval();\n await this.flush();\n }\n\n /**\n * Send a batch of events to the Truth API with exponential backoff retry.\n */\n private async sendBatch(batch: EventEnvelope[]): Promise<void> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(`${this.apiUrl}/api/events/ingest`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n },\n body: JSON.stringify({ events: batch }),\n });\n\n if (response.ok) {\n return;\n }\n\n // Don't retry 4xx client errors (except 429)\n if (\n response.status >= 400 &&\n response.status < 500 &&\n response.status !== 429\n ) {\n return;\n }\n\n lastError = new Error(\n `HTTP ${response.status}: ${response.statusText}`,\n );\n } catch (error) {\n lastError = error;\n }\n\n // Exponential backoff with jitter\n if (attempt < MAX_RETRIES) {\n const delay = BASE_RETRY_DELAY_MS * 2 ** attempt;\n const jitter = Math.random() * delay * 0.5;\n await sleep(delay + jitter);\n }\n }\n\n throw lastError;\n }\n\n private startFlushInterval(): void {\n if (this.config.flushIntervalMs > 0) {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n\n // Unref the timer so it doesn't prevent Node.js from exiting\n if (typeof this.flushTimer === \"object\" && \"unref\" in this.flushTimer) {\n this.flushTimer.unref();\n }\n }\n }\n\n private stopFlushInterval(): void {\n if (this.flushTimer !== null) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n private registerShutdownHooks(): void {\n // Only register in Node.js environments\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.on) {\n const shutdownHandler = () => {\n void this.shutdown();\n };\n\n globalThis.process.on(\"beforeExit\", shutdownHandler);\n globalThis.process.on(\"SIGTERM\", shutdownHandler);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport {\n Tracker,\n generateUuidV7,\n DEFAULT_BATCH_SIZE,\n DEFAULT_FLUSH_INTERVAL_MS,\n};\nexport type { TrackerConfig };\n","/**\n * Web Push helpers for browser environments.\n *\n * Handles service worker registration, VAPID push subscription, and\n * message forwarding from the service worker to the main thread.\n */\n\nexport interface WebPushConfig {\n vapidPublicKey: string;\n serviceWorkerPath?: string;\n}\n\nexport function isWebPushSupported(): boolean {\n return (\n typeof window !== \"undefined\" &&\n \"serviceWorker\" in navigator &&\n \"PushManager\" in window\n );\n}\n\nexport async function registerServiceWorker(\n path = \"/truth-sw.js\",\n): Promise<ServiceWorkerRegistration> {\n return navigator.serviceWorker.register(path);\n}\n\nexport async function subscribeToPush(\n registration: ServiceWorkerRegistration,\n vapidPublicKey: string,\n): Promise<PushSubscription> {\n const existing = await registration.pushManager.getSubscription();\n if (existing) {\n return existing;\n }\n\n return registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(\n vapidPublicKey,\n ) as unknown as ArrayBuffer,\n });\n}\n\nexport function subscriptionToJSON(sub: PushSubscription): {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n} {\n const json = sub.toJSON();\n return {\n endpoint: sub.endpoint,\n keys: {\n p256dh: json.keys?.p256dh ?? \"\",\n auth: json.keys?.auth ?? \"\",\n },\n };\n}\n\nfunction urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = \"=\".repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, \"+\").replace(/_/g, \"/\");\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n\nexport function onServiceWorkerMessage(\n type: string,\n callback: (payload: unknown) => void,\n): () => void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return () => {};\n }\n const handler = (event: MessageEvent) => {\n if (event.data?.type === type) {\n callback(event.data.payload);\n }\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n return () => navigator.serviceWorker.removeEventListener(\"message\", handler);\n}\n","/**\n * Typed event definitions for the Truth Platform event store.\n *\n * All 26 event types from the Communication Hub -> Truth Event Store Contract.\n * Events are grouped by domain and include typed payload interfaces.\n */\n\n// ---------------------------------------------------------------------------\n// Event type constants\n// ---------------------------------------------------------------------------\n\nconst CONVERSATION_EVENTS = {\n created: \"conversation.created.v1\",\n messageSent: \"conversation.message_sent.v1\",\n messageReceived: \"conversation.message_received.v1\",\n markedRead: \"conversation.marked_read.v1\",\n attachmentUploaded: \"conversation.attachment_uploaded.v1\",\n attachmentDownloaded: \"conversation.attachment_downloaded.v1\",\n} as const;\n\nconst CALL_EVENTS = {\n initiated: \"call.initiated.v1\",\n connected: \"call.connected.v1\",\n ended: \"call.ended.v1\",\n missed: \"call.missed.v1\",\n} as const;\n\nconst TASK_EVENTS = {\n created: \"task.created.v1\",\n assigned: \"task.assigned.v1\",\n statusChanged: \"task.status_changed.v1\",\n} as const;\n\nconst REMINDER_EVENTS = {\n scheduled: \"reminder.scheduled.v1\",\n triggered: \"reminder.triggered.v1\",\n} as const;\n\nconst TRANSLATION_EVENTS = {\n requested: \"translation.requested.v1\",\n completed: \"translation.completed.v1\",\n} as const;\n\nconst NOTIFICATION_EVENTS = {\n sent: \"notification.sent.v1\",\n delivered: \"notification.delivered.v1\",\n opened: \"notification.opened.v1\",\n} as const;\n\nconst PROVIDER_EVENTS = {\n syncStarted: \"provider.sync_started.v1\",\n syncSucceeded: \"provider.sync_succeeded.v1\",\n syncFailed: \"provider.sync_failed.v1\",\n} as const;\n\nconst AUTH_EVENTS = {\n loginSucceeded: \"auth.login_succeeded.v1\",\n loginFailed: \"auth.login_failed.v1\",\n} as const;\n\nconst SECURITY_EVENTS = {\n accessDenied: \"security.access_denied.v1\",\n} as const;\n\n/**\n * All event type constants grouped by domain.\n */\nconst EVENT_TYPES = {\n conversation: CONVERSATION_EVENTS,\n call: CALL_EVENTS,\n task: TASK_EVENTS,\n reminder: REMINDER_EVENTS,\n translation: TRANSLATION_EVENTS,\n notification: NOTIFICATION_EVENTS,\n provider: PROVIDER_EVENTS,\n auth: AUTH_EVENTS,\n security: SECURITY_EVENTS,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Event type union\n// ---------------------------------------------------------------------------\n\ntype ConversationEventType =\n (typeof CONVERSATION_EVENTS)[keyof typeof CONVERSATION_EVENTS];\ntype CallEventType = (typeof CALL_EVENTS)[keyof typeof CALL_EVENTS];\ntype TaskEventType = (typeof TASK_EVENTS)[keyof typeof TASK_EVENTS];\ntype ReminderEventType = (typeof REMINDER_EVENTS)[keyof typeof REMINDER_EVENTS];\ntype TranslationEventType =\n (typeof TRANSLATION_EVENTS)[keyof typeof TRANSLATION_EVENTS];\ntype NotificationEventType =\n (typeof NOTIFICATION_EVENTS)[keyof typeof NOTIFICATION_EVENTS];\ntype ProviderEventType = (typeof PROVIDER_EVENTS)[keyof typeof PROVIDER_EVENTS];\ntype AuthEventType = (typeof AUTH_EVENTS)[keyof typeof AUTH_EVENTS];\ntype SecurityEventType = (typeof SECURITY_EVENTS)[keyof typeof SECURITY_EVENTS];\n\n/**\n * Union of all 26 registered event type strings.\n */\ntype EventType =\n | ConversationEventType\n | CallEventType\n | TaskEventType\n | ReminderEventType\n | TranslationEventType\n | NotificationEventType\n | ProviderEventType\n | AuthEventType\n | SecurityEventType;\n\n// ---------------------------------------------------------------------------\n// Payload interfaces (per the contract doc)\n// ---------------------------------------------------------------------------\n\ninterface ConversationCreatedPayload {\n channel: string;\n origin_system: string;\n participant_count: number;\n}\n\ninterface ConversationMessageSentPayload {\n channel: string;\n direction: string;\n message_chars: number;\n has_attachment: boolean;\n provider_system: string;\n}\n\ninterface ConversationMessageReceivedPayload {\n channel: string;\n direction: string;\n message_chars: number;\n provider_system: string;\n}\n\ninterface ConversationMarkedReadPayload {\n read_by_actor_id: string;\n unread_count_before: number;\n unread_count_after: number;\n}\n\ninterface ConversationAttachmentUploadedPayload {\n attachment_id: string;\n mime_type: string;\n size_bytes: number;\n storage_class: string;\n}\n\ninterface ConversationAttachmentDownloadedPayload {\n attachment_id: string;\n download_actor_type: string;\n access_path: string;\n}\n\ninterface CallInitiatedPayload {\n direction: string;\n provider_system: string;\n from_number_ref: string;\n to_number_ref: string;\n}\n\ninterface CallConnectedPayload {\n provider_system: string;\n ring_duration_ms: number;\n}\n\ninterface CallEndedPayload {\n provider_system: string;\n duration_ms: number;\n end_reason: string;\n disposition: string;\n}\n\ninterface CallMissedPayload {\n provider_system: string;\n miss_reason: string;\n}\n\ninterface TaskCreatedPayload {\n task_id: string;\n created_by: string;\n assigned_to: string;\n priority: string;\n due_at: string;\n}\n\ninterface TaskAssignedPayload {\n task_id: string;\n assigned_to: string;\n assigned_by: string;\n}\n\ninterface TaskStatusChangedPayload {\n task_id: string;\n status_from: string;\n status_to: string;\n changed_by: string;\n}\n\ninterface ReminderScheduledPayload {\n reminder_id: string;\n conversation_id: string;\n scheduled_for: string;\n scheduled_by: string;\n}\n\ninterface ReminderTriggeredPayload {\n reminder_id: string;\n trigger_result: string;\n notification_attempted: boolean;\n}\n\ninterface TranslationRequestedPayload {\n target_language: string;\n source_language: string;\n char_count: number;\n mode: string;\n}\n\ninterface TranslationCompletedPayload {\n target_language: string;\n provider: string;\n latency_ms: number;\n success: boolean;\n error_code?: string;\n}\n\ninterface NotificationSentPayload {\n notification_id: string;\n channel: string;\n platform: string;\n recipient_ref: string;\n success: boolean;\n}\n\ninterface NotificationDeliveredPayload {\n notification_id: string;\n platform: string;\n delivered_at: string;\n}\n\ninterface NotificationOpenedPayload {\n notification_id: string;\n platform: string;\n opened_at: string;\n}\n\ninterface ProviderSyncStartedPayload {\n provider_system: string;\n operation: string;\n scope: string;\n batch_id: string;\n}\n\ninterface ProviderSyncSucceededPayload {\n provider_system: string;\n operation: string;\n batch_id: string;\n records_processed: number;\n duration_ms: number;\n}\n\ninterface ProviderSyncFailedPayload {\n provider_system: string;\n operation: string;\n batch_id: string;\n error_code: string;\n retryable: boolean;\n}\n\ninterface AuthLoginSucceededPayload {\n auth_provider: string;\n platform: string;\n session_ref: string;\n}\n\ninterface AuthLoginFailedPayload {\n auth_provider: string;\n platform: string;\n failure_code: string;\n}\n\ninterface SecurityAccessDeniedPayload {\n resource: string;\n policy: string;\n reason_code: string;\n actor_id: string;\n}\n\n// ---------------------------------------------------------------------------\n// Event type -> payload mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each event type string to its required payload interface.\n */\ninterface EventPayloadMap {\n \"conversation.created.v1\": ConversationCreatedPayload;\n \"conversation.message_sent.v1\": ConversationMessageSentPayload;\n \"conversation.message_received.v1\": ConversationMessageReceivedPayload;\n \"conversation.marked_read.v1\": ConversationMarkedReadPayload;\n \"conversation.attachment_uploaded.v1\": ConversationAttachmentUploadedPayload;\n \"conversation.attachment_downloaded.v1\": ConversationAttachmentDownloadedPayload;\n \"call.initiated.v1\": CallInitiatedPayload;\n \"call.connected.v1\": CallConnectedPayload;\n \"call.ended.v1\": CallEndedPayload;\n \"call.missed.v1\": CallMissedPayload;\n \"task.created.v1\": TaskCreatedPayload;\n \"task.assigned.v1\": TaskAssignedPayload;\n \"task.status_changed.v1\": TaskStatusChangedPayload;\n \"reminder.scheduled.v1\": ReminderScheduledPayload;\n \"reminder.triggered.v1\": ReminderTriggeredPayload;\n \"translation.requested.v1\": TranslationRequestedPayload;\n \"translation.completed.v1\": TranslationCompletedPayload;\n \"notification.sent.v1\": NotificationSentPayload;\n \"notification.delivered.v1\": NotificationDeliveredPayload;\n \"notification.opened.v1\": NotificationOpenedPayload;\n \"provider.sync_started.v1\": ProviderSyncStartedPayload;\n \"provider.sync_succeeded.v1\": ProviderSyncSucceededPayload;\n \"provider.sync_failed.v1\": ProviderSyncFailedPayload;\n \"auth.login_succeeded.v1\": AuthLoginSucceededPayload;\n \"auth.login_failed.v1\": AuthLoginFailedPayload;\n \"security.access_denied.v1\": SecurityAccessDeniedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Event envelope\n// ---------------------------------------------------------------------------\n\n/**\n * Subject references for the event. Uses tokenized references only -- no PHI.\n */\ninterface EventSubject {\n patient_ref?: string;\n conversation_id?: string;\n task_id?: string;\n call_id?: string;\n}\n\n/**\n * Actor who triggered the event.\n */\ninterface EventActor {\n actor_id: string;\n actor_type: \"user\" | \"system\" | \"webhook\";\n}\n\n/**\n * Compliance metadata for the event.\n */\ninterface EventCompliance {\n pii_level: \"none\" | \"limited\" | \"full\";\n contains_phi: boolean;\n consent_context: string;\n retention_class: string;\n}\n\n/**\n * Canonical event envelope. Every tracked event is wrapped in this structure\n * before being sent to the Truth API.\n */\ninterface EventEnvelope {\n event_id: string;\n event_type: string;\n schema_version: number;\n occurred_at: string;\n received_at: string;\n source: string;\n source_version: string;\n tenant_id: string;\n actor?: EventActor;\n subject?: EventSubject;\n compliance?: EventCompliance;\n payload: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Track options (per-call overrides)\n// ---------------------------------------------------------------------------\n\n/**\n * Optional overrides when calling truth.track().\n */\ninterface TrackOptions {\n /** Override the default actor for this event */\n actor?: EventActor;\n\n /** Subject references for this event */\n subject?: EventSubject;\n\n /** Compliance metadata for this event */\n compliance?: EventCompliance;\n\n /** Override the default tenant ID for this event */\n tenantId?: string;\n\n /** Override the occurred_at timestamp (ISO 8601) */\n occurredAt?: string;\n}\n\nexport {\n EVENT_TYPES,\n CONVERSATION_EVENTS,\n CALL_EVENTS,\n TASK_EVENTS,\n REMINDER_EVENTS,\n TRANSLATION_EVENTS,\n NOTIFICATION_EVENTS,\n PROVIDER_EVENTS,\n AUTH_EVENTS,\n SECURITY_EVENTS,\n};\n\nexport type {\n EventType,\n ConversationEventType,\n CallEventType,\n TaskEventType,\n ReminderEventType,\n TranslationEventType,\n NotificationEventType,\n ProviderEventType,\n AuthEventType,\n SecurityEventType,\n EventPayloadMap,\n EventEnvelope,\n EventActor,\n EventSubject,\n EventCompliance,\n TrackOptions,\n ConversationCreatedPayload,\n ConversationMessageSentPayload,\n ConversationMessageReceivedPayload,\n ConversationMarkedReadPayload,\n ConversationAttachmentUploadedPayload,\n ConversationAttachmentDownloadedPayload,\n CallInitiatedPayload,\n CallConnectedPayload,\n CallEndedPayload,\n CallMissedPayload,\n TaskCreatedPayload,\n TaskAssignedPayload,\n TaskStatusChangedPayload,\n ReminderScheduledPayload,\n ReminderTriggeredPayload,\n TranslationRequestedPayload,\n TranslationCompletedPayload,\n NotificationSentPayload,\n NotificationDeliveredPayload,\n NotificationOpenedPayload,\n ProviderSyncStartedPayload,\n ProviderSyncSucceededPayload,\n ProviderSyncFailedPayload,\n AuthLoginSucceededPayload,\n AuthLoginFailedPayload,\n SecurityAccessDeniedPayload,\n};\n","/**\n * Configuration interfaces for the Truth SDK.\n */\n\n/**\n * Environment options for the Truth platform.\n */\nconst ENVIRONMENTS = {\n local: \"local\",\n staging: \"staging\",\n stg: \"stg\",\n sandbox: \"sandbox\",\n uat: \"uat\",\n production: \"production\",\n} as const;\n\ntype Environment = (typeof ENVIRONMENTS)[keyof typeof ENVIRONMENTS];\n\n/**\n * Configuration for initializing a TruthClient.\n */\ninterface TruthClientConfig {\n /** API key for authenticating with the Truth platform (e.g. \"hn_live_...\") */\n apiKey: string;\n\n /** Target environment */\n environment: Environment;\n\n /** Override the default Convex URL for data access */\n convexUrl?: string;\n\n /** Event source identifier (e.g. \"communication-hub.backend\") */\n source?: string;\n\n /** Git SHA or version string for event source versioning */\n sourceVersion?: string;\n\n /** Default tenant (organization) ID for events */\n tenantId?: string;\n\n /** Number of events to buffer before flushing (default: 25) */\n batchSize?: number;\n\n /** Interval in milliseconds between automatic flushes (default: 5000) */\n flushIntervalMs?: number;\n\n /** Base URL for the Truth API (overrides environment-based default) */\n apiBaseUrl?: string;\n\n /**\n * Auto-register the service worker and subscribe to web push on init.\n * Only applies in browser environments with Push API support.\n * Default: true.\n */\n autoInitServiceWorker?: boolean;\n\n /** Path to the service worker file. Default: \"/truth-sw.js\" */\n serviceWorkerPath?: string;\n}\n\n/**\n * Actor context attached to tracked events.\n */\ninterface ActorContext {\n actorId: string;\n actorType: \"user\" | \"system\" | \"webhook\";\n}\n\n/**\n * Paginated result wrapper for list operations.\n */\ninterface PaginatedResult<T> {\n data: T[];\n cursor: string | null;\n hasMore: boolean;\n}\n\nexport { ENVIRONMENTS };\nexport type { Environment, TruthClientConfig, ActorContext, PaginatedResult };\n"],"mappings":"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","names":[]}
|