@coventia/softphone 0.2.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.
Files changed (47) hide show
  1. package/dist/{call-DUVnDxsz.d.cts → call-CaPDWVk3.d.cts} +50 -1
  2. package/dist/{call-DUVnDxsz.d.ts → call-CaPDWVk3.d.ts} +50 -1
  3. package/dist/{chunk-YWI6EQYS.js → chunk-EYEDPD7Y.js} +108 -15
  4. package/dist/chunk-EYEDPD7Y.js.map +1 -0
  5. package/dist/{chunk-LMJXX4YM.js → chunk-UHNLEBBO.js} +4 -2
  6. package/dist/chunk-UHNLEBBO.js.map +1 -0
  7. package/dist/element/index.d.ts +76 -11
  8. package/dist/element/index.js +1411 -478
  9. package/dist/element/index.js.map +1 -1
  10. package/dist/{engine-EPCKf-Bl.d.ts → engine-BgBsIcEa.d.ts} +1 -1
  11. package/dist/{engine-CDjkrXHY.d.cts → engine-CU2rEo3g.d.cts} +1 -1
  12. package/dist/index.cjs +318 -32
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +5 -5
  15. package/dist/index.d.ts +5 -5
  16. package/dist/index.js +157 -19
  17. package/dist/index.js.map +1 -1
  18. package/dist/react/index.cjs +2 -0
  19. package/dist/react/index.cjs.map +1 -1
  20. package/dist/react/index.d.cts +2 -2
  21. package/dist/react/index.d.ts +2 -2
  22. package/dist/react/index.js +1 -1
  23. package/dist/{softphone-BtdgZWkm.d.ts → softphone-DEt0TtQw.d.ts} +26 -10
  24. package/dist/{softphone-DHG9MTNg.d.cts → softphone-dZMkW8V1.d.cts} +26 -10
  25. package/dist/softphone.css +192 -46
  26. package/dist/{sources-5L2R4HTV.js → sources-M3GYMS6V.js} +58 -3
  27. package/dist/sources-M3GYMS6V.js.map +1 -0
  28. package/dist/testing/index.d.cts +2 -2
  29. package/dist/testing/index.d.ts +2 -2
  30. package/dist/ui/index.cjs +1308 -366
  31. package/dist/ui/index.cjs.map +1 -1
  32. package/dist/ui/index.d.cts +40 -7
  33. package/dist/ui/index.d.ts +40 -7
  34. package/dist/ui/index.js +1308 -369
  35. package/dist/ui/index.js.map +1 -1
  36. package/dist/zadarma/index.cjs +106 -13
  37. package/dist/zadarma/index.cjs.map +1 -1
  38. package/dist/zadarma/index.d.cts +2 -2
  39. package/dist/zadarma/index.d.ts +2 -2
  40. package/dist/zadarma/index.js +1 -1
  41. package/dist/zadarma-engine-L5DEFXUS.js +5 -0
  42. package/dist/{zadarma-engine-4VOPIBXG.js.map → zadarma-engine-L5DEFXUS.js.map} +1 -1
  43. package/package.json +1 -1
  44. package/dist/chunk-LMJXX4YM.js.map +0 -1
  45. package/dist/chunk-YWI6EQYS.js.map +0 -1
  46. package/dist/sources-5L2R4HTV.js.map +0 -1
  47. package/dist/zadarma-engine-4VOPIBXG.js +0 -5
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { S as Softphone } from './softphone-DHG9MTNg.cjs';
2
- export { A as AudioDevices } from './softphone-DHG9MTNg.cjs';
3
- import { S as SoftphoneConfig } from './call-DUVnDxsz.cjs';
4
- export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as SoftphoneError, l as SoftphoneErrorCode, m as SoftphoneEvent, n as SoftphoneEventMap, o as SoftphoneState, T as TokenEndpointOptions, p as TokenSource, q as TypedEmitter, V as VoiceToken, r as canTransition, s as isFatal, t as isTerminal } from './call-DUVnDxsz.cjs';
1
+ import { S as Softphone } from './softphone-dZMkW8V1.cjs';
2
+ export { A as AudioDevices } from './softphone-dZMkW8V1.cjs';
3
+ import { S as SoftphoneConfig } from './call-CaPDWVk3.cjs';
4
+ export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as RingtoneOption, l as RingtoneSettings, m as RingtoneSource, n as SoftphoneError, o as SoftphoneErrorCode, p as SoftphoneEvent, q as SoftphoneEventMap, r as SoftphoneState, T as TokenEndpointOptions, s as TokenSource, t as TypedEmitter, V as VoiceToken, u as canTransition, v as isFatal, w as isTerminal } from './call-CaPDWVk3.cjs';
5
5
 
6
6
  /**
7
7
  * Construct a softphone.
@@ -57,6 +57,6 @@ declare function isWebRtcSupported(): boolean;
57
57
  * - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
58
58
  */
59
59
 
60
- declare const VERSION = "0.2.0";
60
+ declare const VERSION = "0.3.0";
61
61
 
62
62
  export { Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isWebRtcSupported };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { S as Softphone } from './softphone-BtdgZWkm.js';
2
- export { A as AudioDevices } from './softphone-BtdgZWkm.js';
3
- import { S as SoftphoneConfig } from './call-DUVnDxsz.js';
4
- export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as SoftphoneError, l as SoftphoneErrorCode, m as SoftphoneEvent, n as SoftphoneEventMap, o as SoftphoneState, T as TokenEndpointOptions, p as TokenSource, q as TypedEmitter, V as VoiceToken, r as canTransition, s as isFatal, t as isTerminal } from './call-DUVnDxsz.js';
1
+ import { S as Softphone } from './softphone-DEt0TtQw.js';
2
+ export { A as AudioDevices } from './softphone-DEt0TtQw.js';
3
+ import { S as SoftphoneConfig } from './call-CaPDWVk3.js';
4
+ export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as RingtoneOption, l as RingtoneSettings, m as RingtoneSource, n as SoftphoneError, o as SoftphoneErrorCode, p as SoftphoneEvent, q as SoftphoneEventMap, r as SoftphoneState, T as TokenEndpointOptions, s as TokenSource, t as TypedEmitter, V as VoiceToken, u as canTransition, v as isFatal, w as isTerminal } from './call-CaPDWVk3.js';
5
5
 
6
6
  /**
7
7
  * Construct a softphone.
@@ -57,6 +57,6 @@ declare function isWebRtcSupported(): boolean;
57
57
  * - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
58
58
  */
59
59
 
60
- declare const VERSION = "0.2.0";
60
+ declare const VERSION = "0.3.0";
61
61
 
62
62
  export { Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isWebRtcSupported };
package/dist/index.js CHANGED
@@ -75,6 +75,9 @@ function canTransition(from, to) {
75
75
  function isTerminal(state) {
76
76
  return CALL_TRANSITIONS[state].length === 0;
77
77
  }
78
+ function isOnCallState(state) {
79
+ return state !== "ringing" && !isTerminal(state);
80
+ }
78
81
 
79
82
  // src/core/call-impl.ts
80
83
  var CallImpl = class _CallImpl extends Emitter {
@@ -281,7 +284,7 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
281
284
  }
282
285
  let pending = null;
283
286
  const load = () => {
284
- pending ??= import('./sources-5L2R4HTV.js').then(
287
+ pending ??= import('./sources-M3GYMS6V.js').then(
285
288
  (m) => m.coventiaSources({ ...config, baseUrl, tokenSource })
286
289
  );
287
290
  return pending;
@@ -292,7 +295,12 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
292
295
  resolveContact: async (number) => (await load()).resolveContact(number),
293
296
  listAgents: async () => (await load()).listAgents(),
294
297
  transferOutcomes: async (params) => (await load()).transferOutcomes(params),
295
- presence: async (callbacks) => (await load()).presence(callbacks)
298
+ presence: async (callbacks) => (await load()).presence(callbacks),
299
+ ringtones: {
300
+ get: async (signal) => (await load()).ringtones.get(signal),
301
+ setMine: async (id) => (await load()).ringtones.setMine(id),
302
+ cached: async () => (await load()).ringtones.cached?.() ?? null
303
+ }
296
304
  };
297
305
  }
298
306
 
@@ -426,13 +434,18 @@ function startPresenceSocket(params) {
426
434
  let agents = /* @__PURE__ */ new Map();
427
435
  let lastReportedOnCall = null;
428
436
  let connection = null;
429
- const stop = () => {
437
+ let byeOnArrival = false;
438
+ const stop = (options) => {
430
439
  if (stopped) return;
431
440
  stopped = true;
432
441
  if (reconnectTimer !== null) {
433
442
  cancelTimeout(reconnectTimer);
434
443
  reconnectTimer = null;
435
444
  }
445
+ if (options?.bye) {
446
+ byeOnArrival = true;
447
+ connection?.bye?.();
448
+ }
436
449
  connection?.close();
437
450
  connection = null;
438
451
  };
@@ -471,6 +484,7 @@ function startPresenceSocket(params) {
471
484
  return;
472
485
  }
473
486
  if (stopped) {
487
+ if (byeOnArrival) opened.bye?.();
474
488
  opened.close();
475
489
  return;
476
490
  }
@@ -491,18 +505,36 @@ function startPresenceSocket(params) {
491
505
 
492
506
  // src/core/softphone-impl.ts
493
507
  var liveInstance = false;
508
+ var PRESENCE_TEARDOWN_GRACE_MS = 1e4;
494
509
  var BrowserAudioDevices = class {
495
510
  inputs = [];
496
511
  outputs = [];
497
512
  currentInputId = null;
498
513
  currentOutputId = null;
514
+ currentRingtoneOutputId = null;
499
515
  canSelectOutput;
516
+ canSelectRingtoneOutput;
500
517
  #engine;
501
518
  #onChange;
519
+ /**
520
+ * What the user CHOSE, remembered across reloads — distinct from the `current*` fields, which are
521
+ * what is usable right now. Keeping them apart is what stops a fresh page load from wiping a saved
522
+ * choice: until the microphone has been granted, Chrome enumerates every device with a blank id, so
523
+ * "is the saved device in the list?" is no on every first enumeration. The preference survives
524
+ * that; `current*` simply reads `null` until the device is really there.
525
+ */
526
+ #preferred;
527
+ /** The input the engine was last told about — it starts on the system default (`null`). */
528
+ #engineInputId = null;
502
529
  constructor(engine, onChange) {
503
530
  this.#engine = engine;
504
531
  this.#onChange = onChange;
505
532
  this.canSelectOutput = isBrowser() && typeof HTMLMediaElement !== "undefined" && "setSinkId" in HTMLMediaElement.prototype;
533
+ this.canSelectRingtoneOutput = this.canSelectOutput;
534
+ this.#preferred = readStoredDevices();
535
+ this.currentInputId = this.#preferred.input;
536
+ this.currentOutputId = this.#preferred.output;
537
+ this.currentRingtoneOutputId = this.#preferred.ringtone;
506
538
  if (isBrowser() && navigator.mediaDevices) {
507
539
  void this.refresh();
508
540
  navigator.mediaDevices.addEventListener?.("devicechange", () => void this.refresh());
@@ -518,26 +550,63 @@ var BrowserAudioDevices = class {
518
550
  }
519
551
  this.inputs = all.filter((d) => d.kind === "audioinput");
520
552
  this.outputs = all.filter((d) => d.kind === "audiooutput");
521
- if (this.currentInputId && !this.inputs.some((d) => d.deviceId === this.currentInputId)) {
522
- this.currentInputId = null;
523
- }
524
- if (this.currentOutputId && !this.outputs.some((d) => d.deviceId === this.currentOutputId)) {
525
- this.currentOutputId = null;
526
- }
553
+ this.currentInputId = usable(this.#preferred.input, this.inputs);
554
+ this.currentOutputId = usable(this.#preferred.output, this.outputs);
555
+ this.currentRingtoneOutputId = usable(this.#preferred.ringtone, this.outputs);
527
556
  this.#onChange();
557
+ if (this.currentInputId && this.currentInputId !== this.#engineInputId) {
558
+ await this.#tellEngine(this.currentInputId);
559
+ }
528
560
  }
529
561
  async setInput(deviceId) {
530
562
  this.currentInputId = deviceId;
563
+ this.#remember({ input: deviceId });
531
564
  this.#onChange();
565
+ await this.#tellEngine(deviceId);
566
+ }
567
+ async #tellEngine(deviceId) {
568
+ this.#engineInputId = deviceId;
532
569
  await this.#engine.setInputDevice?.(deviceId);
533
570
  }
534
571
  async setOutput(deviceId) {
535
572
  this.currentOutputId = deviceId;
573
+ this.#remember({ output: deviceId });
536
574
  this.#onChange();
537
575
  }
538
- async setRingtoneOutput() {
576
+ async setRingtoneOutput(deviceId) {
577
+ this.currentRingtoneOutputId = deviceId;
578
+ this.#remember({ ringtone: deviceId });
579
+ this.#onChange();
580
+ }
581
+ #remember(patch) {
582
+ this.#preferred = { ...this.#preferred, ...patch };
583
+ writeStoredDevices(this.#preferred);
539
584
  }
540
585
  };
586
+ var DEVICES_KEY = "coventia-softphone:devices";
587
+ var NO_DEVICES = { input: null, output: null, ringtone: null };
588
+ function usable(id, list) {
589
+ return id && list.some((d) => d.deviceId === id) ? id : null;
590
+ }
591
+ function readStoredDevices() {
592
+ if (!isBrowser()) return NO_DEVICES;
593
+ try {
594
+ const raw = JSON.parse(globalThis.localStorage?.getItem(DEVICES_KEY) ?? "null");
595
+ if (!raw || typeof raw !== "object") return NO_DEVICES;
596
+ const pick = (v) => typeof v === "string" && v ? v : null;
597
+ const r = raw;
598
+ return { input: pick(r.input), output: pick(r.output), ringtone: pick(r.ringtone) };
599
+ } catch {
600
+ return NO_DEVICES;
601
+ }
602
+ }
603
+ function writeStoredDevices(devices) {
604
+ if (!isBrowser()) return;
605
+ try {
606
+ globalThis.localStorage?.setItem(DEVICES_KEY, JSON.stringify(devices));
607
+ } catch {
608
+ }
609
+ }
541
610
  var SoftphoneImpl = class extends Emitter {
542
611
  #state = "stopped";
543
612
  #lastError = null;
@@ -565,6 +634,8 @@ var SoftphoneImpl = class extends Emitter {
565
634
  * rather than by any one call.
566
635
  */
567
636
  #presenceWatch = null;
637
+ /** A pending state-driven teardown of the watch — see `PRESENCE_TEARDOWN_GRACE_MS`. */
638
+ #presenceTeardownTimer = null;
568
639
  #presenceAgents = [];
569
640
  /**
570
641
  * `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an
@@ -624,6 +695,7 @@ var SoftphoneImpl = class extends Emitter {
624
695
  }
625
696
  async stop() {
626
697
  if (!this.#startedHere) return;
698
+ this.#sayGoodbye();
627
699
  this.#tabLock.release();
628
700
  if (this.#isLeader) {
629
701
  await this.#engine.stop();
@@ -639,8 +711,7 @@ var SoftphoneImpl = class extends Emitter {
639
711
  this.#destroyed = true;
640
712
  for (const stopWatch of this.#activeWatches) stopWatch();
641
713
  this.#activeWatches.clear();
642
- this.#presenceWatch?.stop();
643
- this.#presenceWatch = null;
714
+ this.#sayGoodbye();
644
715
  await this.stop().catch(() => {
645
716
  });
646
717
  this.#setState("destroyed");
@@ -675,11 +746,15 @@ var SoftphoneImpl = class extends Emitter {
675
746
  (target, callerNumber) => this.#onTransfer(target, callerNumber)
676
747
  );
677
748
  this.#activeCall = call;
749
+ const offState = call.on("stateChanged" /* StateChanged */, () => this.#syncCallState());
750
+ this.#syncCallState();
678
751
  const off = call.on("ended" /* Ended */, () => {
679
752
  off();
753
+ offState();
680
754
  if (this.#activeCall === call) {
681
755
  this.#activeCall = null;
682
756
  this.emit("activeCallChanged" /* ActiveCallChanged */, { call: null });
757
+ this.#syncCallState();
683
758
  }
684
759
  });
685
760
  if (this.#resolveContact) {
@@ -699,6 +774,21 @@ var SoftphoneImpl = class extends Emitter {
699
774
  this.emit("stateChanged" /* StateChanged */, { state, previous });
700
775
  this.#syncPresenceWatch();
701
776
  }
777
+ /**
778
+ * Whether this softphone is on a call right now, as presence means it (`isOnCallState`). There is a single
779
+ * active-call slot, and a consult leg is a child of that call, so the parent staying alive covers a consult.
780
+ */
781
+ #isOnCall() {
782
+ return this.#activeCall !== null && isOnCallState(this.#activeCall.state);
783
+ }
784
+ /**
785
+ * Reports the current on-a-call value to the backend. The watch de-duplicates (it only sends on change) and
786
+ * remembers the latest value to replay after a reconnect, so this can be called on every transition without
787
+ * ceremony. A no-op while there is no presence watch (not online, not the leader, no backend configured).
788
+ */
789
+ #syncCallState() {
790
+ this.#presenceWatch?.reportCallState(this.#isOnCall());
791
+ }
702
792
  #setLeader(isLeader) {
703
793
  if (isLeader === this.#isLeader) return;
704
794
  this.#isLeader = isLeader;
@@ -717,9 +807,21 @@ var SoftphoneImpl = class extends Emitter {
717
807
  * This still passes `getIsLeader: () => this.#isLeader` for that narrower, defensive recheck; the
718
808
  * BROAD online/leader gate — whether to be connected AT ALL — is owned entirely here, the same
719
809
  * split `#onTransfer` already has with `startTransferWatch`.
810
+ *
811
+ * One exception to "tear down the moment `online` is lost": a leader that is merely `offline` or
812
+ * `reconnecting` keeps its watch for `PRESENCE_TEARDOWN_GRACE_MS` — a blip shorter than that never touches
813
+ * the presence socket at all. Anything else (stopped, starting, destroyed, or no longer the leader) still
814
+ * tears it down at once: those are not blips, and a follower must never hold a connection.
720
815
  */
721
816
  #syncPresenceWatch() {
722
- const shouldRun = Boolean(this.sources) && this.#state === "online" && this.#isLeader;
817
+ const eligible = Boolean(this.sources) && this.#isLeader;
818
+ const shouldRun = eligible && this.#state === "online";
819
+ const blip = eligible && (this.#state === "offline" || this.#state === "reconnecting");
820
+ if (!blip) this.#cancelPresenceTeardown();
821
+ if (blip && this.#presenceWatch !== null) {
822
+ this.#schedulePresenceTeardown();
823
+ return;
824
+ }
723
825
  if (shouldRun === (this.#presenceWatch !== null)) return;
724
826
  if (shouldRun) {
725
827
  this.#presenceWatch = startPresenceSocket({
@@ -730,13 +832,49 @@ var SoftphoneImpl = class extends Emitter {
730
832
  this.emit("presenceChanged" /* PresenceChanged */, { agents });
731
833
  }
732
834
  });
835
+ this.#syncCallState();
733
836
  } else {
734
- this.#presenceWatch?.stop();
735
- this.#presenceWatch = null;
736
- this.#presenceAgents = [];
737
- this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
837
+ this.#stopPresence();
738
838
  }
739
839
  }
840
+ /**
841
+ * Starts the grace period before an `offline` / `reconnecting` leader loses its presence socket. Idempotent
842
+ * on purpose: a second notification inside the same blip must NOT push the deadline back.
843
+ */
844
+ #schedulePresenceTeardown() {
845
+ if (this.#presenceTeardownTimer !== null) return;
846
+ this.#presenceTeardownTimer = setTimeout(() => {
847
+ this.#presenceTeardownTimer = null;
848
+ if (this.#state === "online" && this.#isLeader) return;
849
+ this.#stopPresence();
850
+ }, PRESENCE_TEARDOWN_GRACE_MS);
851
+ }
852
+ #cancelPresenceTeardown() {
853
+ if (this.#presenceTeardownTimer === null) return;
854
+ clearTimeout(this.#presenceTeardownTimer);
855
+ this.#presenceTeardownTimer = null;
856
+ }
857
+ /**
858
+ * Stops the presence watch and forgets its roster. A STATE-DRIVEN teardown (the softphone left `online`
859
+ * for good, or lost leadership) passes nothing and says no goodbye; only a deliberate stop passes `bye`.
860
+ * Any pending grace-period teardown is cancelled — there is nothing left for it to do.
861
+ */
862
+ #stopPresence(options) {
863
+ this.#cancelPresenceTeardown();
864
+ if (!this.#presenceWatch) return;
865
+ this.#presenceWatch.stop(options);
866
+ this.#presenceWatch = null;
867
+ this.#presenceAgents = [];
868
+ this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
869
+ }
870
+ /**
871
+ * A DELIBERATE stop (`stop()` / `destroy()`): say any call is over, then say goodbye, so the backend drops
872
+ * this phone at once instead of waiting out the close grace. Idempotent — a second call finds no watch.
873
+ */
874
+ #sayGoodbye() {
875
+ this.#presenceWatch?.reportCallState(false);
876
+ this.#stopPresence({ bye: true });
877
+ }
740
878
  /**
741
879
  * Phase A (transfer outcome relay, `docs/specs/transfer-outcome-relay.md`) — `CallImpl.transfer()`'s
742
880
  * internal seam calls this. No-ops entirely when no Coventia backend is configured (`sources` is
@@ -811,7 +949,7 @@ function defaultZadarmaEngine(config) {
811
949
  const stateChange = new LazyRelay();
812
950
  const error = new LazyRelay();
813
951
  function load() {
814
- pending ??= import('./zadarma-engine-4VOPIBXG.js').then(({ createZadarmaEngine }) => {
952
+ pending ??= import('./zadarma-engine-L5DEFXUS.js').then(({ createZadarmaEngine }) => {
815
953
  const engine = createZadarmaEngine(config);
816
954
  incoming.attach({ on: (l) => engine.onIncoming(l) });
817
955
  stateChange.attach({ on: (l) => engine.onStateChange(l) });
@@ -871,7 +1009,7 @@ function isCallEngine(value) {
871
1009
  }
872
1010
 
873
1011
  // src/index.ts
874
- var VERSION = "0.2.0";
1012
+ var VERSION = "0.3.0";
875
1013
 
876
1014
  export { CALL_TRANSITIONS, INITIAL_STATES, VERSION, canTransition, createSoftphone, isTerminal };
877
1015
  //# sourceMappingURL=index.js.map