@speechly/browser-client
Advanced tools
Comparing version 0.2.2 to 0.3.0
126
index.d.ts
@@ -9,22 +9,2 @@ | ||
/** | ||
* Microphone implementation for the browser. Uses getUserMedia and Web Audio API. | ||
* @public | ||
*/ | ||
export declare class BrowserMicrophone implements Microphone { | ||
private readonly sampleRate; | ||
private onAudioCb; | ||
private audioTrack?; | ||
private mediaStream?; | ||
private audioProcessor?; | ||
private audioContext?; | ||
private downsampler?; | ||
constructor(sampleRate: number); | ||
onAudio(cb: AudioCallback): void; | ||
initialize(cb: ErrorCallback): void; | ||
close(cb: ErrorCallback): void; | ||
mute(): void; | ||
unmute(): void; | ||
} | ||
/** | ||
* A client for Speechly Spoken Language Understanding (SLU) API. The client handles initializing the microphone | ||
@@ -37,5 +17,7 @@ * and websocket connection to Speechly API, passing control events and audio stream to the API, reading the responses | ||
private readonly debug; | ||
private readonly storage; | ||
private readonly microphone; | ||
private readonly websocket; | ||
private readonly activeContexts; | ||
private deviceId?; | ||
private state; | ||
@@ -145,6 +127,2 @@ private reconnectAttemptCount; | ||
/** | ||
* The identifier of the device which is using the client. | ||
*/ | ||
deviceId?: string; | ||
/** | ||
* The sample rate of the audio to use. | ||
@@ -158,5 +136,11 @@ */ | ||
/** | ||
* Microphone instance. | ||
* Custom microphone implementation. | ||
* If not provided, an implementation based on getUserMedia and Web Audio API is used. | ||
*/ | ||
microphone?: Microphone; | ||
/** | ||
* Custom storage implementation. | ||
* If not provided, browser's LocalStorage API is used. | ||
*/ | ||
storage?: Storage; | ||
} | ||
@@ -243,2 +227,8 @@ | ||
/** | ||
* Error to be thrown if requested key was not found in the storage. | ||
* @public | ||
*/ | ||
export declare const ErrKeyNotFound: Error; | ||
/** | ||
* Error to be thrown when user did not give consent to the application to record audio. | ||
@@ -250,2 +240,8 @@ * @public | ||
/** | ||
* Error to be thrown if storage API is not supported by the device. | ||
* @public | ||
*/ | ||
export declare const ErrNoStorageSupport: Error; | ||
/** | ||
* Error to be thrown when the microphone was accessed before it was initialized. | ||
@@ -288,6 +284,36 @@ * @public | ||
export declare interface Microphone { | ||
/** | ||
* Registers the callback that is invoked whenever an audio chunk is emitted. | ||
* | ||
* @param cb - the callback to invoke. | ||
*/ | ||
onAudio(cb: AudioCallback): void; | ||
/** | ||
* Initialises the microphone. | ||
* | ||
* This should prepare the microphone infrastructure for receiving audio chunks, | ||
* but the microphone should remain muted after the call. | ||
* This method will be called by the Client as part of client initialisation process. | ||
* | ||
* @param cb - the callback that is invoked after initialisation is completed (either successfully or with an error). | ||
*/ | ||
initialize(cb: ErrorCallback): void; | ||
/** | ||
* Closes the microphone, tearing down all the infrastructure. | ||
* | ||
* The microphone should stop emitting audio after this is called. | ||
* Calling `initialize` again after calling `close` should succeed and make microphone ready to use again. | ||
* This method will be called by the Client as part of client closure process. | ||
* | ||
* @param cb - the callback that should be invoked after the closure process is completed | ||
* (either successfully or with an error). | ||
*/ | ||
close(cb: ErrorCallback): void; | ||
/** | ||
* Mutes the microphone. If the microphone is muted, the `onAudio` callbacks should not be called. | ||
*/ | ||
mute(): void; | ||
/** | ||
* Unmutes the microphone. | ||
*/ | ||
unmute(): void; | ||
@@ -347,2 +373,52 @@ } | ||
/** | ||
* An interface for local key-value storage. | ||
* @public | ||
*/ | ||
export declare interface Storage { | ||
/** | ||
* Initialises the storage. | ||
* | ||
* Any long-running operation (or operation that can fail), should be done in this method, | ||
* rather than in a constructor. | ||
* This method will be called by the Client as part of client initialisation process. | ||
* | ||
* @param cb - the callback that is invoked after initialisation is completed (either successfully or with an error). | ||
*/ | ||
initialize(cb: ErrorCallback): void; | ||
/** | ||
* Closes the storage. | ||
* | ||
* Calling `initialize` again after calling `close` should succeed and make storage ready to use again. | ||
* This method will be called by the Client as part of client closure process. | ||
* | ||
* @param cb - the callback that should be invoked after the closure process is completed | ||
* (either successfully or with an error). | ||
*/ | ||
close(cb: ErrorCallback): void; | ||
/** | ||
* Retrieves a key from the storage. | ||
* | ||
* @param key - the key to retrieve | ||
* @param cb - the callback that should be invoked after retrieval operation is done, | ||
* either with the value or with an error. | ||
*/ | ||
get(key: string, cb: StorageGetCallback): void; | ||
/** | ||
* Adds a key to the storage, possibly overwriting existing value. | ||
* | ||
* @param key - the key to write | ||
* @param val - the value to write | ||
* @param cb - the callback that should be invoked after retrieval operation is done, | ||
* either with the value or with an error. | ||
*/ | ||
set(key: string, val: string, cb: ErrorCallback): void; | ||
} | ||
/** | ||
* A callback that receives either an error or the value retrieved from the storage. | ||
* @public | ||
*/ | ||
export declare type StorageGetCallback = (error?: Error, val?: string) => void; | ||
/** | ||
* A callback that is invoked whenever new tentative entities are received from the API. | ||
@@ -349,0 +425,0 @@ * @public |
@@ -7,3 +7,4 @@ "use strict"; | ||
__export(require("./speechly")); | ||
__export(require("./microphone")); | ||
__export(require("./microphone/const")); | ||
__export(require("./storage/const")); | ||
//# sourceMappingURL=index.js.map |
import { AudioCallback, Microphone, ErrorCallback } from '../types'; | ||
/** | ||
* Microphone implementation for the browser. Uses getUserMedia and Web Audio API. | ||
* @public | ||
*/ | ||
export declare class BrowserMicrophone implements Microphone { | ||
@@ -7,0 +3,0 @@ private readonly sampleRate; |
@@ -5,6 +5,2 @@ "use strict"; | ||
const const_1 = require("./const"); | ||
/** | ||
* Microphone implementation for the browser. Uses getUserMedia and Web Audio API. | ||
* @public | ||
*/ | ||
class BrowserMicrophone { | ||
@@ -11,0 +7,0 @@ constructor(sampleRate) { |
{ | ||
"name": "@speechly/browser-client", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"description": "Browser client for Speechly API", | ||
@@ -37,3 +37,4 @@ "private": false, | ||
"dependencies": { | ||
"locale-code": "^2.0.2" | ||
"locale-code": "^2.0.2", | ||
"uuid": "^7.0.2" | ||
}, | ||
@@ -43,2 +44,3 @@ "devDependencies": { | ||
"@types/jest": "^25.1.2", | ||
"@types/uuid": "^7.0.2", | ||
"@typescript-eslint/eslint-plugin": "^2.19.2", | ||
@@ -67,3 +69,6 @@ "@typescript-eslint/parser": "^2.0.0", | ||
"access": "public" | ||
}, | ||
"resolutions": { | ||
"**/optimist/minimist": "0.2.1" | ||
} | ||
} |
@@ -11,5 +11,7 @@ import { ErrorCallback, ContextCallback } from '../types'; | ||
private readonly debug; | ||
private readonly storage; | ||
private readonly microphone; | ||
private readonly websocket; | ||
private readonly activeContexts; | ||
private deviceId?; | ||
private state; | ||
@@ -16,0 +18,0 @@ private reconnectAttemptCount; |
@@ -7,2 +7,3 @@ "use strict"; | ||
const locale_code_1 = __importDefault(require("locale-code")); | ||
const uuid_1 = require("uuid"); | ||
const microphone_1 = require("../microphone"); | ||
@@ -14,2 +15,7 @@ const websocket_1 = require("../websocket"); | ||
const types_1 = require("./types"); | ||
const storage_1 = require("../storage"); | ||
const deviceIdStorageKey = 'speechly-device-id'; | ||
const defaultSpeechlyURL = 'wss://api.speechly.com/ws'; | ||
const initialReconnectDelay = 1000; | ||
const initialReconnectCount = 5; | ||
/** | ||
@@ -119,4 +125,5 @@ * A client for Speechly Spoken Language Understanding (SLU) API. The client handles initializing the microphone | ||
this.debug = (_a = options.debug) !== null && _a !== void 0 ? _a : false; | ||
this.microphone = (_b = options.microphone) !== null && _b !== void 0 ? _b : new microphone_1.BrowserMicrophone((_c = options.sampleRate) !== null && _c !== void 0 ? _c : microphone_1.DefaultSampleRate); | ||
this.websocket = new websocket_1.Websocket((_d = options.url) !== null && _d !== void 0 ? _d : defaultSpeechlyURL, options.appId, options.language, (_e = options.deviceId) !== null && _e !== void 0 ? _e : uuidv4(), (_f = options.sampleRate) !== null && _f !== void 0 ? _f : microphone_1.DefaultSampleRate); | ||
this.storage = (_b = options.storage) !== null && _b !== void 0 ? _b : new storage_1.LocalStorage(); | ||
this.microphone = (_c = options.microphone) !== null && _c !== void 0 ? _c : new microphone_1.BrowserMicrophone((_d = options.sampleRate) !== null && _d !== void 0 ? _d : microphone_1.DefaultSampleRate); | ||
this.websocket = new websocket_1.Websocket((_e = options.url) !== null && _e !== void 0 ? _e : defaultSpeechlyURL, options.appId, options.language, (_f = options.sampleRate) !== null && _f !== void 0 ? _f : microphone_1.DefaultSampleRate); | ||
this.microphone.onAudio(this.handleMicrophoneAudio); | ||
@@ -156,12 +163,39 @@ this.websocket.onResponse(this.handleWebsocketResponse); | ||
} | ||
this.websocket.initialize((err) => { | ||
const initializeWebsocket = (deviceId, cb) => { | ||
this.websocket.initialize(deviceId, (err) => { | ||
if (err !== undefined) { | ||
this.reconnectWebsocket(); | ||
// TODO: I think this can be confusing for the end user. | ||
// We should only invoke callback when initialization is finished, either successfully or with an error. | ||
// We can instead pass the callback to reconnect and let that invoke it. | ||
return cb(); | ||
} | ||
this.setState(types_1.ClientState.Connected); | ||
return cb(); | ||
}); | ||
}; | ||
this.storage.initialize((err) => { | ||
if (err !== undefined) { | ||
this.reconnectWebsocket(); | ||
// TODO: I think this can be confusing for the end user. | ||
// We should only invoke callback when initialization is finished, either successfully or with an error. | ||
// We can instead pass the callback to reconnect and let that invoke it. | ||
return cb(); | ||
return cb(err); | ||
} | ||
this.setState(types_1.ClientState.Connected); | ||
return cb(); | ||
this.storage.get(deviceIdStorageKey, (err, val) => { | ||
if (err !== undefined) { | ||
// Device ID was not found in the storage, generate new ID and store it. | ||
const deviceId = uuid_1.v4(); | ||
this.storage.set(deviceIdStorageKey, deviceId, (err) => { | ||
if (err !== undefined) { | ||
// At this point we couldn't load device ID from storage, nor we could store a new one there. | ||
// Give up initialisation and return an error. | ||
return cb(err); | ||
} | ||
// Newly generated ID was stored, proceed with initialization. | ||
this.deviceId = deviceId; | ||
return initializeWebsocket(deviceId, cb); | ||
}); | ||
} | ||
// Device ID was found in the storage, proceed with initialization. | ||
const deviceId = val; | ||
this.deviceId = deviceId; | ||
return initializeWebsocket(deviceId, cb); | ||
}); | ||
}); | ||
@@ -175,3 +209,3 @@ }); | ||
close(cb = () => { }) { | ||
this.microphone.close((err) => { | ||
this.storage.close((err) => { | ||
const errs = []; | ||
@@ -181,9 +215,14 @@ if (err !== undefined) { | ||
} | ||
const wsErr = this.websocket.close(1000, 'client disconnecting'); | ||
if (wsErr !== undefined) { | ||
errs.push(wsErr.message); | ||
} | ||
this.activeContexts.clear(); | ||
this.setState(types_1.ClientState.Disconnected); | ||
return errs.length > 0 ? cb(Error(errs.join(','))) : cb(); | ||
this.microphone.close((err) => { | ||
if (err !== undefined) { | ||
errs.push(err.message); | ||
} | ||
const wsErr = this.websocket.close(1000, 'client disconnecting'); | ||
if (wsErr !== undefined) { | ||
errs.push(wsErr.message); | ||
} | ||
this.activeContexts.clear(); | ||
this.setState(types_1.ClientState.Disconnected); | ||
return errs.length > 0 ? cb(Error(errs.join(','))) : cb(); | ||
}); | ||
}); | ||
@@ -297,2 +336,6 @@ } | ||
reconnectWebsocket() { | ||
if (this.deviceId === undefined) { | ||
return this.setState(types_1.ClientState.Disconnected); | ||
} | ||
const deviceId = this.deviceId; | ||
if (this.reconnectAttemptCount < 1) { | ||
@@ -311,3 +354,3 @@ return this.setState(types_1.ClientState.Disconnected); | ||
this.nextReconnectDelay = this.nextReconnectDelay * 2; | ||
this.websocket.initialize((err) => { | ||
this.websocket.initialize(deviceId, (err) => { | ||
if (err !== undefined) { | ||
@@ -332,12 +375,2 @@ return this.reconnectWebsocket(); | ||
exports.Client = Client; | ||
const defaultSpeechlyURL = 'wss://api.speechly.com/ws'; | ||
const initialReconnectDelay = 1000; | ||
const initialReconnectCount = 5; | ||
function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { | ||
// eslint-disable-next-line one-var | ||
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
//# sourceMappingURL=client.js.map |
@@ -1,2 +0,2 @@ | ||
import { Microphone } from '../types'; | ||
import { Microphone, Storage } from '../types'; | ||
/** | ||
@@ -20,6 +20,2 @@ * The options which can be used to configure the client. | ||
/** | ||
* The identifier of the device which is using the client. | ||
*/ | ||
deviceId?: string; | ||
/** | ||
* The sample rate of the audio to use. | ||
@@ -33,5 +29,11 @@ */ | ||
/** | ||
* Microphone instance. | ||
* Custom microphone implementation. | ||
* If not provided, an implementation based on getUserMedia and Web Audio API is used. | ||
*/ | ||
microphone?: Microphone; | ||
/** | ||
* Custom storage implementation. | ||
* If not provided, browser's LocalStorage API is used. | ||
*/ | ||
storage?: Storage; | ||
} | ||
@@ -38,0 +40,0 @@ /** |
@@ -17,2 +17,7 @@ /** | ||
/** | ||
* A callback that receives either an error or the value retrieved from the storage. | ||
* @public | ||
*/ | ||
export declare type StorageGetCallback = (error?: Error, val?: string) => void; | ||
/** | ||
* An interface for a microphone. | ||
@@ -22,7 +27,80 @@ * @public | ||
export interface Microphone { | ||
/** | ||
* Registers the callback that is invoked whenever an audio chunk is emitted. | ||
* | ||
* @param cb - the callback to invoke. | ||
*/ | ||
onAudio(cb: AudioCallback): void; | ||
/** | ||
* Initialises the microphone. | ||
* | ||
* This should prepare the microphone infrastructure for receiving audio chunks, | ||
* but the microphone should remain muted after the call. | ||
* This method will be called by the Client as part of client initialisation process. | ||
* | ||
* @param cb - the callback that is invoked after initialisation is completed (either successfully or with an error). | ||
*/ | ||
initialize(cb: ErrorCallback): void; | ||
/** | ||
* Closes the microphone, tearing down all the infrastructure. | ||
* | ||
* The microphone should stop emitting audio after this is called. | ||
* Calling `initialize` again after calling `close` should succeed and make microphone ready to use again. | ||
* This method will be called by the Client as part of client closure process. | ||
* | ||
* @param cb - the callback that should be invoked after the closure process is completed | ||
* (either successfully or with an error). | ||
*/ | ||
close(cb: ErrorCallback): void; | ||
/** | ||
* Mutes the microphone. If the microphone is muted, the `onAudio` callbacks should not be called. | ||
*/ | ||
mute(): void; | ||
/** | ||
* Unmutes the microphone. | ||
*/ | ||
unmute(): void; | ||
} | ||
/** | ||
* An interface for local key-value storage. | ||
* @public | ||
*/ | ||
export interface Storage { | ||
/** | ||
* Initialises the storage. | ||
* | ||
* Any long-running operation (or operation that can fail), should be done in this method, | ||
* rather than in a constructor. | ||
* This method will be called by the Client as part of client initialisation process. | ||
* | ||
* @param cb - the callback that is invoked after initialisation is completed (either successfully or with an error). | ||
*/ | ||
initialize(cb: ErrorCallback): void; | ||
/** | ||
* Closes the storage. | ||
* | ||
* Calling `initialize` again after calling `close` should succeed and make storage ready to use again. | ||
* This method will be called by the Client as part of client closure process. | ||
* | ||
* @param cb - the callback that should be invoked after the closure process is completed | ||
* (either successfully or with an error). | ||
*/ | ||
close(cb: ErrorCallback): void; | ||
/** | ||
* Retrieves a key from the storage. | ||
* | ||
* @param key - the key to retrieve | ||
* @param cb - the callback that should be invoked after retrieval operation is done, | ||
* either with the value or with an error. | ||
*/ | ||
get(key: string, cb: StorageGetCallback): void; | ||
/** | ||
* Adds a key to the storage, possibly overwriting existing value. | ||
* | ||
* @param key - the key to write | ||
* @param val - the value to write | ||
* @param cb - the callback that should be invoked after retrieval operation is done, | ||
* either with the value or with an error. | ||
*/ | ||
set(key: string, val: string, cb: ErrorCallback): void; | ||
} |
import { ContextCallback, ErrorCallback } from '../types'; | ||
import { WebsocketClient, ResponseCallback, CloseCallback } from './types'; | ||
export declare class Websocket implements WebsocketClient { | ||
private readonly url; | ||
private readonly baseUrl; | ||
private readonly languageCode; | ||
private readonly sampleRate; | ||
private readonly appId; | ||
@@ -13,4 +15,4 @@ private websocket?; | ||
onClose(cb: CloseCallback): void; | ||
constructor(baseUrl: string, appId: string, language: string, deviceId: string, sampleRate: number); | ||
initialize(cb: ErrorCallback): void; | ||
constructor(baseUrl: string, appId: string, language: string, sampleRate: number); | ||
initialize(deviceId: string, cb: ErrorCallback): void; | ||
close(closeCode: number, closeReason: string): Error | void; | ||
@@ -17,0 +19,0 @@ start(cb: ContextCallback): void; |
@@ -5,3 +5,3 @@ "use strict"; | ||
class Websocket { | ||
constructor(baseUrl, appId, language, deviceId, sampleRate) { | ||
constructor(baseUrl, appId, language, sampleRate) { | ||
this.startCbs = []; | ||
@@ -54,3 +54,5 @@ this.stopCbs = []; | ||
}; | ||
this.url = generateWsUrl(baseUrl, deviceId, language, sampleRate); | ||
this.baseUrl = baseUrl; | ||
this.languageCode = language; | ||
this.sampleRate = sampleRate; | ||
this.appId = appId; | ||
@@ -64,7 +66,8 @@ } | ||
} | ||
initialize(cb) { | ||
initialize(deviceId, cb) { | ||
if (this.websocket !== undefined) { | ||
return cb(Error('Cannot initialize an already initialized websocket client')); | ||
} | ||
initializeWebsocket(this.url, this.appId, (err, ws) => { | ||
const url = generateWsUrl(this.baseUrl, deviceId, this.languageCode, this.sampleRate); | ||
initializeWebsocket(url, this.appId, (err, ws) => { | ||
if (err !== undefined) { | ||
@@ -71,0 +74,0 @@ return cb(err); |
import { ErrorCallback, ContextCallback } from '../types'; | ||
export interface WebsocketClient { | ||
initialize(cb: ErrorCallback): void; | ||
initialize(deviceID: string, cb: ErrorCallback): void; | ||
close(closeCode: number, closeReason: string): Error | void; | ||
@@ -5,0 +5,0 @@ start(cb: ContextCallback): void; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109619
73
2006
2
23
+ Addeduuid@^7.0.2
+ Addeduuid@7.0.3(transitive)