@ohm_studio/sdk-react-native 0.2.0 → 0.5.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.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Bare React Native recorder — adapter around `react-native-audio-recorder-player`.
3
+ *
4
+ * Mirrors `ExpoRecorder`'s lifecycle (state machine, typed errors, level
5
+ * metering, max-duration cap) so apps can switch between Expo and bare
6
+ * RN without touching their UI code. Also mirrors the same `RNFile`
7
+ * shape so `ohm.audio.extract({ file })` works identically.
8
+ *
9
+ * import AudioRecorderPlayer from "react-native-audio-recorder-player";
10
+ * import { BareRecorder } from "@ohm_studio/sdk-react-native";
11
+ *
12
+ * const rec = new BareRecorder(AudioRecorderPlayer);
13
+ * await rec.start();
14
+ * const file = await rec.stop();
15
+ * const { data } = await ohm.audio.extract({ apiSlug, file });
16
+ *
17
+ * We don't take a hard dependency on the underlying lib — pass either
18
+ * the class itself OR an existing instance.
19
+ */
20
+ import { RecorderError, type RecorderState, type RNFile } from "./recorder";
21
+ interface ARPInstance {
22
+ startRecorder: (uri?: string, audioSet?: any, meteringEnabled?: boolean) => Promise<string>;
23
+ stopRecorder: () => Promise<string>;
24
+ pauseRecorder?: () => Promise<unknown>;
25
+ resumeRecorder?: () => Promise<unknown>;
26
+ addRecordBackListener?: (cb: (e: any) => void) => void;
27
+ removeRecordBackListener?: () => void;
28
+ setSubscriptionDuration?: (sec: number) => Promise<unknown>;
29
+ }
30
+ type ARPCtor = new () => ARPInstance;
31
+ export interface BareRecorderOptions {
32
+ /** File URI to record into. If omitted, the lib picks a default. */
33
+ uri?: string;
34
+ /**
35
+ * Native `audioSet` passed to `startRecorder`. If omitted, a clinical
36
+ * preset is built (16 kHz mono AAC at 32 kbps).
37
+ */
38
+ audioSet?: any;
39
+ /** Mime type tag attached to the file shape — default `audio/mp4`. */
40
+ mimeType?: string;
41
+ /** Filename tag attached to the file shape — default `rec.m4a`. */
42
+ fileName?: string;
43
+ /** Hard cap — auto-stop after this many ms. Default off. */
44
+ maxDurationMs?: number;
45
+ /**
46
+ * Auto-stop after `ms` of sustained metering below `thresholdDb`.
47
+ * dBFS in the same scale as Expo's metering.
48
+ */
49
+ silenceAutoStop?: {
50
+ ms?: number;
51
+ thresholdDb?: number;
52
+ };
53
+ /** Periodic linear level 0–1, derived from the lib's metering callback. */
54
+ onLevel?: (linearLevel: number) => void;
55
+ /** Recorder state changes. */
56
+ onStateChange?: (state: RecorderState) => void;
57
+ /** All errors flow through here in addition to the awaited rejection. */
58
+ onError?: (err: RecorderError) => void;
59
+ /** Per-tick status from the recorder back-listener. */
60
+ onStatus?: (s: {
61
+ durationMillis: number;
62
+ metering?: number;
63
+ }) => void;
64
+ }
65
+ export declare class BareRecorder {
66
+ private arp;
67
+ private opts;
68
+ private state;
69
+ private uri;
70
+ private startedAt;
71
+ private pausedAccumMs;
72
+ private pauseStartedAt;
73
+ private durationMs;
74
+ private silenceSinceMs;
75
+ private maxStopTimer;
76
+ /**
77
+ * @param libOrInstance Pass `AudioRecorderPlayer` (the class) or an
78
+ * existing `new AudioRecorderPlayer()` instance. We use one
79
+ * instance per recorder.
80
+ */
81
+ constructor(libOrInstance: ARPCtor | ARPInstance, opts?: BareRecorderOptions);
82
+ get currentState(): RecorderState;
83
+ getDuration(): number;
84
+ start(): Promise<void>;
85
+ pause(): Promise<void>;
86
+ resume(): Promise<void>;
87
+ stop(): Promise<RNFile>;
88
+ cancel(): Promise<void>;
89
+ private onTick;
90
+ private cleanupListeners;
91
+ private cleanup;
92
+ private setState;
93
+ private fail;
94
+ }
95
+ export {};
96
+ //# sourceMappingURL=bare-recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bare-recorder.d.ts","sourceRoot":"","sources":["../src/bare-recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,aAAa,EACb,KAAK,aAAa,EAElB,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AAEpB,UAAU,WAAW;IACnB,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5F,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,qBAAqB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,CAAC;IACvD,wBAAwB,CAAC,EAAE,MAAM,IAAI,CAAC;IACtC,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AAED,KAAK,OAAO,GAAG,UAAU,WAAW,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC;IAEf,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAExD,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,8BAA8B;IAC9B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACvC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACvE;AAwBD,qBAAa,YAAY;IACvB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAA8C;IAElE;;;;OAIG;gBAED,aAAa,EAAE,OAAO,GAAG,WAAW,EACpC,IAAI,GAAE,mBAAwB;IAShC,IAAI,YAAY,IAAI,aAAa,CAEhC;IAED,WAAW,IAAI,MAAM;IAQf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAetB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAevB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B,OAAO,CAAC,MAAM;IA8Bd,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,IAAI;CASb"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Bare React Native recorder — adapter around `react-native-audio-recorder-player`.
3
+ *
4
+ * Mirrors `ExpoRecorder`'s lifecycle (state machine, typed errors, level
5
+ * metering, max-duration cap) so apps can switch between Expo and bare
6
+ * RN without touching their UI code. Also mirrors the same `RNFile`
7
+ * shape so `ohm.audio.extract({ file })` works identically.
8
+ *
9
+ * import AudioRecorderPlayer from "react-native-audio-recorder-player";
10
+ * import { BareRecorder } from "@ohm_studio/sdk-react-native";
11
+ *
12
+ * const rec = new BareRecorder(AudioRecorderPlayer);
13
+ * await rec.start();
14
+ * const file = await rec.stop();
15
+ * const { data } = await ohm.audio.extract({ apiSlug, file });
16
+ *
17
+ * We don't take a hard dependency on the underlying lib — pass either
18
+ * the class itself OR an existing instance.
19
+ */
20
+ import { RecorderError, } from "./recorder";
21
+ const CLINICAL_AUDIO_SET = {
22
+ // iOS
23
+ AVSampleRateKeyIOS: 16_000,
24
+ AVNumberOfChannelsKeyIOS: 1,
25
+ AVEncoderAudioQualityKeyIOS: "medium",
26
+ AVFormatIDKeyIOS: "aac",
27
+ // Android
28
+ AudioEncoderAndroid: 3, // AAC
29
+ AudioSourceAndroid: 6, // VOICE_RECOGNITION — boosts speech, attenuates noise
30
+ OutputFormatAndroid: 2, // MPEG_4
31
+ AudioSamplingRateAndroid: 16_000,
32
+ AudioChannelsAndroid: 1,
33
+ AudioEncodingBitRateAndroid: 32_000,
34
+ };
35
+ function dbToLinear(db, floorDb = -50) {
36
+ if (!Number.isFinite(db))
37
+ return 0;
38
+ if (db <= floorDb)
39
+ return 0;
40
+ if (db >= 0)
41
+ return 1;
42
+ return (db - floorDb) / -floorDb;
43
+ }
44
+ export class BareRecorder {
45
+ arp;
46
+ opts;
47
+ state = "idle";
48
+ uri = null;
49
+ startedAt = 0;
50
+ pausedAccumMs = 0;
51
+ pauseStartedAt = 0;
52
+ durationMs = 0;
53
+ silenceSinceMs = 0;
54
+ maxStopTimer = null;
55
+ /**
56
+ * @param libOrInstance Pass `AudioRecorderPlayer` (the class) or an
57
+ * existing `new AudioRecorderPlayer()` instance. We use one
58
+ * instance per recorder.
59
+ */
60
+ constructor(libOrInstance, opts = {}) {
61
+ this.arp =
62
+ typeof libOrInstance === "function"
63
+ ? new libOrInstance()
64
+ : libOrInstance;
65
+ this.opts = opts;
66
+ }
67
+ get currentState() {
68
+ return this.state;
69
+ }
70
+ getDuration() {
71
+ if (this.state === "idle")
72
+ return 0;
73
+ if (this.state === "paused") {
74
+ return this.pauseStartedAt - this.startedAt - this.pausedAccumMs;
75
+ }
76
+ return Date.now() - this.startedAt - this.pausedAccumMs;
77
+ }
78
+ async start() {
79
+ if (this.state !== "idle") {
80
+ throw this.fail("InvalidState", `Cannot start while "${this.state}".`);
81
+ }
82
+ this.setState("starting");
83
+ if (typeof this.arp.setSubscriptionDuration === "function") {
84
+ try {
85
+ await this.arp.setSubscriptionDuration(0.1); // 100 ms metering ticks
86
+ }
87
+ catch {
88
+ /* best-effort */
89
+ }
90
+ }
91
+ if (typeof this.arp.addRecordBackListener === "function") {
92
+ this.arp.addRecordBackListener((e) => this.onTick(e));
93
+ }
94
+ try {
95
+ this.uri = await this.arp.startRecorder(this.opts.uri, this.opts.audioSet ?? CLINICAL_AUDIO_SET, true);
96
+ }
97
+ catch (e) {
98
+ this.cleanupListeners();
99
+ this.setState("idle");
100
+ throw this.fail("Unknown", "Could not start recording: " + (e?.message || ""), e);
101
+ }
102
+ this.startedAt = Date.now();
103
+ this.pausedAccumMs = 0;
104
+ this.silenceSinceMs = 0;
105
+ this.durationMs = 0;
106
+ this.setState("recording");
107
+ if (this.opts.maxDurationMs && this.opts.maxDurationMs > 0) {
108
+ this.maxStopTimer = setTimeout(() => {
109
+ this.stop().catch(() => {
110
+ /* swallow */
111
+ });
112
+ }, this.opts.maxDurationMs);
113
+ }
114
+ }
115
+ async pause() {
116
+ if (this.state !== "recording") {
117
+ throw this.fail("InvalidState", `Cannot pause while "${this.state}".`);
118
+ }
119
+ if (typeof this.arp.pauseRecorder !== "function") {
120
+ throw this.fail("NotSupported", "This native module doesn't support pause().");
121
+ }
122
+ await this.arp.pauseRecorder();
123
+ this.pauseStartedAt = Date.now();
124
+ this.setState("paused");
125
+ }
126
+ async resume() {
127
+ if (this.state !== "paused") {
128
+ throw this.fail("InvalidState", `Cannot resume while "${this.state}".`);
129
+ }
130
+ if (typeof this.arp.resumeRecorder !== "function") {
131
+ throw this.fail("NotSupported", "This native module doesn't support resume().");
132
+ }
133
+ this.pausedAccumMs += Date.now() - this.pauseStartedAt;
134
+ await this.arp.resumeRecorder();
135
+ this.setState("recording");
136
+ }
137
+ async stop() {
138
+ if (this.state !== "recording" && this.state !== "paused") {
139
+ throw this.fail("InvalidState", `Cannot stop while "${this.state}".`);
140
+ }
141
+ this.setState("stopping");
142
+ let finalUri;
143
+ try {
144
+ finalUri = await this.arp.stopRecorder();
145
+ }
146
+ catch (e) {
147
+ this.cleanup();
148
+ throw this.fail("Unknown", "Could not stop recording: " + (e?.message || ""), e);
149
+ }
150
+ const uri = finalUri || this.uri || "";
151
+ this.cleanup();
152
+ if (!uri) {
153
+ throw this.fail("Unknown", "Recording produced no file URI");
154
+ }
155
+ return {
156
+ uri,
157
+ name: this.opts.fileName ?? "rec.m4a",
158
+ type: this.opts.mimeType ?? "audio/mp4",
159
+ };
160
+ }
161
+ async cancel() {
162
+ if (this.state === "recording" || this.state === "paused") {
163
+ try {
164
+ await this.arp.stopRecorder();
165
+ }
166
+ catch {
167
+ /* ignore */
168
+ }
169
+ }
170
+ this.cleanup();
171
+ }
172
+ // ─── internals ─────────────────────────────────────────────────────
173
+ onTick(e) {
174
+ if (!e)
175
+ return;
176
+ const dur = Number(e.currentPosition ?? e.current_position ?? 0);
177
+ if (Number.isFinite(dur))
178
+ this.durationMs = dur;
179
+ const meter = Number(e.currentMetering ?? e.current_metering ?? NaN);
180
+ if (Number.isFinite(meter)) {
181
+ const linear = dbToLinear(meter);
182
+ this.opts.onLevel?.(linear);
183
+ if (this.opts.silenceAutoStop && this.state === "recording") {
184
+ const silenceMs = this.opts.silenceAutoStop.ms ?? 6000;
185
+ const thresholdDb = this.opts.silenceAutoStop.thresholdDb ?? -40;
186
+ if (meter < thresholdDb) {
187
+ if (this.silenceSinceMs === 0)
188
+ this.silenceSinceMs = Date.now();
189
+ else if (Date.now() - this.silenceSinceMs >= silenceMs) {
190
+ this.silenceSinceMs = 0;
191
+ this.stop().catch(() => {
192
+ /* swallow */
193
+ });
194
+ }
195
+ }
196
+ else {
197
+ this.silenceSinceMs = 0;
198
+ }
199
+ }
200
+ }
201
+ this.opts.onStatus?.({
202
+ durationMillis: this.durationMs,
203
+ metering: Number.isFinite(meter) ? meter : undefined,
204
+ });
205
+ }
206
+ cleanupListeners() {
207
+ if (typeof this.arp.removeRecordBackListener === "function") {
208
+ try {
209
+ this.arp.removeRecordBackListener();
210
+ }
211
+ catch {
212
+ /* ignore */
213
+ }
214
+ }
215
+ }
216
+ cleanup() {
217
+ if (this.maxStopTimer) {
218
+ clearTimeout(this.maxStopTimer);
219
+ this.maxStopTimer = null;
220
+ }
221
+ this.cleanupListeners();
222
+ this.uri = null;
223
+ this.durationMs = 0;
224
+ this.silenceSinceMs = 0;
225
+ this.startedAt = 0;
226
+ this.pausedAccumMs = 0;
227
+ this.pauseStartedAt = 0;
228
+ this.setState("idle");
229
+ }
230
+ setState(next) {
231
+ if (this.state === next)
232
+ return;
233
+ this.state = next;
234
+ this.opts.onStateChange?.(next);
235
+ }
236
+ fail(code, message, cause) {
237
+ const err = new RecorderError(code, message, cause);
238
+ this.opts.onError?.(err);
239
+ return err;
240
+ }
241
+ }
242
+ //# sourceMappingURL=bare-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bare-recorder.js","sourceRoot":"","sources":["../src/bare-recorder.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
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, isExpoAvAvailable } from "./recorder";
5
- export type { ExpoRecorderOptions, RNFile } from "./recorder";
4
+ export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
5
+ export type { ExpoRecorderOptions, RNFile, RecorderState, RecorderErrorCode, } from "./recorder";
6
+ export { BareRecorder } from "./bare-recorder";
7
+ export type { BareRecorderOptions } from "./bare-recorder";
6
8
  /**
7
9
  * OHM Studio SDK for React Native.
8
10
  *
@@ -26,7 +28,7 @@ export type { ExpoRecorderOptions, RNFile } from "./recorder";
26
28
  * });
27
29
  */
28
30
  export declare class OHM extends OHMCoreClient {
29
- constructor(opts: OHMClientOptions);
31
+ constructor(init: OHMInit);
30
32
  protected runMultipart<T>(opts: {
31
33
  path: string;
32
34
  file: any;
@@ -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;AACrC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAY9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,gBAAgB;cAelB,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"}
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;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAY3D;;;;;;;;;;;;;;;;;;;;;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,6 +1,7 @@
1
1
  import { OHMConfigError, OHMCoreClient } from "@ohm_studio/sdk-core";
2
2
  export * from "@ohm_studio/sdk-core";
3
- export { ExpoRecorder, isExpoAvAvailable } from "./recorder";
3
+ export { ExpoRecorder, RecorderError, isExpoAvAvailable, } from "./recorder";
4
+ export { BareRecorder } from "./bare-recorder";
4
5
  /** Detect React Native runtime — used to gate the bundled-key safeguard. */
5
6
  function isReactNative() {
6
7
  return (
@@ -32,7 +33,8 @@ function isReactNative() {
32
33
  * });
33
34
  */
34
35
  export class OHM extends OHMCoreClient {
35
- constructor(opts) {
36
+ constructor(init) {
37
+ const opts = typeof init === "string" ? { apiKey: init } : init;
36
38
  if (!opts.mock &&
37
39
  isReactNative() &&
38
40
  opts.apiKey?.startsWith("ohms_live_") &&
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;AACrC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG7D,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,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
+ {"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;AAOpB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,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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"}
@@ -1,20 +1,26 @@
1
1
  /**
2
- * Lightweight Expo-AV adapter for OHM. We don't take a hard dependency on
3
- * `expo-av` (it'd force every consumer of the OHM RN SDK to install Expo
4
- * even if they're using bare RN with `react-native-audio-recorder-player`).
5
- * Instead we accept the Expo Audio API surface as a parameter at
6
- * runtime type-safely and provide the start/stop ergonomics customers
7
- * actually want.
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
8
  *
9
- * @example
10
9
  * import { Audio } from "expo-av";
11
10
  * import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
12
11
  *
13
- * const rec = new ExpoRecorder(Audio);
12
+ * const rec = new ExpoRecorder(Audio, {
13
+ * onLevel: setVu,
14
+ * silenceAutoStop: { ms: 6000 },
15
+ * maxDurationMs: 15 * 60_000,
16
+ * });
14
17
  * await rec.start();
15
- * const file = await rec.stop(); // { uri, name, type }
18
+ * const file = await rec.stop(); // { uri, name, type }
19
+ * const { transcript, data } = await ohm.audio.extract({ apiSlug, file });
16
20
  *
17
- * const { data } = await ohm.audio.extract({ apiSlug, file });
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.
18
24
  */
19
25
  /** The shape `ohm.audio.extract` expects on React Native. */
20
26
  export interface RNFile {
@@ -22,33 +28,111 @@ export interface RNFile {
22
28
  name: string;
23
29
  type: string;
24
30
  }
25
- /** Minimal subset of `expo-av`'s Audio API we depend on. */
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. */
26
39
  interface ExpoAudioModule {
27
40
  requestPermissionsAsync: () => Promise<{
28
41
  status: string;
29
42
  granted?: boolean;
30
43
  }>;
44
+ getPermissionsAsync?: () => Promise<{
45
+ status: string;
46
+ granted?: boolean;
47
+ }>;
48
+ setAudioModeAsync?: (mode: any) => Promise<void>;
31
49
  Recording: {
32
- createAsync: (options: any) => Promise<{
50
+ createAsync: (options: any, onStatusUpdate?: ((status: any) => void) | null, progressUpdateIntervalMillis?: number) => Promise<{
33
51
  recording: ExpoAudioRecording;
52
+ status: any;
34
53
  }>;
35
54
  };
36
- RecordingOptionsPresets: {
37
- HIGH_QUALITY: any;
55
+ RecordingOptionsPresets?: {
56
+ HIGH_QUALITY?: any;
38
57
  LOW_QUALITY?: any;
39
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
+ };
40
77
  }
41
78
  interface ExpoAudioRecording {
79
+ startAsync?: () => Promise<unknown>;
80
+ pauseAsync?: () => Promise<unknown>;
42
81
  stopAndUnloadAsync: () => Promise<unknown>;
43
82
  getURI: () => string | null;
83
+ setProgressUpdateInterval?: (ms: number) => void;
84
+ setOnRecordingStatusUpdate?: (cb: (s: any) => void) => void;
44
85
  }
45
86
  export interface ExpoRecorderOptions {
46
- /** One of expo-av's RecordingOptionsPresets, defaults to HIGH_QUALITY. */
47
- recordingPreset?: any;
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;
48
93
  /** Mime type tag attached to the file shape — default `audio/mp4`. */
49
94
  mimeType?: string;
50
95
  /** Filename tag attached to the file shape — default `rec.m4a`. */
51
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;
52
136
  }
53
137
  /** True if `expo-av` is installed in the host RN app. */
54
138
  export declare function isExpoAvAvailable(): boolean;
@@ -56,23 +140,35 @@ export declare class ExpoRecorder {
56
140
  private audio;
57
141
  private recording;
58
142
  private opts;
59
- /**
60
- * @param expoAudio - Pass the `Audio` named import from `expo-av`. We
61
- * take it as a parameter so the OHM SDK doesn't need a runtime
62
- * dependency on Expo.
63
- */
143
+ private state;
144
+ private durationMs;
145
+ private maxStopTimer;
146
+ private silenceSinceMs;
147
+ private keepAwakeActive;
64
148
  constructor(expoAudio: ExpoAudioModule, opts?: ExpoRecorderOptions);
65
- /**
66
- * Request mic permission and start recording. Throws if permission is
67
- * denied. Resolves once the recording has started.
68
- */
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. */
69
156
  start(): Promise<void>;
157
+ /** Pause recording (resumable via `resume()`). */
158
+ pause(): Promise<void>;
159
+ /** Resume after `pause()`. */
160
+ resume(): Promise<void>;
70
161
  /**
71
162
  * Stop recording and return the file shape `ohm.audio.extract` expects.
72
163
  */
73
164
  stop(): Promise<RNFile>;
165
+ /** Convenience — stop after `ms` from now. */
166
+ stopAfter(ms: number): Promise<RNFile>;
74
167
  /** Discard the in-progress recording without producing a file. */
75
168
  cancel(): Promise<void>;
169
+ private handleStatusUpdate;
170
+ private cleanup;
171
+ private setState;
76
172
  }
77
173
  export {};
78
174
  //# sourceMappingURL=recorder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4DAA4D;AAC5D,UAAU,eAAe;IACvB,uBAAuB,EAAE,MAAM,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9E,SAAS,EAAE;QACT,WAAW,EAAE,CACX,OAAO,EAAE,GAAG,KACT,OAAO,CAAC;YAAE,SAAS,EAAE,kBAAkB,CAAA;SAAE,CAAC,CAAC;KACjD,CAAC;IACF,uBAAuB,EAAE;QACvB,YAAY,EAAE,GAAG,CAAC;QAClB,WAAW,CAAC,EAAE,GAAG,CAAC;KACnB,CAAC;CACH;AAED,UAAU,kBAAkB;IAC1B,kBAAkB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,IAAI,OAAO,CAQ3C;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,IAAI,CAAsB;IAElC;;;;OAIG;gBACS,SAAS,EAAE,eAAe,EAAE,IAAI,GAAE,mBAAwB;IAKtE;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAa7B,kEAAkE;IAC5D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAU9B"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
package/dist/recorder.js CHANGED
@@ -1,21 +1,91 @@
1
1
  /**
2
- * Lightweight Expo-AV adapter for OHM. We don't take a hard dependency on
3
- * `expo-av` (it'd force every consumer of the OHM RN SDK to install Expo
4
- * even if they're using bare RN with `react-native-audio-recorder-player`).
5
- * Instead we accept the Expo Audio API surface as a parameter at
6
- * runtime type-safely and provide the start/stop ergonomics customers
7
- * actually want.
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
8
  *
9
- * @example
10
9
  * import { Audio } from "expo-av";
11
10
  * import { ExpoRecorder } from "@ohm_studio/sdk-react-native";
12
11
  *
13
- * const rec = new ExpoRecorder(Audio);
12
+ * const rec = new ExpoRecorder(Audio, {
13
+ * onLevel: setVu,
14
+ * silenceAutoStop: { ms: 6000 },
15
+ * maxDurationMs: 15 * 60_000,
16
+ * });
14
17
  * await rec.start();
15
- * const file = await rec.stop(); // { uri, name, type }
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.
16
38
  *
17
- * const { data } = await ohm.audio.extract({ apiSlug, file });
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.
18
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
+ }
19
89
  /** True if `expo-av` is installed in the host RN app. */
20
90
  export function isExpoAvAvailable() {
21
91
  try {
@@ -31,46 +101,159 @@ export class ExpoRecorder {
31
101
  audio;
32
102
  recording = null;
33
103
  opts;
34
- /**
35
- * @param expoAudio - Pass the `Audio` named import from `expo-av`. We
36
- * take it as a parameter so the OHM SDK doesn't need a runtime
37
- * dependency on Expo.
38
- */
104
+ state = "idle";
105
+ durationMs = 0;
106
+ maxStopTimer = null;
107
+ silenceSinceMs = 0;
108
+ keepAwakeActive = false;
39
109
  constructor(expoAudio, opts = {}) {
40
110
  this.audio = expoAudio;
41
111
  this.opts = opts;
42
112
  }
43
- /**
44
- * Request mic permission and start recording. Throws if permission is
45
- * denied. Resolves once the recording has started.
46
- */
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. */
47
129
  async start() {
48
- const perm = await this.audio.requestPermissionsAsync();
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
+ }
49
144
  if (perm.status !== "granted") {
50
- throw new Error("Microphone permission denied");
145
+ this.setState("idle");
146
+ const err = new RecorderError("PermissionDenied", "Microphone permission denied");
147
+ this.opts.onError?.(err);
148
+ throw err;
51
149
  }
52
- const preset = this.opts.recordingPreset ??
53
- this.audio.RecordingOptionsPresets.HIGH_QUALITY;
54
- const { recording } = await this.audio.Recording.createAsync(preset);
55
- this.recording = recording;
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");
56
218
  }
57
219
  /**
58
220
  * Stop recording and return the file shape `ohm.audio.extract` expects.
59
221
  */
60
222
  async stop() {
61
- if (!this.recording)
62
- throw new Error("ExpoRecorder is not recording");
63
- await this.recording.stopAndUnloadAsync();
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
+ }
64
236
  const uri = this.recording.getURI();
65
- this.recording = null;
66
- if (!uri)
67
- throw new Error("Recording produced no file URI");
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
+ }
68
243
  return {
69
244
  uri,
70
245
  name: this.opts.fileName ?? "rec.m4a",
71
246
  type: this.opts.mimeType ?? "audio/mp4",
72
247
  };
73
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
+ }
74
257
  /** Discard the in-progress recording without producing a file. */
75
258
  async cancel() {
76
259
  if (this.recording) {
@@ -80,8 +263,72 @@ export class ExpoRecorder {
80
263
  catch {
81
264
  /* ignore */
82
265
  }
83
- this.recording = null;
84
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);
85
332
  }
86
333
  }
87
334
  //# sourceMappingURL=recorder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAqCH,yDAAyD;AACzD,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,iEAAiE;QACjE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,OAAO,YAAY;IACf,KAAK,CAAkB;IACvB,SAAS,GAA8B,IAAI,CAAC;IAC5C,IAAI,CAAsB;IAElC;;;;OAIG;IACH,YAAY,SAA0B,EAAE,OAA4B,EAAE;QACpE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,uBAAuB,EAAE,CAAC;QACxD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,MAAM,GACV,IAAI,CAAC,IAAI,CAAC,eAAe;YACzB,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,YAAY,CAAC;QAClD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACtE,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC5D,OAAO;YACL,GAAG;YACH,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,SAAS;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW;SACxC,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAwBH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,IAAI,CAAoB;IACxB,KAAK,CAAW;IAChB,YAAY,IAAuB,EAAE,OAAe,EAAE,KAAe;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAiGD;;;;;GAKG;AACH,SAAS,wBAAwB,CAAC,KAAsB;IACtD,oEAAoE;IACpE,kEAAkE;IAClE,0CAA0C;IAC1C,MAAM,eAAe,GAClB,KAAK,CAAC,eAAe,EAAE,QAAgB,IAAI,MAAM,CAAC;IACrD,MAAM,eAAe,GAAG,KAAK,CAAC,eAAe,EAAE,MAAM,IAAI,IAAI,CAAC;IAC9D,MAAM,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC,CAAC;IACnE,MAAM,mBAAmB,GAAG,KAAK,CAAC,mBAAmB,EAAE,GAAG,IAAI,CAAC,CAAC;IAEhE,OAAO;QACL,iBAAiB,EAAE,IAAI;QACvB,OAAO,EAAE;YACP,SAAS,EAAE,MAAM;YACjB,YAAY,EAAE,mBAAmB;YACjC,YAAY,EAAE,mBAAmB;YACjC,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,CAAC;YACnB,OAAO,EAAE,MAAM;SAChB;QACD,GAAG,EAAE;YACH,SAAS,EAAE,MAAM;YACjB,YAAY,EAAE,eAAe;YAC7B,YAAY,EAAE,eAAe;YAC7B,UAAU,EAAE,MAAM;YAClB,gBAAgB,EAAE,CAAC;YACnB,OAAO,EAAE,MAAM;YACf,iBAAiB,EAAE,EAAE;YACrB,oBAAoB,EAAE,KAAK;YAC3B,gBAAgB,EAAE,KAAK;SACxB;QACD,GAAG,EAAE;YACH,QAAQ,EAAE,wBAAwB;YAClC,aAAa,EAAE,MAAM;SACtB;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,EAAU,EAAE,OAAO,GAAG,CAAC,EAAE;IAC3C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,EAAE,IAAI,OAAO;QAAE,OAAO,CAAC,CAAC;IAC5B,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACtB,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AACnC,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,iEAAiE;QACjE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,OAAO,YAAY;IACf,KAAK,CAAkB;IACvB,SAAS,GAA8B,IAAI,CAAC;IAC5C,IAAI,CAAsB;IAC1B,KAAK,GAAkB,MAAM,CAAC;IAC9B,UAAU,GAAG,CAAC,CAAC;IACf,YAAY,GAAyC,IAAI,CAAC;IAC1D,cAAc,GAAG,CAAC,CAAC;IACnB,eAAe,GAAG,KAAK,CAAC;IAEhC,YAAY,SAA0B,EAAE,OAA4B,EAAE;QACpE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB;YAAE,OAAO,cAAc,CAAC;QAC3D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC;QACjD,OAAQ,CAAC,EAAE,MAAc,IAAI,cAAc,CAAC;IAC9C,CAAC;IAED,qBAAqB;IACrB,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,sDAAsD;IACtD,WAAW;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,kDAAkD;IAClD,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,uBAAuB,IAAI,CAAC,KAAK,IAAI,CACtC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAE1B,IAAI,IAAI,CAAC;QACT,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,uBAAuB,EAAE,CAAC;QACpD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,GAAG,GAAG,IAAI,aAAa,CAC3B,SAAS,EACT,yCAAyC,EACzC,CAAC,CACF,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,GAAG,GAAG,IAAI,aAAa,CAC3B,kBAAkB,EAClB,8BAA8B,CAC/B,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,wBAAwB,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC;YACjF,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;oBACjC,kBAAkB,EAAE,IAAI;oBACxB,oBAAoB,EAAE,IAAI;oBAC1B,uBAAuB,EAAE,KAAK;oBAC9B,mBAAmB,EAChB,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,QAAgB,IAAI,CAAC;oBACxD,iBAAiB,EAAE,IAAI;oBACvB,0BAA0B,EAAE,KAAK;iBAClC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,iBAAiB;YACnB,CAAC;QACH,CAAC;QAED,MAAM,gBAAgB,GACpB,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErE,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAC7C,gBAAgB,EAChB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAC3C,GAAG,CACJ,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,GAAG,GAAG,IAAI,aAAa,CAC3B,SAAS,EACT,6BAA6B,GAAG,CAAE,CAAS,EAAE,OAAO,IAAI,EAAE,CAAC,EAC3D,CAAC,CACF,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QAExB,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;gBAClC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;oBACrB,aAAa;gBACf,CAAC,CAAC,CAAC;YACL,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,iBAAiB;YACnB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;IAED,kDAAkD;IAClD,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClD,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,uBAAuB,IAAI,CAAC,KAAK,IAAI,CACtC,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACpD,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,wCAAwC,CACzC,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,8BAA8B;IAC9B,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/C,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,wBAAwB,IAAI,CAAC,KAAK,IAAI,CACvC,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACpD,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,yCAAyC,CAC1C,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YAC/E,MAAM,IAAI,aAAa,CACrB,cAAc,EACd,sBAAsB,IAAI,CAAC,KAAK,IAAI,CACrC,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC;QAC5C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,IAAI,aAAa,CAC3B,SAAS,EACT,0BAA0B,EAC1B,CAAC,CACF,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,aAAa,CAC3B,SAAS,EACT,gCAAgC,CACjC,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO;YACL,GAAG;YACH,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,SAAS;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW;SACxC,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACpC,CAAC,EAAE,EAAE,CAAC,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC;YAC5C,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,uEAAuE;IAE/D,kBAAkB,CAAC,MAAW;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC;QAC1C,CAAC;QACD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,cAAc,EAAE,IAAI,CAAC,UAAU;YAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW;YACjC,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,eAAe;SAC1C,CAAC,CAAC;QACH,oBAAoB;QACpB,IACE,IAAI,CAAC,IAAI,CAAC,eAAe;YACzB,IAAI,CAAC,KAAK,KAAK,WAAW;YAC1B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EACnC,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,IAAI,IAAI,CAAC;YACvD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC;YACjE,IAAI,MAAM,CAAC,QAAQ,GAAG,WAAW,EAAE,CAAC;gBAClC,IAAI,IAAI,CAAC,cAAc,KAAK,CAAC;oBAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;qBAC3D,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,IAAI,SAAS,EAAE,CAAC;oBACvD,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;oBACxB,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;wBACrB,aAAa;oBACf,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAChD,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YACnC,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAEO,QAAQ,CAAC,IAAmB;QAClC,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohm_studio/sdk-react-native",
3
- "version": "0.2.0",
3
+ "version": "0.5.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",
@@ -39,8 +39,11 @@
39
39
  "access": "public"
40
40
  },
41
41
  "scripts": {
42
- "build": "tsc -p tsconfig.json",
43
- "typecheck": "tsc -p tsconfig.json --noEmit"
42
+ "clean": "rm -rf dist",
43
+ "build": "rm -rf dist && tsc -p tsconfig.json",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest"
44
47
  },
45
48
  "dependencies": {
46
49
  "@ohm_studio/sdk-core": "workspace:*"
@@ -56,6 +59,7 @@
56
59
  "devDependencies": {
57
60
  "@types/react": "^18.3.23",
58
61
  "react": "^18.3.1",
59
- "typescript": "^5.8.3"
62
+ "typescript": "^5.8.3",
63
+ "vitest": "^4.1.5"
60
64
  }
61
65
  }