@medplum/react-hooks 5.1.13 → 5.1.15

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.
@@ -7,6 +7,10 @@ import type { Encounter } from '@medplum/fhirtypes';
7
7
  import type { ExtractResource } from '@medplum/fhirtypes';
8
8
  import type { Identifier } from '@medplum/fhirtypes';
9
9
  import type { JSX } from 'react';
10
+ import type { Medication } from '@medplum/fhirtypes';
11
+ import type { MedicationOrderRequest } from '@medplum/core';
12
+ import type { MedicationOrderResponse } from '@medplum/core';
13
+ import type { MedicationSearchParams } from '@medplum/core';
10
14
  import type { MedplumClient } from '@medplum/core';
11
15
  import type { OperationOutcome } from '@medplum/fhirtypes';
12
16
  import type { Organization } from '@medplum/fhirtypes';
@@ -38,13 +42,6 @@ export declare function buildInitialResponseItem(item: QuestionnaireItem): Quest
38
42
 
39
43
  export declare function convertToPCM16(float32Array: Float32Array): Uint8Array;
40
44
 
41
- export declare interface EPrescribingIFrameOptions {
42
- readonly patientId?: string;
43
- readonly onPatientSyncSuccess?: () => void;
44
- readonly onIframeSuccess?: (url: string) => void;
45
- readonly onError?: (err: unknown) => void;
46
- }
47
-
48
45
  /**
49
46
  * Evaluates the calculated expressions in a questionnaire.
50
47
  * Updates response item answers in place with the calculated values.
@@ -108,6 +105,13 @@ export declare function isChoiceQuestion(item: QuestionnaireItem): boolean;
108
105
  */
109
106
  export declare function isQuestionEnabled(item: QuestionnaireItem, questionnaireResponse: QuestionnaireResponse | undefined): boolean;
110
107
 
108
+ export declare interface MedicationIFrameOptions {
109
+ readonly patientId?: string;
110
+ readonly onPatientSyncSuccess?: () => void;
111
+ readonly onIframeSuccess?: (url: string) => void;
112
+ readonly onError?: (err: unknown) => void;
113
+ }
114
+
111
115
  export declare interface MedplumContext {
112
116
  medplum: MedplumClient;
113
117
  navigate: MedplumNavigateFunction;
@@ -287,8 +291,8 @@ export declare function typedValueToResponseItem(item: QuestionnaireItem, value:
287
291
  export declare const useCachedBinaryUrl: (binaryUrl: string | undefined) => string | undefined;
288
292
 
289
293
  /**
290
- * Generic React hook that syncs a patient to an e-prescribing system and
291
- * returns the iframe URL.
294
+ * Generic React hook that syncs a patient to a medication-order vendor and
295
+ * returns the chart iframe URL.
292
296
  *
293
297
  * Executes the patient-sync bot first (if patientId is provided), then
294
298
  * the iframe bot to obtain the prescribing UI URL.
@@ -299,9 +303,92 @@ export declare const useCachedBinaryUrl: (binaryUrl: string | undefined) => stri
299
303
  * @param syncBotIdentifier - Bot identifier for the patient sync bot.
300
304
  * @param iframeBotIdentifier - Bot identifier for the iframe URL bot.
301
305
  * @param options - Configuration and callback options.
302
- * @returns The e-prescribing iframe URL, or undefined while loading.
306
+ * @returns The medication-order iframe URL, or undefined while loading.
307
+ */
308
+ export declare function useMedicationIFrame(syncBotIdentifier: Identifier, iframeBotIdentifier: Identifier, options: MedicationIFrameOptions): string | undefined;
309
+
310
+ /**
311
+ * Vendor-neutral hook for e-prescribing drug search and order-medication via
312
+ * **FHIR custom operations** (no Bot identifiers required).
313
+ *
314
+ * Hits two project-scoped operations whose backing Bot is chosen at deploy time
315
+ * via an `OperationDefinition` resource carrying the
316
+ * `operationDefinition-implementation` extension — see
317
+ * [bot operations docs](https://www.medplum.com/docs/bots/custom-fhir-operations).
318
+ * The server's `tryCustomOperation` dispatch handles the OD → Bot lookup, so
319
+ * projects can swap vendors (ScriptSure today, DoseSpot tomorrow) by deploying
320
+ * a different bot under the same operation code.
321
+ *
322
+ * - `searchMedications`: `POST /fhir/R4/Medication/$drug-search` — expects the
323
+ * bot to return a `Bundle<Medication>` (single-Resource `return` shortcut on
324
+ * the OperationDefinition).
325
+ * - `orderMedication`: `POST /fhir/R4/MedicationRequest/$order-medication` —
326
+ * expects the bot to return a `Parameters` envelope with named primitives;
327
+ * `parametersToMedicationOrderResponse` decodes it.
328
+ *
329
+ * @returns Callbacks to search medications and submit an order request.
303
330
  */
304
- export declare function useEPrescribingIFrame(syncBotIdentifier: Identifier, iframeBotIdentifier: Identifier, options: EPrescribingIFrameOptions): string | undefined;
331
+ export declare function useMedicationOrder(): UseMedicationOrderReturn;
332
+
333
+ export declare interface UseMedicationOrderReturn {
334
+ searchMedications: (params: MedicationSearchParams) => Promise<Medication[]>;
335
+ orderMedication: (input: MedicationOrderRequest) => Promise<MedicationOrderResponse>;
336
+ }
337
+
338
+ /**
339
+ * Vendor-neutral React hook that calls the `$order-set-url` custom FHIR
340
+ * operation and exposes the resulting iframe URL plus refresh/loading/error
341
+ * state.
342
+ *
343
+ * Hits the project-scoped operation whose backing bot is chosen at deploy time
344
+ * via an `OperationDefinition` resource carrying the
345
+ * `operationDefinition-implementation` extension — see
346
+ * [bot operations docs](https://www.medplum.com/docs/bots/custom-fhir-operations).
347
+ * The server's `tryCustomOperation` dispatch handles the OD → Bot lookup, so
348
+ * projects can swap vendors (ScriptSure today, DoseSpot tomorrow) by deploying
349
+ * a different bot under the same operation code.
350
+ *
351
+ * - URL: `POST /fhir/R4/PlanDefinition/$order-set-url`
352
+ * - Body: `Parameters` with `patientId` + (`planDefinitionId` XOR `vendorOrderSetId`) + optional `appId`.
353
+ * - Returns: `Parameters` whose `launchUrl` is exposed as `url` on the hook.
354
+ *
355
+ * The hook is a "build a URL" hook (mirrors {@link useMedicationIFrame}); it
356
+ * does not stamp Medplum resources or create vendor-side resources, so
357
+ * `refresh` is safe to call repeatedly (the operation is naturally idempotent).
358
+ *
359
+ * Re-runs whenever the input options change. In-flight calls are cancelled on
360
+ * input change and on unmount via a per-effect `cancelled` flag, so React 18
361
+ * Strict Mode double-mount does not surface stale URLs.
362
+ *
363
+ * @param options - Patient, picker (PD or vendor id), and optional appId.
364
+ * @returns `{ url, loading, error, refresh }`.
365
+ */
366
+ export declare function useMedicationOrderSet(options: UseMedicationOrderSetOptions): UseMedicationOrderSetReturn;
367
+
368
+ export declare interface UseMedicationOrderSetOptions {
369
+ /** Patient to prescribe against. Hook stays idle (no operation call) until set. */
370
+ readonly patientId: string | undefined;
371
+ /** Medplum PlanDefinition id (vendor-neutral). Bot resolves it to the vendor's order set id. */
372
+ readonly planDefinitionId?: string;
373
+ /** Vendor-side order set id, when picked directly (escape hatch when no synced PD exists yet). */
374
+ readonly vendorOrderSetId?: number | string;
375
+ readonly appId?: string;
376
+ }
377
+
378
+ export declare interface UseMedicationOrderSetReturn {
379
+ /** Most recent successful URL from the order-set operation, or undefined while loading / on error. */
380
+ readonly url: string | undefined;
381
+ /** True while a request is in flight. */
382
+ readonly loading: boolean;
383
+ /** Last error from the operation call, or undefined. */
384
+ readonly error: unknown;
385
+ /**
386
+ * Force a re-fetch using the current options. Useful when wiring
387
+ * `PrescriptionIFrameModal.onRefreshLaunchUrl` so the session token in
388
+ * the returned widget URL is fresh on every modal open.
389
+ */
390
+ readonly refresh: () => Promise<string | undefined>;
391
+ }
305
392
 
306
393
  /**
307
394
  * Returns the MedplumClient instance.
@@ -475,6 +562,19 @@ export declare type UseSubscriptionOptions = {
475
562
  onError?: (err: Error) => void;
476
563
  };
477
564
 
565
+ /**
566
+ * Vendor-neutral React hook that syncs a Medplum `PlanDefinition` (type=order-set)
567
+ * to the configured e-prescribing vendor via the `$sync-orderset` custom FHIR operation
568
+ * (`POST /fhir/R4/PlanDefinition/$sync-orderset`).
569
+ *
570
+ * Silently no-ops when the operation is not deployed (i.e. no e-prescribing vendor
571
+ * is configured for the project), so callers do not need to guard against missing
572
+ * integrations.
573
+ *
574
+ * @returns A stable `syncOrderSet(planDefinitionId)` callback.
575
+ */
576
+ export declare function useSyncOrderSet(): (planDefinitionId: string) => Promise<void>;
577
+
478
578
  export declare function useThreadInbox({ query, threadId }: UseThreadInboxOptions): UseThreadInboxReturn;
479
579
 
480
580
  export declare interface UseThreadInboxOptions {
@@ -493,12 +593,18 @@ export declare interface UseThreadInboxReturn {
493
593
  refreshThreadMessages: () => Promise<void>;
494
594
  }
495
595
 
496
- export declare function useWhisper({ language, model, onTranscript, }: UseWhisperOptions): UseWhisperResult;
596
+ export declare function useWhisper({ language, model, onTranscript, idleTimeoutMs, }: UseWhisperOptions): UseWhisperResult;
497
597
 
498
598
  export declare type UseWhisperOptions = {
499
599
  language?: string;
500
600
  model?: string;
501
601
  onTranscript?: (text: string) => void;
602
+ /**
603
+ * How long to keep the WebSocket warm after stop() before fully closing it, in milliseconds.
604
+ * Defaults to 120000 (2 minutes). Set to 0 or a non-finite value to keep the socket warm
605
+ * until unmount (the previous behavior).
606
+ */
607
+ idleTimeoutMs?: number;
502
608
  };
503
609
 
504
610
  export declare type UseWhisperResult = {
@@ -7,6 +7,10 @@ import type { Encounter } from '@medplum/fhirtypes';
7
7
  import type { ExtractResource } from '@medplum/fhirtypes';
8
8
  import type { Identifier } from '@medplum/fhirtypes';
9
9
  import type { JSX } from 'react';
10
+ import type { Medication } from '@medplum/fhirtypes';
11
+ import type { MedicationOrderRequest } from '@medplum/core';
12
+ import type { MedicationOrderResponse } from '@medplum/core';
13
+ import type { MedicationSearchParams } from '@medplum/core';
10
14
  import type { MedplumClient } from '@medplum/core';
11
15
  import type { OperationOutcome } from '@medplum/fhirtypes';
12
16
  import type { Organization } from '@medplum/fhirtypes';
@@ -38,13 +42,6 @@ export declare function buildInitialResponseItem(item: QuestionnaireItem): Quest
38
42
 
39
43
  export declare function convertToPCM16(float32Array: Float32Array): Uint8Array;
40
44
 
41
- export declare interface EPrescribingIFrameOptions {
42
- readonly patientId?: string;
43
- readonly onPatientSyncSuccess?: () => void;
44
- readonly onIframeSuccess?: (url: string) => void;
45
- readonly onError?: (err: unknown) => void;
46
- }
47
-
48
45
  /**
49
46
  * Evaluates the calculated expressions in a questionnaire.
50
47
  * Updates response item answers in place with the calculated values.
@@ -108,6 +105,13 @@ export declare function isChoiceQuestion(item: QuestionnaireItem): boolean;
108
105
  */
109
106
  export declare function isQuestionEnabled(item: QuestionnaireItem, questionnaireResponse: QuestionnaireResponse | undefined): boolean;
110
107
 
108
+ export declare interface MedicationIFrameOptions {
109
+ readonly patientId?: string;
110
+ readonly onPatientSyncSuccess?: () => void;
111
+ readonly onIframeSuccess?: (url: string) => void;
112
+ readonly onError?: (err: unknown) => void;
113
+ }
114
+
111
115
  export declare interface MedplumContext {
112
116
  medplum: MedplumClient;
113
117
  navigate: MedplumNavigateFunction;
@@ -287,8 +291,8 @@ export declare function typedValueToResponseItem(item: QuestionnaireItem, value:
287
291
  export declare const useCachedBinaryUrl: (binaryUrl: string | undefined) => string | undefined;
288
292
 
289
293
  /**
290
- * Generic React hook that syncs a patient to an e-prescribing system and
291
- * returns the iframe URL.
294
+ * Generic React hook that syncs a patient to a medication-order vendor and
295
+ * returns the chart iframe URL.
292
296
  *
293
297
  * Executes the patient-sync bot first (if patientId is provided), then
294
298
  * the iframe bot to obtain the prescribing UI URL.
@@ -299,9 +303,92 @@ export declare const useCachedBinaryUrl: (binaryUrl: string | undefined) => stri
299
303
  * @param syncBotIdentifier - Bot identifier for the patient sync bot.
300
304
  * @param iframeBotIdentifier - Bot identifier for the iframe URL bot.
301
305
  * @param options - Configuration and callback options.
302
- * @returns The e-prescribing iframe URL, or undefined while loading.
306
+ * @returns The medication-order iframe URL, or undefined while loading.
307
+ */
308
+ export declare function useMedicationIFrame(syncBotIdentifier: Identifier, iframeBotIdentifier: Identifier, options: MedicationIFrameOptions): string | undefined;
309
+
310
+ /**
311
+ * Vendor-neutral hook for e-prescribing drug search and order-medication via
312
+ * **FHIR custom operations** (no Bot identifiers required).
313
+ *
314
+ * Hits two project-scoped operations whose backing Bot is chosen at deploy time
315
+ * via an `OperationDefinition` resource carrying the
316
+ * `operationDefinition-implementation` extension — see
317
+ * [bot operations docs](https://www.medplum.com/docs/bots/custom-fhir-operations).
318
+ * The server's `tryCustomOperation` dispatch handles the OD → Bot lookup, so
319
+ * projects can swap vendors (ScriptSure today, DoseSpot tomorrow) by deploying
320
+ * a different bot under the same operation code.
321
+ *
322
+ * - `searchMedications`: `POST /fhir/R4/Medication/$drug-search` — expects the
323
+ * bot to return a `Bundle<Medication>` (single-Resource `return` shortcut on
324
+ * the OperationDefinition).
325
+ * - `orderMedication`: `POST /fhir/R4/MedicationRequest/$order-medication` —
326
+ * expects the bot to return a `Parameters` envelope with named primitives;
327
+ * `parametersToMedicationOrderResponse` decodes it.
328
+ *
329
+ * @returns Callbacks to search medications and submit an order request.
303
330
  */
304
- export declare function useEPrescribingIFrame(syncBotIdentifier: Identifier, iframeBotIdentifier: Identifier, options: EPrescribingIFrameOptions): string | undefined;
331
+ export declare function useMedicationOrder(): UseMedicationOrderReturn;
332
+
333
+ export declare interface UseMedicationOrderReturn {
334
+ searchMedications: (params: MedicationSearchParams) => Promise<Medication[]>;
335
+ orderMedication: (input: MedicationOrderRequest) => Promise<MedicationOrderResponse>;
336
+ }
337
+
338
+ /**
339
+ * Vendor-neutral React hook that calls the `$order-set-url` custom FHIR
340
+ * operation and exposes the resulting iframe URL plus refresh/loading/error
341
+ * state.
342
+ *
343
+ * Hits the project-scoped operation whose backing bot is chosen at deploy time
344
+ * via an `OperationDefinition` resource carrying the
345
+ * `operationDefinition-implementation` extension — see
346
+ * [bot operations docs](https://www.medplum.com/docs/bots/custom-fhir-operations).
347
+ * The server's `tryCustomOperation` dispatch handles the OD → Bot lookup, so
348
+ * projects can swap vendors (ScriptSure today, DoseSpot tomorrow) by deploying
349
+ * a different bot under the same operation code.
350
+ *
351
+ * - URL: `POST /fhir/R4/PlanDefinition/$order-set-url`
352
+ * - Body: `Parameters` with `patientId` + (`planDefinitionId` XOR `vendorOrderSetId`) + optional `appId`.
353
+ * - Returns: `Parameters` whose `launchUrl` is exposed as `url` on the hook.
354
+ *
355
+ * The hook is a "build a URL" hook (mirrors {@link useMedicationIFrame}); it
356
+ * does not stamp Medplum resources or create vendor-side resources, so
357
+ * `refresh` is safe to call repeatedly (the operation is naturally idempotent).
358
+ *
359
+ * Re-runs whenever the input options change. In-flight calls are cancelled on
360
+ * input change and on unmount via a per-effect `cancelled` flag, so React 18
361
+ * Strict Mode double-mount does not surface stale URLs.
362
+ *
363
+ * @param options - Patient, picker (PD or vendor id), and optional appId.
364
+ * @returns `{ url, loading, error, refresh }`.
365
+ */
366
+ export declare function useMedicationOrderSet(options: UseMedicationOrderSetOptions): UseMedicationOrderSetReturn;
367
+
368
+ export declare interface UseMedicationOrderSetOptions {
369
+ /** Patient to prescribe against. Hook stays idle (no operation call) until set. */
370
+ readonly patientId: string | undefined;
371
+ /** Medplum PlanDefinition id (vendor-neutral). Bot resolves it to the vendor's order set id. */
372
+ readonly planDefinitionId?: string;
373
+ /** Vendor-side order set id, when picked directly (escape hatch when no synced PD exists yet). */
374
+ readonly vendorOrderSetId?: number | string;
375
+ readonly appId?: string;
376
+ }
377
+
378
+ export declare interface UseMedicationOrderSetReturn {
379
+ /** Most recent successful URL from the order-set operation, or undefined while loading / on error. */
380
+ readonly url: string | undefined;
381
+ /** True while a request is in flight. */
382
+ readonly loading: boolean;
383
+ /** Last error from the operation call, or undefined. */
384
+ readonly error: unknown;
385
+ /**
386
+ * Force a re-fetch using the current options. Useful when wiring
387
+ * `PrescriptionIFrameModal.onRefreshLaunchUrl` so the session token in
388
+ * the returned widget URL is fresh on every modal open.
389
+ */
390
+ readonly refresh: () => Promise<string | undefined>;
391
+ }
305
392
 
306
393
  /**
307
394
  * Returns the MedplumClient instance.
@@ -475,6 +562,19 @@ export declare type UseSubscriptionOptions = {
475
562
  onError?: (err: Error) => void;
476
563
  };
477
564
 
565
+ /**
566
+ * Vendor-neutral React hook that syncs a Medplum `PlanDefinition` (type=order-set)
567
+ * to the configured e-prescribing vendor via the `$sync-orderset` custom FHIR operation
568
+ * (`POST /fhir/R4/PlanDefinition/$sync-orderset`).
569
+ *
570
+ * Silently no-ops when the operation is not deployed (i.e. no e-prescribing vendor
571
+ * is configured for the project), so callers do not need to guard against missing
572
+ * integrations.
573
+ *
574
+ * @returns A stable `syncOrderSet(planDefinitionId)` callback.
575
+ */
576
+ export declare function useSyncOrderSet(): (planDefinitionId: string) => Promise<void>;
577
+
478
578
  export declare function useThreadInbox({ query, threadId }: UseThreadInboxOptions): UseThreadInboxReturn;
479
579
 
480
580
  export declare interface UseThreadInboxOptions {
@@ -493,12 +593,18 @@ export declare interface UseThreadInboxReturn {
493
593
  refreshThreadMessages: () => Promise<void>;
494
594
  }
495
595
 
496
- export declare function useWhisper({ language, model, onTranscript, }: UseWhisperOptions): UseWhisperResult;
596
+ export declare function useWhisper({ language, model, onTranscript, idleTimeoutMs, }: UseWhisperOptions): UseWhisperResult;
497
597
 
498
598
  export declare type UseWhisperOptions = {
499
599
  language?: string;
500
600
  model?: string;
501
601
  onTranscript?: (text: string) => void;
602
+ /**
603
+ * How long to keep the WebSocket warm after stop() before fully closing it, in milliseconds.
604
+ * Defaults to 120000 (2 minutes). Set to 0 or a non-finite value to keep the socket warm
605
+ * until unmount (the previous behavior).
606
+ */
607
+ idleTimeoutMs?: number;
502
608
  };
503
609
 
504
610
  export declare type UseWhisperResult = {
@@ -1,8 +1,8 @@
1
- import{useEffect as Me,useMemo as ke,useState as Ue}from"react";import{createContext as Ae,useContext as _e}from"react";var W=Ae(void 0);function D(){return _e(W)}function v(){return D().medplum}function jn(){return D().navigate}function zn(){return D().profile}import{jsx as Le}from"react/jsx-runtime";var re=["change","storageInitialized","storageInitFailed","profileRefreshing","profileRefreshed"];function Xn(e){let t=e.medplum,n=e.navigate??Ne,[o,r]=Ue({profile:t.getProfile(),loading:t.isLoading()});Me(()=>{function s(){r(f=>({...f,profile:t.getProfile(),loading:t.isLoading()}))}for(let f of re)t.addEventListener(f,s);return()=>{for(let f of re)t.removeEventListener(f,s)}},[t]);let i=ke(()=>({...o,medplum:t,navigate:n}),[o,t,n]);return Le(W.Provider,{value:i,children:e.children})}function Ne(e){window.location.assign(e)}import{useMemo as qe}from"react";var oe=new Map,nt=e=>qe(()=>{if(!e)return;let t=e.split("?")[0];if(!t)return e;let n;try{n=new URLSearchParams(new URL(e).search)}catch{return e}if(!n.has("Key-Pair-Id")||!n.has("Signature"))return e;let o=n.get("Expires");if(!o||o.length>13)return e;let r=oe.get(t);if(r){let s=new URLSearchParams(new URL(r).search).get("Expires");if(s&&Number.parseInt(s,10)*1e3-5e3>Date.now())return r}return oe.set(t,e),e},[e]);import{useEffect as ie,useRef as V,useState as Fe}from"react";function it(e,t,n){let o=v(),{patientId:r,onPatientSyncSuccess:i,onIframeSuccess:s,onError:f}=n,[p,x]=Fe(void 0),I=V(i),b=V(s),Q=V(f);return ie(()=>{I.current=i,b.current=s,Q.current=f},[i,s,f]),ie(()=>{let h=!1;return(async()=>{try{if(r){if(await o.executeBot(e,{patientId:r}),h)return;I.current?.()}let a=await o.executeBot(t,{patientId:r});if(h)return;a.url&&(x(a.url),b.current?.(a.url))}catch(a){h||Q.current?.(a)}})().catch(()=>{}),()=>{h=!0}},[o,e,t,r]),p}import{useCallback as De,useEffect as Ve,useState as $e}from"react";import{deepEquals as se}from"@medplum/core";import{useCallback as M,useEffect as $,useRef as w,useState as ae}from"react";var We=3e3;function ue(e,t,n){let o=v(),[r,i]=ae(),[s,f]=ae(n?.subscriptionProps),p=w(!1),x=w(void 0),I=w(void 0),b=w(void 0),Q=w(t);Q.current=t;let h=w(n?.onWebSocketOpen);h.current=n?.onWebSocketOpen;let y=w(n?.onWebSocketClose);y.current=n?.onWebSocketClose;let a=w(n?.onSubscriptionConnect);a.current=n?.onSubscriptionConnect;let c=w(n?.onSubscriptionDisconnect);c.current=n?.onSubscriptionDisconnect;let m=w(n?.onError);m.current=n?.onError,$(()=>{se(n?.subscriptionProps,s)||f(n?.subscriptionProps)},[s,n]),$(()=>{x.current&&(clearTimeout(x.current),x.current=void 0);let g=!1;return(I.current!==e||!se(b.current,s))&&(g=!0),g&&I.current&&o.unsubscribeFromCriteria(I.current,b.current),I.current=e,b.current=s,g&&e?i(o.subscribeToCriteria(e,s)):e||i(void 0),()=>{x.current=setTimeout(()=>{i(void 0),e&&o.unsubscribeFromCriteria(e,s)},We)}},[o,e,s]);let R=M(g=>{Q.current?.(g.payload)},[]),l=M(()=>{h.current?.()},[]),E=M(()=>{y.current?.()},[]),S=M(g=>{a.current?.(g.payload.subscriptionId)},[]),d=M(g=>{c.current?.(g.payload.subscriptionId)},[]),u=M(g=>{m.current?.(g.payload)},[]);$(()=>r?(p.current||(r.addEventListener("message",R),r.addEventListener("open",l),r.addEventListener("close",E),r.addEventListener("connect",S),r.addEventListener("disconnect",d),r.addEventListener("error",u),p.current=!0),()=>{p.current=!1,r.removeEventListener("message",R),r.removeEventListener("open",l),r.removeEventListener("close",E),r.removeEventListener("connect",S),r.removeEventListener("disconnect",d),r.removeEventListener("error",u)}):()=>{},[r,R,l,E,S,d,u])}function mt(e){let t=v(),{resourceType:n,countCriteria:o,subscriptionCriteria:r}=e,[i,s]=$e(0),f=De(p=>{t.search(n,o,{cache:p}).then(x=>s(x.total)).catch(console.error)},[t,n,o]);return Ve(()=>{f("default")},[f]),ue(r,()=>{f("reload")}),i}import{resolveId as Ke}from"@medplum/core";import{useEffect as Be,useMemo as ce,useState as K}from"react";function de(e){let t=e.patientParam??"subject",n=e.query,o="";if(n!=null)if(typeof n=="string")o=n;else if(n instanceof URLSearchParams){let r=Array.from(n.entries()).sort((i,s)=>i[0].localeCompare(s[0])||i[1].localeCompare(s[1]));o=JSON.stringify(r)}else if(Array.isArray(n)){let r=[...n].sort((i,s)=>i[0].localeCompare(s[0])||i[1].localeCompare(s[1]));o=JSON.stringify(r)}else{let r=Object.entries(n).filter(([,i])=>i!==void 0).sort(([i],[s])=>i.localeCompare(s));o=JSON.stringify(r)}return`${e.resourceType}:${t}:${o}`}function je(e){return e.map(t=>{let n=t.searches?t.searches.map(de).join(","):"";return`${t.key}:[${n}]`}).join("|")}function It(e,t){let n=v(),[o,r]=K([]),[i,s]=K(!0),[f,p]=K(),x=je(t),I=ce(()=>t,[x]),b=ce(()=>Ke(e),[e]);return Be(()=>{if(!b)return;let Q=!1,h=`Patient/${b}`,y={_count:100,_sort:"-_lastUpdated"},a=[],c=new Map,m=[];for(let l of I){let E=[];if(l.searches)for(let S of l.searches){let d=de(S),u=c.get(d);u===void 0&&(u=a.length,c.set(d,u),a.push(S)),E.push({searchIdx:u,resultKey:S.key})}m.push(E)}if(a.length===0){r(I.map(()=>({}))),s(!1);return}let R=a.map(l=>{let E=l.patientParam??"subject",S={[E]:h};if(l.query){if(typeof l.query=="string")return n.searchResources(l.resourceType,`${E}=${h}&${l.query}&_count=100&_sort=-_lastUpdated`);if(l.query instanceof URLSearchParams)l.query.forEach((d,u)=>{S[u]=d});else if(Array.isArray(l.query))for(let[d,u]of l.query)S[d]=u;else for(let[d,u]of Object.entries(l.query))u!==void 0&&(S[d]=u)}return n.searchResources(l.resourceType,{...y,...S})});return s(!0),p(void 0),Promise.allSettled(R).then(l=>{if(Q)return;let E=m.map(d=>{let u={};for(let{searchIdx:g,resultKey:A}of d){let C=l[g];u[A]=C.status==="fulfilled"?C.value:[]}return u});r(E),s(!1);let S=l.filter(d=>d.status==="rejected");if(S.length>0){console.error("Some patient summary searches failed:",S.map(u=>u.reason));let d=S[0].reason;p(d instanceof Error?d:new Error(String(d)))}}).catch(l=>{Q||(p(l instanceof Error?l:new Error(String(l))),s(!1))}),()=>{Q=!0}},[n,b,I]),{sectionData:o,loading:i,error:f}}import{isAddPharmacyResponse as ze,isOrganizationArray as Je}from"@medplum/core";import{useCallback as pe}from"react";function Qt(e,t){let n=v(),o=pe(async i=>{let s=await n.executeBot(e,i);if(!Je(s))throw new Error("Invalid response from pharmacy search");return s},[n,e]),r=pe(async i=>{let s=await n.executeBot(t,{patientId:i.patientId,pharmacy:i.pharmacy,setAsPrimary:i.setAsPrimary});if(!ze(s))throw new Error("Invalid response from add pharmacy bot");return s},[n,t]);return{searchPharmacies:o,addToFavorites:r}}import{useEffect as Ge,useRef as He}from"react";function Pt(e){let t=He(void 0);return Ge(()=>{t.current=e}),t.current}import{getExtension as Pn}from"@medplum/core";import{useReducer as Cn,useRef as wn}from"react";import{deepEquals as Xe,isReference as le,isResource as Ye,normalizeOperationOutcome as Ze}from"@medplum/core";import{useCallback as en,useEffect as nn,useState as tn}from"react";function B(e,t){let n=v(),[o,r]=tn(()=>fe(n,e)),i=en(s=>{Xe(s,o)||r(s)},[o]);return nn(()=>{let s=!0,f=fe(n,e);return!f&&le(e)?n.readReference(e).then(p=>{s&&i(p)}).catch(p=>{s&&(i(void 0),t&&t(Ze(p)))}):i(f),(()=>s=!1)},[n,e,i,t]),o}function fe(e,t){if(t){if(Ye(t))return t;if(le(t))return e.getCachedReference(t)}}import{EMPTY as Re,HTTP_HL7_ORG as O,PropertyType as P,append as me,capitalize as ye,deepClone as rn,evalFhirPathTyped as z,getExtension as k,getReferenceString as on,getTypedPropertyValueWithoutSchema as L,normalizeErrorString as sn,splitN as an,toJsBoolean as un,toTypedValue as ge,typedValueToString as cn}from"@medplum/core";var T={group:"group",display:"display",question:"question",boolean:"boolean",decimal:"decimal",integer:"integer",date:"date",dateTime:"dateTime",time:"time",string:"string",text:"text",url:"url",choice:"choice",openChoice:"open-choice",attachment:"attachment",reference:"reference",quantity:"quantity"},Ie=`${O}/fhir/StructureDefinition/questionnaire-itemControl`,dn=`${O}/fhir/StructureDefinition/questionnaire-referenceFilter`,j=`${O}/fhir/StructureDefinition/questionnaire-referenceResource`,pn=`${O}/fhir/StructureDefinition/questionnaire-validationError`,fn=`${O}/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression`,ln=`${O}/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression`,kt=`${O}/fhir/StructureDefinition/questionnaire-signatureRequired`,q=`${O}/fhir/StructureDefinition/questionnaireresponse-signature`,mn=`${O}/fhir/StructureDefinition/questionnaire-hidden`;function Ut(e){return e.type==="choice"||e.type==="open-choice"}function J(e,t){return{...t,item:Se(e.item,t.item,t)}}function Se(e,t,n){if(!t)return t;let o=[];for(let r of t){let i=e?.find(s=>s.linkId===r.linkId);i&&!yn(i,n)||(i?.item&&r.item?o.push({...r,item:Se(i.item,r.item,n)}):o.push(r))}return o}function yn(e,t){if(k(e,mn)?.valueBoolean===!0)return!1;let o=hn(e,t);return o!==void 0?o:Rn(e,t)}function hn(e,t){let n=k(e,fn);if(t&&n){let o=n.valueExpression?.expression;if(o){let r=ge(t),i=z(o,[r],{"%resource":r});return un(i)}}}function Rn(e,t){if(!e.enableWhen)return!0;let n=e.enableBehavior??"any";for(let o of e.enableWhen){let r=xe(t?.item,o.question);if(o.operator==="exists"&&!o.answerBoolean&&!r?.length){if(n==="any")return!0;continue}let{anyMatch:i,allMatch:s}=En(o,r,n);if(n==="any"&&i)return!0;if(n==="all"&&!s)return!1}return n!=="any"}function G(e,t,n=t.item){for(let o of e){let r=n?.find(i=>i.linkId===o.linkId);r&&(gn(t,o,r),o.item&&r.item&&G(o.item,t,r.item))}}function gn(e,t,n){try{let o=xn(t,e);if(!o)return;let r=Sn(t,o);if(!r)return;n.answer=[r]}catch(o){n.extension=[{url:pn,valueString:`Expression evaluation failed: ${sn(o)}`}]}}var In={[T.boolean]:[P.boolean],[T.date]:[P.date],[T.dateTime]:[P.dateTime],[T.time]:[P.time],[T.url]:[P.string,P.uri,P.url],[T.attachment]:[P.Attachment],[T.reference]:[P.Reference],[T.quantity]:[P.Quantity],[T.decimal]:[P.decimal,P.integer],[T.integer]:[P.decimal,P.integer]};function Sn(e,t){if(!e.type)return;if(e.type===T.choice||e.type===T.openChoice)return{[`value${ye(t.type)}`]:t.value};if(e.type===T.string||e.type===T.text)return typeof t.value=="string"?{valueString:t.value}:void 0;if(In[e.type]?.includes(t.type))return{[`value${ye(e.type)}`]:t.value}}function xn(e,t){if(!t)return;let n=k(e,ln);if(n){let o=n.valueExpression?.expression;if(o){let r=ge(t),i=z(o,[r],{"%resource":r});return i.length!==0?i[0]:void 0}}}function Nt(e,t,n){let o=[];for(let r of e){let i=n.answerOption?.find(s=>cn(he(s))===r);if(i){let s=he(i);s&&o.push({[t]:s.value})}}return o}function xe(e,t){for(let n of e??Re){if(n.linkId===t)return n.answer;if(n.item){let o=xe(n.item,t);if(o)return o}}}function bn(e,t,n){if(n==="exists")return!!e===t.value;if(e){let o=n==="="||n==="!="?n?.replace("=","~"):n,[{value:r}]=z(`%actualAnswer ${o} %expectedAnswer`,[e],{"%actualAnswer":e,"%expectedAnswer":t});return r}else return!1}function En(e,t,n){let o=t||[],r=vn(e),i=!1,s=!0;for(let f of o){let p=Tn(f),{operator:x}=e;if(bn(p,r,x)?i=!0:s=!1,n==="any"&&i)break}return{anyMatch:i,allMatch:s}}function Lt(e){let t=k(e,j);if(t){if(t.valueCode!==void 0)return[t.valueCode];if(t.valueCodeableConcept)return t.valueCodeableConcept?.coding?.map(n=>n.code)}}function qt(e,t){let n=rn(e),o=k(n,j);return!t||t.length===0?(o&&(n.extension=n.extension?.filter(r=>r!==o)),n):(o||(n.extension??=[],o={url:j},n.extension.push(o)),t.length===1?(o.valueCode=t[0],delete o.valueCodeableConcept):(o.valueCodeableConcept={coding:t.map(r=>({code:r}))},delete o.valueCode),n)}function Ft(e,t,n){let o=k(e,dn);if(!o?.valueString)return;let r=o.valueString;t?.reference&&(r=r.replaceAll("$subj",t.reference)),n?.reference&&(r=r.replaceAll("$encounter",n.reference));let i={},s=r.split("&");for(let f of s){let[p,x]=an(f,"=",2);i[p]=x}return i}function H(e,t){return{resourceType:"QuestionnaireResponse",questionnaire:e.url??on(e),item:X(e.item,t?.item),status:"in-progress"}}function X(e,t){let n;for(let o of e??Re){if(o.type===T.display)continue;let r=t?.filter(i=>i.linkId===o.linkId);if(r?.length)for(let i of r)i.id=i.id??be(),i.text=i.text??o.text,i.item=X(o.item,i.item),i.answer=Ee(o,i),n=me(n,i);else n=me(n,Y(o))}return n}function Y(e){return{id:be(),linkId:e.linkId,text:e.text,item:X(e.item,void 0),answer:Ee(e)}}var Qn=1;function be(){return"id-"+Qn++}function Ee(e,t){if(!(e.type===T.display||e.type===T.group)){if(t?.answer&&t.answer.length>0)return t.answer;if(e.initial&&e.initial.length>0)return e.initial.map(n=>({...n}));if(e.answerOption)return e.answerOption.filter(n=>n.initialSelected).map(n=>({...n,initialSelected:void 0}))}}function Wt(e){return L({type:"QuestionnaireItemInitial",value:e},"value")}function he(e){return L({type:"QuestionnaireItemAnswerOption",value:e},"value")}function vn(e){return L({type:"QuestionnaireItemEnableWhen",value:e},"answer")}function Tn(e){return L({type:"QuestionnaireResponseItemAnswer",value:e},"value")}function jt(e){let t=B(e.questionnaire),n=B(e.defaultValue),[,o]=Cn(y=>y+1,0),r=wn({activePage:0});if(!r.current.questionnaire&&t&&(r.current.questionnaire=t,r.current.pages=e.disablePagination?void 0:On(t)),t&&e.defaultValue&&n&&!r.current.questionnaireResponse&&(r.current.questionnaireResponse=H(t,n),h()),t&&!e.defaultValue&&!r.current.questionnaireResponse&&(r.current.questionnaireResponse=H(t),h()),!r.current.questionnaire||!r.current.questionnaireResponse)return{loading:!0};function i(y,a){let c=r.current.questionnaireResponse;for(let m of y)c=c?.item?.find(R=>m.id?R.id===m.id:R.linkId===m.linkId);return a&&(c=c?.item?.find(m=>m.linkId===a.linkId)),c}function s(){r.current.activePage=(r.current.activePage??0)+1,o()}function f(){r.current.activePage=(r.current.activePage??0)-1,o()}function p(y,a){let c=i(y);c&&(c.item??=[],c.item.push(Y(a)),h())}function x(y,a){let c=i(y,a);c&&(c.answer??=[],c.answer.push({}),h())}function I(y,a,c){let m=i(y,a);m&&(m.answer=c,h())}function b(y){let a=r.current.questionnaireResponse;a&&(y?(a.extension=a.extension??[],a.extension=a.extension.filter(c=>c.url!==q),a.extension.push({url:q,valueSignature:y})):a.extension=a.extension?.filter(c=>c.url!==q),h())}function Q(){let y=r.current.questionnaire;if(y?.item){let a=r.current.questionnaireResponse;G(y.item,a)}}function h(){let y=r.current.questionnaireResponse,a=r.current.questionnaire;!y||!a||(Q(),o(),e.onChange?.(J(a,y)))}return{loading:!1,pagination:!!r.current.pages,questionnaire:r.current.questionnaire,questionnaireResponse:J(r.current.questionnaire,r.current.questionnaireResponse),subject:e.subject,encounter:e.encounter,activePage:r.current.activePage,pages:r.current.pages,items:An(r.current.questionnaire,r.current.pages,r.current.activePage),responseItems:_n(r.current.questionnaireResponse,r.current.pages,r.current.activePage),onNextPage:s,onPrevPage:f,onAddGroup:p,onAddAnswer:x,onChangeAnswer:I,onChangeSignature:b}}function On(e){if(!(!e?.item||Pn(e?.item?.[0],Ie)?.valueCodeableConcept?.coding?.[0]?.code!=="page"))return e.item.map((n,o)=>({linkId:n.linkId,title:n.text??`Page ${o+1}`,group:n}))}function An(e,t,n=0){return t&&e?.item?.[n]?[e.item[n]]:e.item??[]}function _n(e,t,n=0){return t&&e?.item?.[n]?[e.item[n]]:e.item??[]}import{allOk as Un,normalizeOperationOutcome as Nn}from"@medplum/core";import{useEffect as Ln,useMemo as qn,useState as ee}from"react";import{useCallback as Mn,useEffect as Qe,useRef as Z,useState as kn}from"react";function ve(e,t,n={leading:!1}){let[o,r]=kn(e),i=Z(!1),s=Z(void 0),f=Z(!1),p=Mn(()=>window.clearTimeout(s.current),[]);return Qe(()=>{i.current&&(!f.current&&n.leading?(f.current=!0,r(e),s.current=setTimeout(()=>{f.current=!1},t)):(p(),s.current=setTimeout(()=>{f.current=!1,r(e)},t)))},[e,n.leading,t,p]),Qe(()=>(i.current=!0,p),[p]),[o,p]}var Fn=250;function er(e,t,n){return ne("search",e,t,n)}function nr(e,t,n){return ne("searchOne",e,t,n)}function tr(e,t,n){return ne("searchResources",e,t,n)}function ne(e,t,n,o){let r=v(),[i,s]=ee(!1),[f,p]=ee(),[x,I]=ee(),b=r.fhirSearchUrl(t,n).toString(),Q=qn(()=>({resourceType:t,query:n}),[b]),h=o?.enabled??!0,y=o?.debounceMs??Fn,[a]=ve(Q,y,{leading:!0});return Ln(()=>{if(!h)return()=>{};s(!0);let c=!0;return r[e](a.resourceType,a.query).then(m=>{c&&(s(!1),p(m),I(Un))}).catch(m=>{c&&(s(!1),p(void 0),I(Nn(m)))}),()=>{c=!1}},[r,e,a,h]),[f,i,x]}import{getReferenceString as Wn}from"@medplum/core";import{useCallback as Dn,useEffect as Te,useState as U}from"react";function ar({query:e,threadId:t}){let n=v(),[o,r]=U(!0),[i,s]=U([]),[f,p]=U(void 0),[x,I]=U(null),[b,Q]=U(void 0),h=Dn(async()=>{let c=new URLSearchParams(e);c.append("identifier:not","http://medplum.com/ai-message|"),c.append("part-of:missing","true"),c.append("_has:Communication:part-of:_id:not","null");let m=await n.search("Communication",c.toString(),{cache:"no-cache"}),R=m.entry?.map(u=>u.resource).filter(u=>u!==void 0)||[];if(m.total!==void 0&&Q(m.total),R.length===0){s([]);return}let E=`
1
+ import{useEffect as ze,useMemo as Je,useState as Ge}from"react";import{createContext as Be,useContext as je}from"react";var K=Be(void 0);function B(){return je(K)}function v(){return B().medplum}function bt(){return B().navigate}function pe(){return B().profile}import{jsx as Xe}from"react/jsx-runtime";var fe=["change","storageInitialized","storageInitFailed","profileRefreshing","profileRefreshed"];function Qt(e){let t=e.medplum,n=e.navigate??He,[o,r]=Ge({profile:t.getProfile(),loading:t.isLoading()});ze(()=>{function s(){r(a=>({...a,profile:t.getProfile(),loading:t.isLoading()}))}for(let a of fe)t.addEventListener(a,s);return()=>{for(let a of fe)t.removeEventListener(a,s)}},[t]);let i=Je(()=>({...o,medplum:t,navigate:n}),[o,t,n]);return Xe(K.Provider,{value:i,children:e.children})}function He(e){window.location.assign(e)}import{useMemo as Ye}from"react";var le=new Map,Mt=e=>Ye(()=>{if(!e)return;let t=e.split("?")[0];if(!t)return e;let n;try{n=new URLSearchParams(new URL(e).search)}catch{return e}if(!n.has("Key-Pair-Id")||!n.has("Signature"))return e;let o=n.get("Expires");if(!o||o.length>13)return e;let r=le.get(t);if(r){let s=new URLSearchParams(new URL(r).search).get("Expires");if(s&&Number.parseInt(s,10)*1e3-5e3>Date.now())return r}return le.set(t,e),e},[e]);import{useEffect as me,useRef as j,useState as Ze}from"react";function kt(e,t,n){let o=v(),{patientId:r,onPatientSyncSuccess:i,onIframeSuccess:s,onError:a}=n,[l,S]=Ze(void 0),x=j(i),b=j(s),g=j(a);return me(()=>{x.current=i,b.current=s,g.current=a},[i,s,a]),me(()=>{let h=!1;return(async()=>{try{if(r){if(await o.executeBot(e,{patientId:r}),h)return;x.current?.()}let u=await o.executeBot(t,{patientId:r});if(h)return;u.url&&(S(u.url),b.current?.(u.url))}catch(u){h||g.current?.(u)}})().catch(()=>{}),()=>{h=!0}},[o,e,t,r]),l}import{INVALID_MEDICATION_ORDER_RESPONSE as en,INVALID_MEDICATION_SEARCH_RESPONSE as nn,isResource as z,medicationOrderRequestToParameters as tn,medicationSearchParamsToParameters as rn,parametersToMedicationOrderResponse as on}from"@medplum/core";import{useCallback as he}from"react";function Wt(){let e=v(),t=he(async o=>{let r=e.fhirUrl("Medication","$drug-search"),i=rn(o),s=await e.post(r,i);if(!z(s,"Bundle"))throw new Error(nn);return(s.entry??[]).map(a=>a.resource).filter(a=>z(a,"Medication"))},[e]),n=he(async o=>{let r=e.fhirUrl("MedicationRequest","$order-medication"),i=tn(o),s=await e.post(r,i);if(!z(s,"Parameters"))throw new Error(en);return on(s)},[e]);return{searchMedications:t,orderMedication:n}}import{INVALID_MEDICATION_ORDER_SET_RESPONSE as sn,isResource as an,medicationOrderSetRequestToParameters as un,parametersToMedicationOrderSetResponse as cn}from"@medplum/core";import{useCallback as ye,useEffect as dn,useRef as Re,useState as J}from"react";function Kt(e){let t=v(),{patientId:n,planDefinitionId:o,vendorOrderSetId:r,appId:i}=e,[s,a]=J(void 0),[l,S]=J(!1),[x,b]=J(void 0),g=Re({patientId:n,planDefinitionId:o,vendorOrderSetId:r,appId:i});g.current={patientId:n,planDefinitionId:o,vendorOrderSetId:r,appId:i};let h=()=>{let c=g.current;if(!c.patientId)return;let f=!!c.planDefinitionId,d=c.vendorOrderSetId!==void 0&&c.vendorOrderSetId!==null&&c.vendorOrderSetId!=="";return!f&&!d?void 0:{patientId:c.patientId,planDefinitionId:f?c.planDefinitionId:void 0,vendorOrderSetId:d?c.vendorOrderSetId:void 0,appId:c.appId}},y=ye(async c=>{let f=t.fhirUrl("PlanDefinition","$order-set-url"),d=un(c),R=await t.post(f,d);if(!an(R,"Parameters"))throw new Error(sn);return cn(R).launchUrl},[t]),u=Re(0),p=ye(async()=>{let c=h();if(!c)return;u.current+=1;let f=u.current;S(!0),b(void 0);try{let d=await y(c);return u.current!==f?void 0:(a(d),d)}catch(d){u.current===f&&(b(d),a(void 0));return}finally{u.current===f&&S(!1)}},[y]);return dn(()=>{let c=!1,f=h();if(!f){u.current+=1,a(void 0),S(!1),b(void 0);return}u.current+=1;let d=u.current;return S(!0),b(void 0),y(f).then(R=>{c||u.current!==d||a(R)}).catch(R=>{c||u.current!==d||(b(R),a(void 0))}).finally(()=>{!c&&u.current===d&&S(!1)}),()=>{c=!0}},[y,n,o,r,i]),{url:s,loading:l,error:x,refresh:p}}import{useCallback as fn,useEffect as ln,useState as mn}from"react";import{deepEquals as ge}from"@medplum/core";import{useCallback as q,useEffect as G,useRef as A,useState as Ie}from"react";var pn=3e3;function Se(e,t,n){let o=v(),i=pe()?e:void 0,[s,a]=Ie(),[l,S]=Ie(n?.subscriptionProps),x=A(!1),b=A(void 0),g=A(void 0),h=A(void 0),y=A(t);y.current=t;let u=A(n?.onWebSocketOpen);u.current=n?.onWebSocketOpen;let p=A(n?.onWebSocketClose);p.current=n?.onWebSocketClose;let c=A(n?.onSubscriptionConnect);c.current=n?.onSubscriptionConnect;let f=A(n?.onSubscriptionDisconnect);f.current=n?.onSubscriptionDisconnect;let d=A(n?.onError);d.current=n?.onError,G(()=>{ge(n?.subscriptionProps,l)||S(n?.subscriptionProps)},[l,n]),G(()=>{b.current&&(clearTimeout(b.current),b.current=void 0);let Q=!1;return(g.current!==i||!ge(h.current,l))&&(Q=!0),Q&&g.current&&o.unsubscribeFromCriteria(g.current,h.current),g.current=i,h.current=l,Q&&i?a(o.subscribeToCriteria(i,l)):i||a(void 0),()=>{b.current=setTimeout(()=>{a(void 0),i&&o.unsubscribeFromCriteria(i,l)},pn)}},[o,i,l]);let R=q(Q=>{y.current?.(Q.payload)},[]),E=q(()=>{u.current?.()},[]),P=q(()=>{p.current?.()},[]),m=q(Q=>{c.current?.(Q.payload.subscriptionId)},[]),w=q(Q=>{f.current?.(Q.payload.subscriptionId)},[]),O=q(Q=>{d.current?.(Q.payload)},[]);G(()=>s?(x.current||(s.addEventListener("message",R),s.addEventListener("open",E),s.addEventListener("close",P),s.addEventListener("connect",m),s.addEventListener("disconnect",w),s.addEventListener("error",O),x.current=!0),()=>{x.current=!1,s.removeEventListener("message",R),s.removeEventListener("open",E),s.removeEventListener("close",P),s.removeEventListener("connect",m),s.removeEventListener("disconnect",w),s.removeEventListener("error",O)}):()=>{},[s,R,E,P,m,w,O])}function Zt(e){let t=v(),{resourceType:n,countCriteria:o,subscriptionCriteria:r}=e,[i,s]=mn(0),a=fn(l=>{t.search(n,o,{cache:l}).then(S=>s(S.total)).catch(console.error)},[t,n,o]);return ln(()=>{a("default")},[a]),Se(r,()=>{a("reload")}),i}import{resolveId as hn}from"@medplum/core";import{useEffect as yn,useMemo as xe,useState as H}from"react";function be(e){let t=e.patientParam??"subject",n=e.query,o="";if(n!=null)if(typeof n=="string")o=n;else if(n instanceof URLSearchParams){let r=Array.from(n.entries()).sort((i,s)=>i[0].localeCompare(s[0])||i[1].localeCompare(s[1]));o=JSON.stringify(r)}else if(Array.isArray(n)){let r=[...n].sort((i,s)=>i[0].localeCompare(s[0])||i[1].localeCompare(s[1]));o=JSON.stringify(r)}else{let r=Object.entries(n).filter(([,i])=>i!==void 0).sort(([i],[s])=>i.localeCompare(s));o=JSON.stringify(r)}return`${e.resourceType}:${t}:${o}`}function Rn(e){return e.map(t=>{let n=t.searches?t.searches.map(be).join(","):"";return`${t.key}:[${n}]`}).join("|")}function or(e,t){let n=v(),[o,r]=H([]),[i,s]=H(!0),[a,l]=H(),S=Rn(t),x=xe(()=>t,[S]),b=xe(()=>hn(e),[e]);return yn(()=>{if(!b)return;let g=!1,h=`Patient/${b}`,y={_count:100,_sort:"-_lastUpdated"},u=[],p=new Map,c=[];for(let d of x){let R=[];if(d.searches)for(let E of d.searches){let P=be(E),m=p.get(P);m===void 0&&(m=u.length,p.set(P,m),u.push(E)),R.push({searchIdx:m,resultKey:E.key})}c.push(R)}if(u.length===0){r(x.map(()=>({}))),s(!1);return}let f=u.map(d=>{let R=d.patientParam??"subject",E={[R]:h};if(d.query){if(typeof d.query=="string")return n.searchResources(d.resourceType,`${R}=${h}&${d.query}&_count=100&_sort=-_lastUpdated`);if(d.query instanceof URLSearchParams)d.query.forEach((P,m)=>{E[m]=P});else if(Array.isArray(d.query))for(let[P,m]of d.query)E[P]=m;else for(let[P,m]of Object.entries(d.query))m!==void 0&&(E[P]=m)}return n.searchResources(d.resourceType,{...y,...E})});return s(!0),l(void 0),Promise.allSettled(f).then(d=>{if(g)return;let R=c.map(P=>{let m={};for(let{searchIdx:w,resultKey:O}of P){let Q=d[w];m[O]=Q.status==="fulfilled"?Q.value:[]}return m});r(R),s(!1);let E=d.filter(P=>P.status==="rejected");if(E.length>0){console.error("Some patient summary searches failed:",E.map(m=>m.reason));let P=E[0].reason;l(P instanceof Error?P:new Error(String(P)))}}).catch(d=>{g||(l(d instanceof Error?d:new Error(String(d))),s(!1))}),()=>{g=!0}},[n,b,x]),{sectionData:o,loading:i,error:a}}import{isAddPharmacyResponse as gn,isOrganizationArray as In}from"@medplum/core";import{useCallback as Ee}from"react";function cr(e,t){let n=v(),o=Ee(async i=>{let s=await n.executeBot(e,i);if(!In(s))throw new Error("Invalid response from pharmacy search");return s},[n,e]),r=Ee(async i=>{let s=await n.executeBot(t,{patientId:i.patientId,pharmacy:i.pharmacy,setAsPrimary:i.setAsPrimary});if(!gn(s))throw new Error("Invalid response from add pharmacy bot");return s},[n,t]);return{searchPharmacies:o,addToFavorites:r}}import{useEffect as Sn,useRef as xn}from"react";function fr(e){let t=xn(void 0);return Sn(()=>{t.current=e}),t.current}import{getExtension as Xn}from"@medplum/core";import{useReducer as Yn,useRef as Zn}from"react";import{deepEquals as bn,isReference as ve,isResource as En,normalizeOperationOutcome as Pn}from"@medplum/core";import{useCallback as vn,useEffect as Qn,useState as Tn}from"react";function X(e,t){let n=v(),[o,r]=Tn(()=>Pe(n,e)),i=vn(s=>{bn(s,o)||r(s)},[o]);return Qn(()=>{let s=!0,a=Pe(n,e);return!a&&ve(e)?n.readReference(e).then(l=>{s&&i(l)}).catch(l=>{s&&(i(void 0),t&&t(Pn(l)))}):i(a),(()=>s=!1)},[n,e,i,t]),o}function Pe(e,t){if(t){if(En(t))return t;if(ve(t))return e.getCachedReference(t)}}import{EMPTY as Oe,HTTP_HL7_ORG as U,PropertyType as C,append as Qe,capitalize as Te,deepClone as Cn,evalFhirPathTyped as Z,getExtension as L,getReferenceString as On,getTypedPropertyValueWithoutSchema as V,normalizeErrorString as Mn,splitN as wn,toJsBoolean as An,toTypedValue as Me,typedValueToString as _n}from"@medplum/core";var T={group:"group",display:"display",question:"question",boolean:"boolean",decimal:"decimal",integer:"integer",date:"date",dateTime:"dateTime",time:"time",string:"string",text:"text",url:"url",choice:"choice",openChoice:"open-choice",attachment:"attachment",reference:"reference",quantity:"quantity"},we=`${U}/fhir/StructureDefinition/questionnaire-itemControl`,kn=`${U}/fhir/StructureDefinition/questionnaire-referenceFilter`,Y=`${U}/fhir/StructureDefinition/questionnaire-referenceResource`,Un=`${U}/fhir/StructureDefinition/questionnaire-validationError`,Nn=`${U}/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression`,qn=`${U}/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression`,Ir=`${U}/fhir/StructureDefinition/questionnaire-signatureRequired`,$=`${U}/fhir/StructureDefinition/questionnaireresponse-signature`,Ln=`${U}/fhir/StructureDefinition/questionnaire-hidden`;function Sr(e){return e.type==="choice"||e.type==="open-choice"}function ee(e,t){return{...t,item:Ae(e.item,t.item,t)}}function Ae(e,t,n){if(!t)return t;let o=[];for(let r of t){let i=e?.find(s=>s.linkId===r.linkId);i&&!Wn(i,n)||(i?.item&&r.item?o.push({...r,item:Ae(i.item,r.item,n)}):o.push(r))}return o}function Wn(e,t){if(L(e,Ln)?.valueBoolean===!0)return!1;let o=Dn(e,t);return o!==void 0?o:Fn(e,t)}function Dn(e,t){let n=L(e,Nn);if(t&&n){let o=n.valueExpression?.expression;if(o){let r=Me(t),i=Z(o,[r],{"%resource":r});return An(i)}}}function Fn(e,t){if(!e.enableWhen)return!0;let n=e.enableBehavior??"any";for(let o of e.enableWhen){let r=_e(t?.item,o.question);if(o.operator==="exists"&&!o.answerBoolean&&!r?.length){if(n==="any")return!0;continue}let{anyMatch:i,allMatch:s}=zn(o,r,n);if(n==="any"&&i)return!0;if(n==="all"&&!s)return!1}return n!=="any"}function ne(e,t,n=t.item){for(let o of e){let r=n?.find(i=>i.linkId===o.linkId);r&&(Vn(t,o,r),o.item&&r.item&&ne(o.item,t,r.item))}}function Vn(e,t,n){try{let o=Bn(t,e);if(!o)return;let r=Kn(t,o);if(!r)return;n.answer=[r]}catch(o){n.extension=[{url:Un,valueString:`Expression evaluation failed: ${Mn(o)}`}]}}var $n={[T.boolean]:[C.boolean],[T.date]:[C.date],[T.dateTime]:[C.dateTime],[T.time]:[C.time],[T.url]:[C.string,C.uri,C.url],[T.attachment]:[C.Attachment],[T.reference]:[C.Reference],[T.quantity]:[C.Quantity],[T.decimal]:[C.decimal,C.integer],[T.integer]:[C.decimal,C.integer]};function Kn(e,t){if(!e.type)return;if(e.type===T.choice||e.type===T.openChoice)return{[`value${Te(t.type)}`]:t.value};if(e.type===T.string||e.type===T.text)return typeof t.value=="string"?{valueString:t.value}:void 0;if($n[e.type]?.includes(t.type))return{[`value${Te(e.type)}`]:t.value}}function Bn(e,t){if(!t)return;let n=L(e,qn);if(n){let o=n.valueExpression?.expression;if(o){let r=Me(t),i=Z(o,[r],{"%resource":r});return i.length!==0?i[0]:void 0}}}function xr(e,t,n){let o=[];for(let r of e){let i=n.answerOption?.find(s=>_n(Ce(s))===r);if(i){let s=Ce(i);s&&o.push({[t]:s.value})}}return o}function _e(e,t){for(let n of e??Oe){if(n.linkId===t)return n.answer;if(n.item){let o=_e(n.item,t);if(o)return o}}}function jn(e,t,n){if(n==="exists")return!!e===t.value;if(e){let o=n==="="||n==="!="?n?.replace("=","~"):n,[{value:r}]=Z(`%actualAnswer ${o} %expectedAnswer`,[e],{"%actualAnswer":e,"%expectedAnswer":t});return r}else return!1}function zn(e,t,n){let o=t||[],r=Gn(e),i=!1,s=!0;for(let a of o){let l=Hn(a),{operator:S}=e;if(jn(l,r,S)?i=!0:s=!1,n==="any"&&i)break}return{anyMatch:i,allMatch:s}}function br(e){let t=L(e,Y);if(t){if(t.valueCode!==void 0)return[t.valueCode];if(t.valueCodeableConcept)return t.valueCodeableConcept?.coding?.map(n=>n.code)}}function Er(e,t){let n=Cn(e),o=L(n,Y);return!t||t.length===0?(o&&(n.extension=n.extension?.filter(r=>r!==o)),n):(o||(n.extension??=[],o={url:Y},n.extension.push(o)),t.length===1?(o.valueCode=t[0],delete o.valueCodeableConcept):(o.valueCodeableConcept={coding:t.map(r=>({code:r}))},delete o.valueCode),n)}function Pr(e,t,n){let o=L(e,kn);if(!o?.valueString)return;let r=o.valueString;t?.reference&&(r=r.replaceAll("$subj",t.reference)),n?.reference&&(r=r.replaceAll("$encounter",n.reference));let i={},s=r.split("&");for(let a of s){let[l,S]=wn(a,"=",2);i[l]=S}return i}function te(e,t){return{resourceType:"QuestionnaireResponse",questionnaire:e.url??On(e),item:re(e.item,t?.item),status:"in-progress"}}function re(e,t){let n;for(let o of e??Oe){if(o.type===T.display)continue;let r=t?.filter(i=>i.linkId===o.linkId);if(r?.length)for(let i of r)i.id=i.id??ke(),i.text=i.text??o.text,i.item=re(o.item,i.item),i.answer=Ue(o,i),n=Qe(n,i);else n=Qe(n,oe(o))}return n}function oe(e){return{id:ke(),linkId:e.linkId,text:e.text,item:re(e.item,void 0),answer:Ue(e)}}var Jn=1;function ke(){return"id-"+Jn++}function Ue(e,t){if(!(e.type===T.display||e.type===T.group)){if(t?.answer&&t.answer.length>0)return t.answer;if(e.initial&&e.initial.length>0)return e.initial.map(n=>({...n}));if(e.answerOption)return e.answerOption.filter(n=>n.initialSelected).map(n=>({...n,initialSelected:void 0}))}}function vr(e){return V({type:"QuestionnaireItemInitial",value:e},"value")}function Ce(e){return V({type:"QuestionnaireItemAnswerOption",value:e},"value")}function Gn(e){return V({type:"QuestionnaireItemEnableWhen",value:e},"answer")}function Hn(e){return V({type:"QuestionnaireResponseItemAnswer",value:e},"value")}function wr(e){let t=X(e.questionnaire),n=X(e.defaultValue),[,o]=Yn(y=>y+1,0),r=Zn({activePage:0});if(!r.current.questionnaire&&t&&(r.current.questionnaire=t,r.current.pages=e.disablePagination?void 0:et(t)),t&&e.defaultValue&&n&&!r.current.questionnaireResponse&&(r.current.questionnaireResponse=te(t,n),h()),t&&!e.defaultValue&&!r.current.questionnaireResponse&&(r.current.questionnaireResponse=te(t),h()),!r.current.questionnaire||!r.current.questionnaireResponse)return{loading:!0};function i(y,u){let p=r.current.questionnaireResponse;for(let c of y)p=p?.item?.find(f=>c.id?f.id===c.id:f.linkId===c.linkId);return u&&(p=p?.item?.find(c=>c.linkId===u.linkId)),p}function s(){r.current.activePage=(r.current.activePage??0)+1,o()}function a(){r.current.activePage=(r.current.activePage??0)-1,o()}function l(y,u){let p=i(y);p&&(p.item??=[],p.item.push(oe(u)),h())}function S(y,u){let p=i(y,u);p&&(p.answer??=[],p.answer.push({}),h())}function x(y,u,p){let c=i(y,u);c&&(c.answer=p,h())}function b(y){let u=r.current.questionnaireResponse;u&&(y?(u.extension=u.extension??[],u.extension=u.extension.filter(p=>p.url!==$),u.extension.push({url:$,valueSignature:y})):u.extension=u.extension?.filter(p=>p.url!==$),h())}function g(){let y=r.current.questionnaire;if(y?.item){let u=r.current.questionnaireResponse;ne(y.item,u)}}function h(){let y=r.current.questionnaireResponse,u=r.current.questionnaire;!y||!u||(g(),o(),e.onChange?.(ee(u,y)))}return{loading:!1,pagination:!!r.current.pages,questionnaire:r.current.questionnaire,questionnaireResponse:ee(r.current.questionnaire,r.current.questionnaireResponse),subject:e.subject,encounter:e.encounter,activePage:r.current.activePage,pages:r.current.pages,items:nt(r.current.questionnaire,r.current.pages,r.current.activePage),responseItems:tt(r.current.questionnaireResponse,r.current.pages,r.current.activePage),onNextPage:s,onPrevPage:a,onAddGroup:l,onAddAnswer:S,onChangeAnswer:x,onChangeSignature:b}}function et(e){if(!(!e?.item||Xn(e?.item?.[0],we)?.valueCodeableConcept?.coding?.[0]?.code!=="page"))return e.item.map((n,o)=>({linkId:n.linkId,title:n.text??`Page ${o+1}`,group:n}))}function nt(e,t,n=0){return t&&e?.item?.[n]?[e.item[n]]:e.item??[]}function tt(e,t,n=0){return t&&e?.item?.[n]?[e.item[n]]:e.item??[]}import{allOk as it,normalizeOperationOutcome as st}from"@medplum/core";import{useEffect as at,useMemo as ut,useState as se}from"react";import{useCallback as rt,useEffect as Ne,useRef as ie,useState as ot}from"react";function qe(e,t,n={leading:!1}){let[o,r]=ot(e),i=ie(!1),s=ie(void 0),a=ie(!1),l=rt(()=>window.clearTimeout(s.current),[]);return Ne(()=>{i.current&&(!a.current&&n.leading?(a.current=!0,r(e),s.current=setTimeout(()=>{a.current=!1},t)):(l(),s.current=setTimeout(()=>{a.current=!1,r(e)},t)))},[e,n.leading,t,l]),Ne(()=>(i.current=!0,l),[l]),[o,l]}var ct=250;function Wr(e,t,n){return ae("search",e,t,n)}function Dr(e,t,n){return ae("searchOne",e,t,n)}function Fr(e,t,n){return ae("searchResources",e,t,n)}function ae(e,t,n,o){let r=v(),[i,s]=se(!1),[a,l]=se(),[S,x]=se(),b=r.fhirSearchUrl(t,n).toString(),g=ut(()=>({resourceType:t,query:n}),[b]),h=o?.enabled??!0,y=o?.debounceMs??ct,[u]=qe(g,y,{leading:!0});return at(()=>{if(!h)return()=>{};s(!0);let p=!0;return r[e](u.resourceType,u.query).then(c=>{p&&(s(!1),l(c),x(it))}).catch(c=>{p&&(s(!1),l(void 0),x(st(c)))}),()=>{p=!1}},[r,e,u,h]),[a,i,S]}import{OperationOutcomeError as dt}from"@medplum/core";import{useCallback as pt}from"react";function jr(){let e=v();return pt(async t=>{try{await e.post(e.fhirUrl("PlanDefinition","$sync-orderset"),{planDefinitionId:t})}catch(n){if(n instanceof dt&&n.outcome.issue?.some(o=>o.code==="not-found"))return;throw n}},[e])}import{getReferenceString as ft}from"@medplum/core";import{useCallback as lt,useEffect as Le,useState as W}from"react";function Xr({query:e,threadId:t}){let n=v(),[o,r]=W(!0),[i,s]=W([]),[a,l]=W(void 0),[S,x]=W(null),[b,g]=W(void 0),h=lt(async()=>{let p=new URLSearchParams(e);p.append("identifier:not","http://medplum.com/ai-message|"),p.append("part-of:missing","true"),p.append("_has:Communication:part-of:_id:not","null");let c=await n.search("Communication",p.toString(),{cache:"no-cache"}),f=c.entry?.map(m=>m.resource).filter(m=>m!==void 0)||[];if(c.total!==void 0&&g(c.total),f.length===0){s([]);return}let R=`
2
2
  query {
3
- ${R.map(u=>{let A=`thread_${u.id?.replaceAll("-","")||""}`,C=Wn(u);return`
4
- ${A}: CommunicationList(
5
- part_of: "${C}"
3
+ ${f.map(m=>{let O=`thread_${m.id?.replaceAll("-","")||""}`,Q=ft(m);return`
4
+ ${O}: CommunicationList(
5
+ part_of: "${Q}"
6
6
  _sort: "-sent"
7
7
  _count: 1
8
8
  ) {
@@ -26,5 +26,38 @@ import{useEffect as Me,useMemo as ke,useState as Ue}from"react";import{createCon
26
26
  `}).join(`
27
27
  `)}
28
28
  }
29
- `,S=await n.graphql(E),d=R.map(u=>{let A=`thread_${u.id?.replaceAll("-","")||""}`,C=S.data[A],F=C&&C.length>0?C[0]:void 0;return[u,F]}).filter(u=>u[1]!==void 0);s(d)},[n,e]);return Te(()=>{r(!0),h().catch(c=>{I(c)}).finally(()=>{r(!1)})},[h]),Te(()=>{(async()=>{if(!t){p(void 0);return}let m=i.find(l=>l[0].id===t);if(m){p(m[0]);return}let R=await n.readResource("Communication",t);if(R.partOf===void 0)p(R);else{let l=R.partOf[0].reference;if(l){let E=await n.readReference({reference:l});p(E)}}})().catch(m=>{I(m)})},[t,i,n]),{loading:o,error:x,threadMessages:i,selectedThread:f,total:b,addThreadMessage:c=>{(async()=>{await h(),s(R=>[[c,void 0],...R])})().catch(R=>I(R))},handleThreadStatusChange:c=>{if(!f)return;(async()=>{let R=await n.updateResource({...f,status:c});p(R),s(l=>l.map(([E,S])=>E.id===R.id?[R,S]:[E,S]))})().catch(R=>I(R))},refreshThreadMessages:h}}import{ReconnectingWebSocket as Vn}from"@medplum/core";import{useCallback as _,useEffect as Pe,useRef as N,useState as te}from"react";function fr({language:e="en",model:t="gpt-4o-transcribe",onTranscript:n}){let o=v(),r=N(n);Pe(()=>{r.current=n},[n]);let[i,s]=te("idle"),[f,p]=te(void 0),[x,I]=te([]),b=N(void 0),Q=N(void 0),h=N(void 0),y=N(void 0),a=_(()=>{y.current?.disconnect(),y.current=void 0,h.current?.close().catch(()=>{}),h.current=void 0,Q.current?.getTracks().forEach(d=>d.stop()),Q.current=void 0,b.current?.close(),b.current=void 0,s("disconnected")},[]),c=_(()=>{b.current?.send(JSON.stringify({type:"session.update",session:{type:"transcription",audio:{input:{format:{type:"audio/pcm",rate:24e3},transcription:{model:t,language:e},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:200},noise_reduction:{type:"near_field"}}}}}))},[e,t]),m=_(()=>{let d=Q.current,u=b.current;if(!d||!u)return;let g=new AudioContext({sampleRate:24e3}),A=g.createMediaStreamSource(d),C=g.createScriptProcessor(4096,1,1);C.onaudioprocess=F=>{if(u.readyState!==WebSocket.OPEN){console.warn("WebSocket is not open. Unable to send audio data.");return}let Ce=F.inputBuffer.getChannelData(0),we=$n(Ce),Oe=btoa(String.fromCharCode(...we));u.send(JSON.stringify({type:"input_audio_buffer.append",audio:Oe}))},A.connect(C),C.connect(g.destination),h.current=g,y.current=C,s("listening")},[]),R=_(d=>{switch(d.type){case"session.created":c();break;case"session.updated":m();break;case"input_audio_buffer.speech_started":s("speech_started");break;case"input_audio_buffer.speech_stopped":s("speech_stopped");break;case"conversation.item.input_audio_transcription.completed":case"input_audio_transcription.completed":if(d.transcript){let u={text:d.transcript,timestamp:new Date().toISOString()};I(g=>[...g,u]),r.current?.(d.transcript)}break;case"ai-realtime:connected":console.debug("[useWhisper] upstream connected");break;case"ai-realtime:error":case"error":console.error("[useWhisper] error event",d),p(d),s("error");break;default:console.debug("[useWhisper] unhandled message",d.type,d);break}},[c,m]),l=_(async()=>{s("requesting_microphone");let d=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:24e3,channelCount:1,echoCancellation:!0,noiseSuppression:!0}});return Q.current=d,d},[]),E=_(()=>{s("connecting");let d=Kn(o.getBaseUrl());console.debug("[useWhisper] connecting to",d);let u=new Vn(d);return b.current=u,u.onopen=()=>s("connected"),u.onmessage=g=>R(JSON.parse(g.data)),u.onerror=g=>{p(g),s("error"),a()},u.onclose=()=>s("disconnected"),u.send(JSON.stringify({type:"ai-realtime:connect",accessToken:o.getAccessToken()})),u},[o,R,a]),S=_(async()=>{try{p(void 0),await l(),E()}catch(d){p(d),s("error"),a()}},[l,E,a]);return Pe(()=>()=>a(),[a]),{status:i,error:f,transcripts:x,start:S,stop:a,isListening:i==="listening"||i==="speech_started"||i==="speech_stopped"}}function $n(e){let t=new Int16Array(e.length);for(let n=0;n<e.length;n++){let o=Math.max(-1,Math.min(1,e[n]));t[n]=o*32767}return new Uint8Array(t.buffer)}function Kn(e){let t=new URL("ws/ai-realtime",e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",t.toString()}export{Xn as MedplumProvider,ln as QUESTIONNAIRE_CALCULATED_EXPRESSION_URL,fn as QUESTIONNAIRE_ENABLED_WHEN_EXPRESSION_URL,mn as QUESTIONNAIRE_HIDDEN_URL,Ie as QUESTIONNAIRE_ITEM_CONTROL_URL,dn as QUESTIONNAIRE_REFERENCE_FILTER_URL,j as QUESTIONNAIRE_REFERENCE_RESOURCE_URL,kt as QUESTIONNAIRE_SIGNATURE_REQUIRED_URL,q as QUESTIONNAIRE_SIGNATURE_RESPONSE_URL,pn as QUESTIONNAIRE_VALIDATION_ERROR_URL,T as QuestionnaireItemType,H as buildInitialResponse,Y as buildInitialResponseItem,$n as convertToPCM16,G as evaluateCalculatedExpressionsInQuestionnaire,he as getItemAnswerOptionValue,vn as getItemEnableWhenValueAnswer,Wt as getItemInitialValue,Nt as getNewMultiSelectValues,Ft as getQuestionnaireItemReferenceFilter,Lt as getQuestionnaireItemReferenceTargetTypes,Tn as getResponseItemAnswerValue,Ut as isChoiceQuestion,yn as isQuestionEnabled,W as reactContext,J as removeDisabledItems,qt as setQuestionnaireItemReferenceTargetTypes,Sn as typedValueToResponseItem,nt as useCachedBinaryUrl,it as useEPrescribingIFrame,v as useMedplum,D as useMedplumContext,jn as useMedplumNavigate,zn as useMedplumProfile,mt as useNotificationCount,It as usePatientSummaryData,Qt as usePharmacySearch,Pt as usePrevious,jt as useQuestionnaireForm,B as useResource,er as useSearch,nr as useSearchOne,tr as useSearchResources,ue as useSubscription,ar as useThreadInbox,fr as useWhisper};
29
+ `,E=await n.graphql(R),P=f.map(m=>{let O=`thread_${m.id?.replaceAll("-","")||""}`,Q=E.data[O],D=Q&&Q.length>0?Q[0]:void 0;return[m,D]}).filter(m=>m[1]!==void 0);s(P)},[n,e]);return Le(()=>{r(!0),h().catch(p=>{x(p)}).finally(()=>{r(!1)})},[h]),Le(()=>{(async()=>{if(!t){l(void 0);return}let c=i.find(d=>d[0].id===t);if(c){l(c[0]);return}let f=await n.readResource("Communication",t);if(f.partOf===void 0)l(f);else{let d=f.partOf[0].reference;if(d){let R=await n.readReference({reference:d});l(R)}}})().catch(c=>{x(c)})},[t,i,n]),{loading:o,error:S,threadMessages:i,selectedThread:a,total:b,addThreadMessage:p=>{(async()=>{await h(),s(f=>[[p,void 0],...f])})().catch(f=>x(f))},handleThreadStatusChange:p=>{if(!a)return;(async()=>{let f=await n.updateResource({...a,status:p});l(f),s(d=>d.map(([R,E])=>R.id===f.id?[f,E]:[R,E]))})().catch(f=>x(f))},refreshThreadMessages:h}}import{ReconnectingWebSocket as mt,sleep as ht}from"@medplum/core";import{useCallback as _,useEffect as ue,useRef as N,useState as ce}from"react";var yt=12e4;function to({language:e="en",model:t="gpt-4o-transcribe",onTranscript:n,idleTimeoutMs:o=yt}){let r=v(),i=N(n);ue(()=>{i.current=n},[n]);let[s,a]=ce("idle"),[l,S]=ce(void 0),[x,b]=ce([]),g=N(void 0),h=N(void 0),y=N(void 0),u=N(void 0),p=N(!1),c=N(!1),f=N(!1),d=_(()=>{f.current=!1,u.current?.disconnect(),u.current=void 0,y.current?.close().catch(()=>{}),y.current=void 0,h.current?.getTracks().forEach(I=>I.stop()),h.current=void 0,a(g.current?"idle":"disconnected")},[]),R=_(()=>{d(),g.current?.close(),g.current=void 0,c.current=!1,a("disconnected")},[d]),E=_(()=>{g.current?.send(JSON.stringify({type:"session.update",session:{type:"transcription",audio:{input:{format:{type:"audio/pcm",rate:24e3},transcription:{model:t,language:e},turn_detection:{type:"server_vad",threshold:.5,prefix_padding_ms:300,silence_duration_ms:200},noise_reduction:{type:"near_field"}}}}}))},[e,t]),P=_(async()=>{if(!(u.current||p.current)&&!(!h.current||!g.current)){p.current=!0;try{let I=new AudioContext({sampleRate:24e3});await I.audioWorklet.addModule(St());let M=h.current,k=g.current;if(!f.current||!M||!k){await I.close().catch(()=>{});return}let Fe=I.createMediaStreamSource(M),F=new AudioWorkletNode(I,We);F.port.onmessage=Ve=>{if(k.readyState!==WebSocket.OPEN){console.warn("WebSocket is not open. Unable to send audio data.");return}let $e=Rt(Ve.data),Ke=btoa(String.fromCharCode(...$e));k.send(JSON.stringify({type:"input_audio_buffer.append",audio:Ke}))},Fe.connect(F),F.connect(I.destination),y.current=I,u.current=F,a("listening")}catch(I){S(I),a("error"),d()}finally{p.current=!1}}},[d]),m=_(()=>{f.current&&c.current&&h.current&&(u.current||(g.current?.send(JSON.stringify({type:"input_audio_buffer.clear"})),P().catch(()=>{})))},[P]),w=_(I=>{switch(I.type){case"session.created":E();break;case"session.updated":c.current=!0,m();break;case"input_audio_buffer.speech_started":a("speech_started");break;case"input_audio_buffer.speech_stopped":a("speech_stopped");break;case"conversation.item.input_audio_transcription.completed":case"input_audio_transcription.completed":if(I.transcript){let M={text:I.transcript,timestamp:new Date().toISOString()};b(k=>[...k,M]),i.current?.(I.transcript)}break;case"ai-realtime:connected":console.debug("[useWhisper] upstream connected");break;case"ai-realtime:error":case"error":console.error("[useWhisper] error event",I),S(I),a("error");break;default:console.debug("[useWhisper] unhandled message",I.type,I);break}},[E,m]),O=_(async()=>{a("requesting_microphone");let I=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:24e3,channelCount:1,echoCancellation:!0,noiseSuppression:!0}});return h.current=I,I},[]),Q=_(()=>{a("connecting");let I=gt(r.getBaseUrl());console.debug("[useWhisper] connecting to",I);let M=new mt(I);return g.current=M,M.onopen=()=>{c.current=!1,f.current?a("connected"):a("idle"),M.send(JSON.stringify({type:"ai-realtime:connect",accessToken:r.getAccessToken()}))},M.onmessage=k=>w(JSON.parse(k.data)),M.onerror=k=>{f.current&&(S(k),R(),a("error"))},M.onclose=()=>{c.current=!1,g.current&&a(f.current?"connecting":"idle")},M},[r,w,R]),D=_(()=>g.current??Q(),[Q]),De=_(async()=>{try{S(void 0),f.current=!0,D(),await O(),m()}catch(I){S(I),a("error"),d()}},[O,D,d,m]);return ue(()=>{if(s!=="idle"||!g.current||!Number.isFinite(o)||o<=0)return;let I=new AbortController;return ht(o,{signal:I.signal}).then(()=>R()).catch(()=>{}),()=>I.abort()},[s,o,R]),ue(()=>()=>R(),[R]),{status:s,error:l,transcripts:x,start:De,stop:d,isListening:s==="listening"||s==="speech_started"||s==="speech_stopped"}}function Rt(e){let t=new Int16Array(e.length);for(let n=0;n<e.length;n++){let o=Math.max(-1,Math.min(1,e[n]));t[n]=o*32767}return new Uint8Array(t.buffer)}function gt(e){let t=new URL("ws/ai-realtime",e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",t.toString()}var We="medplum-pcm-worklet",It=`
30
+ class PcmWorkletProcessor extends AudioWorkletProcessor {
31
+ constructor() {
32
+ super();
33
+ this.chunks = [];
34
+ this.length = 0;
35
+ this.targetLength = 4096;
36
+ }
37
+
38
+ process(inputs) {
39
+ const channel = inputs[0] && inputs[0][0];
40
+ if (!channel) {
41
+ return true;
42
+ }
43
+ // The render quantum buffer is reused between calls, so copy it.
44
+ this.chunks.push(new Float32Array(channel));
45
+ this.length += channel.length;
46
+ if (this.length >= this.targetLength) {
47
+ const merged = new Float32Array(this.length);
48
+ let offset = 0;
49
+ for (const chunk of this.chunks) {
50
+ merged.set(chunk, offset);
51
+ offset += chunk.length;
52
+ }
53
+ this.port.postMessage(merged, [merged.buffer]);
54
+ this.chunks = [];
55
+ this.length = 0;
56
+ }
57
+ return true;
58
+ }
59
+ }
60
+
61
+ registerProcessor('${We}', PcmWorkletProcessor);
62
+ `,de;function St(){if(!de){let e=new Blob([It],{type:"application/javascript"});de=URL.createObjectURL(e)}return de}export{Qt as MedplumProvider,qn as QUESTIONNAIRE_CALCULATED_EXPRESSION_URL,Nn as QUESTIONNAIRE_ENABLED_WHEN_EXPRESSION_URL,Ln as QUESTIONNAIRE_HIDDEN_URL,we as QUESTIONNAIRE_ITEM_CONTROL_URL,kn as QUESTIONNAIRE_REFERENCE_FILTER_URL,Y as QUESTIONNAIRE_REFERENCE_RESOURCE_URL,Ir as QUESTIONNAIRE_SIGNATURE_REQUIRED_URL,$ as QUESTIONNAIRE_SIGNATURE_RESPONSE_URL,Un as QUESTIONNAIRE_VALIDATION_ERROR_URL,T as QuestionnaireItemType,te as buildInitialResponse,oe as buildInitialResponseItem,Rt as convertToPCM16,ne as evaluateCalculatedExpressionsInQuestionnaire,Ce as getItemAnswerOptionValue,Gn as getItemEnableWhenValueAnswer,vr as getItemInitialValue,xr as getNewMultiSelectValues,Pr as getQuestionnaireItemReferenceFilter,br as getQuestionnaireItemReferenceTargetTypes,Hn as getResponseItemAnswerValue,Sr as isChoiceQuestion,Wn as isQuestionEnabled,K as reactContext,ee as removeDisabledItems,Er as setQuestionnaireItemReferenceTargetTypes,Kn as typedValueToResponseItem,Mt as useCachedBinaryUrl,kt as useMedicationIFrame,Wt as useMedicationOrder,Kt as useMedicationOrderSet,v as useMedplum,B as useMedplumContext,bt as useMedplumNavigate,pe as useMedplumProfile,Zt as useNotificationCount,or as usePatientSummaryData,cr as usePharmacySearch,fr as usePrevious,wr as useQuestionnaireForm,X as useResource,Wr as useSearch,Dr as useSearchOne,Fr as useSearchResources,Se as useSubscription,jr as useSyncOrderSet,Xr as useThreadInbox,to as useWhisper};
30
63
  //# sourceMappingURL=index.mjs.map