@ohm_studio/sdk-react-native 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # @ohm_studio/sdk-react-native
1
+ # OHM RN SDK · `@ohm_studio/sdk-react-native`
2
+
3
+ > The **short name** is `OHM RN SDK`. The **full npm name** is `@ohm_studio/sdk-react-native` — that's what you install. Both names refer to the same package.
2
4
 
3
5
  OHM Studio SDK for **React Native / Expo** — voice-to-structured-JSON clinical extraction inside your mobile app. Multi-language STT, FHIR-shaped outputs, formulary-aware prescriptions.
4
6
 
5
- > For web / Node, install [`@ohm_studio/sdk`](https://www.npmjs.com/package/@ohm_studio/sdk) instead.
7
+ > For web / Node, install [`@ohm_studio/sdk`](https://www.npmjs.com/package/@ohm_studio/sdk) (**OHM SDK**) instead.
6
8
 
7
9
  ## Install
8
10
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { OHMCoreClient } from "@ohm_studio/sdk-core";
2
- import type { OHMClientOptions } from "@ohm_studio/sdk-core";
2
+ import type { OHMInit } from "@ohm_studio/sdk-core";
3
3
  export * from "@ohm_studio/sdk-core";
4
+ export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
5
+ export type { ExpoRecorderOptions, RNFile, RecorderState, RecorderErrorCode, } from "./recorder";
4
6
  /**
5
7
  * OHM Studio SDK for React Native.
6
8
  *
@@ -24,12 +26,13 @@ export * from "@ohm_studio/sdk-core";
24
26
  * });
25
27
  */
26
28
  export declare class OHM extends OHMCoreClient {
27
- constructor(opts: OHMClientOptions);
29
+ constructor(init: OHMInit);
28
30
  protected runMultipart<T>(opts: {
29
31
  path: string;
30
32
  file: any;
31
33
  fields?: Record<string, string>;
32
34
  }): Promise<T>;
35
+ protected buildMultipartBody(file: unknown, fields?: Record<string, string>): Promise<unknown>;
33
36
  }
34
37
  export default OHM;
35
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,cAAc,sBAAsB,CAAC;AAYrC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,gBAAgB;cAclB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,CAAC,CAAC;CA2Bf;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAoB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEtE,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,mBAAmB,EACnB,MAAM,EACN,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAYpB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,OAAO;cAiBT,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,CAAC,CAAC;cAQE,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC;CAuBpB;AAED,eAAe,GAAG,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { OHMConfigError, OHMCoreClient } from "@ohm_studio/sdk-core";
2
2
  export * from "@ohm_studio/sdk-core";
3
+ export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
3
4
  /** Detect React Native runtime — used to gate the bundled-key safeguard. */
4
5
  function isReactNative() {
5
6
  return (
@@ -31,8 +32,10 @@ function isReactNative() {
31
32
  * });
32
33
  */
33
34
  export class OHM extends OHMCoreClient {
34
- constructor(opts) {
35
- if (isReactNative() &&
35
+ constructor(init) {
36
+ const opts = typeof init === "string" ? { apiKey: init } : init;
37
+ if (!opts.mock &&
38
+ isReactNative() &&
36
39
  opts.apiKey?.startsWith("ohms_live_") &&
37
40
  !opts.acknowledgeBundledKey) {
38
41
  throw new OHMConfigError({
@@ -42,29 +45,31 @@ export class OHM extends OHMCoreClient {
42
45
  super(opts);
43
46
  }
44
47
  async runMultipart(opts) {
48
+ const fd = (await this.buildMultipartBody(opts.file, opts.fields));
49
+ return this.requestRaw("POST", opts.path, { body: fd });
50
+ }
51
+ async buildMultipartBody(file, fields) {
45
52
  const fd = new FormData();
46
- if (opts.file &&
47
- typeof opts.file === "object" &&
48
- "uri" in opts.file) {
49
- // RN FormData accepts this shape directly.
53
+ if (file && typeof file === "object" && "uri" in file) {
54
+ const f = file;
55
+ // RN FormData accepts the { uri, name, type } shape directly.
50
56
  // @ts-ignore — RN's FormData type isn't standard.
51
57
  fd.append("file", {
52
- uri: opts.file.uri,
53
- name: opts.file.name || "audio.m4a",
54
- type: opts.file.type || "audio/mp4",
58
+ uri: f.uri,
59
+ name: f.name || "audio.m4a",
60
+ type: f.type || "audio/mp4",
55
61
  });
56
62
  }
57
- else if (typeof Blob !== "undefined" && opts.file instanceof Blob) {
58
- // Some RN polyfills support Blob.
59
- fd.append("file", opts.file, "audio.bin");
63
+ else if (typeof Blob !== "undefined" && file instanceof Blob) {
64
+ fd.append("file", file, "audio.bin");
60
65
  }
61
66
  else {
62
67
  throw new Error("Unsupported file input on React Native — pass `{ uri, name, type }`");
63
68
  }
64
- for (const [k, v] of Object.entries(opts.fields || {})) {
69
+ for (const [k, v] of Object.entries(fields || {})) {
65
70
  fd.append(k, v);
66
71
  }
67
- return this.requestRaw("POST", opts.path, { body: fd });
72
+ return fd;
68
73
  }
69
74
  }
70
75
  export default OHM;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrE,cAAc,sBAAsB,CAAC;AAErC,4EAA4E;AAC5E,SAAS,aAAa;IACpB,OAAO;IACL,+BAA+B;IAC/B,OAAO,SAAS,KAAK,WAAW;QAChC,aAAa;QACb,SAAS,CAAC,OAAO,KAAK,aAAa,CACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,GAAI,SAAQ,aAAa;IACpC,YAAY,IAAsB;QAChC,IACE,aAAa,EAAE;YACf,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC;YACrC,CAAC,IAAI,CAAC,qBAAqB,EAC3B,CAAC;YACD,MAAM,IAAI,cAAc,CAAC;gBACvB,OAAO,EACL,kOAAkO;aACrO,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAES,KAAK,CAAC,YAAY,CAAI,IAI/B;QACC,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC1B,IACE,IAAI,CAAC,IAAI;YACT,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;YAC7B,KAAK,IAAI,IAAI,CAAC,IAAI,EAClB,CAAC;YACD,2CAA2C;YAC3C,kDAAkD;YAClD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE;gBAChB,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG;gBAClB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,WAAW;gBACnC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,WAAW;aACpC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,YAAY,IAAI,EAAE,CAAC;YACpE,kCAAkC;YAClC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAW,EAAE,WAAW,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YACvD,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAI,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAS,EAAE,CAAC,CAAC;IACpE,CAAC;CACF;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrE,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAQpB,4EAA4E;AAC5E,SAAS,aAAa;IACpB,OAAO;IACL,+BAA+B;IAC/B,OAAO,SAAS,KAAK,WAAW;QAChC,aAAa;QACb,SAAS,CAAC,OAAO,KAAK,aAAa,CACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,GAAI,SAAQ,aAAa;IACpC,YAAY,IAAa;QACvB,MAAM,IAAI,GACR,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IACE,CAAC,IAAI,CAAC,IAAI;YACV,aAAa,EAAE;YACf,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC;YACrC,CAAC,IAAI,CAAC,qBAAqB,EAC3B,CAAC;YACD,MAAM,IAAI,cAAc,CAAC;gBACvB,OAAO,EACL,kOAAkO;aACrO,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAES,KAAK,CAAC,YAAY,CAAI,IAI/B;QACC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,kBAAkB,CACvC,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,MAAM,CACZ,CAAwB,CAAC;QAC1B,OAAO,IAAI,CAAC,UAAU,CAAI,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAES,KAAK,CAAC,kBAAkB,CAChC,IAAa,EACb,MAA+B;QAE/B,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,IAAK,IAAY,EAAE,CAAC;YAC/D,MAAM,CAAC,GAAG,IAAqD,CAAC;YAChE,8DAA8D;YAC9D,kDAAkD;YAClD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE;gBAChB,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW;gBAC3B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW;aAC5B,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YAC/D,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAW,EAAE,WAAW,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAClD,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED,eAAe,GAAG,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { AudioExtractInput, AudioExtractResult, ExtractInput, ExtractResult, SummarizeInput, SummarizeResult } from "@ohm_studio/sdk-core";
3
3
  import { OHM } from "../index";
4
+ import { RecorderError, type ExpoRecorderOptions, type RecorderState, type RNFile } from "../recorder";
4
5
  export declare function OhmProvider({ client, children, }: {
5
6
  client: OHM;
6
7
  children: ReactNode;
@@ -33,4 +34,56 @@ export declare function useOhmSummarize(): {
33
34
  error: Error | null;
34
35
  isPending: boolean;
35
36
  };
37
+ export interface UseRecorderOptions extends Omit<ExpoRecorderOptions, "onStateChange" | "onLevel" | "onError"> {
38
+ /**
39
+ * Pass `Audio` from `expo-av` here. Required.
40
+ *
41
+ * import { Audio } from "expo-av";
42
+ * useRecorder({ audio: Audio, ... });
43
+ */
44
+ audio: any;
45
+ /**
46
+ * If supplied, on stop the recording is automatically sent to
47
+ * `ohm.audio.extract({ apiSlug, file })` and exposed on `data`/`transcript`.
48
+ * Requires <OhmProvider client={...}> above.
49
+ */
50
+ apiSlug?: string;
51
+ extractInputs?: Record<string, unknown>;
52
+ extractLanguage?: string;
53
+ }
54
+ export interface UseRecorderReturn<T> {
55
+ state: RecorderState;
56
+ isRecording: boolean;
57
+ isPaused: boolean;
58
+ /** Linear level 0–1, derived from Expo's dB metering. */
59
+ level: number;
60
+ /** Recorded duration in seconds. */
61
+ durationSec: number;
62
+ file: RNFile | null;
63
+ error: RecorderError | null;
64
+ start: () => Promise<void>;
65
+ stop: () => Promise<RNFile | null>;
66
+ pause: () => Promise<void>;
67
+ resume: () => Promise<void>;
68
+ cancel: () => Promise<void>;
69
+ reset: () => void;
70
+ extracting: boolean;
71
+ transcript: string | null;
72
+ data: T | null;
73
+ extractError: Error | null;
74
+ }
75
+ /**
76
+ * `useRecorder` for React Native — same shape as the web hook.
77
+ *
78
+ * import { Audio } from "expo-av";
79
+ * const r = useRecorder({
80
+ * audio: Audio,
81
+ * apiSlug: "opd",
82
+ * silenceAutoStop: { ms: 6000 },
83
+ * });
84
+ * <Pressable onPress={r.isRecording ? r.stop : r.start}>
85
+ * <Text>{r.isRecording ? `Stop ${r.durationSec.toFixed(0)}s` : "Record"}</Text>
86
+ * </Pressable>
87
+ */
88
+ export declare function useRecorder<T = Record<string, unknown>>(opts: UseRecorderOptions): UseRecorderReturn<T>;
36
89
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAa/B,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC;CACrB,uFAEA;AAoDD,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAxCQ,KAAK,GAAG,IAAI;eACR,OAAO;EA4CnB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAjDQ,KAAK,GAAG,IAAI;eACR,OAAO;EAqDnB;AAED,wBAAgB,eAAe;;;;;WAxDtB,KAAK,GAAG,IAAI;eACR,OAAO;EA2DnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAEL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,MAAM,EACZ,MAAM,aAAa,CAAC;AAarB,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC;CACrB,uFAEA;AAoDD,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAxCQ,KAAK,GAAG,IAAI;eACR,OAAO;EA4CnB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAjDQ,KAAK,GAAG,IAAI;eACR,OAAO;EAqDnB;AAED,wBAAgB,eAAe;;;;;WAxDtB,KAAK,GAAG,IAAI;eACR,OAAO;EA2DnB;AAID,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1E;;;;;OAKG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,IAAI,EAAE,kBAAkB,GACvB,iBAAiB,CAAC,CAAC,CAAC,CA2ItB"}
@@ -1,4 +1,5 @@
1
- import { createContext, createElement, useCallback, useContext, useState, } from "react";
1
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
2
+ import { ExpoRecorder, } from "../recorder";
2
3
  /**
3
4
  * React hooks for @ohm_studio/sdk. Mirrors `@ohm_studio/sdk-react-native/react` so a
4
5
  * snippet copies cleanly between web and mobile codebases.
@@ -55,4 +56,149 @@ export function useOhmAudioExtract(opts) {
55
56
  export function useOhmSummarize() {
56
57
  return useOhmMutation((c, input) => c.summarize(input));
57
58
  }
59
+ /**
60
+ * `useRecorder` for React Native — same shape as the web hook.
61
+ *
62
+ * import { Audio } from "expo-av";
63
+ * const r = useRecorder({
64
+ * audio: Audio,
65
+ * apiSlug: "opd",
66
+ * silenceAutoStop: { ms: 6000 },
67
+ * });
68
+ * <Pressable onPress={r.isRecording ? r.stop : r.start}>
69
+ * <Text>{r.isRecording ? `Stop ${r.durationSec.toFixed(0)}s` : "Record"}</Text>
70
+ * </Pressable>
71
+ */
72
+ export function useRecorder(opts) {
73
+ const client = useContext(OhmContext);
74
+ const recRef = useRef(null);
75
+ const [state, setState] = useState("idle");
76
+ const [level, setLevel] = useState(0);
77
+ const [durationSec, setDurationSec] = useState(0);
78
+ const [file, setFile] = useState(null);
79
+ const [error, setError] = useState(null);
80
+ const [extracting, setExtracting] = useState(false);
81
+ const [transcript, setTranscript] = useState(null);
82
+ const [data, setData] = useState(null);
83
+ const [extractError, setExtractError] = useState(null);
84
+ const optsRef = useRef(opts);
85
+ optsRef.current = opts;
86
+ const ensureRecorder = useCallback(() => {
87
+ if (recRef.current)
88
+ return recRef.current;
89
+ const { audio, apiSlug, extractInputs, extractLanguage, ...recOpts } = optsRef.current;
90
+ const r = new ExpoRecorder(audio, {
91
+ ...recOpts,
92
+ onStateChange: (s) => setState(s),
93
+ onLevel: (l) => setLevel(l),
94
+ onStatus: (s) => setDurationSec(s.durationMillis / 1000),
95
+ onError: (e) => setError(e),
96
+ });
97
+ recRef.current = r;
98
+ return r;
99
+ }, []);
100
+ useEffect(() => {
101
+ return () => {
102
+ void recRef.current?.cancel().catch(() => {
103
+ /* ignore */
104
+ });
105
+ recRef.current = null;
106
+ };
107
+ }, []);
108
+ const start = useCallback(async () => {
109
+ setError(null);
110
+ setFile(null);
111
+ setTranscript(null);
112
+ setData(null);
113
+ setExtractError(null);
114
+ setDurationSec(0);
115
+ const r = ensureRecorder();
116
+ await r.start();
117
+ }, [ensureRecorder]);
118
+ const stop = useCallback(async () => {
119
+ const r = recRef.current;
120
+ if (!r)
121
+ return null;
122
+ const f = await r.stop();
123
+ setFile(f);
124
+ if (optsRef.current.apiSlug && client) {
125
+ setExtracting(true);
126
+ try {
127
+ const res = await client.audio.extract({
128
+ apiSlug: optsRef.current.apiSlug,
129
+ file: f,
130
+ inputs: optsRef.current.extractInputs,
131
+ language: optsRef.current.extractLanguage,
132
+ });
133
+ setTranscript(res.transcript ?? null);
134
+ setData(res.data);
135
+ }
136
+ catch (e) {
137
+ setExtractError(e);
138
+ }
139
+ finally {
140
+ setExtracting(false);
141
+ }
142
+ }
143
+ return f;
144
+ }, [client]);
145
+ const pause = useCallback(async () => {
146
+ await recRef.current?.pause();
147
+ }, []);
148
+ const resume = useCallback(async () => {
149
+ await recRef.current?.resume();
150
+ }, []);
151
+ const cancel = useCallback(async () => {
152
+ await recRef.current?.cancel();
153
+ }, []);
154
+ const reset = useCallback(() => {
155
+ void recRef.current?.cancel().catch(() => {
156
+ /* ignore */
157
+ });
158
+ recRef.current = null;
159
+ setState("idle");
160
+ setLevel(0);
161
+ setDurationSec(0);
162
+ setFile(null);
163
+ setError(null);
164
+ setTranscript(null);
165
+ setData(null);
166
+ setExtractError(null);
167
+ }, []);
168
+ return useMemo(() => ({
169
+ state,
170
+ isRecording: state === "recording",
171
+ isPaused: state === "paused",
172
+ level,
173
+ durationSec,
174
+ file,
175
+ error,
176
+ start,
177
+ stop,
178
+ pause,
179
+ resume,
180
+ cancel,
181
+ reset,
182
+ extracting,
183
+ transcript,
184
+ data,
185
+ extractError,
186
+ }), [
187
+ state,
188
+ level,
189
+ durationSec,
190
+ file,
191
+ error,
192
+ start,
193
+ stop,
194
+ pause,
195
+ resume,
196
+ cancel,
197
+ reset,
198
+ extracting,
199
+ transcript,
200
+ data,
201
+ extractError,
202
+ ]);
203
+ }
58
204
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,QAAQ,GAET,MAAM,OAAO,CAAC;AAWf;;;;;;;GAOG;AAEH,MAAM,UAAU,GAAG,aAAa,CAAa,IAAI,CAAC,CAAC;AAEnD,MAAM,UAAU,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GAIT;IACC,OAAO,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAQD,SAAS,cAAc,CACrB,EAAoD;IAEpD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAyB;QACzD,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,KAAa,EAAE,EAAE;QACtB,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACrC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YACvD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,EAAE,CAAC,CACb,CAAC;IACF,OAAO;QACL,GAAG,KAAK;QACR,WAAW;QACX,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxB,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CACV,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAA8B,IAE1D;IACC,OAAO,cAAc,CAGnB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAI,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAA8B,IAE/D;IACC,OAAO,cAAc,CAGnB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAI,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,cAAc,CAAkC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAClE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CACnB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * React Native audio recorder for clinical consults — Expo flavour.
3
+ *
4
+ * Wraps `expo-av`'s `Audio.Recording` with clinically-tuned defaults
5
+ * (16 kHz mono AAC), iOS audio-session setup so recording works in
6
+ * silent mode, dB → linear level metering, pause/resume, duration
7
+ * tracking, silence auto-stop, max-duration cap, and typed errors.
8
+ *
9
+ * import { Audio } from "expo-av";
10
+ * import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
11
+ *
12
+ * const rec = new ExpoRecorder(Audio, {
13
+ * onLevel: setVu,
14
+ * silenceAutoStop: { ms: 6000 },
15
+ * maxDurationMs: 15 * 60_000,
16
+ * });
17
+ * await rec.start();
18
+ * const file = await rec.stop(); // → { uri, name, type }
19
+ * const { transcript, data } = await ohm.audio.extract({ apiSlug, file });
20
+ *
21
+ * We don't take a hard dependency on `expo-av` so apps using bare RN
22
+ * with `react-native-audio-recorder-player` aren't forced to install
23
+ * Expo. The Audio module is passed in as a parameter at runtime.
24
+ */
25
+ /** The shape `ohm.audio.extract` expects on React Native. */
26
+ export interface RNFile {
27
+ uri: string;
28
+ name: string;
29
+ type: string;
30
+ }
31
+ export type RecorderState = "idle" | "starting" | "recording" | "paused" | "stopping";
32
+ export type RecorderErrorCode = "NotSupported" | "PermissionDenied" | "NoMicrophone" | "InvalidState" | "Interrupted" | "Unknown";
33
+ export declare class RecorderError extends Error {
34
+ code: RecorderErrorCode;
35
+ cause?: unknown;
36
+ constructor(code: RecorderErrorCode, message: string, cause?: unknown);
37
+ }
38
+ /** Minimal subset of `expo-av`'s Audio module we depend on. */
39
+ interface ExpoAudioModule {
40
+ requestPermissionsAsync: () => Promise<{
41
+ status: string;
42
+ granted?: boolean;
43
+ }>;
44
+ getPermissionsAsync?: () => Promise<{
45
+ status: string;
46
+ granted?: boolean;
47
+ }>;
48
+ setAudioModeAsync?: (mode: any) => Promise<void>;
49
+ Recording: {
50
+ createAsync: (options: any, onStatusUpdate?: ((status: any) => void) | null, progressUpdateIntervalMillis?: number) => Promise<{
51
+ recording: ExpoAudioRecording;
52
+ status: any;
53
+ }>;
54
+ };
55
+ RecordingOptionsPresets?: {
56
+ HIGH_QUALITY?: any;
57
+ LOW_QUALITY?: any;
58
+ };
59
+ IOSOutputFormat?: {
60
+ LINEARPCM?: number;
61
+ MPEG4AAC?: number;
62
+ };
63
+ AndroidOutputFormat?: {
64
+ MPEG_4?: number;
65
+ };
66
+ AndroidAudioEncoder?: {
67
+ AAC?: number;
68
+ };
69
+ IOSAudioQuality?: {
70
+ HIGH?: number;
71
+ MEDIUM?: number;
72
+ };
73
+ InterruptionModeIOS?: {
74
+ DoNotMix?: number;
75
+ MixWithOthers?: number;
76
+ };
77
+ }
78
+ interface ExpoAudioRecording {
79
+ startAsync?: () => Promise<unknown>;
80
+ pauseAsync?: () => Promise<unknown>;
81
+ stopAndUnloadAsync: () => Promise<unknown>;
82
+ getURI: () => string | null;
83
+ setProgressUpdateInterval?: (ms: number) => void;
84
+ setOnRecordingStatusUpdate?: (cb: (s: any) => void) => void;
85
+ }
86
+ export interface ExpoRecorderOptions {
87
+ /**
88
+ * Override the recording options entirely. If omitted, the SDK uses a
89
+ * 16 kHz mono AAC clinical preset. Pass an Expo `RecordingOptionsPresets`
90
+ * value or a fully custom RecordingOptions object.
91
+ */
92
+ recordingOptions?: any;
93
+ /** Mime type tag attached to the file shape — default `audio/mp4`. */
94
+ mimeType?: string;
95
+ /** Filename tag attached to the file shape — default `rec.m4a`. */
96
+ fileName?: string;
97
+ /**
98
+ * Set up the iOS audio session before recording so the mic still works
99
+ * when the device is in silent mode. Default true.
100
+ */
101
+ configureIosAudioSession?: boolean;
102
+ /**
103
+ * Hard cap — auto-stop after this many ms of recording. Default off.
104
+ */
105
+ maxDurationMs?: number;
106
+ /**
107
+ * Auto-stop after sustained silence. dB below `thresholdDb` (default
108
+ * -40 dBFS) for `ms` continuous milliseconds triggers stop. Default off.
109
+ */
110
+ silenceAutoStop?: {
111
+ ms?: number;
112
+ thresholdDb?: number;
113
+ };
114
+ /**
115
+ * Optional keep-awake hooks. Wire to `expo-keep-awake`'s
116
+ * `activateKeepAwake()` / `deactivateKeepAwake()` to prevent the device
117
+ * from sleeping during long consults. Optional — not bundled.
118
+ */
119
+ keepAwake?: {
120
+ activate: () => void | Promise<void>;
121
+ deactivate: () => void | Promise<void>;
122
+ };
123
+ /** Recording state changes. */
124
+ onStateChange?: (state: RecorderState) => void;
125
+ /** Periodic linear level 0–1. Wire to a VU meter. */
126
+ onLevel?: (linearLevel: number) => void;
127
+ /** Progress updates from Expo (durationMillis, metering, isDoneRecording…). */
128
+ onStatus?: (status: {
129
+ durationMillis: number;
130
+ metering?: number;
131
+ isRecording: boolean;
132
+ isDoneRecording: boolean;
133
+ }) => void;
134
+ /** All recorder errors flow through here in addition to the awaited rejection. */
135
+ onError?: (err: RecorderError) => void;
136
+ }
137
+ /** True if `expo-av` is installed in the host RN app. */
138
+ export declare function isExpoAvAvailable(): boolean;
139
+ export declare class ExpoRecorder {
140
+ private audio;
141
+ private recording;
142
+ private opts;
143
+ private state;
144
+ private durationMs;
145
+ private maxStopTimer;
146
+ private silenceSinceMs;
147
+ private keepAwakeActive;
148
+ constructor(expoAudio: ExpoAudioModule, opts?: ExpoRecorderOptions);
149
+ /** Probe permission without prompting. */
150
+ probePermission(): Promise<"granted" | "denied" | "undetermined">;
151
+ /** Current state. */
152
+ get currentState(): RecorderState;
153
+ /** Recorded duration in ms (live; zero when idle). */
154
+ getDuration(): number;
155
+ /** Request mic permission and start recording. */
156
+ start(): Promise<void>;
157
+ /** Pause recording (resumable via `resume()`). */
158
+ pause(): Promise<void>;
159
+ /** Resume after `pause()`. */
160
+ resume(): Promise<void>;
161
+ /**
162
+ * Stop recording and return the file shape `ohm.audio.extract` expects.
163
+ */
164
+ stop(): Promise<RNFile>;
165
+ /** Convenience — stop after `ms` from now. */
166
+ stopAfter(ms: number): Promise<RNFile>;
167
+ /** Discard the in-progress recording without producing a file. */
168
+ cancel(): Promise<void>;
169
+ private handleStatusUpdate;
170
+ private cleanup;
171
+ private setState;
172
+ }
173
+ export {};
174
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * React Native audio recorder for clinical consults — Expo flavour.
3
+ *
4
+ * Wraps `expo-av`'s `Audio.Recording` with clinically-tuned defaults
5
+ * (16 kHz mono AAC), iOS audio-session setup so recording works in
6
+ * silent mode, dB → linear level metering, pause/resume, duration
7
+ * tracking, silence auto-stop, max-duration cap, and typed errors.
8
+ *
9
+ * import { Audio } from "expo-av";
10
+ * import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
11
+ *
12
+ * const rec = new ExpoRecorder(Audio, {
13
+ * onLevel: setVu,
14
+ * silenceAutoStop: { ms: 6000 },
15
+ * maxDurationMs: 15 * 60_000,
16
+ * });
17
+ * await rec.start();
18
+ * const file = await rec.stop(); // → { uri, name, type }
19
+ * const { transcript, data } = await ohm.audio.extract({ apiSlug, file });
20
+ *
21
+ * We don't take a hard dependency on `expo-av` so apps using bare RN
22
+ * with `react-native-audio-recorder-player` aren't forced to install
23
+ * Expo. The Audio module is passed in as a parameter at runtime.
24
+ */
25
+ export class RecorderError extends Error {
26
+ code;
27
+ cause;
28
+ constructor(code, message, cause) {
29
+ super(message);
30
+ this.name = "RecorderError";
31
+ this.code = code;
32
+ this.cause = cause;
33
+ }
34
+ }
35
+ /**
36
+ * Clinical-tuned RecordingOptions: 16 kHz mono AAC, 32 kbps. Plenty for
37
+ * speech; keeps file size small for upload over flaky hospital wifi.
38
+ *
39
+ * We build this dynamically so we tolerate constant renames across Expo SDKs.
40
+ */
41
+ function clinicalRecordingOptions(audio) {
42
+ // Expo SDK 50+ uses string outputs ("aac ") on iOS; older SDKs used
43
+ // numeric enums via Audio.IOSOutputFormat. We pass both shapes so
44
+ // whichever the runtime understands wins.
45
+ const iosOutputFormat = audio.IOSOutputFormat?.MPEG4AAC ?? "aac ";
46
+ const iosAudioQuality = audio.IOSAudioQuality?.MEDIUM ?? 0x40;
47
+ const androidOutputFormat = audio.AndroidOutputFormat?.MPEG_4 ?? 2;
48
+ const androidAudioEncoder = audio.AndroidAudioEncoder?.AAC ?? 3;
49
+ return {
50
+ isMeteringEnabled: true,
51
+ android: {
52
+ extension: ".m4a",
53
+ outputFormat: androidOutputFormat,
54
+ audioEncoder: androidAudioEncoder,
55
+ sampleRate: 16_000,
56
+ numberOfChannels: 1,
57
+ bitRate: 32_000,
58
+ },
59
+ ios: {
60
+ extension: ".m4a",
61
+ outputFormat: iosOutputFormat,
62
+ audioQuality: iosAudioQuality,
63
+ sampleRate: 16_000,
64
+ numberOfChannels: 1,
65
+ bitRate: 32_000,
66
+ linearPCMBitDepth: 16,
67
+ linearPCMIsBigEndian: false,
68
+ linearPCMIsFloat: false,
69
+ },
70
+ web: {
71
+ mimeType: "audio/webm;codecs=opus",
72
+ bitsPerSecond: 32_000,
73
+ },
74
+ };
75
+ }
76
+ /**
77
+ * Convert Expo's `metering` (dBFS, typically -160..0) to a linear 0..1
78
+ * level useful for a VU meter. Mic floor is mapped to 0, peak to 1.
79
+ */
80
+ function dbToLinear(db, floorDb = -50) {
81
+ if (!Number.isFinite(db))
82
+ return 0;
83
+ if (db <= floorDb)
84
+ return 0;
85
+ if (db >= 0)
86
+ return 1;
87
+ return (db - floorDb) / -floorDb;
88
+ }
89
+ /** True if `expo-av` is installed in the host RN app. */
90
+ export function isExpoAvAvailable() {
91
+ try {
92
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
93
+ require.resolve("expo-av");
94
+ return true;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ export class ExpoRecorder {
101
+ audio;
102
+ recording = null;
103
+ opts;
104
+ state = "idle";
105
+ durationMs = 0;
106
+ maxStopTimer = null;
107
+ silenceSinceMs = 0;
108
+ keepAwakeActive = false;
109
+ constructor(expoAudio, opts = {}) {
110
+ this.audio = expoAudio;
111
+ this.opts = opts;
112
+ }
113
+ /** Probe permission without prompting. */
114
+ async probePermission() {
115
+ if (!this.audio.getPermissionsAsync)
116
+ return "undetermined";
117
+ const r = await this.audio.getPermissionsAsync();
118
+ return r?.status || "undetermined";
119
+ }
120
+ /** Current state. */
121
+ get currentState() {
122
+ return this.state;
123
+ }
124
+ /** Recorded duration in ms (live; zero when idle). */
125
+ getDuration() {
126
+ return this.durationMs;
127
+ }
128
+ /** Request mic permission and start recording. */
129
+ async start() {
130
+ if (this.state !== "idle") {
131
+ throw new RecorderError("InvalidState", `Cannot start while "${this.state}".`);
132
+ }
133
+ this.setState("starting");
134
+ let perm;
135
+ try {
136
+ perm = await this.audio.requestPermissionsAsync();
137
+ }
138
+ catch (e) {
139
+ this.setState("idle");
140
+ const err = new RecorderError("Unknown", "Failed to request microphone permission", e);
141
+ this.opts.onError?.(err);
142
+ throw err;
143
+ }
144
+ if (perm.status !== "granted") {
145
+ this.setState("idle");
146
+ const err = new RecorderError("PermissionDenied", "Microphone permission denied");
147
+ this.opts.onError?.(err);
148
+ throw err;
149
+ }
150
+ if (this.opts.configureIosAudioSession !== false && this.audio.setAudioModeAsync) {
151
+ try {
152
+ await this.audio.setAudioModeAsync({
153
+ allowsRecordingIOS: true,
154
+ playsInSilentModeIOS: true,
155
+ staysActiveInBackground: false,
156
+ interruptionModeIOS: this.audio.InterruptionModeIOS?.DoNotMix ?? 1,
157
+ shouldDuckAndroid: true,
158
+ playThroughEarpieceAndroid: false,
159
+ });
160
+ }
161
+ catch {
162
+ /* best-effort */
163
+ }
164
+ }
165
+ const recordingOptions = this.opts.recordingOptions ?? clinicalRecordingOptions(this.audio);
166
+ let result;
167
+ try {
168
+ result = await this.audio.Recording.createAsync(recordingOptions, (status) => this.handleStatusUpdate(status), 100);
169
+ }
170
+ catch (e) {
171
+ this.setState("idle");
172
+ const err = new RecorderError("Unknown", "Failed to start recording: " + (e?.message || ""), e);
173
+ this.opts.onError?.(err);
174
+ throw err;
175
+ }
176
+ this.recording = result.recording;
177
+ this.durationMs = 0;
178
+ this.silenceSinceMs = 0;
179
+ if (this.opts.maxDurationMs && this.opts.maxDurationMs > 0) {
180
+ this.maxStopTimer = setTimeout(() => {
181
+ this.stop().catch(() => {
182
+ /* swallow */
183
+ });
184
+ }, this.opts.maxDurationMs);
185
+ }
186
+ if (this.opts.keepAwake) {
187
+ try {
188
+ await this.opts.keepAwake.activate();
189
+ this.keepAwakeActive = true;
190
+ }
191
+ catch {
192
+ /* best-effort */
193
+ }
194
+ }
195
+ this.setState("recording");
196
+ }
197
+ /** Pause recording (resumable via `resume()`). */
198
+ async pause() {
199
+ if (this.state !== "recording" || !this.recording) {
200
+ throw new RecorderError("InvalidState", `Cannot pause while "${this.state}".`);
201
+ }
202
+ if (typeof this.recording.pauseAsync !== "function") {
203
+ throw new RecorderError("NotSupported", "This Expo SDK doesn't support pause().");
204
+ }
205
+ await this.recording.pauseAsync();
206
+ this.setState("paused");
207
+ }
208
+ /** Resume after `pause()`. */
209
+ async resume() {
210
+ if (this.state !== "paused" || !this.recording) {
211
+ throw new RecorderError("InvalidState", `Cannot resume while "${this.state}".`);
212
+ }
213
+ if (typeof this.recording.startAsync !== "function") {
214
+ throw new RecorderError("NotSupported", "This Expo SDK doesn't support resume().");
215
+ }
216
+ await this.recording.startAsync();
217
+ this.setState("recording");
218
+ }
219
+ /**
220
+ * Stop recording and return the file shape `ohm.audio.extract` expects.
221
+ */
222
+ async stop() {
223
+ if (!this.recording || (this.state !== "recording" && this.state !== "paused")) {
224
+ throw new RecorderError("InvalidState", `Cannot stop while "${this.state}".`);
225
+ }
226
+ this.setState("stopping");
227
+ try {
228
+ await this.recording.stopAndUnloadAsync();
229
+ }
230
+ catch (e) {
231
+ const err = new RecorderError("Unknown", "Failed to stop recording", e);
232
+ this.opts.onError?.(err);
233
+ this.cleanup();
234
+ throw err;
235
+ }
236
+ const uri = this.recording.getURI();
237
+ this.cleanup();
238
+ if (!uri) {
239
+ const err = new RecorderError("Unknown", "Recording produced no file URI");
240
+ this.opts.onError?.(err);
241
+ throw err;
242
+ }
243
+ return {
244
+ uri,
245
+ name: this.opts.fileName ?? "rec.m4a",
246
+ type: this.opts.mimeType ?? "audio/mp4",
247
+ };
248
+ }
249
+ /** Convenience — stop after `ms` from now. */
250
+ stopAfter(ms) {
251
+ return new Promise((resolve, reject) => {
252
+ setTimeout(() => {
253
+ this.stop().then(resolve, reject);
254
+ }, ms);
255
+ });
256
+ }
257
+ /** Discard the in-progress recording without producing a file. */
258
+ async cancel() {
259
+ if (this.recording) {
260
+ try {
261
+ await this.recording.stopAndUnloadAsync();
262
+ }
263
+ catch {
264
+ /* ignore */
265
+ }
266
+ }
267
+ this.cleanup();
268
+ }
269
+ // ─── internals ──────────────────────────────────────────────────────
270
+ handleStatusUpdate(status) {
271
+ if (!status)
272
+ return;
273
+ if (typeof status.durationMillis === "number") {
274
+ this.durationMs = status.durationMillis;
275
+ }
276
+ let linear = 0;
277
+ if (typeof status.metering === "number") {
278
+ linear = dbToLinear(status.metering);
279
+ this.opts.onLevel?.(linear);
280
+ }
281
+ this.opts.onStatus?.({
282
+ durationMillis: this.durationMs,
283
+ metering: status.metering,
284
+ isRecording: !!status.isRecording,
285
+ isDoneRecording: !!status.isDoneRecording,
286
+ });
287
+ // Silence auto-stop
288
+ if (this.opts.silenceAutoStop &&
289
+ this.state === "recording" &&
290
+ typeof status.metering === "number") {
291
+ const silenceMs = this.opts.silenceAutoStop.ms ?? 6000;
292
+ const thresholdDb = this.opts.silenceAutoStop.thresholdDb ?? -40;
293
+ if (status.metering < thresholdDb) {
294
+ if (this.silenceSinceMs === 0)
295
+ this.silenceSinceMs = Date.now();
296
+ else if (Date.now() - this.silenceSinceMs >= silenceMs) {
297
+ this.silenceSinceMs = 0;
298
+ this.stop().catch(() => {
299
+ /* swallow */
300
+ });
301
+ }
302
+ }
303
+ else {
304
+ this.silenceSinceMs = 0;
305
+ }
306
+ }
307
+ }
308
+ cleanup() {
309
+ if (this.maxStopTimer) {
310
+ clearTimeout(this.maxStopTimer);
311
+ this.maxStopTimer = null;
312
+ }
313
+ if (this.keepAwakeActive && this.opts.keepAwake) {
314
+ try {
315
+ this.opts.keepAwake.deactivate();
316
+ }
317
+ catch {
318
+ /* ignore */
319
+ }
320
+ this.keepAwakeActive = false;
321
+ }
322
+ this.recording = null;
323
+ this.durationMs = 0;
324
+ this.silenceSinceMs = 0;
325
+ this.setState("idle");
326
+ }
327
+ setState(next) {
328
+ if (this.state === next)
329
+ return;
330
+ this.state = next;
331
+ this.opts.onStateChange?.(next);
332
+ }
333
+ }
334
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohm_studio/sdk-react-native",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "OHM Studio SDK for React Native. Voice-to-structured-JSON clinical extraction APIs in your mobile app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,10 +18,7 @@
18
18
  "import": "./dist/react/index.js"
19
19
  }
20
20
  },
21
- "files": [
22
- "dist",
23
- "README.md"
24
- ],
21
+ "files": ["dist", "README.md"],
25
22
  "keywords": [
26
23
  "ohm",
27
24
  "react-native",
@@ -41,28 +38,24 @@
41
38
  "publishConfig": {
42
39
  "access": "public"
43
40
  },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "typecheck": "tsc -p tsconfig.json --noEmit"
44
+ },
44
45
  "dependencies": {
45
- "@ohm_studio/sdk-core": "0.1.0"
46
+ "@ohm_studio/sdk-core": "workspace:*"
46
47
  },
47
48
  "peerDependencies": {
48
49
  "react": ">=17",
49
50
  "react-native": ">=0.71"
50
51
  },
51
52
  "peerDependenciesMeta": {
52
- "react": {
53
- "optional": true
54
- },
55
- "react-native": {
56
- "optional": true
57
- }
53
+ "react": { "optional": true },
54
+ "react-native": { "optional": true }
58
55
  },
59
56
  "devDependencies": {
60
57
  "@types/react": "^18.3.23",
61
58
  "react": "^18.3.1",
62
59
  "typescript": "^5.8.3"
63
- },
64
- "scripts": {
65
- "build": "tsc -p tsconfig.json",
66
- "typecheck": "tsc -p tsconfig.json --noEmit"
67
60
  }
68
- }
61
+ }