@corti/dictation-web 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ The **Corti Dictation SDK** is a web component that enables real-time speech-to-
12
12
  Include the SDK in your project by importing the JavaScript module:
13
13
 
14
14
  ```html
15
- <script type="module" src="path-to/corti-dictation.js"></script>
15
+ npm i @corti/dictation-web
16
16
  ```
17
17
 
18
18
  ---
@@ -29,21 +29,12 @@ Include the SDK in your project by importing the JavaScript module:
29
29
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
30
  </head>
31
31
  <body>
32
- <corti-dictation></corti-dictation>
32
+ <corti-dictation authToken="xyz"></corti-dictation>
33
33
  <textarea id="transcript" placeholder="Transcript will appear here..."></textarea>
34
34
 
35
35
  <script type="module">
36
- import './path-to/corti-dictation.js';
37
-
38
- const dictationEl = document.querySelector('corti-dictation');
39
-
40
- // Provide server configuration
41
- dictationEl.serverConfig = {
42
- environment: "dev-weu",
43
- tenant: "copsdev",
44
- token: "your-api-token"
45
- };
46
-
36
+ import '@corti/dictation-web';
37
+
47
38
  // Listen for events
48
39
  dictationEl.addEventListener('transcript', (e) => {
49
40
  document.getElementById('transcript').value += e.detail.data.text + ' ';
@@ -64,7 +55,7 @@ Include the SDK in your project by importing the JavaScript module:
64
55
  | `devices` | Array | List of available recording devices. |
65
56
  | `recordingState` | String | Current state of recording (`stopped`, `recording`). |
66
57
  | `dictationConfig` | Object | Configuration settings for dictation. |
67
- | `serverConfig` | Object | Server authentication and API settings. Must include `environment`, `tenant`, and `token`. |
58
+ | `authToken` | String | Authentication token from OAuth server |
68
59
 
69
60
  ### Methods
70
61
 
@@ -85,16 +76,7 @@ Include the SDK in your project by importing the JavaScript module:
85
76
 
86
77
  ## Authentication
87
78
 
88
- This SDK does not handle OAuth 2.0 authentication. The client must provide an API key or access token in `serverConfig.token`.
89
-
90
- Example:
91
- ```js
92
- dictationEl.serverConfig = {
93
- environment: "prod-eu",
94
- tenant: "your-tenant-id",
95
- token: "your-api-key"
96
- };
97
- ```
79
+ This SDK does not handle OAuth 2.0 authentication. The client must provide an API key or access token as a string in `authToken`.
98
80
 
99
81
  ---
100
82
 
@@ -2,13 +2,13 @@ import { LitElement } from 'lit';
2
2
  import './components/settings-menu';
3
3
  import './components/audio-visualiser';
4
4
  import './icons/icons';
5
- import { DictationConfig, ServerConfig } from './types';
5
+ import { DictationConfig } from './types';
6
6
  export declare class CortiDictation extends LitElement {
7
7
  static styles: import("lit").CSSResult[];
8
8
  devices: MediaDeviceInfo[];
9
9
  recordingState: string;
10
10
  dictationConfig: DictationConfig;
11
- serverConfig: ServerConfig;
11
+ authToken: string | undefined;
12
12
  private _audioLevel;
13
13
  private recorderManager;
14
14
  connectedCallback(): Promise<void>;
@@ -17,7 +17,6 @@ export class CortiDictation extends LitElement {
17
17
  this.devices = [];
18
18
  this.recordingState = 'stopped';
19
19
  this.dictationConfig = DEFAULT_DICTATION_CONFIG;
20
- this.serverConfig = {};
21
20
  this._audioLevel = 0;
22
21
  this.recorderManager = new RecorderManager();
23
22
  }
@@ -61,33 +60,34 @@ export class CortiDictation extends LitElement {
61
60
  this._toggleRecording();
62
61
  }
63
62
  _toggleRecording() {
63
+ if (!this.authToken)
64
+ return;
64
65
  if (this.recordingState === 'recording') {
65
66
  this.recorderManager.stopRecording();
66
67
  }
67
68
  else if (this.recordingState === 'stopped') {
68
69
  this.recorderManager.startRecording({
69
70
  dictationConfig: this.dictationConfig,
70
- serverConfig: this.serverConfig,
71
+ authToken: this.authToken,
71
72
  });
72
73
  }
73
74
  }
74
75
  // Handle device change events if needed
75
76
  async _onRecordingDeviceChanged(event) {
76
77
  const customEvent = event;
78
+ if (!this.authToken)
79
+ return;
77
80
  this.recorderManager.selectedDevice = customEvent.detail.deviceId;
78
81
  if (this.recordingState === 'recording') {
79
82
  await this.recorderManager.stopRecording();
80
83
  await this.recorderManager.startRecording({
81
84
  dictationConfig: this.dictationConfig,
82
- serverConfig: this.serverConfig,
85
+ authToken: this.authToken,
83
86
  });
84
87
  }
85
88
  }
86
89
  render() {
87
- const isConfigured = this.serverConfig &&
88
- this.serverConfig.token &&
89
- this.serverConfig.environment &&
90
- this.serverConfig.tenant;
90
+ const isConfigured = this.authToken;
91
91
  if (!isConfigured) {
92
92
  return html `
93
93
  <div class="wrapper">
@@ -138,8 +138,8 @@ __decorate([
138
138
  property({ type: Object })
139
139
  ], CortiDictation.prototype, "dictationConfig", void 0);
140
140
  __decorate([
141
- property({ type: Object })
142
- ], CortiDictation.prototype, "serverConfig", void 0);
141
+ property({ type: String })
142
+ ], CortiDictation.prototype, "authToken", void 0);
143
143
  __decorate([
144
144
  state()
145
145
  ], CortiDictation.prototype, "_audioLevel", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"CortiDictation.js","sourceRoot":"","sources":["../src/CortiDictation.ts"],"names":[],"mappings":";AAAA,qBAAqB;AACrB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,4BAA4B,CAAC;AACpC,OAAO,+BAA+B,CAAC;AACvC,OAAO,eAAe,CAAC;AACvB,OAAO,WAAW,MAAM,gBAAgB,CAAC;AACzC,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,MAAM,OAAO,cAAe,SAAQ,UAAU;IAA9C;;QAIE,YAAO,GAAsB,EAAE,CAAC;QAGhC,mBAAc,GAAG,SAAS,CAAC;QAG3B,oBAAe,GAAoB,wBAAwB,CAAC;QAG5D,iBAAY,GAAiB,EAAE,CAAC;QAGxB,gBAAW,GAAG,CAAC,CAAC;QAEhB,oBAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IAsHlD,CAAC;IApHC,KAAK,CAAC,iBAAiB;QACrB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAE5C,8CAA8C;QAC9C,MAAM,aAAa,GAA6C;YAC9D,yBAAyB,EAAE,CAAC,CAAC,EAAE;gBAC7B,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YACvC,CAAC;YACD,qBAAqB,EAAE,CAAC,CAAC,EAAE;gBACzB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;gBACvC,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;SACF,CAAC;QAEF,MAAM,aAAa,GAAG;YACpB,yBAAyB;YACzB,qBAAqB;YACrB,OAAO;YACP,YAAY;SACb,CAAC;QAEF,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;YAChC,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAQ,EAAE,EAAE;gBAC5D,MAAM,WAAW,GAAG,CAAgB,CAAC;gBACrC,6CAA6C;gBAC7C,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,aAAa,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,CAAC;gBACxC,CAAC;gBACD,2CAA2C;gBAC3C,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,SAAS,EAAE;oBACzB,MAAM,EAAE,WAAW,CAAC,MAAM;oBAC1B,OAAO,EAAE,IAAI;oBACb,QAAQ,EAAE,IAAI;iBACf,CAAC,CACH,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,eAAe;QACpB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAED,gBAAgB;QACd,IAAI,IAAI,CAAC,cAAc,KAAK,WAAW,EAAE,CAAC;YACxC,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC;QACvC,CAAC;aAAM,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC;gBAClC,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,YAAY,EAAE,IAAI,CAAC,YAAY;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,KAAK,CAAC,yBAAyB,CAAC,KAAY;QAC1C,MAAM,WAAW,GAAG,KAAoB,CAAC;QACzC,IAAI,CAAC,eAAe,CAAC,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;QAClE,IAAI,IAAI,CAAC,cAAc,KAAK,WAAW,EAAE,CAAC;YACxC,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC;YAC3C,MAAM,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC;gBACxC,eAAe,EAAE,IAAI,CAAC,eAAe;gBACrC,YAAY,EAAE,IAAI,CAAC,YAAY;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM;QACJ,MAAM,YAAY,GAChB,IAAI,CAAC,YAAY;YACjB,IAAI,CAAC,YAAY,CAAC,KAAK;YACvB,IAAI,CAAC,YAAY,CAAC,WAAW;YAC7B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,IAAI,CAAA;;;;;;OAMV,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GACb,IAAI,CAAC,cAAc,KAAK,cAAc;YACtC,IAAI,CAAC,cAAc,KAAK,UAAU,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,KAAK,WAAW,CAAC;QACxD,OAAO,IAAI,CAAA;;;mBAGI,IAAI,CAAC,gBAAgB;kBACtB,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;;YAEpC,SAAS;YACT,CAAC,CAAC,IAAI,CAAA,+CAA+C;YACrD,CAAC,CAAC,WAAW;gBACX,CAAC,CAAC,IAAI,CAAA,mCAAmC;gBACzC,CAAC,CAAC,IAAI,CAAA,6BAA6B;;qBAE5B,IAAI,CAAC,WAAW;sBACf,WAAW;;;;;qBAKZ,IAAI,CAAC,OAAO;4BACL,IAAI,CAAC,eAAe,CAAC,cAAc;8BACjC,IAAI,CAAC,cAAc,KAAK,SAAS;sCACzB,IAAI,CAAC,yBAAyB;;;KAG/D,CAAC;IACJ,CAAC;;AAtIM,qBAAM,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,CAAC,AAA9D,CAA+D;AAG5E;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;+CACM;AAGhC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;sDACf;AAG3B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;uDACiC;AAG5D;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oDACK;AAGxB;IADP,KAAK,EAAE;mDACgB;AA0H1B,eAAe,cAAc,CAAC","sourcesContent":["// corti-dictation.ts\nimport { html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { RecorderManager } from './RecorderManager';\nimport './components/settings-menu';\nimport './components/audio-visualiser';\nimport './icons/icons';\nimport ThemeStyles from './styles/theme';\nimport ButtonStyles from './styles/buttons';\nimport ComponentStyles from './styles/ComponentStyles';\n\nimport { DictationConfig, ServerConfig } from './types';\nimport { DEFAULT_DICTATION_CONFIG } from './constants';\nimport CalloutStyles from './styles/callout';\n\nexport class CortiDictation extends LitElement {\n static styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];\n\n @property({ type: Array })\n devices: MediaDeviceInfo[] = [];\n\n @property({ type: String, reflect: true })\n recordingState = 'stopped';\n\n @property({ type: Object })\n dictationConfig: DictationConfig = DEFAULT_DICTATION_CONFIG;\n\n @property({ type: Object })\n serverConfig: ServerConfig = {};\n\n @state()\n private _audioLevel = 0;\n\n private recorderManager = new RecorderManager();\n\n async connectedCallback() {\n super.connectedCallback();\n await this.recorderManager.initialize();\n this.devices = this.recorderManager.devices;\n\n // Map event names to any extra handling logic\n const eventHandlers: Record<string, (e: CustomEvent) => void> = {\n 'recording-state-changed': e => {\n this.recordingState = e.detail.state;\n },\n 'audio-level-changed': e => {\n this._audioLevel = e.detail.audioLevel;\n this.requestUpdate();\n },\n };\n\n const eventsToRelay = [\n 'recording-state-changed',\n 'audio-level-changed',\n 'error',\n 'transcript',\n ];\n\n eventsToRelay.forEach(eventName => {\n this.recorderManager.addEventListener(eventName, (e: Event) => {\n const customEvent = e as CustomEvent;\n // Perform any additional handling if defined\n if (eventHandlers[eventName]) {\n eventHandlers[eventName](customEvent);\n }\n // Re-dispatch the event from the component\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail: customEvent.detail,\n bubbles: true,\n composed: true,\n }),\n );\n });\n });\n }\n\n public toggleRecording() {\n this._toggleRecording();\n }\n\n _toggleRecording() {\n if (this.recordingState === 'recording') {\n this.recorderManager.stopRecording();\n } else if (this.recordingState === 'stopped') {\n this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n serverConfig: this.serverConfig,\n });\n }\n }\n\n // Handle device change events if needed\n async _onRecordingDeviceChanged(event: Event) {\n const customEvent = event as CustomEvent;\n this.recorderManager.selectedDevice = customEvent.detail.deviceId;\n if (this.recordingState === 'recording') {\n await this.recorderManager.stopRecording();\n await this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n serverConfig: this.serverConfig,\n });\n }\n }\n\n render() {\n const isConfigured =\n this.serverConfig &&\n this.serverConfig.token &&\n this.serverConfig.environment &&\n this.serverConfig.tenant;\n if (!isConfigured) {\n return html`\n <div class=\"wrapper\">\n <div class=\"callout red tiny\">\n Please configure the server settings in the parent component.\n </div>\n </div>\n `;\n }\n\n const isLoading =\n this.recordingState === 'initializing' ||\n this.recordingState === 'stopping';\n const isRecording = this.recordingState === 'recording';\n return html`\n <div class=\"wrapper\">\n <button\n @click=${this._toggleRecording}\n class=${isRecording ? 'red' : 'accent'}\n >\n ${isLoading\n ? html`<icon-loading-spinner></icon-loading-spinner>`\n : isRecording\n ? html`<icon-recording></icon-recording>`\n : html`<icon-mic-on></icon-mic-on>`}\n <audio-visualiser\n .level=${this._audioLevel}\n .active=${isRecording}\n ></audio-visualiser>\n </button>\n\n <settings-menu\n .devices=${this.devices}\n .selectedDevice=${this.recorderManager.selectedDevice}\n ?settingsDisabled=${this.recordingState !== 'stopped'}\n @recording-device-changed=${this._onRecordingDeviceChanged}\n ></settings-menu>\n </div>\n `;\n }\n}\n\nexport default CortiDictation;\n"]}
1
+ {"version":3,"file":"CortiDictation.js","sourceRoot":"","sources":["../src/CortiDictation.ts"],"names":[],"mappings":"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","sourcesContent":["// corti-dictation.ts\nimport { html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { RecorderManager } from './RecorderManager';\nimport './components/settings-menu';\nimport './components/audio-visualiser';\nimport './icons/icons';\nimport ThemeStyles from './styles/theme';\nimport ButtonStyles from './styles/buttons';\nimport ComponentStyles from './styles/ComponentStyles';\n\nimport { DictationConfig } from './types';\nimport { DEFAULT_DICTATION_CONFIG } from './constants';\nimport CalloutStyles from './styles/callout';\n\nexport class CortiDictation extends LitElement {\n static styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];\n\n @property({ type: Array })\n devices: MediaDeviceInfo[] = [];\n\n @property({ type: String, reflect: true })\n recordingState = 'stopped';\n\n @property({ type: Object })\n dictationConfig: DictationConfig = DEFAULT_DICTATION_CONFIG;\n\n @property({ type: String })\n authToken: string | undefined;\n\n @state()\n private _audioLevel = 0;\n\n private recorderManager = new RecorderManager();\n\n async connectedCallback() {\n super.connectedCallback();\n await this.recorderManager.initialize();\n this.devices = this.recorderManager.devices;\n\n // Map event names to any extra handling logic\n const eventHandlers: Record<string, (e: CustomEvent) => void> = {\n 'recording-state-changed': e => {\n this.recordingState = e.detail.state;\n },\n 'audio-level-changed': e => {\n this._audioLevel = e.detail.audioLevel;\n this.requestUpdate();\n },\n };\n\n const eventsToRelay = [\n 'recording-state-changed',\n 'audio-level-changed',\n 'error',\n 'transcript',\n ];\n\n eventsToRelay.forEach(eventName => {\n this.recorderManager.addEventListener(eventName, (e: Event) => {\n const customEvent = e as CustomEvent;\n // Perform any additional handling if defined\n if (eventHandlers[eventName]) {\n eventHandlers[eventName](customEvent);\n }\n // Re-dispatch the event from the component\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail: customEvent.detail,\n bubbles: true,\n composed: true,\n }),\n );\n });\n });\n }\n\n public toggleRecording() {\n this._toggleRecording();\n }\n\n _toggleRecording() {\n if (!this.authToken) return;\n if (this.recordingState === 'recording') {\n this.recorderManager.stopRecording();\n } else if (this.recordingState === 'stopped') {\n this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n authToken: this.authToken,\n });\n }\n }\n\n // Handle device change events if needed\n async _onRecordingDeviceChanged(event: Event) {\n const customEvent = event as CustomEvent;\n if (!this.authToken) return;\n this.recorderManager.selectedDevice = customEvent.detail.deviceId;\n if (this.recordingState === 'recording') {\n await this.recorderManager.stopRecording();\n await this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n authToken: this.authToken,\n });\n }\n }\n\n render() {\n const isConfigured = this.authToken;\n if (!isConfigured) {\n return html`\n <div class=\"wrapper\">\n <div class=\"callout red tiny\">\n Please configure the server settings in the parent component.\n </div>\n </div>\n `;\n }\n\n const isLoading =\n this.recordingState === 'initializing' ||\n this.recordingState === 'stopping';\n const isRecording = this.recordingState === 'recording';\n return html`\n <div class=\"wrapper\">\n <button\n @click=${this._toggleRecording}\n class=${isRecording ? 'red' : 'accent'}\n >\n ${isLoading\n ? html`<icon-loading-spinner></icon-loading-spinner>`\n : isRecording\n ? html`<icon-recording></icon-recording>`\n : html`<icon-mic-on></icon-mic-on>`}\n <audio-visualiser\n .level=${this._audioLevel}\n .active=${isRecording}\n ></audio-visualiser>\n </button>\n\n <settings-menu\n .devices=${this.devices}\n .selectedDevice=${this.recorderManager.selectedDevice}\n ?settingsDisabled=${this.recordingState !== 'stopped'}\n @recording-device-changed=${this._onRecordingDeviceChanged}\n ></settings-menu>\n </div>\n `;\n }\n}\n\nexport default CortiDictation;\n"]}
@@ -1,12 +1,12 @@
1
- import { DictationConfig, ServerConfig } from './types';
1
+ import { DictationConfig } from './types';
2
2
  export declare class DictationService extends EventTarget {
3
3
  private mediaRecorder;
4
4
  private webSocket;
5
- private serverConfig;
5
+ private authToken;
6
6
  private dictationConfig;
7
- constructor(mediaStream: MediaStream, { dictationConfig, serverConfig, }: {
7
+ constructor(mediaStream: MediaStream, { dictationConfig, authToken, }: {
8
8
  dictationConfig: DictationConfig;
9
- serverConfig: ServerConfig;
9
+ authToken: string;
10
10
  });
11
11
  startRecording(): void;
12
12
  stopRecording(): Promise<void>;
@@ -1,8 +1,9 @@
1
+ import { decodeToken } from './utils';
1
2
  export class DictationService extends EventTarget {
2
- constructor(mediaStream, { dictationConfig, serverConfig, }) {
3
+ constructor(mediaStream, { dictationConfig, authToken, }) {
3
4
  super();
4
5
  this.mediaRecorder = new MediaRecorder(mediaStream);
5
- this.serverConfig = serverConfig;
6
+ this.authToken = authToken;
6
7
  this.dictationConfig = dictationConfig;
7
8
  this.mediaRecorder.ondataavailable = event => {
8
9
  // if webSocket is open, send the data
@@ -12,7 +13,8 @@ export class DictationService extends EventTarget {
12
13
  };
13
14
  }
14
15
  startRecording() {
15
- const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.serverConfig.token}`;
16
+ const serverConfig = decodeToken(this.authToken || '');
17
+ const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;
16
18
  this.webSocket = new WebSocket(url);
17
19
  this.webSocket.onopen = () => {
18
20
  this.webSocket.send(JSON.stringify({
@@ -1 +1 @@
1
- {"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"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","sourcesContent":["import { DictationConfig, ServerConfig } from './types';\n\nexport class DictationService extends EventTarget {\n private mediaRecorder: MediaRecorder;\n\n private webSocket!: WebSocket;\n\n private serverConfig!: ServerConfig;\n\n private dictationConfig!: DictationConfig;\n\n constructor(\n mediaStream: MediaStream,\n {\n dictationConfig,\n serverConfig,\n }: { dictationConfig: DictationConfig; serverConfig: ServerConfig },\n ) {\n super();\n this.mediaRecorder = new MediaRecorder(mediaStream);\n this.serverConfig = serverConfig;\n this.dictationConfig = dictationConfig;\n this.mediaRecorder.ondataavailable = event => {\n // if webSocket is open, send the data\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(event.data);\n }\n };\n }\n\n public startRecording() {\n const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.serverConfig.token}`;\n this.webSocket = new WebSocket(url);\n this.webSocket.onopen = () => {\n this.webSocket.send(\n JSON.stringify({\n type: 'config',\n configuration: this.dictationConfig,\n }),\n );\n };\n this.webSocket.onmessage = event => {\n const message = JSON.parse(event.data);\n if (message.type === 'config') {\n this.mediaRecorder.start(250);\n } else if (message.type === 'transcript') {\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: message,\n bubbles: true,\n composed: true,\n }),\n );\n }\n };\n this.webSocket.onerror = event => {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: event,\n bubbles: true,\n composed: true,\n }),\n );\n };\n this.webSocket.onclose = event => {\n this.dispatchEvent(\n new CustomEvent('stream-closed', {\n detail: event,\n bubbles: true,\n composed: true,\n }),\n );\n };\n }\n\n public async stopRecording() {\n this.mediaRecorder.stop();\n\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(\n JSON.stringify({\n type: 'end',\n }),\n );\n }\n\n const timeOut: NodeJS.Timeout = setTimeout(() => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.close();\n }\n }, 10000);\n\n // This implementation should be replaced by handling a proper 'ended' message from the server\n this.webSocket.onclose = () => {\n this.webSocket?.close();\n clearTimeout(timeOut);\n };\n }\n}\n"]}
1
+ {"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"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","sourcesContent":["import { DictationConfig } from './types';\nimport { decodeToken } from './utils';\n\nexport class DictationService extends EventTarget {\n private mediaRecorder: MediaRecorder;\n\n private webSocket!: WebSocket;\n\n private authToken!: string;\n\n private dictationConfig!: DictationConfig;\n\n constructor(\n mediaStream: MediaStream,\n {\n dictationConfig,\n authToken,\n }: { dictationConfig: DictationConfig; authToken: string },\n ) {\n super();\n this.mediaRecorder = new MediaRecorder(mediaStream);\n this.authToken = authToken;\n this.dictationConfig = dictationConfig;\n this.mediaRecorder.ondataavailable = event => {\n // if webSocket is open, send the data\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(event.data);\n }\n };\n }\n\n public startRecording() {\n const serverConfig = decodeToken(this.authToken || '');\n\n const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;\n this.webSocket = new WebSocket(url);\n this.webSocket.onopen = () => {\n this.webSocket.send(\n JSON.stringify({\n type: 'config',\n configuration: this.dictationConfig,\n }),\n );\n };\n this.webSocket.onmessage = event => {\n const message = JSON.parse(event.data);\n if (message.type === 'config') {\n this.mediaRecorder.start(250);\n } else if (message.type === 'transcript') {\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: message,\n bubbles: true,\n composed: true,\n }),\n );\n }\n };\n this.webSocket.onerror = event => {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: event,\n bubbles: true,\n composed: true,\n }),\n );\n };\n this.webSocket.onclose = event => {\n this.dispatchEvent(\n new CustomEvent('stream-closed', {\n detail: event,\n bubbles: true,\n composed: true,\n }),\n );\n };\n }\n\n public async stopRecording() {\n this.mediaRecorder.stop();\n\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(\n JSON.stringify({\n type: 'end',\n }),\n );\n }\n\n const timeOut: NodeJS.Timeout = setTimeout(() => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.close();\n }\n }, 10000);\n\n // This implementation should be replaced by handling a proper 'ended' message from the server\n this.webSocket.onclose = () => {\n this.webSocket?.close();\n clearTimeout(timeOut);\n };\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { DictationConfig, RecordingState, ServerConfig } from './types';
1
+ import type { DictationConfig, RecordingState } from './types';
2
2
  export declare class RecorderManager extends EventTarget {
3
3
  devices: MediaDeviceInfo[];
4
4
  selectedDevice: string;
@@ -13,7 +13,7 @@ export declare class RecorderManager extends EventTarget {
13
13
  }>;
14
14
  startRecording(params: {
15
15
  dictationConfig: DictationConfig;
16
- serverConfig: ServerConfig;
16
+ authToken: string;
17
17
  }): Promise<void>;
18
18
  stopRecording(): Promise<void>;
19
19
  private _updateRecordingState;
@@ -1 +1 @@
1
- {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices } from './utils';\nimport { AudioService } from './audioService';\nimport { DictationService } from './DictationService';\nimport type { DictationConfig, RecordingState, ServerConfig } from './types';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: string = '';\n\n public recordingState: RecordingState = 'stopped';\n\n private _mediaStream: MediaStream | null = null;\n\n private _audioService: AudioService | null = null;\n\n private _dictationService: DictationService | null = null;\n\n private _visualiserInterval?: number;\n\n async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDeviceId || '';\n return deviceResponse;\n }\n\n async startRecording(params: {\n dictationConfig: DictationConfig;\n serverConfig: ServerConfig;\n }) {\n this._updateRecordingState('initializing');\n try {\n this._mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { deviceId: this.selectedDevice },\n });\n this._audioService = new AudioService(this._mediaStream);\n this._dictationService = new DictationService(this._mediaStream, params);\n\n // Forward custom events from dictation service\n this._dictationService.addEventListener('error', e =>\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n this._dictationService.addEventListener('stream-closed', () =>\n this.stopRecording(),\n );\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n } catch (error) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: error,\n bubbles: true,\n composed: true,\n }),\n );\n this._updateRecordingState('stopped');\n return;\n }\n\n this._dictationService?.startRecording();\n this._updateRecordingState('recording');\n this._visualiserInterval = window.setInterval(() => {\n const level = this._audioService\n ? this._audioService.getAudioLevel() * 3\n : 0;\n this.dispatchEvent(\n new CustomEvent('audio-level-changed', {\n detail: { audioLevel: level },\n bubbles: true,\n composed: true,\n }),\n );\n }, 150);\n }\n\n async stopRecording() {\n this._updateRecordingState('stopping');\n if (this._visualiserInterval) {\n clearInterval(this._visualiserInterval);\n this._visualiserInterval = undefined;\n }\n if (this._mediaStream) {\n this._mediaStream.getTracks().forEach(track => track.stop());\n this._mediaStream = null;\n }\n await this._dictationService?.stopRecording();\n this._updateRecordingState('stopped');\n }\n\n private _updateRecordingState(state: RecordingState) {\n this.recordingState = state;\n this.dispatchEvent(\n new CustomEvent('recording-state-changed', {\n detail: { state },\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices } from './utils';\nimport { AudioService } from './audioService';\nimport { DictationService } from './DictationService';\nimport type { DictationConfig, RecordingState } from './types';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: string = '';\n\n public recordingState: RecordingState = 'stopped';\n\n private _mediaStream: MediaStream | null = null;\n\n private _audioService: AudioService | null = null;\n\n private _dictationService: DictationService | null = null;\n\n private _visualiserInterval?: number;\n\n async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDeviceId || '';\n return deviceResponse;\n }\n\n async startRecording(params: {\n dictationConfig: DictationConfig;\n authToken: string;\n }) {\n this._updateRecordingState('initializing');\n try {\n this._mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { deviceId: this.selectedDevice },\n });\n this._audioService = new AudioService(this._mediaStream);\n this._dictationService = new DictationService(this._mediaStream, params);\n\n // Forward custom events from dictation service\n this._dictationService.addEventListener('error', e =>\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n this._dictationService.addEventListener('stream-closed', () =>\n this.stopRecording(),\n );\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n } catch (error) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: error,\n bubbles: true,\n composed: true,\n }),\n );\n this._updateRecordingState('stopped');\n return;\n }\n\n this._dictationService?.startRecording();\n this._updateRecordingState('recording');\n this._visualiserInterval = window.setInterval(() => {\n const level = this._audioService\n ? this._audioService.getAudioLevel() * 3\n : 0;\n this.dispatchEvent(\n new CustomEvent('audio-level-changed', {\n detail: { audioLevel: level },\n bubbles: true,\n composed: true,\n }),\n );\n }, 150);\n }\n\n async stopRecording() {\n this._updateRecordingState('stopping');\n if (this._visualiserInterval) {\n clearInterval(this._visualiserInterval);\n this._visualiserInterval = undefined;\n }\n if (this._mediaStream) {\n this._mediaStream.getTracks().forEach(track => track.stop());\n this._mediaStream = null;\n }\n await this._dictationService?.stopRecording();\n this._updateRecordingState('stopped');\n }\n\n private _updateRecordingState(state: RecordingState) {\n this.recordingState = state;\n this.dispatchEvent(\n new CustomEvent('recording-state-changed', {\n detail: { state },\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import CortiDictation from "./CortiDictation";
1
+ import CortiDictation from './CortiDictation';
2
2
  export default CortiDictation;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import CortiDictation from "./CortiDictation";
2
- if (!customElements.get("corti-dictation")) {
3
- customElements.define("corti-dictation", CortiDictation);
1
+ import CortiDictation from './CortiDictation';
2
+ if (!customElements.get('corti-dictation')) {
3
+ customElements.define('corti-dictation', CortiDictation);
4
4
  }
5
5
  export default CortiDictation;
6
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAE9C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;IAC3C,cAAc,CAAC,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAC3D,CAAC;AAED,eAAe,cAAc,CAAC","sourcesContent":["import CortiDictation from \"./CortiDictation\";\n\nif (!customElements.get(\"corti-dictation\")) {\n customElements.define(\"corti-dictation\", CortiDictation);\n}\n\nexport default CortiDictation;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAE9C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;IAC3C,cAAc,CAAC,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;AAC3D,CAAC;AAED,eAAe,cAAc,CAAC","sourcesContent":["import CortiDictation from './CortiDictation';\n\nif (!customElements.get('corti-dictation')) {\n customElements.define('corti-dictation', CortiDictation);\n}\n\nexport default CortiDictation;\n"]}
package/dist/utils.d.ts CHANGED
@@ -29,3 +29,32 @@ export declare function getAudioDevices(): Promise<{
29
29
  devices: MediaDeviceInfo[];
30
30
  defaultDeviceId?: string;
31
31
  }>;
32
+ /**
33
+ * Decodes a JWT token and extracts environment and tenant details from its issuer URL.
34
+ *
35
+ * This function assumes the JWT token follows the standard header.payload.signature format.
36
+ * It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
37
+ * to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
38
+ * https://keycloak.{environment}.corti.app/realms/{tenant}.
39
+ *
40
+ * @param token - A JSON Web Token (JWT) string.
41
+ * @returns An object containing:
42
+ * - `environment`: The extracted environment from the issuer URL.
43
+ * - `tenant`: The extracted tenant from the issuer URL.
44
+ * - `token`: The original token string.
45
+ * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
46
+ *
47
+ * @throws Will throw an error if:
48
+ * - The token format is invalid.
49
+ * - The base64 decoding or URI decoding fails.
50
+ * - The JSON payload is invalid.
51
+ * - The token payload does not contain an issuer (iss) field.
52
+ */
53
+ export declare function decodeToken(token: string): {
54
+ [key: string]: unknown;
55
+ iss: string;
56
+ } | {
57
+ environment: string;
58
+ tenant: string;
59
+ token: string;
60
+ };
package/dist/utils.js CHANGED
@@ -74,4 +74,75 @@ export async function getAudioDevices() {
74
74
  return { devices: [] };
75
75
  }
76
76
  }
77
+ /**
78
+ * Decodes a JWT token and extracts environment and tenant details from its issuer URL.
79
+ *
80
+ * This function assumes the JWT token follows the standard header.payload.signature format.
81
+ * It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
82
+ * to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
83
+ * https://keycloak.{environment}.corti.app/realms/{tenant}.
84
+ *
85
+ * @param token - A JSON Web Token (JWT) string.
86
+ * @returns An object containing:
87
+ * - `environment`: The extracted environment from the issuer URL.
88
+ * - `tenant`: The extracted tenant from the issuer URL.
89
+ * - `token`: The original token string.
90
+ * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
91
+ *
92
+ * @throws Will throw an error if:
93
+ * - The token format is invalid.
94
+ * - The base64 decoding or URI decoding fails.
95
+ * - The JSON payload is invalid.
96
+ * - The token payload does not contain an issuer (iss) field.
97
+ */
98
+ export function decodeToken(token) {
99
+ // Validate the token structure (should contain at least header and payload parts)
100
+ const parts = token.split('.');
101
+ if (parts.length < 2) {
102
+ throw new Error('Invalid token format: expected header.payload.signature');
103
+ }
104
+ // Retrieve the payload (second part) of the JWT token
105
+ const base64Url = parts[1];
106
+ // Replace URL-safe characters to match standard base64 encoding
107
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
108
+ // Decode the base64 string into a JSON string
109
+ let jsonPayload;
110
+ try {
111
+ jsonPayload = decodeURIComponent(atob(base64)
112
+ .split('')
113
+ .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
114
+ .join(''));
115
+ }
116
+ catch (error) {
117
+ throw new Error('Failed to decode token payload');
118
+ }
119
+ // Parse the JSON string to obtain token details
120
+ let tokenDetails;
121
+ try {
122
+ tokenDetails = JSON.parse(jsonPayload);
123
+ }
124
+ catch (error) {
125
+ throw new Error('Invalid JSON payload in token');
126
+ }
127
+ // Extract the issuer URL from the token details
128
+ const issuerUrl = tokenDetails.iss;
129
+ if (!issuerUrl) {
130
+ throw new Error('Token payload does not contain an issuer (iss) field');
131
+ }
132
+ // Regex to extract environment and tenant from issuer URL:
133
+ // Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}
134
+ // Note: Unnecessary escapes in character classes have been removed.
135
+ const regex = /^https:\/\/keycloak\.([^.]+)\.corti\.app\/realms\/([^/]+)/;
136
+ const match = issuerUrl.match(regex);
137
+ // If the issuer URL matches the expected pattern, return the extracted values along with the token
138
+ if (match) {
139
+ return {
140
+ environment: match[1],
141
+ tenant: match[2],
142
+ token,
143
+ };
144
+ }
145
+ // Fallback: return the full decoded token details if URL pattern doesn't match
146
+ return tokenDetails;
147
+ }
77
148
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,UAAU,CAAC,EAAE;QACvD,IAAI,EAAE,UAAU;KACjB,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,YAAY,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;IACnD,OAAO,YAAY,IAAI,YAAY,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC;YACzD,oCAAoC;YACpC,IAAI,EAAE,YAA8B;SACrC,CAAC,CAAC;QAEH,IAAI,gBAAgB,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,gBAAgB,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IAInC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,gBAAgB,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,gBAAgB,EAAE,CAAC;IAEzB,IAAI,CAAC;QACH,0EAA0E;QAC1E,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;QAC5E,MAAM,eAAe,GACnB,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACjE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;AACH,CAAC","sourcesContent":["/* eslint-disable no-console */\n/**\n * Returns the localized name of a language given its BCP-47 code.\n *\n * @param languageCode - The BCP-47 language code (e.g. \"en\")\n * @returns The localized language name (e.g. \"English\") or the original code if unavailable.\n */\nexport function getLanguageName(languageCode: string): string {\n const userLocale = navigator.language || 'en';\n const displayNames = new Intl.DisplayNames([userLocale], {\n type: 'language',\n });\n const languageName = displayNames.of(languageCode);\n return languageName || languageCode;\n}\n\n/**\n * Requests access to the microphone.\n *\n * This function checks if the microphone permission is in \"prompt\" state, then requests\n * access and stops any active tracks immediately. It also logs if permission is already granted.\n *\n * @returns A promise that resolves when the permission request is complete.\n */\nexport async function requestMicAccess(): Promise<void> {\n try {\n // Fallback if Permissions API is not available\n if (!navigator.permissions) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const permissionStatus = await navigator.permissions.query({\n // eslint-disable-next-line no-undef\n name: 'microphone' as PermissionName,\n });\n\n if (permissionStatus.state === 'prompt') {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n } else if (permissionStatus.state === 'denied') {\n console.warn('Microphone permission is denied.');\n }\n } catch (error) {\n console.error('Error checking/requesting microphone permission:', error);\n }\n}\n\n/**\n * Retrieves available audio input devices.\n *\n * This function uses the mediaDevices API to enumerate devices and filters out those\n * which are audio inputs. In some browsers, you may need to request user media before\n * device labels are populated.\n *\n * @returns A promise that resolves with an object containing:\n * - `devices`: an array of MediaDeviceInfo objects for audio inputs.\n * - `defaultDeviceId`: the deviceId of the first audio input, if available.\n */\nexport async function getAudioDevices(): Promise<{\n devices: MediaDeviceInfo[];\n defaultDeviceId?: string;\n}> {\n if (!navigator.mediaDevices?.enumerateDevices) {\n console.error('Media devices API not supported.');\n return { devices: [] };\n }\n\n await requestMicAccess();\n\n try {\n // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\n const defaultDeviceId =\n audioDevices.length > 0 ? audioDevices[0].deviceId : undefined;\n return { devices: audioDevices, defaultDeviceId };\n } catch (error) {\n console.error('Error enumerating devices:', error);\n return { devices: [] };\n }\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-console */\n/**\n * Returns the localized name of a language given its BCP-47 code.\n *\n * @param languageCode - The BCP-47 language code (e.g. \"en\")\n * @returns The localized language name (e.g. \"English\") or the original code if unavailable.\n */\nexport function getLanguageName(languageCode: string): string {\n const userLocale = navigator.language || 'en';\n const displayNames = new Intl.DisplayNames([userLocale], {\n type: 'language',\n });\n const languageName = displayNames.of(languageCode);\n return languageName || languageCode;\n}\n\n/**\n * Requests access to the microphone.\n *\n * This function checks if the microphone permission is in \"prompt\" state, then requests\n * access and stops any active tracks immediately. It also logs if permission is already granted.\n *\n * @returns A promise that resolves when the permission request is complete.\n */\nexport async function requestMicAccess(): Promise<void> {\n try {\n // Fallback if Permissions API is not available\n if (!navigator.permissions) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const permissionStatus = await navigator.permissions.query({\n // eslint-disable-next-line no-undef\n name: 'microphone' as PermissionName,\n });\n\n if (permissionStatus.state === 'prompt') {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n } else if (permissionStatus.state === 'denied') {\n console.warn('Microphone permission is denied.');\n }\n } catch (error) {\n console.error('Error checking/requesting microphone permission:', error);\n }\n}\n\n/**\n * Retrieves available audio input devices.\n *\n * This function uses the mediaDevices API to enumerate devices and filters out those\n * which are audio inputs. In some browsers, you may need to request user media before\n * device labels are populated.\n *\n * @returns A promise that resolves with an object containing:\n * - `devices`: an array of MediaDeviceInfo objects for audio inputs.\n * - `defaultDeviceId`: the deviceId of the first audio input, if available.\n */\nexport async function getAudioDevices(): Promise<{\n devices: MediaDeviceInfo[];\n defaultDeviceId?: string;\n}> {\n if (!navigator.mediaDevices?.enumerateDevices) {\n console.error('Media devices API not supported.');\n return { devices: [] };\n }\n\n await requestMicAccess();\n\n try {\n // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\n const defaultDeviceId =\n audioDevices.length > 0 ? audioDevices[0].deviceId : undefined;\n return { devices: audioDevices, defaultDeviceId };\n } catch (error) {\n console.error('Error enumerating devices:', error);\n return { devices: [] };\n }\n}\n\n/**\n * Decodes a JWT token and extracts environment and tenant details from its issuer URL.\n *\n * This function assumes the JWT token follows the standard header.payload.signature format.\n * It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex\n * to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:\n * https://keycloak.{environment}.corti.app/realms/{tenant}.\n *\n * @param token - A JSON Web Token (JWT) string.\n * @returns An object containing:\n * - `environment`: The extracted environment from the issuer URL.\n * - `tenant`: The extracted tenant from the issuer URL.\n * - `token`: The original token string.\n * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.\n *\n * @throws Will throw an error if:\n * - The token format is invalid.\n * - The base64 decoding or URI decoding fails.\n * - The JSON payload is invalid.\n * - The token payload does not contain an issuer (iss) field.\n */\nexport function decodeToken(token: string) {\n // Validate the token structure (should contain at least header and payload parts)\n const parts = token.split('.');\n if (parts.length < 2) {\n throw new Error('Invalid token format: expected header.payload.signature');\n }\n\n // Retrieve the payload (second part) of the JWT token\n const base64Url = parts[1];\n\n // Replace URL-safe characters to match standard base64 encoding\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Decode the base64 string into a JSON string\n let jsonPayload: string;\n try {\n jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n } catch (error) {\n throw new Error('Failed to decode token payload');\n }\n\n // Parse the JSON string to obtain token details\n let tokenDetails: { iss: string; [key: string]: unknown };\n try {\n tokenDetails = JSON.parse(jsonPayload);\n } catch (error) {\n throw new Error('Invalid JSON payload in token');\n }\n\n // Extract the issuer URL from the token details\n const issuerUrl: string = tokenDetails.iss;\n if (!issuerUrl) {\n throw new Error('Token payload does not contain an issuer (iss) field');\n }\n\n // Regex to extract environment and tenant from issuer URL:\n // Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}\n // Note: Unnecessary escapes in character classes have been removed.\n const regex = /^https:\\/\\/keycloak\\.([^.]+)\\.corti\\.app\\/realms\\/([^/]+)/;\n const match = issuerUrl.match(regex);\n\n // If the issuer URL matches the expected pattern, return the extracted values along with the token\n if (match) {\n return {\n environment: match[1],\n tenant: match[2],\n token,\n };\n }\n\n // Fallback: return the full decoded token details if URL pattern doesn't match\n return tokenDetails;\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@corti/dictation-web",
3
3
  "description": "Web component for Corti Dictation",
4
4
  "author": "Corti ApS",
5
- "version": "0.0.2",
5
+ "version": "0.1.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",