med-scribe-alliance-ts-sdk 2.0.29 → 2.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -456,7 +456,8 @@ export interface ProcessTemplateResponse {
456
456
  message: string;
457
457
  }
458
458
  export interface RecordingOptions {
459
- templates: string[];
459
+ templates: string[] | [
460
+ ];
460
461
  model?: string;
461
462
  languageHint?: string[];
462
463
  transcriptLanguage?: string;
@@ -1041,6 +1042,7 @@ export declare class RecordingManager {
1041
1042
  private activeBaseUrl;
1042
1043
  private _isRecording;
1043
1044
  private _isStarting;
1045
+ private _startGeneration;
1044
1046
  private retryContext;
1045
1047
  constructor(callbackRegistry: CallbackRegistry, sessionManager: SessionManager, discoveryManager: DiscoveryManager, transport: ITransport, config?: RecordingManagerConfig);
1046
1048
  /**
@@ -1051,6 +1053,11 @@ export declare class RecordingManager {
1051
1053
  * 4. Start recording
1052
1054
  * 5. Dispatch events
1053
1055
  *
1056
+ * Race-safety: Uses a generation counter (`_startGeneration`) so that if
1057
+ * clearRecordingState() or reset() is called while this method is suspended
1058
+ * at an `await`, the resumed call detects the mismatch and aborts instead
1059
+ * of creating an orphaned recorder with a leaked mic/VAD.
1060
+ *
1054
1061
  * @param baseUrl - Server base URL (from discovery or SDK config)
1055
1062
  * @param options - Recording options (templates, model, etc.)
1056
1063
  * @param accessToken - Current Bearer token for upload auth headers
package/dist/index.mjs CHANGED
@@ -957,7 +957,7 @@ var oe = {
957
957
  } catch (e) {
958
958
  console.error("[ScribeSDK] Error destroying VAD:", e);
959
959
  }
960
- this.isRecording = !1;
960
+ this.micVad = null, this.isRecording = !1;
961
961
  }
962
962
  reset() {
963
963
  this.destroy(), this.vadPast = [], this.lastClipIndex = 0, this.silDurationAcc = 0, this.noSpeechStartTime = null, this.lastWarningTime = null, this.isLoading = !0, this.micVad = null;
@@ -1605,15 +1605,17 @@ var Y = class {
1605
1605
  }
1606
1606
  }, Q = class {
1607
1607
  constructor(e, t, n, r, i) {
1608
- this.recorder = null, this.activeSession = null, this.activeBaseUrl = "", this._isRecording = !1, this._isStarting = !1, this.retryContext = null, this.callbackRegistry = e, this.sessionManager = t, this.discoveryManager = n, this.transport = r, this.config = i ?? {};
1608
+ this.recorder = null, this.activeSession = null, this.activeBaseUrl = "", this._isRecording = !1, this._isStarting = !1, this._startGeneration = 0, this.retryContext = null, this.callbackRegistry = e, this.sessionManager = t, this.discoveryManager = n, this.transport = r, this.config = i ?? {};
1609
1609
  }
1610
1610
  async start(e, t, n) {
1611
1611
  if (this._isRecording || this._isStarting) throw new y("Recording is already in progress. Stop the current recording first.");
1612
- this._isStarting = !0, this.retryContext = null, this.activeBaseUrl = e;
1613
- let r = t.uploadType ?? "chunked", i = t.communicationProtocol ?? "http", a = {
1612
+ this._isStarting = !0;
1613
+ let r = ++this._startGeneration;
1614
+ this.retryContext = null, this.activeBaseUrl = e;
1615
+ let i = t.uploadType ?? "chunked", a = t.communicationProtocol ?? "http", o = {
1614
1616
  templates: t.templates,
1615
- upload_type: r,
1616
- communication_protocol: i,
1617
+ upload_type: i,
1618
+ communication_protocol: a,
1617
1619
  model: t.model,
1618
1620
  language_hint: t.languageHint,
1619
1621
  transcript_language: t.transcriptLanguage,
@@ -1623,69 +1625,99 @@ var Y = class {
1623
1625
  session_id: t.sessionId
1624
1626
  };
1625
1627
  try {
1626
- let i, o;
1628
+ let a, s;
1627
1629
  try {
1628
- let t = await this.sessionManager.createSession(e, a);
1629
- i = t.data, o = t.httpStatus;
1630
+ let t = await this.sessionManager.createSession(e, o);
1631
+ a = t.data, s = t.httpStatus;
1630
1632
  } catch (e) {
1631
- throw this.dispatchStartError(m.TRANSPORT_ERROR, g.SESSION_CREATION_FAILED, e), e;
1633
+ throw r === this._startGeneration && this.dispatchStartError(m.TRANSPORT_ERROR, g.SESSION_CREATION_FAILED, e), e;
1632
1634
  }
1633
- this.activeSession = i, this.callbackRegistry.dispatch("onSessionEvent", {
1635
+ if (r !== this._startGeneration) throw new y("Recording start was superseded by a concurrent operation.");
1636
+ this.activeSession = a, this.callbackRegistry.dispatch("onSessionEvent", {
1634
1637
  type: p.CREATED,
1635
1638
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1636
- data: i
1637
- }), this.recorder = this.createRecorder(r);
1638
- let s = {
1639
+ data: a
1640
+ });
1641
+ let c = this.createRecorder(i);
1642
+ this.recorder = c;
1643
+ let l = {
1639
1644
  accessToken: n,
1640
- uploadUrl: i.upload_url,
1645
+ uploadUrl: a.upload_url,
1641
1646
  uploadHeaders: this.buildUploadHeaders(n),
1642
- sessionId: i.session_id
1647
+ sessionId: a.session_id
1643
1648
  };
1644
1649
  try {
1645
- this.recorder.initialize(i, s);
1650
+ c.initialize(a, l);
1646
1651
  } catch (e) {
1647
- throw this.cleanupRecordingState(), this.dispatchStartError(m.VALIDATION_ERROR, g.RECORDER_INIT_FAILED, e), e;
1652
+ try {
1653
+ c.reset();
1654
+ } catch {}
1655
+ throw r === this._startGeneration && (this.cleanupRecordingState(), this.dispatchStartError(m.VALIDATION_ERROR, g.RECORDER_INIT_FAILED, e)), e;
1648
1656
  }
1649
- this.recorder instanceof X && this.applyDiscoveryOverrides(this.recorder);
1657
+ c instanceof X && this.applyDiscoveryOverrides(c);
1650
1658
  try {
1651
- await this.recorder.start(t.deviceId);
1659
+ await c.start(t.deviceId);
1652
1660
  } catch (e) {
1653
- throw this.cleanupRecordingState(), this.dispatchStartError(m.VAD_ERROR, g.VAD_START_FAILED, e), e;
1661
+ try {
1662
+ c.reset();
1663
+ } catch {}
1664
+ throw r === this._startGeneration && (this.cleanupRecordingState(), this.dispatchStartError(m.VAD_ERROR, g.VAD_START_FAILED, e)), e;
1665
+ }
1666
+ if (r !== this._startGeneration) {
1667
+ try {
1668
+ c.reset();
1669
+ } catch {}
1670
+ throw new y("Recording start was superseded by a concurrent operation.");
1654
1671
  }
1655
1672
  return this._isRecording = !0, this.callbackRegistry.dispatch("onRecordingStateChange", {
1656
1673
  type: u.STARTED,
1657
1674
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1658
- }), this.config.debug && console.log("[ScribeSDK] Recording started:", i.session_id), {
1659
- data: i,
1660
- httpStatus: o
1675
+ }), this.config.debug && console.log("[ScribeSDK] Recording started:", a.session_id), {
1676
+ data: a,
1677
+ httpStatus: s
1661
1678
  };
1662
1679
  } finally {
1663
- this._isStarting = !1;
1680
+ r === this._startGeneration && (this._isStarting = !1);
1664
1681
  }
1665
1682
  }
1666
1683
  async startWithExistingSession(e, t, n, r) {
1667
1684
  if (this._isRecording || this._isStarting) throw new y("Recording is already in progress. Stop the current recording first.");
1668
- this._isStarting = !0, this.retryContext = null, this.activeBaseUrl = e;
1669
- let i = n?.uploadType ?? "chunked";
1685
+ this._isStarting = !0;
1686
+ let i = ++this._startGeneration;
1687
+ this.retryContext = null, this.activeBaseUrl = e;
1688
+ let a = n?.uploadType ?? "chunked";
1670
1689
  this.activeSession = t;
1671
1690
  try {
1672
- this.recorder = this.createRecorder(i);
1673
- let e = {
1691
+ let e = this.createRecorder(a);
1692
+ this.recorder = e;
1693
+ let o = {
1674
1694
  accessToken: r,
1675
1695
  uploadUrl: t.upload_url,
1676
1696
  uploadHeaders: this.buildUploadHeaders(r),
1677
1697
  sessionId: t.session_id
1678
1698
  };
1679
1699
  try {
1680
- this.recorder.initialize(t, e);
1681
- } catch (e) {
1682
- throw this.cleanupRecordingState(), this.dispatchStartError(m.VALIDATION_ERROR, g.RECORDER_INIT_FAILED, e), e;
1700
+ e.initialize(t, o);
1701
+ } catch (t) {
1702
+ try {
1703
+ e.reset();
1704
+ } catch {}
1705
+ throw i === this._startGeneration && (this.cleanupRecordingState(), this.dispatchStartError(m.VALIDATION_ERROR, g.RECORDER_INIT_FAILED, t)), t;
1683
1706
  }
1684
- this.recorder instanceof X && this.applyDiscoveryOverrides(this.recorder);
1707
+ e instanceof X && this.applyDiscoveryOverrides(e);
1685
1708
  try {
1686
- await this.recorder.start(n?.deviceId);
1687
- } catch (e) {
1688
- throw this.cleanupRecordingState(), this.dispatchStartError(m.VAD_ERROR, g.VAD_START_FAILED, e), e;
1709
+ await e.start(n?.deviceId);
1710
+ } catch (t) {
1711
+ try {
1712
+ e.reset();
1713
+ } catch {}
1714
+ throw i === this._startGeneration && (this.cleanupRecordingState(), this.dispatchStartError(m.VAD_ERROR, g.VAD_START_FAILED, t)), t;
1715
+ }
1716
+ if (i !== this._startGeneration) {
1717
+ try {
1718
+ e.reset();
1719
+ } catch {}
1720
+ throw new y("Recording start was superseded by a concurrent operation.");
1689
1721
  }
1690
1722
  return this._isRecording = !0, this.callbackRegistry.dispatch("onRecordingStateChange", {
1691
1723
  type: u.STARTED,
@@ -1695,7 +1727,7 @@ var Y = class {
1695
1727
  httpStatus: void 0
1696
1728
  };
1697
1729
  } finally {
1698
- this._isStarting = !1;
1730
+ i === this._startGeneration && (this._isStarting = !1);
1699
1731
  }
1700
1732
  }
1701
1733
  pause() {
@@ -1801,9 +1833,11 @@ var Y = class {
1801
1833
  }
1802
1834
  }
1803
1835
  forceStop() {
1804
- if (!(!this.recorder || !this._isRecording)) try {
1836
+ if (++this._startGeneration, !(!this.recorder || !this._isRecording)) try {
1805
1837
  this.recorder.reset();
1806
- } catch {} finally {
1838
+ } catch (e) {
1839
+ console.error("[ScribeSDK] Error in forceStop:", e);
1840
+ } finally {
1807
1841
  this.callbackRegistry.dispatch("onRecordingStateChange", {
1808
1842
  type: u.ENDED,
1809
1843
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1821,7 +1855,7 @@ var Y = class {
1821
1855
  this.recorder && this.recorder instanceof X && this.recorder.updateAuthToken(e), this.transport.setAuthToken(e);
1822
1856
  }
1823
1857
  reset() {
1824
- this.recorder && this.recorder.reset(), this.retryContext = null, this.cleanupRecordingState();
1858
+ ++this._startGeneration, this.recorder && this.recorder.reset(), this.retryContext = null, this.cleanupRecordingState();
1825
1859
  }
1826
1860
  isRecording() {
1827
1861
  return this._isRecording;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "med-scribe-alliance-ts-sdk",
3
- "version": "2.0.29",
3
+ "version": "2.0.31",
4
4
  "description": "TypeScript SDK for the MedScribe Alliance Protocol",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",