Merge some squashed changes from master
Related: #7, #8, #10, #11, #13, #14, #18, #22, #23, #24, #25, #26, #27
This commit is contained in:
Родитель
94d2a31cd8
Коммит
57cbb6df12
|
@ -0,0 +1,23 @@
|
|||
root = true
|
||||
|
||||
[*.{js,ts}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{cmd,json,md,txt,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
|
||||
[*.sh]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
|
@ -1,3 +1,4 @@
|
|||
.editorconfig text
|
||||
.gitattributes text
|
||||
.gitignore text
|
||||
.npmignore text
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Jest All",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": ["--runInBand", "--coverage", "false"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Jest Current File",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": ["--runInBand", "--coverage", "false", "${relativeFile}"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -10,32 +10,32 @@ if "%~1" equ "-h" goto :Usage
|
|||
|
||||
set TEST_SETTING_FILE_DIR=%~dp0\secrets
|
||||
if NOT EXIST "%TEST_SETTING_FILE_DIR%" (
|
||||
md "%TEST_SETTING_FILE_DIR%" || (
|
||||
echo Error creating directory %TEST_SETTING_FILE_DIR%
|
||||
exit /b 1
|
||||
)
|
||||
md "%TEST_SETTING_FILE_DIR%" || (
|
||||
echo Error creating directory %TEST_SETTING_FILE_DIR%
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
set TEST_SETTING_FILE_NAME=%TEST_SETTING_FILE_DIR%\TestConfiguration.ts
|
||||
|
||||
if EXIST "%TEST_SETTING_FILE_NAME%" (
|
||||
echo Clearing values from settings file.
|
||||
echo. > "%TEST_SETTING_FILE_NAME%" || (
|
||||
echo Error creating file %TEST_SETTING_FILE_NAME%
|
||||
exit /b 1
|
||||
)
|
||||
echo Clearing values from settings file.
|
||||
echo. > "%TEST_SETTING_FILE_NAME%" || (
|
||||
echo Error creating file %TEST_SETTING_FILE_NAME%
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
@echo import { Settings } from "../tests/Settings" > "%TEST_SETTING_FILE_NAME%"
|
||||
|
||||
:NextArg
|
||||
if "%~1" == "" (
|
||||
goto :eof
|
||||
goto :eof
|
||||
)
|
||||
|
||||
for /f "tokens=1,2 delims=:" %%I in ("%~1") do (
|
||||
echo Setting Settings.%%I = "%%J"
|
||||
echo Settings.%%I = "%%J"; >> "%TEST_SETTING_FILE_NAME%"
|
||||
echo Setting Settings.%%I = "%%J"
|
||||
echo Settings.%%I = "%%J"; >> "%TEST_SETTING_FILE_NAME%"
|
||||
)
|
||||
|
||||
shift
|
||||
|
@ -46,7 +46,7 @@ exit /b 0
|
|||
:Usage
|
||||
@echo off
|
||||
echo.
|
||||
echo Usage: %~n0 ^<ParamName^>:^<Value^>
|
||||
echo Usage: %~n0 ^<ParamName^>:^<Value^>
|
||||
echo.
|
||||
echo Writes any ^<ParamName^>:^<Value^> pair to the test settings file for JavaScript bindings tests.
|
||||
echo.
|
||||
|
|
86
README.md
86
README.md
|
@ -1,55 +1,61 @@
|
|||
[![npm version](https://badge.fury.io/js/microsoft-cognitiveservices-speech-sdk.svg)](https://badge.fury.io/js/microsoft-cognitiveservices-speech-sdk)
|
||||
[![Downloads](https://img.shields.io/npm/dm/microsoft-cognitiveservices-speech-sdk.svg)](https://www.npmjs.com/package/microsoft-cognitiveservices-speech-sdk)
|
||||
|
||||
# Microsoft Cognitive Services Speech SDK for JavaScript
|
||||
|
||||
Visit https://aka.ms/csspeech.
|
||||
The Microsoft Cognitive Services Speech SDK for JavaScript is the JavaScript version of the Microsoft Cognitive Services Speech SDK. An in-depth description of feature set, functionality, supported platforms, as well as installation options is available [here](https://aka.ms/csspeech).
|
||||
|
||||
## Build the project
|
||||
The JavaScript versions of the Cognitive Services Speech SDK supports browser scenarios as well as the Node.js environment.
|
||||
|
||||
To build the project you need to install the required packages, and then run the actual build.
|
||||
## Installing
|
||||
|
||||
### Install the required packages
|
||||
For the latest stable version:
|
||||
|
||||
Installation of the required packages is required once in your enlistment. Our automated build restores the packages with the following command:
|
||||
|
||||
```
|
||||
npm ci
|
||||
```bash
|
||||
npm install microsoft-cognitiveservices-speech-sdk
|
||||
```
|
||||
|
||||
This will install packages exactly matching the package configuration from `package-lock.json`.
|
||||
## Documentation
|
||||
|
||||
In a development environment you can also use the following command:
|
||||
* [Quick tutorial - Node.js](https://docs.microsoft.com/azure/cognitive-services/speech-service/quickstart-js-node)
|
||||
* [Quick tutorial - Browser](https://docs.microsoft.com/azure/cognitive-services/speech-service/quickstart-js-browser)
|
||||
* [API Reference](https://aka.ms/csspeech/javascriptref)
|
||||
* [Samples](https://aka.ms/csspeech/samples)
|
||||
* [Speech SDK Homepage](https://aka.ms/csspeech)
|
||||
|
||||
## Building
|
||||
|
||||
This source code for the Cognitive Services Speeck SDK (JavaScript) is available in a public [GitHub repository](https://github.com/Microsoft/cognitive-services-speech-sdk-js). You are not required to go through the build process. We create prebuilt packages tuned for your use-cases. These are updated in regular intervals.
|
||||
|
||||
In order to build the Speech SDK, ensure that you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed.
|
||||
|
||||
Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/cognitive-services-speech-sdk-js
|
||||
```
|
||||
|
||||
Change to the Speech SDK directory:
|
||||
|
||||
```bash
|
||||
cd cognitive-services-speech-sdk-js
|
||||
```
|
||||
|
||||
Install the required packages:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Run the build
|
||||
|
||||
Once the dependencies are installed run the build by
|
||||
Run the build:
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
npx gulp bundle
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
npx gulp compress
|
||||
```
|
||||
|
||||
> Note: `npx` is packaged with NPM 5.2 or above. Update NPM / Node if you
|
||||
> don't have it or install it globally with `npm install -g npx` (less
|
||||
> preferable).
|
||||
|
||||
## Data / Telemetry
|
||||
|
||||
This project collects data and sends it to Microsoft to help monitor our
|
||||
service performance and improve our products and services. Read the [Microsoft
|
||||
This project collects data and sends it to Microsoft to help monitor our service performance and improve our products and services. Read the [Microsoft
|
||||
Privacy Statement](https://aka.ms/csspeech/privacy) to learn more.
|
||||
|
||||
To disable telemetry, you can call the following API:
|
||||
|
@ -59,26 +65,16 @@ To disable telemetry, you can call the following API:
|
|||
sdk.Recognizer.enableTelemetry(false);
|
||||
```
|
||||
|
||||
This is a global setting and will disable telemetry for all recognizers
|
||||
(already created or new recognizers).
|
||||
This is a global setting and will disable telemetry for all recognizers (already created or new recognizers).
|
||||
|
||||
We strongly recommend you keep telemetry enabled. With telemetry enabled you
|
||||
transmit information about your platform (operating system and possibly, Speech
|
||||
Service relevant information like microphone characteristics, etc.), and
|
||||
information about the performance of the Speech Service (the time when you did
|
||||
send data and when you received data). It can be used to tune the service,
|
||||
monitor service performance and stability, and might help us to analyze
|
||||
reported problems. Without telemetry enabled, it is not possible for us to do any
|
||||
form of detailed analysis in case of a support request.
|
||||
We strongly recommend you keep telemetry enabled. With telemetry enabled you transmit information about your platform (operating system and possibly, Speech Service relevant information like microphone characteristics, etc.), and information about the performance of the Speech Service (the time when you did send data and when you received data). It can be used to tune the service, monitor service performance and stability, and might help us to analyze reported problems. Without telemetry enabled, it is not possible for us to do any form of detailed analysis in case of a support request.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
|
10
RunTests.cmd
10
RunTests.cmd
|
@ -14,10 +14,10 @@ set TEST_SETTING_FILE_EXISTED=0
|
|||
if EXIST "%TEST_SETTING_FILE_NAME%" set TEST_SETTING_FILE_EXISTED=1
|
||||
|
||||
if "%~1" NEQ "" (
|
||||
call "%~dp0BuildTestConfig.cmd" %* || (
|
||||
echo Error creating test config.
|
||||
exit /b 1
|
||||
)
|
||||
call "%~dp0BuildTestConfig.cmd" %* || (
|
||||
echo Error creating test config.
|
||||
exit /b 1
|
||||
)
|
||||
) else if %TEST_SETTING_FILE_EXISTED% EQU 0 (
|
||||
echo Warning: No test config and no parameters specified. This will probably fail. 1>&2
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ call npm run test
|
|||
set NPM_ERROR=%ERRORLEVEL%
|
||||
|
||||
if %TEST_SETTING_FILE_EXISTED% EQU 0 (
|
||||
del "%TEST_SETTING_FILE_NAME%"
|
||||
del "%TEST_SETTING_FILE_NAME%"
|
||||
)
|
||||
|
||||
popd
|
||||
|
|
81
ci/build.yml
81
ci/build.yml
|
@ -13,20 +13,77 @@ resources:
|
|||
- repo: self
|
||||
clean: true
|
||||
|
||||
variables:
|
||||
SPEECHSDK_JS_ROOT: .
|
||||
|
||||
jobs:
|
||||
|
||||
- job: Pre
|
||||
- job: Build
|
||||
pool:
|
||||
name: Hosted Ubuntu 1604
|
||||
timeoutInMinutes: 30
|
||||
name: Hosted VS2017
|
||||
demands: npm
|
||||
timeoutInMinutes: 60
|
||||
variables:
|
||||
ArtifactOut: $(Build.ArtifactStagingDirectory)/Out/JavaScript
|
||||
SPEECHSDK_RUN_TESTS: true
|
||||
steps:
|
||||
- bash: ./ci/check-git-head.sh
|
||||
displayName: Repository checks
|
||||
|
||||
- template: jsbuild.yml
|
||||
parameters:
|
||||
dependsOn: Pre
|
||||
condition: true
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: Component Detection
|
||||
condition: >
|
||||
and(eq(variables['System.CollectionId'], '19422243-19b9-4d85-9ca6-bc961861d287'),
|
||||
eq(variables['System.DefinitionId'], '7863'),
|
||||
eq(variables['Build.SourceBranch'], 'refs/heads/master'),
|
||||
or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual')))
|
||||
- bash: npm ci && npm run civersion
|
||||
displayName: Install packages and set version / SPEECHSDK_SEMVER2NOMETA
|
||||
- bash: |
|
||||
F=src/common.speech/RecognizerConfig.ts
|
||||
[[ -f $F ]] || exit 1
|
||||
perl -i.bak -p -e 'BEGIN { $c = 0 } $c += s/(?<=const SPEECHSDK_CLIENTSDK_VERSION = ")[^"]*/$(SPEECHSDK_SEMVER2NOMETA)/g; END { die "Patched SPEECHSDK_CLIENTSDK_VERSION $c time(s), expected 1.\n" if $c != 1 }' "$F"
|
||||
E=$?
|
||||
rm -f "$F.bak"
|
||||
git diff
|
||||
exit $E
|
||||
displayName: Stamp SPEECHSDK_CLIENTSDK_VERSION
|
||||
- bash: npm pack
|
||||
displayName: Build and pack SDK
|
||||
- bash: "echo '##vso[task.setvariable variable=SPEECHSDK_RUN_TESTS]false'"
|
||||
condition: or(failed(), canceled())
|
||||
displayName: Skip tests on build failure
|
||||
- script: |
|
||||
RunTests.cmd ^
|
||||
SpeechSubscriptionKey:$(speech-ne-s0-key1) ^
|
||||
SpeechRegion:northeurope ^
|
||||
LuisSubscriptionKey:$(luis-westus-s0-201809-key1) ^
|
||||
LuisRegion:westus ^
|
||||
SpeechTestEndpointId:ec3432b2-8584-4736-865a-556213b9f6fd
|
||||
displayName: Run tests
|
||||
condition: eq(variables['SPEECHSDK_RUN_TESTS'], 'true')
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish test results
|
||||
condition: eq(variables['SPEECHSDK_RUN_TESTS'], 'true')
|
||||
- bash: |
|
||||
set -u -e -o pipefail -x
|
||||
PACKAGE_BASE=microsoft-cognitiveservices-speech-sdk
|
||||
PACKAGE_NAME=$PACKAGE_BASE-$SPEECHSDK_SEMVER2NOMETA.tgz
|
||||
PACKAGE_IN=$PACKAGE_NAME
|
||||
PACKAGE_OUT="/npm"
|
||||
ZIP_OUT="$(ArtifactOut)/SpeechSDK-JavaScript-$SPEECHSDK_SEMVER2NOMETA"
|
||||
mkdir -p "$PACKAGE_OUT" "$ZIP_OUT"
|
||||
cp --preserve "$PACKAGE_IN" "$PACKAGE_OUT"
|
||||
echo SRI hash for microsoft.cognitiveservices.speech.sdk.bundle.js: sha512-"$(openssl dgst -sha512 -binary distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.js | openssl base64 -A)"
|
||||
cp --preserve LICENSE REDIST.txt distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.* distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle-min.* "$ZIP_OUT"
|
||||
displayName: Create drop
|
||||
- task: ArchiveFiles@2
|
||||
inputs:
|
||||
rootFolderOrFile: $(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)
|
||||
includeRootFolder: true
|
||||
archiveType: zip
|
||||
archiveFile: $(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA).zip
|
||||
displayName: Create .zip
|
||||
- bash: rm -rf "$(ArtifactOut)/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)"
|
||||
displayName: Remove temporary directory
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish drop
|
||||
inputs:
|
||||
PathtoPublish: $(ArtifactOut)
|
||||
ArtifactName: JavaScript
|
||||
publishLocation: Container
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
#
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT license.
|
||||
#
|
||||
|
||||
parameters:
|
||||
dependsOn: ''
|
||||
condition: succeeded()
|
||||
|
||||
jobs:
|
||||
|
||||
- job: JsBuild
|
||||
dependsOn: ${{ parameters.dependsOn }}
|
||||
condition: ${{ parameters.condition }}
|
||||
pool:
|
||||
name: Hosted VS2017
|
||||
demands: npm
|
||||
timeoutInMinutes: 60
|
||||
steps:
|
||||
- bash: npm ci && npm run civersion
|
||||
displayName: Set version
|
||||
workingDirectory: $(SPEECHSDK_JS_ROOT)
|
||||
- bash: |
|
||||
F=$(SPEECHSDK_JS_ROOT)/src/common.speech/RecognizerConfig.ts
|
||||
[[ -f $F ]] || exit 1
|
||||
perl -i.bak -p -e 'BEGIN { $c = 0 } $c += s/(?<=const SPEECHSDK_CLIENTSDK_VERSION = ")[^"]*/$(SPEECHSDK_SEMVER2NOMETA)/g; END { die "Patched SPEECHSDK_CLIENTSDK_VERSION $c time(s), expected 1.\n" if $c != 1 }' "$F"
|
||||
E=$?
|
||||
rm -f "$F.bak"
|
||||
git diff
|
||||
exit $E
|
||||
displayName: Stamp SPEECHSDK_CLIENTSDK_VERSION
|
||||
- bash: npm run build && npm pack
|
||||
displayName: Build and pack SDK
|
||||
workingDirectory: $(SPEECHSDK_JS_ROOT)
|
||||
- script: |
|
||||
RunTests.cmd ^
|
||||
SpeechSubscriptionKey:$(speech-ne-s0-key1) ^
|
||||
SpeechRegion:northeurope ^
|
||||
LuisSubscriptionKey:$(luis-westus-s0-201809-key1) ^
|
||||
LuisRegion:westus ^
|
||||
SpeechTestEndpointId:ec3432b2-8584-4736-865a-556213b9f6fd
|
||||
displayName: Run tests
|
||||
workingDirectory: $(SPEECHSDK_JS_ROOT)
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish test results
|
||||
- bash: |
|
||||
set -u -e -o pipefail -x
|
||||
PACKAGE_BASE=microsoft-cognitiveservices-speech-sdk
|
||||
PACKAGE_NAME=$PACKAGE_BASE-$SPEECHSDK_SEMVER2NOMETA.tgz
|
||||
PACKAGE_IN=$(SPEECHSDK_JS_ROOT)/$PACKAGE_NAME
|
||||
PACKAGE_OUT="$(Build.ArtifactStagingDirectory)/Out/JavaScript/npm"
|
||||
ZIP_OUT="$(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$SPEECHSDK_SEMVER2NOMETA"
|
||||
mkdir -p "$PACKAGE_OUT" "$ZIP_OUT"
|
||||
cp --preserve "$PACKAGE_IN" "$PACKAGE_OUT"
|
||||
echo SRI hash for microsoft.cognitiveservices.speech.sdk.bundle.js: sha512-"$(openssl dgst -sha512 -binary $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.js | openssl base64 -A)"
|
||||
cp --preserve $(SPEECHSDK_JS_ROOT)/LICENSE $(SPEECHSDK_JS_ROOT)/REDIST.txt $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle.* $(SPEECHSDK_JS_ROOT)/distrib/browser/microsoft.cognitiveservices.speech.sdk.bundle-min.* "$ZIP_OUT"
|
||||
displayName: Create drop
|
||||
- task: ArchiveFiles@2
|
||||
inputs:
|
||||
rootFolderOrFile: $(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)
|
||||
includeRootFolder: true
|
||||
archiveType: zip
|
||||
archiveFile: $(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA).zip
|
||||
displayName: Create .zip
|
||||
- bash: rm -rf "$(Build.ArtifactStagingDirectory)/Out/JavaScript/SpeechSDK-JavaScript-$(SPEECHSDK_SEMVER2NOMETA)"
|
||||
displayName: Remove temporary directory
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish drop
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)/Out/JavaScript'
|
||||
ArtifactName: JavaScript
|
||||
publishLocation: Container
|
134
ci/version.js
134
ci/version.js
|
@ -4,80 +4,80 @@
|
|||
//
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
var semver = require("semver");
|
||||
var exec = require("child_process").exec;
|
||||
'use strict';
|
||||
var semver = require("semver");
|
||||
var exec = require("child_process").exec;
|
||||
|
||||
if (!process.env.npm_package_version) {
|
||||
throw "npm_package_version not set; run this via 'npm run'"
|
||||
}
|
||||
|
||||
var givenVersion = process.env.npm_package_version;
|
||||
|
||||
if (!semver.valid(givenVersion)) {
|
||||
throw "Invalid version " + givenVersion;
|
||||
}
|
||||
|
||||
var baseVersion = semver.major(givenVersion) + "." + semver.minor(givenVersion) + "." + semver.patch(givenVersion);
|
||||
var prerelease = semver.prerelease(givenVersion) || []
|
||||
|
||||
var buildId = process.env.BUILD_BUILDID || "1"
|
||||
|
||||
var buildType = "dev"
|
||||
var inAzureDevOps = false
|
||||
|
||||
if (process.env.SYSTEM_COLLECTIONID === "19422243-19b9-4d85-9ca6-bc961861d287" &&
|
||||
(process.env.SYSTEM_DEFINITIONID === "4833" || process.env.SYSTEM_DEFINITIONID === "7863")) {
|
||||
|
||||
inAzureDevOps = true
|
||||
|
||||
if (process.env.BUILD_SOURCEBRANCH.match("^refs/heads/release/")) {
|
||||
buildType = "prod"
|
||||
} else if (process.env.BUILD_SOURCEBRANCH === "refs/heads/master" &&
|
||||
(process.env.BUILD_REASON === "Schedule" ||
|
||||
process.env.BUILD_REASON === "Manual")) {
|
||||
buildType = "int"
|
||||
if (!process.env.npm_package_version) {
|
||||
throw "npm_package_version not set; run this via 'npm run'"
|
||||
}
|
||||
}
|
||||
|
||||
// Check our version constraints
|
||||
if (buildType === "prod") {
|
||||
// Full version give in package.json
|
||||
if (prerelease.length != 0 &&
|
||||
(prerelease.length != 2 ||
|
||||
!prerelease[0].match("^(?:alpha|beta|rc)$") ||
|
||||
prerelease[1] < 1)) {
|
||||
throw "For prod build types, version must have no pre-release tag, or alpha / beta / rc with a positive number."
|
||||
var givenVersion = process.env.npm_package_version;
|
||||
|
||||
if (!semver.valid(givenVersion)) {
|
||||
throw "Invalid version " + givenVersion;
|
||||
}
|
||||
} else if (prerelease.length != 3 ||
|
||||
(prerelease[0] !== "alpha" || prerelease[1] !== 0 || prerelease[2] !== 1)) {
|
||||
throw "For non-prod build types, checked-in version must end in -alpha.0.1"
|
||||
}
|
||||
|
||||
var versionToUse
|
||||
var baseVersion = semver.major(givenVersion) + "." + semver.minor(givenVersion) + "." + semver.patch(givenVersion);
|
||||
var prerelease = semver.prerelease(givenVersion) || []
|
||||
|
||||
if (buildType === "dev") {
|
||||
versionToUse = baseVersion + "-alpha.0." + buildId
|
||||
} else if (buildType === "int") {
|
||||
versionToUse = baseVersion + "-beta.0." + buildId
|
||||
} else if (buildType === "prod") {
|
||||
versionToUse = givenVersion
|
||||
} else {
|
||||
throw "Internal error. Unexpected build type: " + buildType
|
||||
}
|
||||
var buildId = process.env.BUILD_BUILDID || "1"
|
||||
|
||||
if (inAzureDevOps) {
|
||||
console.log("##vso[task.setvariable variable=SPEECHSDK_SEMVER2NOMETA]" + versionToUse);
|
||||
}
|
||||
var buildType = "dev"
|
||||
var inAzureDevOps = false
|
||||
|
||||
if (givenVersion !== versionToUse) {
|
||||
console.log("Setting version to " + versionToUse);
|
||||
exec("npm version --no-git-tag-version " + versionToUse, { cwd: __dirname + "/.." },
|
||||
function (error) {
|
||||
if (error) {
|
||||
throw error;
|
||||
if (process.env.SYSTEM_COLLECTIONID === "19422243-19b9-4d85-9ca6-bc961861d287" &&
|
||||
process.env.SYSTEM_DEFINITIONID === "7863") {
|
||||
|
||||
inAzureDevOps = true
|
||||
|
||||
if (process.env.BUILD_SOURCEBRANCH.match("^refs/heads/release/")) {
|
||||
buildType = "prod"
|
||||
} else if (process.env.BUILD_SOURCEBRANCH === "refs/heads/master" &&
|
||||
(process.env.BUILD_REASON === "Schedule" ||
|
||||
process.env.BUILD_REASON === "Manual")) {
|
||||
buildType = "int"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check our version constraints
|
||||
if (buildType === "prod") {
|
||||
// Full version give in package.json
|
||||
if (prerelease.length != 0 &&
|
||||
(prerelease.length != 2 ||
|
||||
!prerelease[0].match("^(?:alpha|beta|rc)$") ||
|
||||
prerelease[1] < 1)) {
|
||||
throw "For prod build types, version must have no pre-release tag, or alpha / beta / rc with a positive number."
|
||||
}
|
||||
} else if (prerelease.length != 3 ||
|
||||
(prerelease[0] !== "alpha" || prerelease[1] !== 0 || prerelease[2] !== 1)) {
|
||||
throw "For non-prod build types, checked-in version must end in -alpha.0.1"
|
||||
}
|
||||
|
||||
var versionToUse
|
||||
|
||||
if (buildType === "dev") {
|
||||
versionToUse = baseVersion + "-alpha.0." + buildId
|
||||
} else if (buildType === "int") {
|
||||
versionToUse = baseVersion + "-beta.0." + buildId
|
||||
} else if (buildType === "prod") {
|
||||
versionToUse = givenVersion
|
||||
} else {
|
||||
throw "Internal error. Unexpected build type: " + buildType
|
||||
}
|
||||
|
||||
if (inAzureDevOps) {
|
||||
console.log("##vso[task.setvariable variable=SPEECHSDK_SEMVER2NOMETA]" + versionToUse);
|
||||
}
|
||||
|
||||
if (givenVersion !== versionToUse) {
|
||||
console.log("Setting version to " + versionToUse);
|
||||
exec("npm version --no-git-tag-version " + versionToUse, { cwd: __dirname + "/.." },
|
||||
function (error) {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}());
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
{
|
||||
"templates": {
|
||||
"applicationName": "Microsoft Cognitive Services Speech SDK",
|
||||
"disqus": "",
|
||||
"googleAnalytics": "",
|
||||
"openGraph": {
|
||||
"title": "",
|
||||
"type": "website",
|
||||
"image": "",
|
||||
"site_name": "",
|
||||
"url": ""
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"title": "Microsoft Cognitive Services Speech SDK",
|
||||
"description": "Microsoft Cognitive Services Speech SDK",
|
||||
"keyword": ""
|
||||
},
|
||||
"linenums": true,
|
||||
"source": {
|
||||
"include": [
|
||||
"./distrib/src/sdk"
|
||||
],
|
||||
"includePattern": ".+\\.js(doc)?$",
|
||||
"excludePattern": "(^|\\/|\\\\)_"
|
||||
},
|
||||
"opts": {
|
||||
"encoding": "utf8",
|
||||
"recurse": true,
|
||||
"private": false,
|
||||
"lenient": true,
|
||||
"destination": "./outjs",
|
||||
"template": "templates/default"
|
||||
"templates": {
|
||||
"applicationName": "Microsoft Cognitive Services Speech SDK",
|
||||
"disqus": "",
|
||||
"googleAnalytics": "",
|
||||
"openGraph": {
|
||||
"title": "",
|
||||
"type": "website",
|
||||
"image": "",
|
||||
"site_name": "",
|
||||
"url": ""
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"title": "Microsoft Cognitive Services Speech SDK",
|
||||
"description": "Microsoft Cognitive Services Speech SDK",
|
||||
"keyword": ""
|
||||
},
|
||||
"linenums": true,
|
||||
"source": {
|
||||
"include": [
|
||||
"./distrib/src/sdk"
|
||||
],
|
||||
"includePattern": ".+\\.js(doc)?$",
|
||||
"excludePattern": "(^|\\/|\\\\)_"
|
||||
},
|
||||
"opts": {
|
||||
"encoding": "utf8",
|
||||
"recurse": true,
|
||||
"private": false,
|
||||
"lenient": true,
|
||||
"destination": "./outjs",
|
||||
"template": "templates/default"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { ConsoleLoggingListener, LocalStorage, SessionStorage } from "./src/common.browser/Exports";
|
||||
import { Events, Storage } from "./src/common/Exports";
|
||||
import { ConsoleLoggingListener } from "./src/common.browser/Exports";
|
||||
import { Events } from "./src/common/Exports";
|
||||
|
||||
// Common.Storage.SetLocalStorage(new Common.Browser.LocalStorage());
|
||||
// Common.Storage.SetSessionStorage(new Common.Browser.SessionStorage());
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
12
package.json
12
package.json
|
@ -2,7 +2,7 @@
|
|||
"name": "microsoft-cognitiveservices-speech-sdk",
|
||||
"author": "Microsoft Corporation",
|
||||
"homepage": "https://docs.microsoft.com/azure/cognitive-services/speech-service/",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0-alpha.0.1",
|
||||
"license": "MIT",
|
||||
"description": "Microsoft Cognitive Services Speech SDK for JavaScript",
|
||||
"keywords": [
|
||||
|
@ -34,19 +34,21 @@
|
|||
"REDIST.txt"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/jest": "^23.3.1",
|
||||
"@types/jest": "^23.3.10",
|
||||
"@types/node": "^10.7.0",
|
||||
"@types/request": "^2.48.1",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-uglify": "^3.0.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-tslint": "^8.0.0",
|
||||
"gulp-typescript": "^3.2.3",
|
||||
"jest": "^23.5.0",
|
||||
"gulp-uglify": "^3.0.1",
|
||||
"jest": "^23.6.0",
|
||||
"jest-junit": "^5.1.0",
|
||||
"request": "^2.88.0",
|
||||
"semver": "^5.6.0",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"ts-jest": "^23.1.3",
|
||||
"ts-jest": "^23.10.5",
|
||||
"tslint": "^5.11.0",
|
||||
"typescript": "^3.2.1",
|
||||
"webpack-stream": "^4.0.0"
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
export * from "./ConsoleLoggingListener";
|
||||
export * from "./IRecorder";
|
||||
export * from "./LocalStorage";
|
||||
export * from "./MicAudioSource";
|
||||
export * from "./FileAudioSource";
|
||||
export * from "./OpusRecorder";
|
||||
export * from "./PCMRecorder";
|
||||
export * from "./SessionStorage";
|
||||
export * from "./WebsocketConnection";
|
||||
export * from "./WebsocketMessageAdapter";
|
||||
export * from "./ReplayableAudioNode";
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ArgumentNullError, IKeyValueStorage } from "../common/Exports";
|
||||
|
||||
export class LocalStorage implements IKeyValueStorage {
|
||||
|
||||
public get = (key: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
public getOrAdd = (key: string, valueToAdd: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
const value = localStorage.getItem(key);
|
||||
if (value === null || value === undefined) {
|
||||
localStorage.setItem(key, valueToAdd);
|
||||
}
|
||||
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
public set = (key: string, value: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
public remove = (key: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ArgumentNullError, IKeyValueStorage } from "../common/Exports";
|
||||
|
||||
export class SessionStorage implements IKeyValueStorage {
|
||||
|
||||
public get = (key: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
return sessionStorage.getItem(key);
|
||||
}
|
||||
|
||||
public getOrAdd = (key: string, valueToAdd: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
const value = sessionStorage.getItem(key);
|
||||
if (value === null || value === undefined) {
|
||||
sessionStorage.setItem(key, valueToAdd);
|
||||
}
|
||||
|
||||
return sessionStorage.getItem(key);
|
||||
}
|
||||
|
||||
public set = (key: string, value: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
sessionStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
public remove = (key: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import { WebsocketConnection } from "../common.browser/Exports";
|
||||
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
|
||||
import { IConnection, IStringDictionary } from "../common/Exports";
|
||||
import { PropertyId } from "../sdk/Exports";
|
||||
import { AuthInfo, IConnectionFactory, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
|
||||
|
||||
|
@ -20,7 +20,7 @@ export class IntentConnectionFactory implements IConnectionFactory {
|
|||
if (!endpoint) {
|
||||
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_IntentRegion);
|
||||
|
||||
endpoint = this.host() + Storage.local.getOrAdd("TranslationRelativeUri", "/speech/" + this.getSpeechRegionFromIntentRegion(region) + "/recognition/interactive/cognitiveservices/v1");
|
||||
endpoint = "wss://speech.platform.bing.com/speech/" + this.getSpeechRegionFromIntentRegion(region) + "/recognition/interactive/cognitiveservices/v1";
|
||||
}
|
||||
|
||||
const queryParams: IStringDictionary<string> = {
|
||||
|
@ -28,10 +28,6 @@ export class IntentConnectionFactory implements IConnectionFactory {
|
|||
language: config.parameters.getProperty(PropertyId.SpeechServiceConnection_RecoLanguage),
|
||||
};
|
||||
|
||||
if (this.isDebugModeEnabled) {
|
||||
queryParams[TestHooksParamName] = "1";
|
||||
}
|
||||
|
||||
const headers: IStringDictionary<string> = {};
|
||||
headers[authInfo.headerName] = authInfo.token;
|
||||
headers[ConnectionIdHeader] = connectionId;
|
||||
|
@ -39,15 +35,6 @@ export class IntentConnectionFactory implements IConnectionFactory {
|
|||
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
|
||||
}
|
||||
|
||||
private host(): string {
|
||||
return Storage.local.getOrAdd("Host", "wss://speech.platform.bing.com");
|
||||
}
|
||||
|
||||
private get isDebugModeEnabled(): boolean {
|
||||
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
|
||||
return value.toLowerCase() === "true";
|
||||
}
|
||||
|
||||
private getSpeechRegionFromIntentRegion(intentRegion: string): string {
|
||||
switch (intentRegion) {
|
||||
case "West US":
|
||||
|
|
|
@ -53,6 +53,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
private privAuthFetchEventId: string;
|
||||
private privIsDisposed: boolean;
|
||||
private privRecognizer: Recognizer;
|
||||
private privMustReportEndOfStream: boolean;
|
||||
protected privRecognizerConfig: RecognizerConfig;
|
||||
|
||||
public constructor(
|
||||
|
@ -78,6 +79,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
throw new ArgumentNullError("recognizerConfig");
|
||||
}
|
||||
|
||||
this.privMustReportEndOfStream = false;
|
||||
this.privAuthentication = authentication;
|
||||
this.privConnectionFactory = connectionFactory;
|
||||
this.privAudioSource = audioSource;
|
||||
|
@ -189,7 +191,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
} catch { }
|
||||
}
|
||||
|
||||
return this.fetchConnection(requestSession).onSuccessContinueWithPromise((connection: IConnection): Promise<boolean> => {
|
||||
return this.fetchConnection(requestSession).onSuccessContinueWith((connection: IConnection): Promise<boolean> => {
|
||||
return connection.send(new SpeechConnectionMessage(
|
||||
MessageType.Text,
|
||||
"telemetry",
|
||||
|
@ -295,13 +297,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
requestSession: RequestSession,
|
||||
successCallback: (e: SpeechRecognitionResult) => void,
|
||||
errorCallBack: (e: string) => void,
|
||||
): Promise<boolean> => {
|
||||
return this.fetchConnection(requestSession).onSuccessContinueWithPromise((connection: IConnection): Promise<boolean> => {
|
||||
): Promise<IConnection> => {
|
||||
return this.fetchConnection(requestSession).on((connection: IConnection): Promise<IConnection> => {
|
||||
return connection.read()
|
||||
.onSuccessContinueWithPromise((message: ConnectionMessage) => {
|
||||
if (this.privIsDisposed) {
|
||||
// We're done.
|
||||
return PromiseHelper.fromResult(true);
|
||||
return PromiseHelper.fromResult(undefined);
|
||||
}
|
||||
|
||||
// indicates we are draining the queue and it came with no message;
|
||||
|
@ -318,6 +320,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
if (connectionMessage.requestId.toLowerCase() === requestSession.requestId.toLowerCase()) {
|
||||
switch (connectionMessage.path.toLowerCase()) {
|
||||
case "turn.start":
|
||||
this.privMustReportEndOfStream = true;
|
||||
break;
|
||||
case "speech.startdetected":
|
||||
const speechStartDetected: SpeechDetected = SpeechDetected.fromJSON(connectionMessage.textBody);
|
||||
|
@ -349,12 +352,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
if (!!this.privRecognizer.speechEndDetected) {
|
||||
this.privRecognizer.speechEndDetected(this.privRecognizer, speechStopEventArgs);
|
||||
}
|
||||
|
||||
if (requestSession.isSpeechEnded && this.privRecognizerConfig.isContinuousRecognition) {
|
||||
this.cancelRecognitionLocal(requestSession, CancellationReason.EndOfStream, CancellationErrorCode.NoError, undefined, successCallback);
|
||||
}
|
||||
break;
|
||||
case "turn.end":
|
||||
if (requestSession.isSpeechEnded && this.privMustReportEndOfStream) {
|
||||
this.privMustReportEndOfStream = false;
|
||||
this.cancelRecognitionLocal(requestSession, CancellationReason.EndOfStream, CancellationErrorCode.NoError, undefined, successCallback);
|
||||
}
|
||||
|
||||
const sessionStopEventArgs: SessionEventArgs = new SessionEventArgs(requestSession.sessionId);
|
||||
requestSession.onServiceTurnEndResponse(this.privRecognizerConfig.isContinuousRecognition);
|
||||
if (!this.privRecognizerConfig.isContinuousRecognition || requestSession.isSpeechEnded) {
|
||||
|
@ -380,6 +384,7 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
|
||||
return this.receiveMessage(requestSession, successCallback, errorCallBack);
|
||||
});
|
||||
}, (error: string) => {
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -437,11 +442,11 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
|
||||
const audioFormat: AudioStreamFormatImpl = this.privAudioSource.format as AudioStreamFormatImpl;
|
||||
|
||||
const readAndUploadCycle = (_: boolean) => {
|
||||
const readAndUploadCycle = () => {
|
||||
|
||||
// If speech is done, stop sending audio.
|
||||
if (!this.privIsDisposed && !requestSession.isSpeechEnded && !requestSession.isCompleted) {
|
||||
this.fetchConnection(requestSession).onSuccessContinueWith((connection: IConnection) => {
|
||||
this.fetchConnection(requestSession).on((connection: IConnection) => {
|
||||
audioStreamNode.read().on(
|
||||
(audioStreamChunk: IStreamChunk<ArrayBuffer>) => {
|
||||
|
||||
|
@ -464,10 +469,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
|
||||
const delay: number = Math.max(0, (lastSendTime - Date.now() + minSendTime));
|
||||
|
||||
uploaded.onSuccessContinueWith((result: boolean) => {
|
||||
uploaded.continueWith((_: PromiseResult<boolean>) => {
|
||||
// Regardless of success or failure, schedule the next upload.
|
||||
// If the underlying connection was broken, the next cycle will
|
||||
// get a new connection and re-transmit missing audio automatically.
|
||||
setTimeout(() => {
|
||||
lastSendTime = Date.now();
|
||||
readAndUploadCycle(result);
|
||||
readAndUploadCycle();
|
||||
}, delay);
|
||||
});
|
||||
} else {
|
||||
|
@ -489,11 +497,13 @@ export abstract class ServiceRecognizerBase implements IDisposable {
|
|||
deferred.reject(error);
|
||||
}
|
||||
});
|
||||
}, (error: string) => {
|
||||
deferred.reject(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
readAndUploadCycle(true);
|
||||
readAndUploadCycle();
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
|
||||
import { WebsocketConnection } from "../common.browser/Exports";
|
||||
import { OutputFormatPropertyName } from "../common.speech/Exports";
|
||||
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
|
||||
import { IConnection, IStringDictionary } from "../common/Exports";
|
||||
import { OutputFormat, PropertyId } from "../sdk/Exports";
|
||||
import { AuthInfo, IConnectionFactory, RecognitionMode, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
|
||||
import { QueryParameterNames } from "./QueryParameterNames";
|
||||
|
||||
export class SpeechConnectionFactory implements IConnectionFactory {
|
||||
|
||||
private readonly interactiveRelativeUri: string = "/speech/recognition/interactive/cognitiveservices/v1";
|
||||
private readonly conversationRelativeUri: string = "/speech/recognition/conversation/cognitiveservices/v1";
|
||||
private readonly dictationRelativeUri: string = "/speech/recognition/dictation/cognitiveservices/v1";
|
||||
|
||||
public create = (
|
||||
config: RecognizerConfig,
|
||||
authInfo: AuthInfo,
|
||||
|
@ -36,22 +40,20 @@ export class SpeechConnectionFactory implements IConnectionFactory {
|
|||
queryParams[QueryParameterNames.FormatParamName] = config.parameters.getProperty(OutputFormatPropertyName, OutputFormat[OutputFormat.Simple]).toLowerCase();
|
||||
}
|
||||
|
||||
if (this.isDebugModeEnabled) {
|
||||
queryParams[QueryParameterNames.TestHooksParamName] = "1";
|
||||
}
|
||||
|
||||
if (!endpoint) {
|
||||
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_Region, undefined);
|
||||
|
||||
const host: string = "wss://" + region + ".stt.speech.microsoft.com";
|
||||
|
||||
switch (config.recognitionMode) {
|
||||
case RecognitionMode.Conversation:
|
||||
endpoint = this.host(region) + this.conversationRelativeUri;
|
||||
endpoint = host + this.conversationRelativeUri;
|
||||
break;
|
||||
case RecognitionMode.Dictation:
|
||||
endpoint = this.host(region) + this.dictationRelativeUri;
|
||||
endpoint = host + this.dictationRelativeUri;
|
||||
break;
|
||||
default:
|
||||
endpoint = this.host(region) + this.interactiveRelativeUri; // default is interactive
|
||||
endpoint = host + this.interactiveRelativeUri; // default is interactive
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -62,25 +64,4 @@ export class SpeechConnectionFactory implements IConnectionFactory {
|
|||
|
||||
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
|
||||
}
|
||||
|
||||
private host(region: string): string {
|
||||
return Storage.local.getOrAdd("Host", "wss://" + region + ".stt.speech.microsoft.com");
|
||||
}
|
||||
|
||||
private get interactiveRelativeUri(): string {
|
||||
return Storage.local.getOrAdd("InteractiveRelativeUri", "/speech/recognition/interactive/cognitiveservices/v1");
|
||||
}
|
||||
|
||||
private get conversationRelativeUri(): string {
|
||||
return Storage.local.getOrAdd("ConversationRelativeUri", "/speech/recognition/conversation/cognitiveservices/v1");
|
||||
}
|
||||
|
||||
private get dictationRelativeUri(): string {
|
||||
return Storage.local.getOrAdd("DictationRelativeUri", "/speech/recognition/dictation/cognitiveservices/v1");
|
||||
}
|
||||
|
||||
private get isDebugModeEnabled(): boolean {
|
||||
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
|
||||
return value.toLowerCase() === "true";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ export class SpeechServiceRecognizer extends ServiceRecognizerBase {
|
|||
const simple: SimpleSpeechPhrase = SimpleSpeechPhrase.fromJSON(connectionMessage.textBody);
|
||||
const resultReason: ResultReason = EnumTranslation.implTranslateRecognitionResult(simple.RecognitionStatus);
|
||||
|
||||
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + simple.Offset);
|
||||
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + simple.Offset + simple.Duration);
|
||||
|
||||
if (ResultReason.Canceled === resultReason) {
|
||||
const cancelReason: CancellationReason = EnumTranslation.implTranslateCancelResult(simple.RecognitionStatus);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
import { WebsocketConnection } from "../common.browser/Exports";
|
||||
import { IConnection, IStringDictionary, Storage } from "../common/Exports";
|
||||
import { IConnection, IStringDictionary } from "../common/Exports";
|
||||
import { PropertyId } from "../sdk/Exports";
|
||||
import { AuthInfo, IConnectionFactory, RecognizerConfig, WebsocketMessageFormatter } from "./Exports";
|
||||
|
||||
|
@ -20,7 +20,7 @@ export class TranslationConnectionFactory implements IConnectionFactory {
|
|||
if (!endpoint) {
|
||||
const region: string = config.parameters.getProperty(PropertyId.SpeechServiceConnection_Region, undefined);
|
||||
|
||||
endpoint = this.host(region) + Storage.local.getOrAdd("TranslationRelativeUri", "/speech/translation/cognitiveservices/v1");
|
||||
endpoint = "wss://" + region + ".s2s.speech.microsoft.com/speech/translation/cognitiveservices/v1";
|
||||
}
|
||||
|
||||
const queryParams: IStringDictionary<string> = {
|
||||
|
@ -28,10 +28,6 @@ export class TranslationConnectionFactory implements IConnectionFactory {
|
|||
to: config.parameters.getProperty(PropertyId.SpeechServiceConnection_TranslationToLanguages),
|
||||
};
|
||||
|
||||
if (this.isDebugModeEnabled) {
|
||||
queryParams[TestHooksParamName] = "1";
|
||||
}
|
||||
|
||||
const voiceName: string = "voice";
|
||||
const featureName: string = "features";
|
||||
|
||||
|
@ -46,13 +42,4 @@ export class TranslationConnectionFactory implements IConnectionFactory {
|
|||
|
||||
return new WebsocketConnection(endpoint, queryParams, headers, new WebsocketMessageFormatter(), connectionId);
|
||||
}
|
||||
|
||||
private host(region: string): string {
|
||||
return Storage.local.getOrAdd("Host", "wss://" + region + ".s2s.speech.microsoft.com");
|
||||
}
|
||||
|
||||
private get isDebugModeEnabled(): boolean {
|
||||
const value = Storage.local.getOrAdd("IsDebugModeEnabled", "false");
|
||||
return value.toLowerCase() === "true";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ export class TranslationServiceRecognizer extends ServiceRecognizerBase {
|
|||
const translatedPhrase: TranslationPhrase = TranslationPhrase.fromJSON(connectionMessage.textBody);
|
||||
|
||||
if (translatedPhrase.RecognitionStatus === RecognitionStatus.Success) {
|
||||
requestSession.onServiceRecognized(requestSession.currentTurnAudioOffset + translatedPhrase.Offset + translatedPhrase.Duration);
|
||||
|
||||
// OK, the recognition was successful. How'd the translation do?
|
||||
const result: TranslationRecognitionEventArgs = this.fireEventForResult(translatedPhrase, requestSession);
|
||||
if (!!this.privTranslationRecognizer.recognized) {
|
||||
|
@ -292,18 +294,20 @@ export class TranslationServiceRecognizer extends ServiceRecognizerBase {
|
|||
resultReason = ResultReason.TranslatingSpeech;
|
||||
}
|
||||
|
||||
const offset: number = serviceResult.Offset + requestSession.currentTurnAudioOffset;
|
||||
|
||||
const result = new TranslationRecognitionResult(
|
||||
translations,
|
||||
requestSession.requestId,
|
||||
resultReason,
|
||||
serviceResult.Text,
|
||||
serviceResult.Duration,
|
||||
serviceResult.Offset,
|
||||
offset,
|
||||
serviceResult.Translation.FailureReason,
|
||||
JSON.stringify(serviceResult),
|
||||
undefined);
|
||||
|
||||
const ev = new TranslationRecognitionEventArgs(result, serviceResult.Offset, requestSession.sessionId);
|
||||
const ev = new TranslationRecognitionEventArgs(result, offset, requestSession.sessionId);
|
||||
return ev;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ export * from "./IDetachable";
|
|||
export * from "./IDictionary";
|
||||
export * from "./IDisposable";
|
||||
export * from "./IEventSource";
|
||||
export * from "./IKeyValueStorage";
|
||||
export * from "./InMemoryStorage";
|
||||
export * from "./ITimer";
|
||||
export * from "./IWebsocketMessageFormatter";
|
||||
export * from "./List";
|
||||
|
@ -25,6 +23,5 @@ export * from "./Promise";
|
|||
export * from "./Queue";
|
||||
export * from "./RawWebsocketMessage";
|
||||
export * from "./RiffPcmEncoder";
|
||||
export * from "./Storage";
|
||||
export * from "./Stream";
|
||||
export { TranslationStatus } from "../common.speech/TranslationStatus";
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export interface IKeyValueStorage {
|
||||
get(key: string): string;
|
||||
getOrAdd(key: string, valueToAdd: string): string;
|
||||
set(key: string, value: string): void;
|
||||
remove(key: string): void;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ArgumentNullError } from "./Error";
|
||||
import { IStringDictionary } from "./IDictionary";
|
||||
import { IKeyValueStorage } from "./IKeyValueStorage";
|
||||
|
||||
export class InMemoryStorage implements IKeyValueStorage {
|
||||
|
||||
private privStore: IStringDictionary<string> = {};
|
||||
|
||||
public get = (key: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
return this.privStore[key];
|
||||
}
|
||||
|
||||
public getOrAdd = (key: string, valueToAdd: string): string => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
if (this.privStore[key] === undefined) {
|
||||
this.privStore[key] = valueToAdd;
|
||||
}
|
||||
|
||||
return this.privStore[key];
|
||||
}
|
||||
|
||||
public set = (key: string, value: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
this.privStore[key] = value;
|
||||
}
|
||||
|
||||
public remove = (key: string): void => {
|
||||
if (!key) {
|
||||
throw new ArgumentNullError("key");
|
||||
}
|
||||
|
||||
if (this.privStore[key] !== undefined) {
|
||||
delete this.privStore[key];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ArgumentNullError } from "./Error";
|
||||
import { IKeyValueStorage } from "./IKeyValueStorage";
|
||||
import { InMemoryStorage } from "./InMemoryStorage";
|
||||
|
||||
export class Storage {
|
||||
private static privSessionStorage: IKeyValueStorage = new InMemoryStorage();
|
||||
private static privLocalStorage: IKeyValueStorage = new InMemoryStorage();
|
||||
|
||||
public static setSessionStorage = (sessionStorage: IKeyValueStorage): void => {
|
||||
if (!sessionStorage) {
|
||||
throw new ArgumentNullError("sessionStorage");
|
||||
}
|
||||
|
||||
Storage.privSessionStorage = sessionStorage;
|
||||
}
|
||||
|
||||
public static setLocalStorage = (localStorage: IKeyValueStorage): void => {
|
||||
if (!localStorage) {
|
||||
throw new ArgumentNullError("localStorage");
|
||||
}
|
||||
|
||||
Storage.privLocalStorage = localStorage;
|
||||
}
|
||||
|
||||
public static get session(): IKeyValueStorage {
|
||||
return Storage.privSessionStorage;
|
||||
}
|
||||
|
||||
public static get local(): IKeyValueStorage {
|
||||
return Storage.privLocalStorage;
|
||||
}
|
||||
}
|
|
@ -114,7 +114,8 @@ export class IntentRecognizer extends Recognizer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the authorization token used to communicate with the service.
|
||||
* Gets/Sets the authorization token used to communicate with the service.
|
||||
* Note: Please use a token derived from your LanguageUnderstanding subscription key for the Intent recognizer.
|
||||
* @member IntentRecognizer.prototype.authorizationToken
|
||||
* @function
|
||||
* @public
|
||||
|
|
|
@ -49,10 +49,10 @@ export enum PropertyId {
|
|||
SpeechServiceAuthorization_Type,
|
||||
|
||||
/**
|
||||
* The Cognitive Services Custom Speech Service endpoint id. Under normal circumstances, you shouldn't
|
||||
* The Cognitive Services Speech Service endpoint id. Under normal circumstances, you shouldn't
|
||||
* have to use this property directly.
|
||||
* Instead, use @see SpeechConfig.setEndpointId.
|
||||
* NOTE: The endpoint id is available in the Custom Speech Portal, listed under Endpoint Details.
|
||||
* NOTE: The endpoint id is available in the Speech Portal, listed under Endpoint Details.
|
||||
* @member PropertyId.SpeechServiceConnection_EndpointId
|
||||
*/
|
||||
SpeechServiceConnection_EndpointId,
|
||||
|
|
|
@ -18,6 +18,7 @@ export abstract class SpeechConfig {
|
|||
|
||||
/**
|
||||
* Static instance of SpeechConfig returned by passing subscriptionKey and service region.
|
||||
* Note: Please use your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
|
||||
* @member SpeechConfig.fromSubscription
|
||||
* @function
|
||||
* @public
|
||||
|
@ -41,6 +42,7 @@ export abstract class SpeechConfig {
|
|||
* Creates an instance of the speech factory with specified endpoint and subscription key.
|
||||
* This method is intended only for users who use a non-standard service endpoint or paramters.
|
||||
* the language setting in uri takes precedence, and the effective language is "de-DE".
|
||||
* Note: Please use your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
|
||||
* @member SpeechConfig.fromEndpoint
|
||||
* @function
|
||||
* @public
|
||||
|
@ -60,6 +62,7 @@ export abstract class SpeechConfig {
|
|||
|
||||
/**
|
||||
* Creates an instance of the speech factory with specified initial authorization token and region.
|
||||
* Note: Please use a token derived from your LanguageUnderstanding subscription key in case you want to use the Intent recognizer.
|
||||
* @member SpeechConfig.fromAuthorizationToken
|
||||
* @function
|
||||
* @public
|
||||
|
@ -87,8 +90,8 @@ export abstract class SpeechConfig {
|
|||
public abstract get authorizationToken(): string;
|
||||
|
||||
/**
|
||||
* Sets the authorization token.
|
||||
* If this is set, subscription key is ignored.
|
||||
* Get/Sets the authorization token.
|
||||
* If the autorizaton token is set, the subscription key is ignored.
|
||||
* User needs to make sure the provided authorization token is valid and not expired.
|
||||
* @member SpeechConfig.prototype.authorizationToken
|
||||
* @function
|
||||
|
@ -106,7 +109,7 @@ export abstract class SpeechConfig {
|
|||
public abstract get speechRecognitionLanguage(): string;
|
||||
|
||||
/**
|
||||
* Sets the input language.
|
||||
* Gets/Sets the input language.
|
||||
* @member SpeechConfig.prototype.speechRecognitionLanguage
|
||||
* @function
|
||||
* @public
|
||||
|
@ -135,14 +138,6 @@ export abstract class SpeechConfig {
|
|||
*/
|
||||
public abstract getProperty(name: string, def?: string): string;
|
||||
|
||||
/**
|
||||
* Sets output format.
|
||||
* @member SpeechConfig.prototype.outputFormat
|
||||
* @function
|
||||
* @public
|
||||
*/
|
||||
public abstract set outputFormat(format: OutputFormat);
|
||||
|
||||
/**
|
||||
* Gets output format.
|
||||
* @member SpeechConfig.prototype.outputFormat
|
||||
|
@ -153,13 +148,12 @@ export abstract class SpeechConfig {
|
|||
public abstract get outputFormat(): OutputFormat;
|
||||
|
||||
/**
|
||||
* Sets the endpoint ID of a customized speech model that is used for speech recognition.
|
||||
* @member SpeechConfig.prototype.endpointId
|
||||
* Gets/Sets the output format.
|
||||
* @member SpeechConfig.prototype.outputFormat
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} value - The endpoint ID
|
||||
*/
|
||||
public abstract set endpointId(value: string);
|
||||
public abstract set outputFormat(format: OutputFormat);
|
||||
|
||||
/**
|
||||
* Gets the endpoint ID of a customized speech model that is used for speech recognition.
|
||||
|
@ -170,6 +164,15 @@ export abstract class SpeechConfig {
|
|||
*/
|
||||
public abstract get endpointId(): string;
|
||||
|
||||
/**
|
||||
* Gets/Sets the endpoint ID of a customized speech model that is used for speech recognition.
|
||||
* @member SpeechConfig.prototype.endpointId
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} value - The endpoint ID
|
||||
*/
|
||||
public abstract set endpointId(value: string);
|
||||
|
||||
/**
|
||||
* Closes the configuration.
|
||||
* @member SpeechConfig.prototype.close
|
||||
|
@ -236,14 +239,14 @@ export class SpeechConfigImpl extends SpeechConfig {
|
|||
this.privProperties.setProperty(OutputFormatPropertyName, OutputFormat[value]);
|
||||
}
|
||||
|
||||
public set endpointId(value: string) {
|
||||
this.privProperties.setProperty(PropertyId.SpeechServiceConnection_EndpointId, value);
|
||||
}
|
||||
|
||||
public get endpointId(): string {
|
||||
return this.privProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
|
||||
}
|
||||
|
||||
public set endpointId(value: string) {
|
||||
this.privProperties.setProperty(PropertyId.SpeechServiceConnection_EndpointId, value);
|
||||
}
|
||||
|
||||
public setProperty(name: string | PropertyId, value: string): void {
|
||||
Contracts.throwIfNullOrWhitespace(value, "value");
|
||||
|
||||
|
|
|
@ -90,18 +90,6 @@ export class SpeechRecognizer extends Recognizer {
|
|||
return this.properties.getProperty(PropertyId.SpeechServiceConnection_EndpointId, "00000000-0000-0000-0000-000000000000");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authorization token used to communicate with the service.
|
||||
* @member SpeechRecognizer.prototype.authorizationToken
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} token - Authorization token.
|
||||
*/
|
||||
public set authorizationToken(token: string) {
|
||||
Contracts.throwIfNullOrWhitespace(token, "token");
|
||||
this.properties.setProperty(PropertyId.SpeechServiceAuthorization_Token, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the authorization token used to communicate with the service.
|
||||
* @member SpeechRecognizer.prototype.authorizationToken
|
||||
|
@ -113,6 +101,18 @@ export class SpeechRecognizer extends Recognizer {
|
|||
return this.properties.getProperty(PropertyId.SpeechServiceAuthorization_Token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets/Sets the authorization token used to communicate with the service.
|
||||
* @member SpeechRecognizer.prototype.authorizationToken
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} token - Authorization token.
|
||||
*/
|
||||
public set authorizationToken(token: string) {
|
||||
Contracts.throwIfNullOrWhitespace(token, "token");
|
||||
this.properties.setProperty(PropertyId.SpeechServiceAuthorization_Token, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the spoken language of recognition.
|
||||
* @member SpeechRecognizer.prototype.speechRecognitionLanguage
|
||||
|
|
|
@ -86,7 +86,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the authorization token.
|
||||
* Gets/Sets the authorization token.
|
||||
* If this is set, subscription key is ignored.
|
||||
* User needs to make sure the provided authorization token is valid and not expired.
|
||||
* @member SpeechTranslationConfig.prototype.authorizationToken
|
||||
|
@ -97,9 +97,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
|
|||
public abstract set authorizationToken(value: string);
|
||||
|
||||
/**
|
||||
* Sets the authorization token.
|
||||
* If this is set, subscription key is ignored.
|
||||
* User needs to make sure the provided authorization token is valid and not expired.
|
||||
* Gets/Sets the speech recognition language.
|
||||
* @member SpeechTranslationConfig.prototype.speechRecognitionLanguage
|
||||
* @function
|
||||
* @public
|
||||
|
@ -117,7 +115,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
|
|||
public abstract addTargetLanguage(value: string): void;
|
||||
|
||||
/**
|
||||
* Add a (text) target language to translate into.
|
||||
* Gets the (text) target language to translate into.
|
||||
* @member SpeechTranslationConfig.prototype.targetLanguages
|
||||
* @function
|
||||
* @public
|
||||
|
@ -126,7 +124,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
|
|||
public abstract get targetLanguages(): string[];
|
||||
|
||||
/**
|
||||
* Returns the selected voice name.
|
||||
* Gets the selected voice name.
|
||||
* @member SpeechTranslationConfig.prototype.voiceName
|
||||
* @function
|
||||
* @public
|
||||
|
@ -135,7 +133,7 @@ export abstract class SpeechTranslationConfig extends SpeechConfig {
|
|||
public abstract get voiceName(): string;
|
||||
|
||||
/**
|
||||
* Sets voice of the translated language, enable voice synthesis output.
|
||||
* Gets/Sets voice of the translated language, enable voice synthesis output.
|
||||
* @member SpeechTranslationConfig.prototype.voiceName
|
||||
* @function
|
||||
* @public
|
||||
|
@ -176,7 +174,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
this.outputFormat = OutputFormat.Simple;
|
||||
}
|
||||
/**
|
||||
* Sets the authorization token.
|
||||
* Gets/Sets the authorization token.
|
||||
* If this is set, subscription key is ignored.
|
||||
* User needs to make sure the provided authorization token is valid and not expired.
|
||||
* @member SpeechTranslationConfigImpl.prototype.authorizationToken
|
||||
|
@ -191,9 +189,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the authorization token.
|
||||
* If this is set, subscription key is ignored.
|
||||
* User needs to make sure the provided authorization token is valid and not expired.
|
||||
* Gets/Sets the speech recognition language.
|
||||
* @member SpeechTranslationConfigImpl.prototype.speechRecognitionLanguage
|
||||
* @function
|
||||
* @public
|
||||
|
@ -214,6 +210,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the output format
|
||||
* @member SpeechTranslationConfigImpl.prototype.outputFormat
|
||||
* @function
|
||||
* @public
|
||||
|
@ -223,6 +220,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets/Sets the output format
|
||||
* @member SpeechTranslationConfigImpl.prototype.outputFormat
|
||||
* @function
|
||||
* @public
|
||||
|
@ -232,6 +230,17 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the endpoint id.
|
||||
* @member SpeechTranslationConfigImpl.prototype.endpointId
|
||||
* @function
|
||||
* @public
|
||||
*/
|
||||
public get endpointId(): string {
|
||||
return this.privSpeechProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets/Sets the endpoint id.
|
||||
* @member SpeechTranslationConfigImpl.prototype.endpointId
|
||||
* @function
|
||||
* @public
|
||||
|
@ -240,14 +249,6 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
this.privSpeechProperties.setProperty(PropertyId.SpeechServiceConnection_Endpoint, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @member SpeechTranslationConfigImpl.prototype.endpointId
|
||||
* @function
|
||||
* @public
|
||||
*/
|
||||
public get endpointId(): string {
|
||||
return this.privSpeechProperties.getProperty(PropertyId.SpeechServiceConnection_EndpointId);
|
||||
}
|
||||
/**
|
||||
* Add a (text) target language to translate into.
|
||||
* @member SpeechTranslationConfigImpl.prototype.addTargetLanguage
|
||||
|
@ -264,7 +265,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a (text) target language to translate into.
|
||||
* Gets the (text) target language to translate into.
|
||||
* @member SpeechTranslationConfigImpl.prototype.targetLanguages
|
||||
* @function
|
||||
* @public
|
||||
|
@ -281,6 +282,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the voice name.
|
||||
* @member SpeechTranslationConfigImpl.prototype.voiceName
|
||||
* @function
|
||||
* @public
|
||||
|
@ -290,7 +292,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets voice of the translated language, enable voice synthesis output.
|
||||
* Gets/Sets the voice of the translated language, enable voice synthesis output.
|
||||
* @member SpeechTranslationConfigImpl.prototype.voiceName
|
||||
* @function
|
||||
* @public
|
||||
|
@ -314,19 +316,7 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Allows for setting arbitrary properties.
|
||||
* @member SpeechTranslationConfigImpl.prototype.setProperty
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} name - The name of the property.
|
||||
* @param {string} value - The value of the property.
|
||||
*/
|
||||
public setProperty(name: string, value: string): void {
|
||||
this.privSpeechProperties.setProperty(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for retrieving arbitrary property values.
|
||||
* Gets an arbitrary property value.
|
||||
* @member SpeechTranslationConfigImpl.prototype.getProperty
|
||||
* @function
|
||||
* @public
|
||||
|
@ -338,6 +328,18 @@ export class SpeechTranslationConfigImpl extends SpeechTranslationConfig {
|
|||
return this.privSpeechProperties.getProperty(name, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets/Sets an arbitrary property value.
|
||||
* @member SpeechTranslationConfigImpl.prototype.setProperty
|
||||
* @function
|
||||
* @public
|
||||
* @param {string} name - The name of the property.
|
||||
* @param {string} value - The value of the property.
|
||||
*/
|
||||
public setProperty(name: string, value: string): void {
|
||||
this.privSpeechProperties.setProperty(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to custom properties.
|
||||
* @member SpeechTranslationConfigImpl.prototype.properties
|
||||
|
|
|
@ -147,7 +147,7 @@ export class TranslationRecognizer extends Recognizer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the authorization token used to communicate with the service.
|
||||
* Gets/Sets the authorization token used to communicate with the service.
|
||||
* @member TranslationRecognizer.prototype.authorizationToken
|
||||
* @function
|
||||
* @public
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
|
||||
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
|
||||
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
|
||||
|
||||
import { Settings } from "../Settings";
|
||||
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
|
||||
|
||||
import * as request from "request";
|
||||
|
||||
import WaitForCondition from "../Utilities";
|
||||
|
||||
let objsToClose: any[];
|
||||
|
||||
beforeAll(() => {
|
||||
// override inputs, if necessary
|
||||
Settings.LoadSettings();
|
||||
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
|
||||
});
|
||||
|
||||
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
|
||||
// report the name.
|
||||
beforeEach(() => {
|
||||
objsToClose = [];
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("---------------------------------------Starting test case-----------------------------------");
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("End Time: " + new Date(Date.now()).toLocaleString());
|
||||
objsToClose.forEach((value: any, index: number, array: any[]) => {
|
||||
if (typeof value.close === "function") {
|
||||
value.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("Non-refreshed auth token has sensible error message", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Non-refreshed auth token has sensible error message");
|
||||
|
||||
if (!Settings.ExecuteLongRunningTestsBool) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Skipping test.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const req = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Ocp-Apim-Subscription-Key": Settings.SpeechSubscriptionKey,
|
||||
},
|
||||
url: "https://" + Settings.SpeechRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken",
|
||||
};
|
||||
|
||||
let authToken: string;
|
||||
|
||||
request.post(req, (error: any, response: request.Response, body: any) => {
|
||||
authToken = body;
|
||||
});
|
||||
|
||||
WaitForCondition(() => {
|
||||
return !!authToken;
|
||||
}, () => {
|
||||
// Pump valid speech and then silence until at least one speech end cycle hits.
|
||||
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
|
||||
|
||||
const s: sdk.SpeechConfig = sdk.SpeechConfig.fromAuthorizationToken(authToken, Settings.SpeechRegion);
|
||||
objsToClose.push(s);
|
||||
|
||||
let pumpSilence: boolean = false;
|
||||
let bytesSent: number = 0;
|
||||
|
||||
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
|
||||
const p: sdk.PullAudioInputStream = sdk.AudioInputStream.createPullStream(
|
||||
{
|
||||
close: () => { return; },
|
||||
read: (buffer: ArrayBuffer): number => {
|
||||
if (pumpSilence) {
|
||||
bytesSent += buffer.byteLength;
|
||||
if (bytesSent >= 32000) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = false;
|
||||
}
|
||||
return buffer.byteLength;
|
||||
} else {
|
||||
const copyArray: Uint8Array = new Uint8Array(buffer);
|
||||
const start: number = bytesSent;
|
||||
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
|
||||
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
|
||||
const readyToSend: number = (end - start) + 1;
|
||||
bytesSent += readyToSend;
|
||||
|
||||
if (readyToSend < buffer.byteLength) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = true;
|
||||
}
|
||||
|
||||
return readyToSend;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
|
||||
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
|
||||
objsToClose.push(r);
|
||||
|
||||
let lastOffset: number = 0;
|
||||
let canceled: boolean = false;
|
||||
|
||||
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
|
||||
try {
|
||||
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
|
||||
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
|
||||
lastOffset = e.offset;
|
||||
|
||||
r.authorizationToken = "BadToken";
|
||||
|
||||
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
|
||||
// connection is disconnected.
|
||||
if ("" !== e.result.text) {
|
||||
expect(e.result.text).toEqual(Settings.WaveFileText);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
|
||||
try {
|
||||
expect(e.errorDetails.indexOf("Unable to contact server.")).toBeGreaterThanOrEqual(0);
|
||||
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.Error]);
|
||||
canceled = true;
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.startContinuousRecognitionAsync(() => {
|
||||
WaitForCondition(() => (canceled), () => {
|
||||
done();
|
||||
});
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
}, 1000 * 60 * 20); // 20 minutes.
|
|
@ -0,0 +1,210 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
|
||||
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
|
||||
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
|
||||
|
||||
import { Settings } from "../Settings";
|
||||
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
|
||||
|
||||
import * as request from "request";
|
||||
|
||||
import WaitForCondition from "../Utilities";
|
||||
|
||||
let objsToClose: any[];
|
||||
|
||||
beforeAll(() => {
|
||||
// override inputs, if necessary
|
||||
Settings.LoadSettings();
|
||||
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
|
||||
});
|
||||
|
||||
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
|
||||
// report the name.
|
||||
beforeEach(() => {
|
||||
objsToClose = [];
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("---------------------------------------Starting test case-----------------------------------");
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("End Time: " + new Date(Date.now()).toLocaleString());
|
||||
objsToClose.forEach((value: any, index: number, array: any[]) => {
|
||||
if (typeof value.close === "function") {
|
||||
value.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("AuthToken refresh works correctly", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: AuthToken refresh works correctly");
|
||||
|
||||
if (!Settings.ExecuteLongRunningTestsBool) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Skipping test.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const req = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Ocp-Apim-Subscription-Key": Settings.SpeechSubscriptionKey,
|
||||
},
|
||||
url: "https://" + Settings.SpeechRegion + ".api.cognitive.microsoft.com/sts/v1.0/issueToken",
|
||||
};
|
||||
|
||||
let authToken: string;
|
||||
|
||||
request.post(req, (error: any, response: request.Response, body: any) => {
|
||||
authToken = body;
|
||||
});
|
||||
|
||||
WaitForCondition(() => {
|
||||
return !!authToken;
|
||||
}, () => {
|
||||
// Pump valid speech and then silence until at least one speech end cycle hits.
|
||||
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
|
||||
|
||||
const s: sdk.SpeechConfig = sdk.SpeechConfig.fromAuthorizationToken(authToken, Settings.SpeechRegion);
|
||||
objsToClose.push(s);
|
||||
|
||||
let pumpSilence: boolean = false;
|
||||
let bytesSent: number = 0;
|
||||
let streamStopped: boolean = false;
|
||||
|
||||
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
|
||||
const p: sdk.PullAudioInputStream = sdk.AudioInputStream.createPullStream(
|
||||
{
|
||||
close: () => { return; },
|
||||
read: (buffer: ArrayBuffer): number => {
|
||||
if (pumpSilence) {
|
||||
bytesSent += buffer.byteLength;
|
||||
if (bytesSent >= 32000) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = false;
|
||||
}
|
||||
return buffer.byteLength;
|
||||
} else {
|
||||
const copyArray: Uint8Array = new Uint8Array(buffer);
|
||||
const start: number = bytesSent;
|
||||
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
|
||||
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
|
||||
const readyToSend: number = (end - start) + 1;
|
||||
bytesSent += readyToSend;
|
||||
|
||||
if (readyToSend < buffer.byteLength) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = true;
|
||||
}
|
||||
|
||||
return readyToSend;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
// Close p in 20 minutes.
|
||||
const endTime: number = Date.now() + (1000 * 60 * 20); // 20 min.
|
||||
WaitForCondition(() => {
|
||||
return Date.now() >= endTime;
|
||||
}, () => {
|
||||
streamStopped = true;
|
||||
p.close();
|
||||
});
|
||||
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
|
||||
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
|
||||
objsToClose.push(r);
|
||||
|
||||
// auto refresh the auth token.
|
||||
const refreshAuthToken = () => {
|
||||
if (canceled && !inTurn) {
|
||||
return;
|
||||
}
|
||||
|
||||
request.post(req, (error: any, response: request.Response, body: any) => {
|
||||
r.authorizationToken = body;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
refreshAuthToken();
|
||||
}, 1000 * 60 * 9); // 9 minutes
|
||||
};
|
||||
|
||||
refreshAuthToken();
|
||||
|
||||
let speechEnded: number = 0;
|
||||
let lastOffset: number = 0;
|
||||
let canceled: boolean = false;
|
||||
let inTurn: boolean = false;
|
||||
|
||||
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
|
||||
try {
|
||||
// The last chunk may be partial depending on where the audio was when the stream was closed.
|
||||
// So just ignore it.
|
||||
if (streamStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
|
||||
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
|
||||
lastOffset = e.offset;
|
||||
|
||||
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
|
||||
// connection is disconnected.
|
||||
if ("" !== e.result.text) {
|
||||
expect(e.result.text).toEqual(Settings.WaveFileText);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
|
||||
try {
|
||||
expect(e.errorDetails).toBeUndefined();
|
||||
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
|
||||
canceled = true;
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = true;
|
||||
});
|
||||
|
||||
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = false;
|
||||
});
|
||||
|
||||
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
|
||||
speechEnded++;
|
||||
};
|
||||
|
||||
r.startContinuousRecognitionAsync(() => {
|
||||
WaitForCondition(() => (canceled && !inTurn), () => {
|
||||
r.stopContinuousRecognitionAsync(() => {
|
||||
try {
|
||||
expect(speechEnded).toEqual(1);
|
||||
done();
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
(err: string) => {
|
||||
done.fail(err);
|
||||
});
|
||||
});
|
||||
}, 1000 * 60 * 25); // 25 minutes.
|
|
@ -0,0 +1,205 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
|
||||
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
|
||||
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
|
||||
|
||||
import { Settings } from "../Settings";
|
||||
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
|
||||
|
||||
import * as request from "request";
|
||||
|
||||
import WaitForCondition from "../Utilities";
|
||||
|
||||
let objsToClose: any[];
|
||||
|
||||
beforeAll(() => {
|
||||
// override inputs, if necessary
|
||||
Settings.LoadSettings();
|
||||
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
|
||||
});
|
||||
|
||||
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
|
||||
// report the name.
|
||||
beforeEach(() => {
|
||||
objsToClose = [];
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("---------------------------------------Starting test case-----------------------------------");
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("End Time: " + new Date(Date.now()).toLocaleString());
|
||||
objsToClose.forEach((value: any, index: number, array: any[]) => {
|
||||
if (typeof value.close === "function") {
|
||||
value.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const BuildSpeechConfig: () => sdk.SpeechConfig = (): sdk.SpeechConfig => {
|
||||
|
||||
let s: sdk.SpeechConfig;
|
||||
if (undefined === Settings.SpeechEndpoint) {
|
||||
s = sdk.SpeechConfig.fromSubscription(Settings.SpeechSubscriptionKey, Settings.SpeechRegion);
|
||||
} else {
|
||||
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechEndpoint), Settings.SpeechSubscriptionKey);
|
||||
}
|
||||
|
||||
expect(s).not.toBeUndefined();
|
||||
return s;
|
||||
};
|
||||
|
||||
// Tests client reconnect after speech timeouts.
|
||||
test("Reconnect After timeout", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Reconnect After timeout");
|
||||
|
||||
if (!Settings.ExecuteLongRunningTestsBool) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Skipping test.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pump valid speech and then silence until at least one speech end cycle hits.
|
||||
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
|
||||
|
||||
const alternatePhraseFileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.LuisWaveFile);
|
||||
|
||||
let p: sdk.PullAudioInputStream;
|
||||
let s: sdk.SpeechConfig;
|
||||
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.warn("Running timeout test against production, this will be very slow...");
|
||||
s = BuildSpeechConfig();
|
||||
} else {
|
||||
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
|
||||
}
|
||||
objsToClose.push(s);
|
||||
|
||||
let pumpSilence: boolean = false;
|
||||
let sendAlternateFile: boolean = false;
|
||||
|
||||
let bytesSent: number = 0;
|
||||
const targetLoops: number = 500;
|
||||
|
||||
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
|
||||
p = sdk.AudioInputStream.createPullStream(
|
||||
{
|
||||
close: () => { return; },
|
||||
read: (buffer: ArrayBuffer): number => {
|
||||
if (pumpSilence) {
|
||||
bytesSent += buffer.byteLength;
|
||||
if (bytesSent >= 16000) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = false;
|
||||
}
|
||||
return buffer.byteLength;
|
||||
} else {
|
||||
// Alternate between the two files with different phrases in them.
|
||||
const sendBuffer: ArrayBuffer = sendAlternateFile ? alternatePhraseFileBuffer : fileBuffer;
|
||||
|
||||
const copyArray: Uint8Array = new Uint8Array(buffer);
|
||||
const start: number = bytesSent;
|
||||
const end: number = buffer.byteLength > (sendBuffer.byteLength - bytesSent) ? (sendBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
|
||||
copyArray.set(new Uint8Array(sendBuffer.slice(start, end)));
|
||||
const readyToSend: number = (end - start) + 1;
|
||||
bytesSent += readyToSend;
|
||||
|
||||
if (readyToSend < buffer.byteLength) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = true;
|
||||
sendAlternateFile = !sendAlternateFile;
|
||||
}
|
||||
|
||||
return readyToSend;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
|
||||
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
|
||||
objsToClose.push(r);
|
||||
|
||||
let speechEnded: number = 0;
|
||||
let lastOffset: number = 0;
|
||||
let recogCount: number = 0;
|
||||
let canceled: boolean = false;
|
||||
let inTurn: boolean = false;
|
||||
let alternatePhrase: boolean = false;
|
||||
|
||||
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
|
||||
try {
|
||||
// If the target number of loops has been seen already, don't check as the audio being sent could have been clipped randomly during a phrase,
|
||||
// and failing because of that isn't warranted.
|
||||
if (recogCount <= targetLoops) {
|
||||
|
||||
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
|
||||
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
|
||||
lastOffset = e.offset;
|
||||
|
||||
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
|
||||
// connection is disconnected.
|
||||
if ("" !== e.result.text) {
|
||||
if (alternatePhrase) {
|
||||
expect(e.result.text).toEqual(Settings.LuisWavFileText);
|
||||
} else {
|
||||
expect(e.result.text).toEqual(Settings.WaveFileText);
|
||||
}
|
||||
|
||||
alternatePhrase = !alternatePhrase;
|
||||
}
|
||||
if (recogCount++ >= targetLoops) {
|
||||
p.close();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
|
||||
try {
|
||||
expect(e.errorDetails).toBeUndefined();
|
||||
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
|
||||
canceled = true;
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = true;
|
||||
});
|
||||
|
||||
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = false;
|
||||
});
|
||||
|
||||
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
|
||||
speechEnded++;
|
||||
};
|
||||
|
||||
r.startContinuousRecognitionAsync(() => {
|
||||
WaitForCondition(() => (canceled && !inTurn), () => {
|
||||
r.stopContinuousRecognitionAsync(() => {
|
||||
try {
|
||||
expect(speechEnded).toEqual(1);
|
||||
done();
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
(err: string) => {
|
||||
done.fail(err);
|
||||
});
|
||||
}, 1000 * 60 * 12);
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as sdk from "../../microsoft.cognitiveservices.speech.sdk";
|
||||
import { ConsoleLoggingListener, WebsocketMessageAdapter } from "../../src/common.browser/Exports";
|
||||
import { Events, EventType, PlatformEvent } from "../../src/common/Exports";
|
||||
|
||||
import { Settings } from "../Settings";
|
||||
import { WaveFileAudioInput } from "../WaveFileAudioInputStream";
|
||||
|
||||
import * as request from "request";
|
||||
|
||||
import WaitForCondition from "../Utilities";
|
||||
|
||||
let objsToClose: any[];
|
||||
|
||||
beforeAll(() => {
|
||||
// override inputs, if necessary
|
||||
Settings.LoadSettings();
|
||||
Events.instance.attachListener(new ConsoleLoggingListener(EventType.Debug));
|
||||
});
|
||||
|
||||
// Test cases are run linerally, the only other mechanism to demark them in the output is to put a console line in each case and
|
||||
// report the name.
|
||||
beforeEach(() => {
|
||||
objsToClose = [];
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("---------------------------------------Starting test case-----------------------------------");
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Start Time: " + new Date(Date.now()).toLocaleString());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("End Time: " + new Date(Date.now()).toLocaleString());
|
||||
objsToClose.forEach((value: any, index: number, array: any[]) => {
|
||||
if (typeof value.close === "function") {
|
||||
value.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const BuildSpeechConfig: () => sdk.SpeechTranslationConfig = (): sdk.SpeechTranslationConfig => {
|
||||
const s: sdk.SpeechTranslationConfig = sdk.SpeechTranslationConfig.fromSubscription(Settings.SpeechSubscriptionKey, Settings.SpeechRegion);
|
||||
expect(s).not.toBeUndefined();
|
||||
return s;
|
||||
};
|
||||
|
||||
// Tests client reconnect after speech timeouts.
|
||||
test("Reconnect After timeout", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Reconnect After timeout");
|
||||
|
||||
if (!Settings.ExecuteLongRunningTestsBool) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Skipping test.");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pump valid speech and then silence until at least one speech end cycle hits.
|
||||
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
|
||||
|
||||
const alternatePhraseFileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.LuisWaveFile);
|
||||
|
||||
let p: sdk.PullAudioInputStream;
|
||||
let s: sdk.SpeechTranslationConfig;
|
||||
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.warn("Running timeout test against production, this will be very slow...");
|
||||
s = BuildSpeechConfig();
|
||||
} else {
|
||||
s = sdk.SpeechTranslationConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
|
||||
}
|
||||
objsToClose.push(s);
|
||||
|
||||
s.addTargetLanguage(Settings.WaveFileLanguage);
|
||||
s.speechRecognitionLanguage = Settings.WaveFileLanguage;
|
||||
|
||||
let pumpSilence: boolean = false;
|
||||
let sendAlternateFile: boolean = false;
|
||||
|
||||
let bytesSent: number = 0;
|
||||
const targetLoops: number = 250;
|
||||
|
||||
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
|
||||
p = sdk.AudioInputStream.createPullStream(
|
||||
{
|
||||
close: () => { return; },
|
||||
read: (buffer: ArrayBuffer): number => {
|
||||
if (pumpSilence) {
|
||||
bytesSent += buffer.byteLength;
|
||||
if (bytesSent >= 16000) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = false;
|
||||
}
|
||||
return buffer.byteLength;
|
||||
} else {
|
||||
// Alternate between the two files with different phrases in them.
|
||||
const sendBuffer: ArrayBuffer = sendAlternateFile ? alternatePhraseFileBuffer : fileBuffer;
|
||||
|
||||
const copyArray: Uint8Array = new Uint8Array(buffer);
|
||||
const start: number = bytesSent;
|
||||
const end: number = buffer.byteLength > (sendBuffer.byteLength - bytesSent) ? (sendBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
|
||||
copyArray.set(new Uint8Array(sendBuffer.slice(start, end)));
|
||||
const readyToSend: number = (end - start) + 1;
|
||||
bytesSent += readyToSend;
|
||||
|
||||
if (readyToSend < buffer.byteLength) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = true;
|
||||
sendAlternateFile = !sendAlternateFile;
|
||||
}
|
||||
|
||||
return readyToSend;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
|
||||
const r: sdk.TranslationRecognizer = new sdk.TranslationRecognizer(s, config);
|
||||
objsToClose.push(r);
|
||||
|
||||
let speechEnded: number = 0;
|
||||
let lastOffset: number = 0;
|
||||
let recogCount: number = 0;
|
||||
let canceled: boolean = false;
|
||||
let inTurn: boolean = false;
|
||||
let alternatePhrase: boolean = false;
|
||||
|
||||
r.recognized = (o: sdk.Recognizer, e: sdk.TranslationRecognitionEventArgs) => {
|
||||
try {
|
||||
// If the target number of loops has been seen already, don't check as the audio being sent could have been clipped randomly during a phrase,
|
||||
// and failing because of that isn't warranted.
|
||||
if (recogCount <= targetLoops) {
|
||||
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.TranslatedSpeech]);
|
||||
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
|
||||
lastOffset = e.offset;
|
||||
|
||||
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
|
||||
// connection is disconnected.
|
||||
if ("" !== e.result.text) {
|
||||
if (alternatePhrase) {
|
||||
expect(e.result.text).toEqual(Settings.LuisWavFileText);
|
||||
} else {
|
||||
expect(e.result.text).toEqual(Settings.WaveFileText);
|
||||
}
|
||||
|
||||
alternatePhrase = !alternatePhrase;
|
||||
}
|
||||
if (recogCount++ >= targetLoops) {
|
||||
p.close();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.canceled = (o: sdk.Recognizer, e: sdk.TranslationRecognitionCanceledEventArgs): void => {
|
||||
try {
|
||||
expect(e.errorDetails).toBeUndefined();
|
||||
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
|
||||
canceled = true;
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = true;
|
||||
});
|
||||
|
||||
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = false;
|
||||
});
|
||||
|
||||
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
|
||||
speechEnded++;
|
||||
};
|
||||
|
||||
r.startContinuousRecognitionAsync(() => {
|
||||
WaitForCondition(() => (canceled && !inTurn), () => {
|
||||
r.stopContinuousRecognitionAsync(() => {
|
||||
try {
|
||||
expect(speechEnded).toEqual(1);
|
||||
done();
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
(err: string) => {
|
||||
done.fail(err);
|
||||
});
|
||||
}, 1000 * 60 * 12);
|
|
@ -24,6 +24,12 @@ export class Settings {
|
|||
|
||||
public static InputDir: string = "tests/input/audio/";
|
||||
|
||||
public static ExecuteLongRunningTests: string = "false";
|
||||
|
||||
public static get ExecuteLongRunningTestsBool(): boolean {
|
||||
return "false" !== this.ExecuteLongRunningTests;
|
||||
}
|
||||
|
||||
/*
|
||||
* The intent behing this setting is that at test execution time the WaveFile below will contain speech
|
||||
* that the LUIS app above will recognize as an intent with this ID.
|
||||
|
|
|
@ -11,6 +11,8 @@ import { Settings } from "./Settings";
|
|||
import { WaveFileAudioInput } from "./WaveFileAudioInputStream";
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as request from "request";
|
||||
|
||||
import { setTimeout } from "timers";
|
||||
import { ByteBufferAudioFile } from "./ByteBufferAudioFile";
|
||||
import WaitForCondition from "./Utilities";
|
||||
|
@ -1606,132 +1608,6 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
done.fail(err);
|
||||
});
|
||||
}, 35000);
|
||||
|
||||
// Tests client reconnect after speech timeouts.
|
||||
test.skip("Reconnect After timeout", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: Reconnect After timeout");
|
||||
// Pump valid speech and then silence until at least one speech end cycle hits.
|
||||
const fileBuffer: ArrayBuffer = WaveFileAudioInput.LoadArrayFromFile(Settings.WaveFile);
|
||||
|
||||
let p: sdk.PullAudioInputStream;
|
||||
const bigFileBuffer: Uint8Array = new Uint8Array(32 * 1024 * 30); // ~30 seconds.
|
||||
let s: sdk.SpeechConfig;
|
||||
if (undefined === Settings.SpeechTimeoutEndpoint || undefined === Settings.SpeechTimeoutKey) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.warn("Running timeout test against production, this will be very slow...");
|
||||
s = BuildSpeechConfig();
|
||||
} else {
|
||||
s = sdk.SpeechConfig.fromEndpoint(new URL(Settings.SpeechTimeoutEndpoint), Settings.SpeechTimeoutKey);
|
||||
}
|
||||
objsToClose.push(s);
|
||||
|
||||
let pumpSilence: boolean = false;
|
||||
let bytesSent: number = 0;
|
||||
const targetLoops: number = 250;
|
||||
|
||||
// Pump the audio from the wave file specified with 1 second silence between iterations indefinetly.
|
||||
p = sdk.AudioInputStream.createPullStream(
|
||||
{
|
||||
close: () => { return; },
|
||||
read: (buffer: ArrayBuffer): number => {
|
||||
if (pumpSilence) {
|
||||
bytesSent += buffer.byteLength;
|
||||
if (bytesSent >= 8000) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = false;
|
||||
}
|
||||
return buffer.byteLength;
|
||||
} else {
|
||||
const copyArray: Uint8Array = new Uint8Array(buffer);
|
||||
const start: number = bytesSent;
|
||||
const end: number = buffer.byteLength > (fileBuffer.byteLength - bytesSent) ? (fileBuffer.byteLength - 1) : (bytesSent + buffer.byteLength - 1);
|
||||
copyArray.set(new Uint8Array(fileBuffer.slice(start, end)));
|
||||
const readyToSend: number = (end - start) + 1;
|
||||
bytesSent += readyToSend;
|
||||
|
||||
if (readyToSend < buffer.byteLength) {
|
||||
bytesSent = 0;
|
||||
pumpSilence = true;
|
||||
}
|
||||
|
||||
return readyToSend;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
|
||||
const r: sdk.SpeechRecognizer = new sdk.SpeechRecognizer(s, config);
|
||||
objsToClose.push(r);
|
||||
|
||||
let speechEnded: number = 0;
|
||||
let lastOffset: number = 0;
|
||||
let recogCount: number = 0;
|
||||
let canceled: boolean = false;
|
||||
let inTurn: boolean = false;
|
||||
|
||||
r.recognized = (o: sdk.Recognizer, e: sdk.SpeechRecognitionEventArgs) => {
|
||||
try {
|
||||
expect(sdk.ResultReason[e.result.reason]).toEqual(sdk.ResultReason[sdk.ResultReason.RecognizedSpeech]);
|
||||
expect(e.offset).toBeGreaterThanOrEqual(lastOffset);
|
||||
lastOffset = e.offset;
|
||||
|
||||
// If there is silence exactly at the moment of disconnect, an extra speech.phrase with text ="" is returned just before the
|
||||
// connection is disconnected.
|
||||
if ("" !== e.result.text) {
|
||||
expect(e.result.text).toEqual(Settings.WaveFileText);
|
||||
}
|
||||
if (recogCount++ > targetLoops) {
|
||||
p.close();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.canceled = (o: sdk.Recognizer, e: sdk.SpeechRecognitionCanceledEventArgs): void => {
|
||||
try {
|
||||
expect(e.errorDetails).toBeUndefined();
|
||||
expect(sdk.CancellationReason[e.reason]).toEqual(sdk.CancellationReason[sdk.CancellationReason.EndOfStream]);
|
||||
canceled = true;
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
};
|
||||
|
||||
r.sessionStarted = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = true;
|
||||
});
|
||||
|
||||
r.sessionStopped = ((s: sdk.Recognizer, e: sdk.SessionEventArgs): void => {
|
||||
inTurn = false;
|
||||
});
|
||||
|
||||
r.speechEndDetected = (o: sdk.Recognizer, e: sdk.RecognitionEventArgs): void => {
|
||||
speechEnded++;
|
||||
};
|
||||
|
||||
r.startContinuousRecognitionAsync(() => {
|
||||
WaitForCondition(() => (canceled && !inTurn), () => {
|
||||
r.stopContinuousRecognitionAsync(() => {
|
||||
try {
|
||||
expect(speechEnded).toEqual(1);
|
||||
done();
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
(err: string) => {
|
||||
done.fail(err);
|
||||
});
|
||||
}, 35000);
|
||||
});
|
||||
|
||||
test("Push Stream Async", (done: jest.DoneCallback) => {
|
||||
|
@ -1772,7 +1648,7 @@ test("Push Stream Async", (done: jest.DoneCallback) => {
|
|||
(error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
test("Bad DataType for PushStreams results in error", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
|
|
|
@ -632,7 +632,7 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
});
|
||||
}, 10000);
|
||||
|
||||
test.skip("MultiPhrase", (done: jest.DoneCallback) => {
|
||||
test("MultiPhrase", (done: jest.DoneCallback) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.info("Name: MultiPhrase");
|
||||
const s: sdk.SpeechTranslationConfig = BuildSpeechConfig();
|
||||
|
@ -646,9 +646,11 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
const p: sdk.PushAudioInputStream = sdk.AudioInputStream.createPushStream();
|
||||
const config: sdk.AudioConfig = sdk.AudioConfig.fromStreamInput(p);
|
||||
const numPhrases: number = 3;
|
||||
const silentBuffer: ArrayBuffer = new ArrayBuffer(32000);
|
||||
|
||||
for (let i: number = 0; i < 3; i++) {
|
||||
p.write(f);
|
||||
p.write(silentBuffer);
|
||||
}
|
||||
|
||||
p.close();
|
||||
|
@ -686,18 +688,13 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
let inTurn: boolean = false;
|
||||
|
||||
r.canceled = ((o: sdk.Recognizer, e: sdk.TranslationRecognitionCanceledEventArgs) => {
|
||||
try {
|
||||
switch (e.reason) {
|
||||
case sdk.CancellationReason.Error:
|
||||
done.fail(e.errorDetails);
|
||||
break;
|
||||
case sdk.CancellationReason.EndOfStream:
|
||||
expect(synthCount).toEqual(numPhrases);
|
||||
canceled = true;
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
switch (e.reason) {
|
||||
case sdk.CancellationReason.Error:
|
||||
done.fail(e.errorDetails);
|
||||
break;
|
||||
case sdk.CancellationReason.EndOfStream:
|
||||
canceled = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -718,6 +715,7 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
|
||||
for (let i: number = 0; i < synthFragmentCount; i++) {
|
||||
p.write(rEvents[i]);
|
||||
p.write(silentBuffer);
|
||||
}
|
||||
p.close();
|
||||
|
||||
|
@ -764,8 +762,14 @@ describe.each([true, false])("Service based tests", (forceNodeWebSocket: boolean
|
|||
WaitForCondition(() => (canceled && !inTurn),
|
||||
() => {
|
||||
r2.stopContinuousRecognitionAsync(() => {
|
||||
expect(numEvents).toEqual(numPhrases);
|
||||
done();
|
||||
try {
|
||||
expect(synthCount).toEqual(numPhrases);
|
||||
expect(numEvents).toEqual(numPhrases);
|
||||
done();
|
||||
} catch (error) {
|
||||
done.fail(error);
|
||||
}
|
||||
|
||||
}, (error: string) => {
|
||||
done.fail(error);
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true,
|
||||
"target": "ES5",
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false
|
||||
"module": "commonjs",
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true,
|
||||
"target": "ES5",
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"removeComments": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,4 +48,4 @@
|
|||
"secrets/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче