[communication] Move @azure/communication-common to ESM/vitest (#31419)

### Packages impacted by this PR

- @azure/communication-common

### Issues associated with this PR

- https://github.com/Azure/azure-sdk-for-js/issues/31338

### Describe the problem that is addressed by this PR

Updates to ESM/vitest for @azure/communication-common

### What are the possible designs available to address the problem? If
there are more than one possible design, why was the one in this PR
chosen?


### Are there test cases added in this PR? _(If not, why?)_


### Provide a list of related PRs _(if any)_


### Command used to generate this PR:**_(Applicable only to SDK release
request PRs)_

### Checklists
- [ ] Added impacted package name to the issue description
- [ ] Does this PR needs any fixes in the SDK Generator?** _(If so,
create an Issue in the
[Autorest/typescript](https://github.com/Azure/autorest.typescript)
repository and link it here)_
- [ ] Added a changelog (if necessary)
This commit is contained in:
Matthew Podwysocki 2024-10-16 11:51:06 -04:00 коммит произвёл GitHub
Родитель 0cacd2113a
Коммит fe7545b8bd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
34 изменённых файлов: 263 добавлений и 323 удалений

Просмотреть файл

@ -19904,7 +19904,7 @@ packages:
dev: false
file:projects/communication-common.tgz:
resolution: {integrity: sha512-lWGXpp9xHaRFtOdJrZsv/5OrHYEAlT1diEen2KkDnO9udQ8XTDs9EGdRwnuprs7bAj/FzawXckFxj27gJ1C85Q==, tarball: file:projects/communication-common.tgz}
resolution: {integrity: sha512-BoEaaH3sJLyt/JMQFr7Fw+iEtJ+73S+Ycef8OXgaLJZqUqrKFHONVPvT9PgnMKMm7Drv75jkfxY/yY18ZlOp8A==, tarball: file:projects/communication-common.tgz}
name: '@rush-temp/communication-common'
version: 0.0.0
dependencies:
@ -19914,6 +19914,8 @@ packages:
'@types/mocha': 10.0.9
'@types/node': 18.19.55
'@types/sinon': 17.0.3
'@vitest/browser': 2.1.3(playwright@1.48.0)(typescript@5.6.3)(vitest@2.1.3)
'@vitest/coverage-istanbul': 2.1.3(vitest@2.1.3)
chai: 4.3.10
chai-as-promised: 7.1.2(chai@4.3.10)
cross-env: 7.0.3
@ -19933,20 +19935,34 @@ packages:
mocha: 10.7.3
mockdate: 3.0.5
nyc: 17.1.0
playwright: 1.48.0
rimraf: 5.0.10
sinon: 17.0.1
ts-node: 10.9.2(@types/node@18.19.55)(typescript@5.6.3)
tslib: 2.7.0
typescript: 5.6.3
util: 0.12.5
vitest: 2.1.3(@types/node@18.19.55)(@vitest/browser@2.1.3)
transitivePeerDependencies:
- '@swc/core'
- '@swc/wasm'
- '@edge-runtime/vm'
- '@vitest/ui'
- bufferutil
- debug
- happy-dom
- jiti
- jsdom
- less
- lightningcss
- msw
- safaridriver
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- utf-8-validate
- vite
- webdriverio
dev: false
file:projects/communication-email.tgz:
@ -24257,7 +24273,7 @@ packages:
dev: false
file:projects/storage-file-datalake.tgz:
resolution: {integrity: sha512-SUsQnAA/geN3tUNpcVlFI+/vnrIcbnEYN/xfR5sWlu+qTJ6Hh0tqGofCusgJTlzfZyLSm/hv2GW6c3/Yj8iung==, tarball: file:projects/storage-file-datalake.tgz}
resolution: {integrity: sha512-e+GSDtmAiZZkAHoCQZQAEWsR4wCqZTTbBfC85vnIBIVdlBTho8fI5YywC7VMtYylhd3J2SF/wWl7PGqbz/1MoA==, tarball: file:projects/storage-file-datalake.tgz}
name: '@rush-temp/storage-file-datalake'
version: 0.0.0
dependencies:

Просмотреть файл

@ -1,10 +0,0 @@
{
"include": ["dist-esm/src/**/*.js"],
"exclude": ["**/*.d.ts", "dist-esm/src/generated/*"],
"reporter": ["text-summary", "html", "cobertura"],
"exclude-after-remap": false,
"sourceMap": true,
"produce-source-map": true,
"instrument": true,
"all": true
}

Просмотреть файл

@ -1,6 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "types/src/index.d.ts",
"mainEntryPointFilePath": "dist/esm/index.d.ts",
"docModel": {
"enabled": true
},
@ -11,7 +11,7 @@
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "",
"publicTrimmedFilePath": "./types/communication-common.d.ts"
"publicTrimmedFilePath": "dist/communication-common.d.ts"
},
"messages": {
"tsdocMessageReporting": {

Просмотреть файл

@ -1,115 +0,0 @@
// https://github.com/karma-runner/karma-chrome-launcher
process.env.CHROME_BIN = require("puppeteer").executablePath();
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: "./",
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ["mocha"],
plugins: [
"karma-mocha",
"karma-mocha-reporter",
"karma-chrome-launcher",
"karma-firefox-launcher",
"karma-env-preprocessor",
"karma-coverage",
"karma-sourcemap-loader",
"karma-junit-reporter",
],
// list of files / patterns to load in the browser
files: ["dist-test/index.browser.js"],
// list of files / patterns to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
"**/*.js": ["env"],
// IMPORTANT: COMMENT following line if you want to debug in your browsers!!
// Preprocess source file to calculate code coverage, however this will make source file unreadable
//"dist-test/index.browser.js": ["coverage"]
},
// inject following environment values into browser testing with window.__env__
// environment values MUST be exported or set with same console running "karma start"
// https://www.npmjs.com/package/karma-env-preprocessor
envPreprocessor: ["ACCOUNT_NAME", "ACCOUNT_SAS"],
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ["mocha", "coverage", "junit"],
coverageReporter: {
// specify a common output directory
dir: "coverage-browser/",
reporters: [
{ type: "json", subdir: ".", file: "coverage.json" },
{ type: "lcovonly", subdir: ".", file: "lcov.info" },
{ type: "html", subdir: "html" },
{ type: "cobertura", subdir: ".", file: "cobertura-coverage.xml" },
],
},
junitReporter: {
outputDir: "", // results will be saved as $outputDir/$browserName.xml
outputFile: "test-results.browser.xml", // if included, results will be saved as $outputDir/$browserName/$outputFile
suite: "", // suite will become the package name attribute in xml testsuite element
useBrowserName: false, // add browser name to report and classes names
nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element
classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element
properties: {}, // key value pair of properties to add to the <properties> section of the report
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// 'ChromeHeadless', 'Chrome', 'Firefox', 'Edge', 'IE'
browsers: ["HeadlessChrome"],
customLaunchers: {
HeadlessChrome: {
base: "ChromeHeadless",
flags: ["--no-sandbox"],
},
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
// Concurrency level
// how many browser should be started simultaneous
concurrency: 1,
browserNoActivityTimeout: 600000,
browserDisconnectTimeout: 10000,
browserDisconnectTolerance: 3,
client: {
mocha: {
// change Karma's debug.html to the mocha web reporter
reporter: "html",
timeout: "600000",
},
},
});
};

Просмотреть файл

@ -3,28 +3,25 @@
"version": "2.3.2",
"description": "Common package for Azure Communication services.",
"sdk-type": "client",
"main": "dist/index.js",
"module": "dist-esm/src/index.js",
"types": "types/communication-common.d.ts",
"browser": {
"./dist-esm/src/credential/cryptoUtils.js": "./dist-esm/src/credential/cryptoUtils.browser.js",
"./dist-esm/src/credential/isNode.js": "./dist-esm/src/credential/isNode.browser.js"
},
"main": "./dist/commonjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/commonjs/index.d.ts",
"browser": "./dist/browser/index.js",
"scripts": {
"audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit",
"build": "npm run clean && tsc -p . && dev-tool run bundle && dev-tool run extract-api",
"build:browser": "tsc -p . && dev-tool run bundle",
"build:node": "tsc -p . && dev-tool run bundle",
"build": "npm run clean && dev-tool run build-package && dev-tool run extract-api",
"build:browser": "dev-tool run build-package && dev-tool run bundle",
"build:node": "dev-tool run build-package && dev-tool run bundle",
"build:samples": "echo Skipped.",
"build:test": "tsc -p . && dev-tool run bundle",
"build:test": "dev-tool run build-package && dev-tool run bundle",
"check-format": "dev-tool run vendored prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
"clean": "rimraf --glob dist dist-* temp types *.tgz *.log",
"execute:samples": "echo skipped",
"extract-api": "tsc -p . && dev-tool run extract-api",
"extract-api": "dev-tool run build-package && dev-tool run extract-api",
"format": "dev-tool run vendored prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
"integration-test": "npm run integration-test:node && npm run integration-test:browser",
"integration-test:browser": "karma start --single-run",
"integration-test:node": "dev-tool run test:node-js-input -- --timeout 5000000 'dist-esm/test/**/*.spec.js'",
"integration-test:browser": "npm run clean && dev-tool run build-package && dev-tool run build-test && dev-tool run test:vitest --no-test-proxy --browser",
"integration-test:node": "dev-tool run test:vitest --no-test-proxy",
"lint": "eslint package.json api-extractor.json README.md src test",
"lint:fix": "eslint package.json api-extractor.json README.md src test --fix --fix-type [problem,suggestion]",
"pack": "npm pack 2>&1",
@ -32,14 +29,12 @@
"test:browser": "npm run build:test && npm run unit-test:browser && npm run integration-test:browser",
"test:node": "npm run build:test && npm run unit-test:node && npm run integration-test:node",
"unit-test": "npm run unit-test:node && npm run unit-test:browser",
"unit-test:browser": "karma start --single-run",
"unit-test:node": "dev-tool run test:node-ts-input --no-test-proxy",
"unit-test:browser": "npm run clean && dev-tool run build-package && dev-tool run build-test && dev-tool run test:vitest --no-test-proxy --browser",
"unit-test:node": "dev-tool run test:vitest --no-test-proxy",
"update-snippets": "echo skipped"
},
"files": [
"dist/",
"dist-esm/src",
"types/communication-common.d.ts",
"README.md",
"LICENSE"
],
@ -63,45 +58,68 @@
"sideEffects": false,
"prettier": "@azure/eslint-plugin-azure-sdk/prettier.json",
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-rest-pipeline": "^1.3.2",
"@azure/core-tracing": "^1.0.0",
"@azure/core-util": "^1.0.0",
"@azure/abort-controller": "^2.1.2",
"@azure/core-auth": "^1.8.0",
"@azure/core-rest-pipeline": "^1.17.0",
"@azure/core-tracing": "^1.2.0",
"@azure/core-util": "^1.10.0",
"events": "^3.0.0",
"jwt-decode": "^4.0.0",
"tslib": "^2.2.0"
"tslib": "^2.7.0"
},
"devDependencies": {
"@azure-tools/test-utils-vitest": "^1.0.0",
"@azure/dev-tool": "^1.0.0",
"@azure/eslint-plugin-azure-sdk": "^3.0.0",
"@microsoft/api-extractor": "^7.31.1",
"@types/chai": "^4.1.6",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^10.0.0",
"@types/node": "^18.0.0",
"@types/sinon": "^17.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"@vitest/browser": "^2.1.3",
"@vitest/coverage-istanbul": "^2.1.3",
"cross-env": "^7.0.2",
"eslint": "^9.9.0",
"inherits": "^2.0.3",
"karma": "^6.2.0",
"karma-chrome-launcher": "^3.0.0",
"karma-coverage": "^2.0.0",
"karma-env-preprocessor": "^0.1.1",
"karma-firefox-launcher": "^1.1.0",
"karma-junit-reporter": "^2.0.1",
"karma-mocha": "^2.0.1",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.8",
"mocha": "^10.0.0",
"mockdate": "^3.0.5",
"nyc": "^17.0.0",
"playwright": "^1.48.0",
"rimraf": "^5.0.5",
"sinon": "^17.0.0",
"ts-node": "^10.0.0",
"typescript": "~5.6.2",
"util": "^0.12.1"
"util": "^0.12.1",
"vitest": "^2.1.3"
},
"type": "module",
"tshy": {
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts"
},
"dialects": [
"esm",
"commonjs"
],
"esmDialects": [
"browser",
"react-native"
],
"selfLink": false
},
"exports": {
"./package.json": "./package.json",
".": {
"browser": {
"types": "./dist/browser/index.d.ts",
"default": "./dist/browser/index.js"
},
"react-native": {
"types": "./dist/react-native/index.d.ts",
"default": "./dist/react-native/index.js"
},
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
}
}
}

Просмотреть файл

@ -4,8 +4,8 @@
```ts
import { AbortSignalLike } from '@azure/abort-controller';
import { AccessToken } from '@azure/core-auth';
import type { AbortSignalLike } from '@azure/abort-controller';
import type { AccessToken } from '@azure/core-auth';
import { KeyCredential } from '@azure/core-auth';
import { PipelinePolicy } from '@azure/core-rest-pipeline';
import { TokenCredential } from '@azure/core-auth';

Просмотреть файл

Просмотреть файл

@ -1,10 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { CommunicationGetTokenOptions, TokenCredential } from "./communicationTokenCredential";
import { AbortSignalLike } from "@azure/abort-controller";
import { AccessToken } from "@azure/core-auth";
import { parseToken } from "./tokenParser";
import type {
CommunicationGetTokenOptions,
TokenCredential,
} from "./communicationTokenCredential.js";
import type { AbortSignalLike } from "@azure/abort-controller";
import type { AccessToken } from "@azure/core-auth";
import { parseToken } from "./tokenParser.js";
/**
* Options for auto-refreshing a Communication Token credential.

Просмотреть файл

@ -3,16 +3,16 @@
import {
AutoRefreshTokenCredential,
CommunicationTokenRefreshOptions,
} from "./autoRefreshTokenCredential";
import {
type CommunicationTokenRefreshOptions,
} from "./autoRefreshTokenCredential.js";
import type {
CommunicationGetTokenOptions,
CommunicationTokenCredential,
TokenCredential,
} from "./communicationTokenCredential";
import { AccessToken } from "@azure/core-auth";
import { StaticTokenCredential } from "./staticTokenCredential";
import { parseToken } from "./tokenParser";
} from "./communicationTokenCredential.js";
import type { AccessToken } from "@azure/core-auth";
import { StaticTokenCredential } from "./staticTokenCredential.js";
import { parseToken } from "./tokenParser.js";
/**
* The CommunicationTokenCredential implementation with support for proactive token refresh.

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AbortSignalLike } from "@azure/abort-controller";
import { AccessToken } from "@azure/core-auth";
import type { AbortSignalLike } from "@azure/abort-controller";
import type { AccessToken } from "@azure/core-auth";
export type TokenCredential = Pick<CommunicationTokenCredential, "getToken" | "dispose">;

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { KeyCredential, TokenCredential, isTokenCredential } from "@azure/core-auth";
import { parseConnectionString } from "./connectionString";
import { type KeyCredential, type TokenCredential, isTokenCredential } from "@azure/core-auth";
import { parseConnectionString } from "./connectionString.js";
const isValidEndpoint = (host: string): boolean => {
const url = new URL(host);

Просмотреть файл

@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import {
import type {
PipelinePolicy,
PipelineRequest,
PipelineResponse,
SendRequest,
} from "@azure/core-rest-pipeline";
import { shaHMAC, shaHash } from "./cryptoUtils";
import { KeyCredential } from "@azure/core-auth";
import { isNode } from "@azure/core-util";
import { shaHMAC, shaHash } from "./cryptoUtils.js";
import type { KeyCredential } from "@azure/core-auth";
import { isNodeLike } from "@azure/core-util";
/**
* CommunicationKeyCredentialPolicy provides a means of signing requests made through
@ -44,7 +44,7 @@ export function createCommunicationAccessKeyCredentialPolicy(
const stringToSign = `${verb}\n${urlPathAndQuery}\n${utcNow};${hostAndPort};${contentHash}`;
const signature = await shaHMAC(credential.key, stringToSign);
if (isNode) {
if (isNodeLike) {
request.headers.set("Host", hostAndPort || "");
}

Просмотреть файл

@ -2,12 +2,12 @@
// Licensed under the MIT License.
import {
BearerTokenAuthenticationPolicyOptions,
PipelinePolicy,
type BearerTokenAuthenticationPolicyOptions,
type PipelinePolicy,
bearerTokenAuthenticationPolicy,
} from "@azure/core-rest-pipeline";
import { KeyCredential, TokenCredential, isTokenCredential } from "@azure/core-auth";
import { createCommunicationAccessKeyCredentialPolicy } from "./communicationAccessKeyCredentialPolicy";
import { type KeyCredential, type TokenCredential, isTokenCredential } from "@azure/core-auth";
import { createCommunicationAccessKeyCredentialPolicy } from "./communicationAccessKeyCredentialPolicy.js";
/**
* Creates a pipeline policy to authenticate request based
* on the credential passed in.

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AzureKeyCredential, KeyCredential } from "@azure/core-auth";
import { AzureKeyCredential, type KeyCredential } from "@azure/core-auth";
/**
* Represents different properties of connection string
* using format "/endpoint=(.*);accesskey=(.*)".

Просмотреть файл

@ -3,13 +3,13 @@
/// <reference lib="dom" />
import { encodeBase64, encodeUTF8, encodeUTF8fromBase64 } from "./encodeUtils.browser";
import { encodeBase64, encodeUTF8, encodeUTF8fromBase64 } from "./encodeUtils.browser.js";
const subtle = (globalThis as any)?.crypto?.subtle as SubtleCrypto;
const subtleCrypto = globalThis.crypto.subtle;
export const shaHash = async (content: string): Promise<string> => {
const data = encodeUTF8(content);
const hash = await subtle.digest("SHA-256", data);
const hash = await subtleCrypto.digest("SHA-256", data);
return encodeBase64(hash);
};
@ -17,8 +17,7 @@ export const shaHMAC = async (secret: string, content: string): Promise<string>
const importParams: HmacImportParams = { name: "HMAC", hash: { name: "SHA-256" } };
const encodedMessage = encodeUTF8(content);
const encodedKey = encodeUTF8fromBase64(secret);
const crypto = subtle;
const cryptoKey = await crypto.importKey("raw", encodedKey, importParams, false, ["sign"]);
const signature = await crypto.sign(importParams, cryptoKey, encodedMessage);
const cryptoKey = await subtleCrypto.importKey("raw", encodedKey, importParams, false, ["sign"]);
const signature = await subtleCrypto.sign(importParams, cryptoKey, encodedMessage);
return encodeBase64(signature);
};

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { createHash, createHmac } from "crypto";
import { createHash, createHmac } from "node:crypto";
export const shaHash = async (content: string): Promise<string> =>
createHash("sha256").update(content).digest("base64");

Просмотреть файл

@ -8,11 +8,7 @@ export function encodeUTF8fromBase64(str: string): Uint8Array {
throw new Error("Your browser environment is missing the global `atob` function");
}
const binary = atob(str);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
}
export function encodeBase64(value: ArrayBuffer): string {
@ -20,9 +16,6 @@ export function encodeBase64(value: ArrayBuffer): string {
throw new Error("Your browser environment is missing the global `btoa` function");
}
const bytes = new Uint8Array(value);
let binary = "";
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}
const binary = String.fromCharCode.apply(null, [...bytes]);
return btoa(binary);
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export * from "./communicationAccessKeyCredentialPolicy";
export * from "./communicationAuthPolicy";
export * from "./clientArguments";
export * from "./connectionString";
export * from "./communicationAccessKeyCredentialPolicy.js";
export * from "./communicationAuthPolicy.js";
export * from "./clientArguments.js";
export * from "./connectionString.js";

Просмотреть файл

@ -2,11 +2,11 @@
// Licensed under the MIT License.
import {
CommunicationIdentifier,
CommunicationIdentifierKind,
type CommunicationIdentifier,
type CommunicationIdentifierKind,
getIdentifierKind,
getIdentifierRawId,
} from "./identifierModels";
} from "./identifierModels.js";
/**
* @hidden

Просмотреть файл

@ -4,10 +4,10 @@
export {
CommunicationTokenCredential,
CommunicationGetTokenOptions,
} from "./communicationTokenCredential";
export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCredential";
export * from "./credential";
export { CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential";
export * from "./credential";
export * from "./identifierModels";
export * from "./identifierModelSerializer";
} from "./communicationTokenCredential.js";
export { AzureCommunicationTokenCredential } from "./azureCommunicationTokenCredential.js";
export * from "./credential/index.js";
export { CommunicationTokenRefreshOptions } from "./autoRefreshTokenCredential.js";
export * from "./credential/index.js";
export * from "./identifierModels.js";
export * from "./identifierModelSerializer.js";

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AccessToken } from "@azure/core-auth";
import { TokenCredential } from "./communicationTokenCredential";
import type { AccessToken } from "@azure/core-auth";
import type { TokenCredential } from "./communicationTokenCredential.js";
/**
* StaticTokenCredential

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AccessToken } from "@azure/core-auth";
import type { AccessToken } from "@azure/core-auth";
import { jwtDecode } from "jwt-decode";
interface JwtToken {

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { shaHMAC, shaHash } from "../../src/credential/cryptoUtils";
import { assert } from "chai";
import { shaHMAC, shaHash } from "../../src/credential/cryptoUtils.js";
import { describe, it, assert } from "vitest";
describe("CryptoUtils", function () {
it("calculates correct hash", async function () {

Просмотреть файл

@ -2,10 +2,10 @@
// Licensed under the MIT License.
import { AzureKeyCredential } from "@azure/core-auth";
import { assert } from "chai";
import { parseClientArguments } from "../../src";
import { parseConnectionString } from "../../src";
import { assertPropertyNames } from "./utils/credentialUtils";
import { parseClientArguments } from "../../src/index.js";
import { parseConnectionString } from "../../src/index.js";
import { assertPropertyNames } from "./utils/credentialUtils.js";
import { describe, it, assert } from "vitest";
const mockCredential = new AzureKeyCredential("secret");
const host = "https://contoso.communicationservices.azure.com";

Просмотреть файл

@ -9,10 +9,10 @@ import {
createPipelineRequest,
} from "@azure/core-rest-pipeline";
import { KeyCredential } from "@azure/core-auth";
import { assert } from "chai";
import { createCommunicationAccessKeyCredentialPolicy } from "../../src";
import { isNode } from "@azure/core-util";
import { createCommunicationAccessKeyCredentialPolicy } from "../../src/index.js";
import { isNodeLike } from "@azure/core-util";
import { set } from "mockdate";
import { describe, it, assert } from "vitest";
const date = "2022-04-13T18:09:12.451Z";
set(date); // Any request to Date will return this date
@ -84,7 +84,7 @@ async function verifyHeadersForUrlReturnAuthHeader(urlToTest: string): Promise<s
assert.equal(dateHeader, "Wed, 13 Apr 2022 18:09:12 GMT");
assert.equal(hashHeader, "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
if (isNode) {
if (isNodeLike) {
assert.isNotEmpty(hostHeader);
}
return authHeader;

Просмотреть файл

@ -1,20 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { assert, use } from "chai";
import { AzureCommunicationTokenCredential } from "../../src";
import chaiAsPromised from "chai-as-promised";
import { isNode } from "@azure/core-util";
import sinon from "sinon";
use(chaiAsPromised);
import { AzureCommunicationTokenCredential } from "../../src/index.js";
import { isNodeLike } from "@azure/core-util";
import { describe, it, assert, expect, vi, beforeEach, afterEach } from "vitest";
declare function btoa(stringToEncode: string): string;
const generateToken = (validForMinutes: number): string => {
const expiresOn = (Date.now() + validForMinutes * 60 * 1000) / 1000;
const tokenString = JSON.stringify({ exp: expiresOn });
const base64Token = isNode ? Buffer.from(tokenString).toString("base64") : btoa(tokenString);
const base64Token = isNodeLike ? Buffer.from(tokenString).toString("base64") : btoa(tokenString);
return `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${base64Token}.adM-ddBZZlQ1WlN3pdPBOF5G4Wh9iZpxNP_fSvpF4cWs`;
};
@ -34,14 +29,12 @@ const exposeInternalUpdatePromise = async (
};
describe("CommunicationTokenCredential", function () {
let clock: sinon.SinonFakeTimers;
beforeEach(function () {
clock = sinon.useFakeTimers();
vi.useFakeTimers();
});
afterEach(function () {
clock.restore();
vi.useRealTimers();
});
it("handles valid JWT strings", async function () {
@ -53,11 +46,11 @@ describe("CommunicationTokenCredential", function () {
});
it("throws if non-JWT passed as lambda", async function () {
await assert.isRejected(
await expect(() =>
new AzureCommunicationTokenCredential({
tokenRefresher: async () => "IAmNotAToken",
}).getToken(),
);
).rejects.toThrow();
});
it("returns token as expected", async function () {
@ -69,18 +62,18 @@ describe("CommunicationTokenCredential", function () {
it("returns token as expected when using lambda", async function () {
const token = generateToken(60);
const tokenRefresher = sinon.stub().resolves(token);
const tokenRefresher = vi.fn().mockResolvedValue(token);
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
});
const tokenResult = (await tokenCredential.getToken()).token;
assert.strictEqual(tokenResult, token);
sinon.assert.calledOnce(tokenRefresher);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("uses initial token as expected", async function () {
const token = generateToken(60);
const tokenRefresher = sinon.stub().resolves(generateToken(120));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(120));
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
token,
@ -88,42 +81,39 @@ describe("CommunicationTokenCredential", function () {
});
const tokenResult = (await tokenCredential.getToken()).token;
assert.strictEqual(tokenResult, token);
sinon.assert.notCalled(tokenRefresher);
expect(tokenRefresher).not.toHaveBeenCalled();
});
it("no proactive refresh, accepts expired token", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(-1));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(-1));
new AzureCommunicationTokenCredential({
tokenRefresher,
});
clock.tick(5 * 60 * 1000);
sinon.assert.notCalled(tokenRefresher);
vi.advanceTimersByTime(5 * 60 * 1000);
expect(tokenRefresher).not.toHaveBeenCalled();
});
it("with proactive refresh, passing an expired token to constructor triggers immediate refresh", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(30));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(30));
new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
token: generateToken(-1),
});
clock.tick(5 * 60 * 1000);
vi.advanceTimersByTime(5 * 60 * 1000);
sinon.assert.calledOnce(tokenRefresher);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("throws if tokenRefresher returns an expired token", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(-1));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(-1));
const credential = new AzureCommunicationTokenCredential({
tokenRefresher: tokenRefresher,
});
clock.tick(5 * 60 * 1000);
await assert.isRejected(
credential.getToken(),
Error,
"The token returned from the tokenRefresher is expired.",
);
sinon.assert.calledOnce(tokenRefresher);
vi.advanceTimersByTime(5 * 60 * 1000);
await expect(credential.getToken()).rejects.toThrow(Error);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("returns expired token when not using a lambda", async function () {
@ -134,14 +124,14 @@ describe("CommunicationTokenCredential", function () {
});
it("passes abortSignal through to tokenRefresher", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(60));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(60));
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
});
const aborter = new AbortController();
const options = { abortSignal: aborter.signal };
tokenCredential.getToken(options);
sinon.assert.calledOnceWithExactly(tokenRefresher, options.abortSignal);
expect(tokenRefresher).toHaveBeenCalledWith(options.abortSignal);
});
it("throws if disposed", async function () {
@ -151,46 +141,48 @@ describe("CommunicationTokenCredential", function () {
});
withStatic.dispose();
withLambda.dispose();
await assert.isRejected(withStatic.getToken());
await assert.isRejected(withLambda.getToken());
await expect(() => withStatic.getToken()).rejects.toThrow();
await expect(() => withLambda.getToken()).rejects.toThrow();
});
it("doesn't swallow error from tokenRefresher", async function () {
const tokenRefresher = sinon.stub().throws(new Error("No token for you!"));
const tokenRefresher = vi.fn().mockImplementation(() => {
new Error("No token for you!");
});
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
});
await assert.isRejected(tokenCredential.getToken());
await expect(() => tokenCredential.getToken()).rejects.toThrow();
});
it("requests new token when token is about to expire", async function () {
const token = generateToken(20);
const newToken = generateToken(60);
const tokenRefresher = sinon.stub().resolves(token);
const tokenRefresher = vi.fn().mockResolvedValue(token);
const tokenCredential = new AzureCommunicationTokenCredential({ tokenRefresher });
const tokenResult = await tokenCredential.getToken();
assert.strictEqual(tokenResult.token, token);
tokenRefresher.resolves(newToken);
tokenRefresher.mockResolvedValue(newToken);
// go into the soon-to-expire window
clock.tick(19 * 60 * 1000);
vi.advanceTimersByTime(19 * 60 * 1000);
const secondTokenResult = await tokenCredential.getToken();
// returns old token because it's still valid
assert.strictEqual(secondTokenResult.token, token);
clock.tick(5 * 60 * 1000);
vi.advanceTimersByTime(5 * 60 * 1000);
// now it returns new token
const thirdTokenResult = await tokenCredential.getToken();
assert.strictEqual(thirdTokenResult.token, newToken);
sinon.assert.calledTwice(tokenRefresher);
expect(tokenRefresher).toHaveBeenCalledTimes(2);
});
it("proactively refreshes token when it is about to expire", async function () {
const token = (): string => generateToken(20);
const tokenRefresher = sinon.stub().resolves(token());
const tokenRefresher = vi.fn().mockResolvedValue(token());
new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
@ -198,13 +190,13 @@ describe("CommunicationTokenCredential", function () {
});
// go into the soon-to-expire window
clock.tick(19 * 60 * 1000);
sinon.assert.calledOnce(tokenRefresher);
vi.advanceTimersByTime(19 * 60 * 1000);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("repeats proactive refreshing", async function () {
const token = (): string => generateToken(20);
const tokenRefresher = sinon.stub().resolves(token());
const tokenRefresher = vi.fn().mockResolvedValue(token());
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
@ -213,7 +205,7 @@ describe("CommunicationTokenCredential", function () {
const internalTimeout = exposeInternalTimeout(tokenCredential);
// go into the soon-to-expire window
clock.tick(19 * 60 * 1000);
vi.advanceTimersByTime(19 * 60 * 1000);
await exposeInternalUpdatePromise(tokenCredential);
const newInternalTimeout = exposeInternalTimeout(tokenCredential);
@ -225,7 +217,7 @@ describe("CommunicationTokenCredential", function () {
it("dispose cancels timer", async function () {
const token = (): string => generateToken(20);
const tokenRefresher = sinon.stub().resolves(token());
const tokenRefresher = vi.fn().mockResolvedValue(token());
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
@ -233,33 +225,34 @@ describe("CommunicationTokenCredential", function () {
});
tokenCredential.dispose();
// go into the soon-to-expire window
clock.tick(19 * 60 * 1000);
sinon.assert.notCalled(tokenRefresher);
vi.advanceTimersByTimeAsync(19 * 60 * 1000);
expect(tokenRefresher).not.toHaveBeenCalled();
});
it("multiple calls to getToken call tokenRefresher only once", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(60));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(60));
const tokenCredential = new AzureCommunicationTokenCredential({ tokenRefresher });
for (let i = 0; i < 10; i++) {
await tokenCredential.getToken();
}
sinon.assert.calledOnce(tokenRefresher);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("calls tokenRefresher only once when proactive refreshing is in progress", async function () {
const tokenRefresher = sinon.stub().resolves(generateToken(20));
const tokenRefresher = vi.fn().mockResolvedValue(generateToken(20));
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
});
// go into the soon-to-expire window
clock.tick(19 * 60 * 1000);
vi.advanceTimersByTime(19 * 60 * 1000);
await tokenCredential.getToken();
sinon.assert.calledOnce(tokenRefresher);
expect(tokenRefresher).toHaveBeenCalledOnce();
});
it("applies fractional backoff when the token is about to expire", async function () {
@ -270,7 +263,7 @@ describe("CommunicationTokenCredential", function () {
Math.log(tokenExpirationMinutes * 60) / Math.log(1 / defaultRefreshAfterLifetimePercentage),
);
const staticToken = generateToken(tokenExpirationMinutes);
const tokenRefresher = sinon.stub().resolves(((): string => staticToken)()); // keep returning the same token for the duration of the test
const tokenRefresher = vi.fn().mockResolvedValue(((): string => staticToken)()); // keep returning the same token for the duration of the test
const tokenCredential = new AzureCommunicationTokenCredential({
tokenRefresher,
refreshProactively: true,
@ -283,21 +276,21 @@ describe("CommunicationTokenCredential", function () {
for (let i = 0; i < 10 * 60; i++) {
// perform token refreshing & scheduling
await exposeInternalUpdatePromise(tokenCredential);
clock.tick(1000);
vi.advanceTimersByTime(1000);
}
// expect the token to be refreshed only once within the first 10 minutes
sinon.assert.callCount(tokenRefresher, expectedPreBackOffCallCount);
expect(tokenRefresher).toHaveBeenCalledTimes(expectedPreBackOffCallCount);
// iterate until the penultimate second of the token expiration
// to prevent an exception being thrown due to the token being expired
while (Math.floor((newToken.expiresOnTimestamp - Date.now()) / 1000) > 1) {
// perform token refreshing & scheduling
await exposeInternalUpdatePromise(tokenCredential);
clock.tick(1000);
vi.advanceTimersByTime(1000);
}
// expect the token to be refreshed with an increasing frequency in the remaining 10 minutes
sinon.assert.callCount(tokenRefresher, expectedTotalCallCount);
expect(tokenRefresher).toHaveBeenCalledTimes(expectedTotalCallCount);
});
});

Просмотреть файл

@ -2,9 +2,9 @@
// Licensed under the MIT License.
import { AzureKeyCredential } from "@azure/core-auth";
import { assert } from "chai";
import { parseConnectionString } from "../../src";
import { assertPropertyNames } from "./utils/credentialUtils";
import { parseConnectionString } from "../../src/index.js";
import { assertPropertyNames } from "./utils/credentialUtils.js";
import { describe, it, assert } from "vitest";
const CONNECTION_STRING =
"endpoint=https://contoso.communicationservices.azure.com:443/;accesskey=secret";

Просмотреть файл

@ -7,8 +7,8 @@ import {
SerializedCommunicationIdentifier,
deserializeCommunicationIdentifier,
serializeCommunicationIdentifier,
} from "../../src";
import { assert } from "chai";
} from "../../src/index.js";
import { describe, it, assert } from "vitest";
const assertSerialize = (
identifier: CommunicationIdentifier,

Просмотреть файл

@ -12,8 +12,8 @@ import {
isMicrosoftTeamsUserIdentifier,
isPhoneNumberIdentifier,
isUnknownIdentifier,
} from "../../src";
import { assert } from "chai";
} from "../../src/index.js";
import { describe, it, assert } from "vitest";
describe("Identifier models", function () {
it("type guards", function () {
@ -35,7 +35,7 @@ describe("Identifier models", function () {
});
it("get raw id of identifier", function () {
const assertRawId = (identifier: CommunicationIdentifier, expectedRawId: string) =>
const assertRawId = (identifier: CommunicationIdentifier, expectedRawId: string): void =>
assert.strictEqual(getIdentifierRawId(identifier), expectedRawId);
assertRawId(
@ -159,8 +159,10 @@ describe("Identifier models", function () {
});
it("create identifier from raw id", function () {
const assertIdentifier = (rawId: string, expectedIdentifier: CommunicationIdentifierKind) =>
assert.deepStrictEqual(createIdentifierFromRawId(rawId), expectedIdentifier);
const assertIdentifier = (
rawId: string,
expectedIdentifier: CommunicationIdentifierKind,
): void => assert.deepStrictEqual(createIdentifierFromRawId(rawId), expectedIdentifier);
assertIdentifier(
"8:acs:bbbcbc1e-9f06-482a-b5d8-20e3f26ef0cd_45ab2481-1c1c-4005-be24-0ffb879b1130",
@ -302,7 +304,7 @@ describe("Identifier models", function () {
});
it("rawId stays the same after conversion to identifier and back", function () {
const assertRoundtrip = (rawId: string) =>
const assertRoundtrip = (rawId: string): void =>
assert.strictEqual(getIdentifierRawId(createIdentifierFromRawId(rawId)), rawId);
assertRoundtrip(

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { AzureKeyCredential } from "@azure/core-auth";
import { assert } from "chai";
import { assert } from "vitest";
export const assertPropertyNames = <T>(
expectedInstance: AzureKeyCredential,

Просмотреть файл

@ -0,0 +1,10 @@
{
"extends": "./.tshy/build.json",
"include": ["./src/**/*.ts", "./src/**/*.mts", "./test/**/*.spec.ts"],
"exclude": ["./test/**/node/**/*.ts"],
"compilerOptions": {
"outDir": "./dist-test/browser",
"rootDir": ".",
"skipLibCheck": true
}
}

Просмотреть файл

@ -1,8 +1,10 @@
{
"extends": "../../../tsconfig",
"compilerOptions": {
"outDir": "./dist-esm",
"declarationDir": "./types"
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "."
},
"exclude": ["node_modules", "types", "temp", "browser", "dist", "dist-esm", "./samples/**/*.ts"]
"exclude": ["node_modules", "types", "temp", "browser", "dist", "dist-esm", "./samples/**/*.ts"],
"include": ["src/**/*.ts", "src/**/*.mts", "src/**/*.cts", "samples-dev/**/*.ts", "test/**/*.ts"]
}

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { defineConfig, mergeConfig } from "vitest/config";
import viteConfig from "../../../vitest.browser.base.config.ts";
export default mergeConfig(
viteConfig,
defineConfig({
test: {
include: ["dist-test/browser/test/**/*.spec.js"],
},
}),
);

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { defineConfig, mergeConfig } from "vitest/config";
import viteConfig from "../../../vitest.shared.config.ts";
export default mergeConfig(
viteConfig,
defineConfig({
test: {
include: ["test/**/*.spec.ts"],
},
}),
);