Technical Documentation: Azure/azure-sdk-for-js
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
Azure SDK for JavaScript
This repository is for the Azure SDK for JavaScript (Node.js & Browser). It contains libraries for the breadth of Azure services. Management libraries are packages that you would use to provision and manage Azure resources. Client libraries are packages that you would use to consume these resources and interact with them.
Getting started
A few helpful resources to get started are:
- The readme for each package contains code samples and package information. This readme can be found in the corresponding package folder under the folder of the service of your choice in the
/sdkfolder of this repository. The same readme file can be found on the landing page for the package in npm. - The API reference documentation of the latest versions of these packages, can be found at our public developer docs.
- The API reference documentation of older versions, can be found in our versioned developer docs.
Each service might have a number of libraries available from each of the following categories:
NOTE: Some of these packages have beta versions. If you need to ensure your code is ready for production, use one of the stable, non-beta packages.
Client
Given an Azure resource already exists, you would use the client libraries to consume it and interact with it.
Most of these libraries follow the Azure SDK Design Guidelines for JavaScript & TypeScript and share a number of core functionalities such as retries, logging, transport protocols, authentication protocols, etc. Others will be updated in the near future to follow the guidelines as well.
To get a list of all client libraries that follow the new guidelines, please visit our Azure SDK releases page.
Management
Management libraries enable you to provision and manage Azure resources via the Azure Resource Manager i.e. ARM. You can recognize these libraries by @azure/arm- in their package names. These are purely auto-generated based on the swagger files that represent the APIs for resource management.
Newer versions of these libraries follow the Azure SDK Design Guidelines for TypeScript. These new versions provide a number of core capabilities that are shared amongst all Azure SDKs, including the intuitive Azure Identity library, an HTTP Pipeline with custom policies, error-handling, distributed tracing, and much more. A few helpful resources to get started with these are:
- List of management libraries that follow the new guidelines
- Documentation and code samples.
- Migration guide that shows how to transition from older versions of libraries.
NOTE: If you are experiencing authentication issues with the management libraries after upgrading certain packages, it's possible that you upgraded to the new versions of SDK without changing the authentication code, please refer to the migration guide mentioned above for proper instructions.
Need help?
- For detailed documentation, visit our Azure SDK for JavaScript documentation
- File an issue via GitHub Issues
- Check previous questions or ask new ones on StackOverflow using
azure-sdk-jstag. - Read our Support documentation.
Data Collection
The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described below. You can learn more about data collection and use in the help documentation and Microsoft’s privacy statement. For more information on the data collected by the Azure SDK, please visit the Telemetry Guidelines page.
Telemetry Configuration
Telemetry collection is on by default.
To opt out, you can disable telemetry at client construction. Create a custom HTTP pipeline policy that removes the user agent string, and then pass that policy into the additionalPolicies option during client creation. This will disable telemetry for all methods in the client. Do this for every new client.
The example below uses the @azure/keyvault-secrets package. In your code, you can replace @azure/keyvault-secrets with the package you are using.
import { SecretClient } from "@azure/keyvault-secrets";
import { ManagedIdentityCredential } from "@azure/identity";
function removeUserAgentPolicy() {
return {
name: "removeUserAgentPolicy",
sendRequest(request, next) {
request.headers.delete("User-Agent");
return next(request);
},
};
}
/**
* Creates a SecretClient with managed identity authentication and empty user agent
* @param keyvaultUri - The URI of the Azure Key Vault
* @returns configured SecretClient instance
*/
function createSecretClientWithManagedIdentity(
keyvaultUri: string
): SecretClient {
// Create ManagedIdentityCredential for managed identity authentication
const credential = new ManagedIdentityCredential();
// Create secret client with managed identity and empty user agent
const secretClient = new SecretClient(keyvaultUri, credential, {
additionalPolicies: [
{
position: "perCall",
policy: removeUserAgentPolicy(),
},
],
});
return secretClient;
}
// Usage example
const keyvaultUri = "https://your-keyvault-name.vault.azure.net";
const secretClient = createSecretClientWithManagedIdentity(keyvaultUri);
// Now you can use the secret client to perform operations
// For example:
// const secretValue = await secretClient.getSecret("your-secret-name");Community
Try our community resources.
Reporting security issues and security bugs
Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) [email protected]. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the Security TechCenter.
Contributing
For details on contributing to this repository, see the contributing guide.
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://opensource.microsoft.com/cla.
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 repositories using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information, see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
Documentation DPG Quickstart (`documentation/DPG-quickstart.md`)
Getting Started: Generate JavaScript/TypeScript DPG Libraries with TypeSpec
Before you start
:warning: Ensure that your TypeSpec definition has been merged into the main branch of the Azure REST API specs repository before you begin.
For an overview of the review and release process for new libraries, visit: https://aka.ms/azsdk/dpcodegen.
If you have code generation queries, post them in the TypeSpec Discussion channel. Tag @DPG TypeScript for JavaScript/TypeScript-specific inquiries.
Join the JavaScript - Reviews channel for assistance with the API review process.
Prerequisites
- LTS versions of Node.js
- Install pnpm with the pnpm installation instructions.
- Install tsp-client dependencies with
npm --prefix eng/common/tsp-client ci
Set up your development environment
Follow the setup guide for environment prerequisites in the azure-sdk-for-js repository.
Identify your project's service and package name
The service name is a concise identifier for the Azure service and should be consistent across all SDK languages. It's typically the name of the directory in the azure-rest-api-specs repository containing your service's REST API definition.
The package name is used when publishing to npmjs. It usually follows the format @azure/{service-name} or @azure/{service-name}-{module} for services with multiple modules.
Structure your project
SDK Repo Root: the generated libraries should be in the azure-sdk-for-js repo, so fork and clone it in your local then the absolute path is called ${SDK_REPO_ROOT} folder.
Project Folder Structure: the typical structure is
sdk/{servicename}/{servicename}-{modulename}, e.g.,sdk/storage/storage-blob. That folder is under {SDK_REPO_ROOT} and will be your ${PROJECT_ROOT} folder.Package Name Convention: follow the format
@azure/{service-name}-{module}, like@azure/storage-blob.
Steps to generate DPG
Configure tspconfig.yaml in spec repository
In your specs repository, update or create
tspconfig.yamlto configure the TypeScript emitter. ReplaceYOUR_SERVICE_DIRECTORY,YOUR_SERVICE_FOLDERandYOUR_PACKAGE_NAMEwith your specific details.yamlparameters: "service-dir": default: "YOUR_SERVICE_DIRECTORY" emit: ["@azure-tools/typespec-ts"] options: "@azure-tools/typespec-ts": emitter-output-dir: "{output-dir}/{service-dir}/YOUR_SERVICE_FOLDER" package-details: name: YOUR_PACKAGE_NAME description: "SHORT_DESCRIPTION" version: "1.0.0-beta.1"Generate code
Initialize a new TypeScript DPG library
For initial set up, use the
tsp-clientCLI tool to initialize the generation process. From the root of your localazure-sdk-for-jsrepository clone, run the following command, replacingYOUR_REMOTE_TSPCONFIG_URLwith the URL to your TypeSpec configuration file:shnpm --prefix eng/common/tsp-client exec --no -- tsp-client init -c YOUR_REMOTE_TSPCONFIG_URLIf you are generating the DPG library for Azure Cognitive Services Content Safety, and your TypeSpec configuration file is located at
https://github.com/Azure/azure-rest-api-specs/blob/46ca83821edd120552403d4d11cf1dd22360c0b5/specification/cognitiveservices/ContentSafety/tspconfig.yaml, you would initialize the library like this:shnpm --prefix eng/common/tsp-client exec --no -- tsp-client init -c https://github.com/Azure/azure-rest-api-specs/blob/46ca83821edd120552403d4d11cf1dd22360c0b5/specification/cognitiveservices/ContentSafety/tspconfig.yamlThis command sets up your local SDK repository with the necessary structure and files based on your
tspconfig.yamlfile and then generate SDKs with given url typespec.Re-generate an existing TypeScript DPG library
If you'd like to update/regenerate an existing SDK, go to your SDK folder and then update
tsp-location.yaml. You can refer to the tsp-location.yaml which describes the supported properties in the file.yamldirectory: specification/agrifood/DataPlane commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 # the commit id you'd like to refer for generation repo: Azure/azure-rest-api-specsRun the
updatecommand from SDK directory (i.e sdk/agrifood/agrifood-farming) to re-generate the code:shnpm --prefix ../../../eng/common/tsp-client exec --no -- tsp-client update
NOTE
The version of typespec-ts is configured in emitter-package.json and relevant lock file emitter-package-lock.json. Change them in local, if you would like to use a different version of typespec-ts.
After SDK generation
The generated code is not enough to release at once and you need to update it for better usage experience. Please follow steps after generation guide to check the code.
Documentation RLC Swagger Quickstart (`documentation/RLC-Swagger-quickstart.md`)
Getting Started - Generate the RLC REST-level client libraries with Swagger
Before you start
Please refer to this link for the environment set up prerequisites in azure-sdk-for-js repository. We highly recommend to read this blog to get familiar with REST libraries for JavaScript.
:warning: Note: if you’re generating from TypeSpec with RLC, please read this doc for TypeSpec specific details.
Project folder and name convention
If you are the first time to prepare the SDK, please follow the Azure SDK guidance and discuss with architects to decide the project folder and name convention for RLC libraries.
- Project Folder Structure.
normally, the folder structure would be something likesdk/{servicename}/{servicename}-{modulename}-rest. For example, we havesdk/agrifood/agrifood-farming-restfolder for Farmbeats account modules. That folder will be your ${PROJECT_ROOT} folder. - Package Name Convention.
The package name for RLC is something like@azure-rest/{servicename}-{modulename}. For example, the package name for Farmbeats module is@azure-rest/agrifood-farming.
How to generate RLC
We are working on to automatically generate everything right now, but currently, we still need some manual work to get a releasable package. Here're the steps of how to get the package.
Create a swagger/README.md file under ${PROJECT_ROOT} folder
We are using autorest to generate the code, but there's a lot of command options and in order to make the regenerate process easier in the cases of refresh the rest api input or change the code generator version, you need to document the generate command parameters.
Here's an example of theswagger/README.md:text# Azure Farmbeats TypeScript Protocol Layer > see https://aka.ms/autorest ## Configuration ```yaml package-name: "@azure-rest/agrifood-farming" title: Farmbeats description: Farmbeats Client generate-metadata: true generate-test: true generate-sample: true license-header: MICROSOFT_MIT_NO_VERSION output-folder: ../ source-code-folder-path: ./src input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/683e3f4849ee1d84629d0d0fa17789e80a9cee08/specification/agfood/data-plane/Microsoft.AgFoodPlatform/preview/2021-03-31-preview/agfood.json package-version: 1.0.0-beta.2 rest-level-client: true security: AADToken security-scopes: https://farmbeats.azure.net/.default use-extension: "@autorest/typescript": "6.0.0-rc.10" ```Here, we need to replace the value in
package-name,title,description,input-file,package-version,security,security-scopesinto your own service'spackage-name,title,descriptionetc.How to configure authentication
Autorest only support two types of authentication: Azure Key Credential(AzureKey) and Token credential(AADToken), any other will need to be handled manually.
This could be either configured in OpenAPI spec or configuration file e.g
README.md. You could learn more in Authentication in AutoRest.Here are the details if we configure in README.md file.
- Support AAD token authentication
yamlsecurity: AADToken security-scopes: https://yourendpoint.azure.com/.default- Support key authentication
yamlsecurity: AzureKey security-header-name: Your-Subscription-Key- Support both credentials
yamlsecurity: - AADToken - AzureKey security-header-name: Your-Subscription-Key security-scopes: https://yourendpoint.azure.com/.default- Disable neither authentications
yamladd-credentials: false
NOTE
It's always recommended to replace the version of code generator @autorest/typescript with the latest version you can find in npmjs.com in latest tag.
If the
input-fileis followed by an.mdfile, you need to replace theinput-filewithrequire. If it is aJSONfile, do not change it.We enable the samples generation by default, this may fail the generation due to the example quality or codegen issue. You could turn this option off by
generate-sample: falseto non-block your process.After the first generation, you need to switch
generate-metadata: falseas we have some manual changes in this file and don't want them get overwritten by generated ones.
Run autorest to generate the SDK
Now, you can run this command in swagger folder you just created.
shellautorest --typescript ./README.mdAfter this finishes, you will see the generated code in
${PROJECT_ROOT}/srcfolder.
After that, you can get a workable package, and run the following commands to get a artifact if you like.shellpnpm install pnpm turbo build --filter=<your-package-name>... --token 1 cd <your-sdk-folder> pnpm packBut, we still need to add some tests for it.
Improve README.md document
A minimal README.md is generated by our codegen and you could improve README.md file per your service. To learn more about README, see an example README.md here.
How to write test for RLC
In order to release your RLC library, we need to add some tests for it to make sure we are delivering high quality packages. But before adding the test, we need to enable the option generate-test: true to generate the necessary change in package.json and tsconfig.json so that test framework can work. Once the generation finished, you will see a sampleTest.spec.ts file in your {PROJECT_ROOT}/test/public folder, which has an empty test and you could add/update test cases against your own services.
See the JavaScript Codegen Quick Start for Test for information on how to write and run tests for the JavaScript SDK.
Prerequisites
To be able to leverage the asset-sync workflow
- Install Powershell
- Make sure
pwshcommand works at this step (If you follow the above link,pwshis typically added to the system environment variables by default)
- Make sure
- Add
dev-toolto thedevDependenciesin thepackage.json.
The package needs to use the current version of the recorder that uses the test proxy (
@azure-tools/test-recorder@^4.0.0).Then, we need to generate a
assets.jsonfile. If your package is new or has never been pushed before, you could use below commands:shellnpx dev-tool test-proxy init # this will generate assets.json file, you will get some info in this file.- Install Powershell
Write the test
You could follow the basic RLC test interaction and recording example to write your test step by step. Also you could refer the test of MapsRouteClient for more cases.
Run the test
Now, you can run the test like this. If you are the first time to run test, you need to set the environment variable
TEST_MODEtorecord. This will generate recordings for your test they could be used inplaybackmode.On Linux, you could use
exportto set env variable:shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 export TEST_MODE=record && pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.On Windows, you could use
SET:shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 SET TEST_MODE=record&& pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.You can also run the
playbackmode test if your apis don't have breaking changes and you've already done the recording before.On Linux, you could use below commands:
shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 export TEST_MODE=playback && pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.On Windows, you can use:
shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 SET TEST_MODE=playback&& pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.Push recording to assets repo
Notice:the tests have to be recorded using the
TEST_MODE=record, then the recording files will be generate.Before push your recording file, you must confirm that you are able to push recordings to the
azure-sdk-assetsrepo, you need write-access to the assets repo.
Permissions toAzure/azure-sdk-assets
Here is the command to push:
shellnpx dev-tool test-proxy pushAfter above command finished, you can find your local recording files in
./azure-sdk-for-js/.assets.And if you want to find your recording on
assets repo, you can get the tag inassets.jsonin your package root, which is a tag pointing to your recordings in the Azure/azure-sdk-assets repo.Example
assets.jsonfromkeyvault-certificatesSDK.json{ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "js", "TagPrefix": "js/keyvault/keyvault-certificates", "Tag": "js/keyvault/keyvault-certificates_43821e21b3" }And the recordings are located at here.
How to write samples
If you enable generate-sample: true option, the codegen would do two things for you:
- Add samples metadata in
tsconfig.jsonandpackage.json. - Generate a collection of TypeScript sample files (based on
x-ms-examplesin OpenAPI specs) undersamples-devfolder.
Please notice that the generated samples might not be directly usable as runnable codes, however, we could get the basic idea on how code works, and update them to be more valuable samples.
And the errors may come from two kinds, the codegen issue or swagger example issue. For the former one, we need to report them with codegen owner while as for the latter one we need to fix our swagger examples.
Now, you can generate both JavaScript and TypeScript workable samples with the following commands.
cd ${PROJECT_ROOT}
npx dev-tool samples publish -fYou will see the workable samples in the ${PROJECT_ROOT}/samples folder.
Besides the generated samples, we also recommend you to add your HERO sample scenarios per your services to guide customers on how to use your library. You could refer to the samples of MapsRouteClient here as an example.
Format both the generated code and manual code
After you have finished the generation and added your own tests or samples, You can use the following command to format the code.
cd ${PROJECT_ROOT} && pnpm formatAlso we'll recommend you to run lint command to analyze your code and quickly find any problems.
cd ${PROJECT_ROOT} && pnpm lintAnd we could use lint:fix if there are any errors.
cd ${PROJECT_ROOT} && pnpm lint:fixHow to do customizations
You may want to do your customizations based on generated code. We collect some common customization cases and you can read Customization on the RLC rest-level client libraries for more details.
How to create package
Now, we can use the exact same steps to build an releasable artifact.
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1
cd <your-sdk-folder>
export TEST_MODE=record && pnpm test
pnpm packYou may send this artifact to your customer if your services are still in private preview and some customers want to try it out.
Create/Update the ci.yaml
Now, if everything looks good to you, you can submit a PR in azure-sdk-for-js repo with all the changes you made above. Before you do that, you need to add/update the ci.yml file. Depends on whether there's already one in your package folder.
If there's no such file, then you can add the following template.
# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file.
trigger:
branches:
include:
- main
- release/*
- hotfix/*
paths:
include:
- sdk/purview/
pr:
branches:
include:
- main
- feature/*
- release/*
- hotfix/*
exclude:
- feature/v4
paths:
include:
- sdk/purview/
extends:
template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml
parameters:
ServiceDirectory: purview
Artifacts:
- name: azure-rest-agrifood-farming
safeName: azurerestagrifoodfarmingPlease change the paths.include value as your own project path, and change the Artifacts name and safeName into yours.
If there's already a ci.yml file in your project path, then the only thing you need to do is to add the Artifacts name and safeName of yours into that ci.yml.
Please notice the Artifacts name should align with your package name. Here, the package name is @azure-rest/agrifood-farming, so the relevant Artifacts name is azure-rest-agrifood-farming.
Prepare PR
The codegen can only help you generate SDK code, there is something you need to update manually:
CHANGELOG.md
CHANGELOG can help customers know the change of new version quickly, so you need to update it according to the change of this new version. It is also necessary to update release date like 1.0.0-beta.1 (2022-11-11) (rough time is fine and no need to be very accurate).
Version Number
You shall update the version number according to semantic versioning rule.
Test recordings
Please ensure that your test recordings are committed together with your code.
Fix CI for PR
You may meet the CI failures after submitting the PR, so please refer to Troubleshoot CI Failure to fix it.
CC dpg-devs for review
Please add below comment in your PR to include dpg-devs to review your PR timely.
cc @Azure/dpg-devs for awarenessCreate API View
When submitting a PR, our pipeline would automatically prepare the API view in API View Website. You could see an example link here. You could click the API view link in that comment to know more details.
Release
After the PR is merged, it is time to release package. Here is the Release Checklist you should know before release.
Documentation RLC Quickstart (`documentation/RLC-quickstart.md`)
Getting Started: Generate JavaScript/TypeScript RLC (Rest Level Client) Libraries with TypeSpec
Before you start
:warning: Important: Only proceed with RLC code generation for JavaScript/TypeScript if you have been explicitly instructed to do so by the AZSDK Architecture Board. Otherwise, please use the DPG Quickstart Guide.
:warning: Ensure that your TypeSpec definition has been merged into the main branch of the Azure REST API specs repository before you begin.
For an overview of the review and release process for new libraries, visit: https://aka.ms/azsdk/dpcodegen.
If you have code generation queries, post them in the TypeSpec Discussion channel. Tag @DPG TypeScript for JavaScript/TypeScript-specific inquiries.
Join the JavaScript - Reviews channel for assistance with the API review process.
Prerequisites
- LTS versions of Node.js
- Install pnpm via the pnpm installation instructions.
- Install tsp-client dependencies with
npm --prefix eng/common/tsp-client ci
Set up your development environment
Follow the setup guide for environment prerequisites in the Azure SDK for JS repository.
Identify your project's service and package name
The service name is a concise identifier for the Azure service and should be consistent across all SDK languages. It's typically the name of the directory in the azure-rest-api-specs repository containing your service's REST API definition.
The package name is used when publishing to npmjs. It usually follows the format @azure/{service-name}-rest or @azure/{service-name}-{module}-rest for services with multiple modules.
Structure your project
SDK Repo Root: the generated libraries should be in the azure-sdk-for-js repo, so fork and clone it in your local. Then, the absolute path is called ${SDK_REPO_ROOT} folder.
Project Folder Structure: the typical structure is
sdk/{servicename}/{servicename}-{modulename}-rest, e.g.,sdk/agrifood/agrifood-farming-rest. That folder is under {SDK_REPO_ROOT} and will be your ${PROJECT_ROOT} folder.Package Name Convention: follow the format
@azure-rest/{service-name}-{module}, like@azure-rest/agrifood-farming.
Steps to generate RLC
Configure tspconfig.yaml in spec repository
In your specs repository, update or create
tspconfig.yamlto configure the TypeScript emitter. ReplaceYOUR_SERVICE_DIRECTORY,YOUR_SERVICE_FOLDERandYOUR_PACKAGE_NAMEwith your specific details.yamlparameters: "service-dir": default: "YOUR_SERVICE_DIRECTORY" emit: ["@azure-tools/typespec-ts"] options: "@azure-tools/typespec-ts": emitter-output-dir: "{output-dir}/{service-dir}/YOUR_SERVICE_FOLDER-rest" is-modular-library: false package-details: name: YOUR_PACKAGE_NAME description: "SHORT_DESCRIPTION" version: "1.0.0-beta.1"Generate code
Initialize a new TypeScript RLC library
For initial set up, use the
tsp-clientCLI tool to initialize the generation process. From the root of your localazure-sdk-for-jsrepository clone, run the following command, replacingYOUR_REMOTE_TSPCONFIG_URLwith the URL to your TypeSpec configuration file:shnpm --prefix eng/common/tsp-client exec --no -- tsp-client init -c YOUR_REMOTE_TSPCONFIG_URLIf you are generating the RLC library for Azure Cognitive Services Content Safety, and your TypeSpec configuration file is located at
https://github.com/Azure/azure-rest-api-specs/blob/46ca83821edd120552403d4d11cf1dd22360c0b5/specification/cognitiveservices/ContentSafety/tspconfig.yaml, you would initialize the library like this:shellnpm --prefix eng/common/tsp-client exec --no -- tsp-client init -c https://github.com/Azure/azure-rest-api-specs/blob/46ca83821edd120552403d4d11cf1dd22360c0b5/specification/cognitiveservices/ContentSafety/tspconfig.yamlThis command sets up your local SDK repository with the necessary structure and files based on your
tspconfig.yamlfile and then generate SDKs with given url typespec.Re-generate an existing TypeScript RLC library
If you'd like to update/regenerate an existing SDK, go to your SDK folder and then update
tsp-location.yaml. You can refer to the tsp-location.yaml which describes the supported properties in the file.yamldirectory: specification/agrifood/DataPlane commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 # the commit id you'd like to refer for generation repo: Azure/azure-rest-api-specsRun the
updatecommand from SDK directory (i.e sdk/agrifood/agrifood-farming) to re-generate the code:shellnpm --prefix ../../../eng/common/tsp-client exec --no -- tsp-client update
NOTE
The version of typespec-ts is configured in emitter-package.json and relevant lock file emitter-package-lock.json. Change them in local, if you would like to use a different version of typespec-ts.
After SDK generation
The generated code is not enough to release at once and you need to update it for better usage experience. Please follow steps after generation guide to check the code.
Documentation Next Generation Quickstart (`documentation/next-generation-quickstart.md`)
Getting Started - Using the next-generation management libraries of Azure SDK for JavaScript/TypeScript
We are excited to announce the GA of a new set of management plane libraries for JavaScript/TypeScript. Those libraries contain a number of new features including Azure Identity support, HTTP pipeline, error-handling, etc, and follow the new Azure SDK guidelines which create easy-to-use
APIs that are idiomatic, compatible, and dependable. See TypeScript Design Guidelines for more information.
Currently, we have released GA version of several packages such as @azure/arm-resources, @azure/arm-storage,@azure/arm-compute, @azure/arm-network for next-generation. Please find the latest version of those libraries in npm and have a try.
In this basic quickstart guide, we will walk you through how to
authenticate to Azure and start interacting with Azure resources. There are several possible approaches to
authentication. This document illustrates the most common scenario.
Migrating from an older generation of Azure management libraries for JavaScript/TypeScript
If you are current user of an older generation of the JavaScript SDK, and are interested in upgrading to the latest version, please refer to this migration guide for more information.
Prerequisites
You will need the following values to authenticate to Azure
- Subscription ID
- Client ID
- Client Secret
- Tenant ID
These values can be obtained from the portal, here's the instructions:
Get Subscription ID
- Login into your Azure account
- Select Subscriptions in the left sidebar
- Select whichever subscription is needed
- Click on Overview
- Copy the Subscription ID
Get Client ID / Client Secret / Tenant ID
For information on how to get Client ID, Client Secret, and Tenant ID,
please refer to this document
Setting Environment Variables
After you obtained the values, you need to set the following values as
your environment variables
AZURE_CLIENT_IDAZURE_CLIENT_SECRETAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID
To set the following environment variables on your development system:
Windows (Note: Administrator access is required)
- Open the Control Panel
- Click System Security, then System
- Click Advanced system settings on the left
- Inside the System Properties window, click the
Environment Variables…button. - Click on the property you would like to change, then click the
Edit…button. If the property name is not listed, then click theNew…button.
Linux-based OS :
export AZURE_CLIENT_ID="azure_client_id"
export AZURE_CLIENT_SECRET="azure_client_secret"
export AZURE_TENANT_ID="azure_tenant_id"
export AZURE_SUBSCRIPTION_ID="azure_subscription_id"Install the package
As an example, to install the Azure Compute module, you would run :
npm i @azure/arm-compute@latestYou can always find the latest preview version of our next-generation management libraries via npm under the next tag of each package.
We also recommend installing other packages for authentication and core functionalities :
npm i @azure/identityAuthentication
Once the environment is setup, all you need to do is to create an authenticated client. Before creating a client, you will first need to authenticate to Azure. In specific, you will need to provide a credential for authenticating with the Azure service. The @azure/identity module provides facilities for various ways of authenticating with Azure including client/secret, certificate, managed identity, and more.
Our default option is to use DefaultAzureCredential which will make use of the environment variables we have set and take care of the authentication flow for us.
const credential = new DefaultAzureCredential();For more details on how authentication works in @azure/identity, please see the documentation for @azure/identity.
Creating a Resource Management Client
Now, you will need to decide what service to use and create a client to connect to that service. In this section, we will use Compute as our target service.
To show an example, we will create a client to manage Virtual Machines. The code to achieve this task would be:
const client = new ComputeManagementClient(credential, subscriptionId);Interacting with Azure Resources
Now that we are authenticated and have created our clients, we can use our client to make API calls. For resource management scenarios, most of our cases are centered around creating / updating / reading / deleting Azure resources. Those scenarios correspond to what we call "operations" in Azure. Once you are sure of which operations you want to call, you can then implement the operation call using the management client we just created in previous section.
In the following samples, we are going to show
- Step 1 : How to Create a simple resource Resource Group.
- Step 2 : How to Manage Resource Group with Azure SDK for JavaScript/TypeScript
- Step 3 : How to Create a complex resource Virtual Machine.
Let's show what our final code looks like
Example: Creating a Resource Group
Import the packages
TypeScript
import { ResourceManagementClient, ResourceGroup } from "@azure/arm-resources";
import { DefaultAzureCredential } from "@azure/identity";JavaScript
const resources = require("@azure/arm-resources");
const identity = require("@azure/identity");Define some global variables
TypeScript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);JavaScript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new identity.DefaultAzureCredential();
const resourcesClient = new resources.ResourceManagementClient(credential, subscriptionId);Create a resource group
TypeScript
async function updateResourceGroup(resourceGroupName: string) {
const parameter: ResourceGroup = {
location: "eastus",
tags: {
tag1: "value1",
},
};
await resourcesClient.resourceGroups
.createOrUpdate(resourceGroupName, parameter)
.then((result) => {
console.log(result);
});
}JavaScript
async function createResourceGroup(resourceGroupName) {
const parameter = {
location: "eastus",
tags: {
tag1: "value1",
},
};
const resourcesClient = new resources.ResourceManagementClient(credential, subscriptionId);
await resourcesClient.resourceGroups
.createOrUpdate(resourceGroupName, parameter)
.then((result) => {
console.log(result);
});
}Example: Managing Resource Groups with JS/TS SDK
Import the packages
TypeScript
import {
ResourceManagementClient,
ResourceGroup,
ResourceGroupPatchable,
} from "@azure/arm-resources";
import { DefaultAzureCredential } from "@azure/identity";JavaScript
const resources = require("@azure/arm-resources");
const identity = require("@azure/identity");Authentication and Setup
TypeScript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);JavaScript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new identity.DefaultAzureCredential();
const resourcesClient = new resources.ResourceManagementClient(credential, subscriptionId);Update a resource group
TypeScript
async function updateResourceGroup(resourceGroupName: string) {
const parameter: ResourceGroupPatchable = {
tags: {
tag1: "value1",
tag2: "value2",
},
};
await resourcesClient.resourceGroups.update(resourceGroupName, parameter).then((result) => {
console.log(result);
});
}JavaScript
async function updateResourceGroup(resourceGroupName) {
const parameter = {
tags: {
tag1: "value1",
tag2: "value2",
},
};
await resourcesClient.resourceGroups.update(resourceGroupName, parameter).then((result) => {
console.log(result);
});
}List all resource groups
TypeScript or JavaScript
async function listResourceGroup() {
const result_list = new Array();
for await (let item of resourcesClient.resourceGroups.list()) {
result_list.push(item);
}
console.log(result_list);
}Get a Resource Group
TypeScript
async function getResourceGroup(resourceGroupName: string) {
const get_result = await resourcesClient.resourceGroups.get(resourceGroupName);
console.log(get_result);
}JavaScript
async function getResourceGroup(resourceGroupName) {
const get_result = await resourcesClient.resourceGroups.get(resourceGroupName);
console.log(get_result);
}Delete a resource group
TypeScript
async function deleteResourceGroup(resourceGroupName: string) {
await resourcesClient.resourceGroups.delete(resourceGroupName).then((result) => {
console.log(result);
});
}JavaScript
async function deleteResourceGroup(resourceGroupName) {
await resourcesClient.resourceGroups.delete(resourceGroupName).then((result) => {
console.log(result);
});
}Manage Resource Group
TypeScript or JavaScript
async function main() {
const resourceGroupName = "jstest";
await createResourceGroup(resourceGroupName);
await listResourceGroup();
await getResourceGroup(resourceGroupName);
await updateResourceGroup(resourceGroupName);
await getResourceGroup(resourceGroupName);
await deleteResourceGroup(resourceGroupName);
await listResourceGroup();
}Example: Managing Virtual Machines
In addition to resource groups, we will also use Virtual Machine as an example and show how to manage how to create a Virtual Machine which involves three Azure services (Resource Group, Network and Compute)
Import the packages
TypeScript
import { ComputeManagementClient, VirtualMachine } from "@azure/arm-compute";
import {
NetworkManagementClient,
VirtualNetwork,
Subnet,
NetworkInterface,
} from "@azure/arm-network";
import { ResourceManagementClient, ResourceGroup } from "@azure/arm-resources";
import { DefaultAzureCredential } from "@azure/identity";JavaScript
const identity = require("@azure/identity");
const resources = require("@azure/arm-resources");
const compute = require("@azure/arm-compute");
const network = require("@azure/arm-network");Define the global variables
TypeScript or JavaScript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const resourceGroupName = "testRG";
const virtualMachineName = "virtualmachinex";
const subnetName = "subnetnamex";
const interfaceName = "interfacex";
const networkName = "networknamex";
const location = "eastus";Authentication and Setup
TypeScript
const credential = new DefaultAzureCredential();
const computeClient = new ComputeManagementClient(credential, subscriptionId);
const networkClient = new NetworkManagementClient(credential, subscriptionId);
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);JavaScript
const credential = new identity.DefaultAzureCredential();
const computeClient = new compute.ComputeManagementClient(credential, subscriptionId);
const networkClient = new network.NetworkManagementClient(credential, subscriptionId);
const resourcesClient = new resources.ResourceManagementClient(credential, subscriptionId);Creating a Resource Group
TypeScript
async function createResourceGroup() {
const parameter: ResourceGroup = {
location: "eastus",
tags: {
tag1: "value1",
},
};
await resourcesClient.resourceGroups
.createOrUpdate(resourceGroupName, parameter)
.then((result) => {
console.log(result);
});
}JavaScript
async function createResourceGroup() {
const parameter = {
location: "eastus",
tags: {
tag1: "value1",
},
};
await resourcesClient.resourceGroups
.createOrUpdate(resourceGroupName, parameter)
.then((result) => {
console.log(result);
});
}Creating a Virtual Network
TypeScript
async function createVirtualNetwork() {
const parameter: VirtualNetwork = {
location: location,
addressSpace: {
addressPrefixes: ["10.0.0.0/16"],
},
};
const poller_result = await networkClient.virtualNetworks.beginCreateOrUpdateAndWait(
resourceGroupName,
networkName,
parameter,
);
console.log(poller_result);
const virtualNetworks_create_info = await networkClient.virtualNetworks.get(
resourceGroupName,
networkName,
);
console.log(virtualNetworks_create_info);
}JavaScript
async function createVirtualNetwork() {
const parameter = {
location: location,
addressSpace: {
addressPrefixes: ["10.0.0.0/16"],
},
};
const poller_result = await networkClient.virtualNetworks.beginCreateOrUpdateAndWait(
resourceGroupName,
networkName,
parameter,
);
console.log(poller_result);
const virtualNetworks_create_info = await networkClient.virtualNetworks.get(
resourceGroupName,
networkName,
);
console.log(virtualNetworks_create_info);
}Creating a Subnet
TypeScript
async function createSubnet() {
const subnet_parameter: Subnet = {
addressPrefix: "10.0.0.0/24",
};
const poller_result = await networkClient.subnets.beginCreateOrUpdateAndWait(
resourceGroupName,
networkName,
subnetName,
subnet_parameter,
);
console.log(poller_result);
const subnet_create_info = await networkClient.subnets.get(
resourceGroupName,
networkName,
subnetName,
);
console.log(subnet_create_info);
}JavaScript
async function createSubnet() {
const subnet_parameter = {
addressPrefix: "10.0.0.0/24",
};
const poller_result = await networkClient.subnets.beginCreateOrUpdateAndWait(
resourceGroupName,
networkName,
subnetName,
subnet_parameter,
);
console.log(poller_result);
const subnet_create_info = await networkClient.subnets.get(
resourceGroupName,
networkName,
subnetName,
);
console.log(subnet_create_info);
}Creating a Network Interface
TypeScript
async function createNetworkInterface(group_name: any, location: any, nic_name: any) {
const parameter: NetworkInterface = {
location: location,
ipConfigurations: [
{
name: "MyIpConfig",
subnet: {
id:
"/subscriptions/" +
subscriptionId +
"/resourceGroups/" +
resourceGroupName +
"/providers/Microsoft.Network/virtualNetworks/" +
networkName +
"/subnets/" +
subnetName,
},
},
],
};
const poller_result = await networkClient.networkInterfaces.beginCreateOrUpdateAndWait(
group_name,
nic_name,
parameter,
);
console.log(poller_result);
const nic_info = await networkClient.networkInterfaces.get(group_name, nic_name);
console.log(nic_info);
}JavaScript
async function createNetworkInterface(group_name, location, nic_name) {
const parameter = {
location: location,
ipConfigurations: [
{
name: "MyIpConfig",
subnet: {
id:
"/subscriptions/" +
subscriptionId +
"/resourceGroups/" +
resourceGroupName +
"/providers/Microsoft.Network/virtualNetworks/" +
networkName +
"/subnets/" +
subnetName,
},
},
],
};
const poller_result = await networkClient.networkInterfaces.beginCreateOrUpdateAndWait(
group_name,
nic_name,
parameter,
);
console.log(poller_result);
const nic_info = await networkClient.networkInterfaces.get(group_name, nic_name);
console.log(nic_info);
}Creating a Virtual Machine
TypeScript
/* Detailed source-code truncated for AI context efficiency. */JavaScript
/* Detailed source-code truncated for AI context efficiency. */The following example shows how to delete a Virtual Machine
Deleting a Virtual Machine
TypeScript or JavaScript
async function deleteVirtualMachine() {
const res = await computeClient.virtualMachines.beginDeleteAndWait(
resourceGroupName,
virtualMachineName,
);
console.log(res);
}Code Samples
More code samples for using the management library for JS/TS SDK can be found in JS/TS SDK Code Samples
Please Note that these samples provided in JS/TS SDK Code Samples are written in TypeScript
Need help?
- File an issue via Github
Issues
Contributing
For details on contributing to this repository, see the contributing
guide.
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 provided by the
bot. You will only need to do this once across all repositories using
our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For
more information see the Code of Conduct FAQ or contact
[email protected] with any additional questions or comments.
Documentation Changelog For Next Generation (`documentation/Changelog-for-next-generation.md`)
Changelog for Next Generation Libraries
The next-generation Azure JavaScript libraries introduce a few important changes:
- Authentication: The packages
@azure/ms-rest-nodeauthor@azure/ms-rest-browserauthare no longer supported. Use package @azure/identity instead. Select a credential from Azure Identity examples based on the authentication method of your choice. You may find out the new authentication examples here - Callbacks: Method overloads that use callbacks have been removed and please use Promise instead. You may find out the examples here
- List operations now return an iterable result that follows the
PagedAsyncIterableIteratorinterface as opposite to the previous model where you have to make a new request using the link to the next page. You may find out the new list examples here - Interface and API change for Long running operations: To check the final result of the Poller object returned by long running operations like
beginCreateOrUpdate, please usepollUntilDoneinstead ofpollUntilFinished. To get the final result directly, use the method with the suffixAndWaite.g.beginCreateOrUpdateAndWait. You may find out the LRO examples here - The ClientContext class has been removed and all its properties could be found in Client class itself.
- The SDK only supports ECMAScript 2015 (ES6) and beyond, all projects that referenced this SDK should be upgraded to use ES6.
Tips:
- For more details on how to migrate the next-generation libraries, please visit the migration guide.
- To get started, please visit the quickstart guide.
- For more sample code, please visit our samples repo.
Documentation Generate Code From TypeSpec (`documentation/Generate-code-from-TypeSpec.md`)
Getting Started: Generate JS SDK with TypeSpec
Before you Start
TypeScript Azure SDK Design Guidelines is the overall design guideline of the client SDK.
Prerequisites
- LTS versions of Node.js
- Git
- Local Clone of Rest API Spec Repo Fork
- If you don't already have a fork, Fork the Rest API Spec Repo.
- Clone your fork of the repo.text
git clone https://github.com/{YOUR_GITHUB_USERNAME}/azure-rest-api-specs.git
- Local Clone of JS Language Repo Fork
Generate SDK
Use TypeSpec defined in REST API specifications
It is recommended to configure TypeSpec package on REST API specifications. Please refer to these guidelines.
How to configure tspconfig.yaml
You can reference these two config files to configure the Modular or RLC package:
Please make sure service-dir, emitter-output-dir, package-details, flavor(for typespec-ts) is correctly configured. experimental-extensible-enums is the optional config.The is-modular-library option is true by default, if you want to generate RLC libraries for data-plane SDKs, you must add is-modular-library: false in your tspconfig.yaml.
- "parameters.service-dir.default" would be
sdk/<service> - "options.@azure-tools/typespec-ts.emitter-output-dir" would be
{output-dir}/{service-dir}/<module>
SDK module would be generated under the SDK project folder at sdk/<service>/<module>.
Generate Code with code-gen-pipeline tool (recommend)
Notice These steps are to generate code using the local spec repo. If you want to generate code with the github url, please refer Generate Code with tsp-client tool
Install dependencies to use code-gen-pipeline,
npm --prefix eng/common/tsp-client ci
$packageManager = node -p "require('./package.json').packageManager"
$npmRegistry = npm config get registry
npm install $packageManager --registry=$npmRegistry
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1Create a local json file named generatedInput.json with content similar to that shown below
{
"dryRun": false,
"specFolder": <your-local-spec-repo-path>,
"headSha": <commit-id-you-want-to-generate-from>,
"repoHttpsUrl": "https://github.com/Azure/azure-rest-api-specs",
"relatedTypeSpecProjectFolder": [
"specification/SERVICE_DIRECTORY_NAME/PACKAGE_DIRECTORY_NAME/"
]
}Run the command
node eng/tools/js-sdk-release-tools/dist/autoGenerateInPipeline.js --inputJsonPath=<path-to-generatedInput.json> --outputJsonPath=<path-to-generatedOutput.json> --typespecEmitter=@azure-tools/typespec-ts --localpath-to-generatedOutput.json is the detailed information of generated package, you can ignore it without pipeline. generateOutput.json is to show us the location of generated artifact and any other messages.
This command will automatically:
- Generate package code with TypeSpec.
- Build the package.
- Generate and run tests (optional, with warnings displayed if they fail).
- Generate samples, if enabled.
- Create or update the
CHANGELOG.md. - Bump the version according to the Azure SDK for JS policy.
- Generate or update
ci.mgmt.ymlorci.yml(if the package is new).
After all the steps finished, you can prepare the release for this generation. See Prepare Release
Generate Code with tsp-client tool
To reduce workload and unnecessary mistakes, it is recommended to use the simple method from the previous section. Only if you are clear about what you are doing and the method from the previous section does not meet your needs, should you consider using the method below.
Install tsp-client CLI tool
npm --prefix eng/common/tsp-client ciFor initial set up, from the root of the SDK repo, call
npm --prefix eng/common/tsp-client exec --no -- tsp-client init -c <url-to-tspconfig>For updating TypeSpec generated SDK, call below in the SDK module folder (sdk/<service>/<module>) where tsp-location.yaml exists
npm --prefix ../../../eng/common/tsp-client exec --no -- tsp-client updateNotice
If you use tsp-client to generate code and your generated SDK is new, you need to do one extra thing:
1: You should add ci.yml or ci.mgmt.yml under sdk/<service>/<module. ci.yml is for Data Plane SDKs and ci.mgmt.yml is for Mgmt Plane SDKs. See Create/Update the ci.yaml
Build
After all the steps finished, you can prepare the release PullRequest for this generation. See Prepare Release
Prepare Release
Prepare your SDK for release. The necessary approvals, guidance for testing, documentation, and release pipelines is described in your release plan. More information about the Azure SDK Release Tool is here.
Documentation MIGRATION Guide For Next Generation Management Libraries (`documentation/MIGRATION-guide-for-next-generation-management-libraries.md`)
Guide for migrating to the next generation of Azure JavaScript SDK for Management Libraries
This document shows the customers of the JavaScript/TypeScript management libraries on how to migrate their code to use the next-generation libraries.
For new customers of the JavaScript/TypeScript SDK (azure-sdk-for-js), please see quick start for next generation.
Current status
Currently, we have released GA version of selected services including @azure/arm-resources, @azure/arm-storage, @azure/arm-compute, @azure/arm-network. We are actively working on releasing more packages and eventually cover all Azure services. Please find the latest version of those libraries in npm and have a try.
Why Switching to the next-generation
Compared to the current management libraries, the next-generation libraries have the following changes:
- Authentication: The packages
@azure/ms-rest-nodeauthor@azure/ms-rest-browserauthare no longer supported. Use package @azure/identity instead. Select a credential from Azure Identity examples based on the authentication method of your choice. - Callbacks: Method overloads that use callbacks have been replaced to use Promise instead.
- You could iterate the result of List operations by using the
PagedAsyncIterableIteratorinterface, compared with in previous model, you have to make a new request using the link to the next page. - Interface and API change for long-running operations: To check the final result of the Poller object returned by long-running operations like
beginCreateOrUpdate, please usepollUntilDoneinstead ofpollUntilFinished. To get the final result directly, use the method with the suffixAndWaite.g.beginCreateOrUpdateAndWait. - The SDK only supports ECMAScript 2015 (ES6) and beyond, all projects that referenced this SDK should be upgraded to use ES6.
If you have an existing application that uses the JavaScript/TypeScript Azure SDK packages and you're interested in updating your application to use the next-generation SDKs, here are the things that you need to do for the migration:
Authentication
In the next-generation JavaScript/TypeScript packages, we only support using @azure/identity to do the Authentication. We have deprecated the authentication methods defined in @azure/ms-rest-nodeauth and @azure/ms-rest-browserauth. If you are still using them, please follow the below example to do the migration.
Replace loginWithServicePrincipalSecret method in @azure/ms-rest-nodeauth by ClientSecretCredential in @azure/identity to get the credential.
change
import * as msRestNodeAuth from "@azure/ms-rest-nodeauth";
const credentials = msRestNodeAuth.loginWithServicePrincipalSecret(
clientId,
clientSecret,
tenantId
);into
import { ClientSecretCredential } from "@azure/identity";
const credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);Please refer to @azure/identity for more details about @azure/identity and migration guide from @azure/ms-rest-nodeauth to @azure/identity on how to migrate from @azure/ms-rest-nodeauth.
Callbacks
In current libraries, we have some operations that allow customers to use callback such as
| Current Libraries | Next Generation |
|---|---|
| getInstanceView( | |
| resourceGroupName: string, | |
| cloudServiceName: string, | |
| options?: msRest.RequestOptionsBase | |
| ): Promise<Models.CloudServicesGetInstanceViewResponse> |
getInstanceView(
resourceGroupName: string,
cloudServiceName: string,
callback: msRest.ServiceCallback<Models.CloudServiceInstanceView>
): void;
getInstanceView(
resourceGroupName: string,
cloudServiceName: string,
options: msRest.RequestOptionsBase,
callback: msRest.ServiceCallback<Models.CloudServiceInstanceView>
): void;
getInstanceView(
resourceGroupName: string,
cloudServiceName: string,
options?: msRest.RequestOptionsBase
msRest.ServiceCallback<Models.CloudServiceInstanceView>,
callback?: msRest.ServiceCallback<Models.CloudServiceInstanceView>
): Promise<Models.CloudServicesGetInstanceViewResponse> | getInstanceView(
resourceGroupName: string,
cloudServiceName: string,
options?: CloudServicesGetInstanceViewOptionalParams
): Promise<CloudServicesGetInstanceViewResponse> |
Now, we have removed these operations that allows callback as a parameter, if you are using something like
const callback = function handle(...args) {
// callback function body
};
getInstanceView(resourceGroupName, cloudServiceName, callback);You may change it into a promise based
const callback = function handle(...args) {
// callback function body
};
getInstanceView(resourceGroupName, cloudServiceName).then(...(args) => handle(...args));List Operations
List operations now return an iterable result that follows the PagedAsyncIterableIterator interface as opposed to the previous model where you had to make a new request using the link to the next page.
The below example shows how you could handle the list result in previous version:
await client.availabilitySets.list(this.resourceName).then((response) => handle(response));Now, you will get a iterator, and you need to do the iteration to get the result.
const result = client.availabilitySets.list(this.resourceName);
for await (const item of result) {
handle(item);
}The newly added PagedAsyncIterableIterator also allows you to get these items by page if the List operation result is too long.
const result = client.availabilitySets.list(this.resourceName);
for await (const item of result.byPage({ maxPageSize: 2 })) {
handle(item);
}Refer to @azure/core-paging for more details.
Long Running Operations
Many operations may take a long time to finish before receiving the desired response. The SDK provides two types of methods to interact with such operations. First type is a method that simply returns the result after the operation finishes processing and those methods' names correspond to the name of the API they call. One issue with these methods is that they do not provide a way to check on the current status of the operation or to access any partial results computed so far. The next-generation SDK provides similar methods but with a slightly different name, it has the prefix begin and the postfix AndWait. The other type of methods is one that returns a poller object which gives you access to the underlying state of the operation. Previously, these methods returned an object of type LROPoller, a class that is exported by @azure/ms-rest-azure-js but the new operations return an object that implements the PollerLike interface instead which is exported by @azure/core-lro. The name of those methods did not change, both versions use the prefix begin with no postfix.
| Operations that return a poller. | |
|---|---|
| Current Libraries | Next Generation |
| beginCreateOrUpdate( |
resourceGroupName: string,
cloudServiceName: string,
options?: Models.CloudServicesBeginCreateOrUpdateOptionalParams ): Promise<msRestAzure.LROPoller> | async beginCreateOrUpdate(
resourceGroupName: string,
cloudServiceName: string,
options?: CloudServicesCreateOrUpdateOptionalParams
): Promise<PollerLike,
CloudServicesCreateOrUpdateResponse
>
> |
| Operations that will poll until finish and return the result directly | |
| Current Libraries | Next Generation |
| createOrUpdate(
resourceGroupName: string,
cloudServiceName: string,
options?: Models.CloudServicesCreateOrUpdateOptionalParams
): Promise<Models.CloudServicesCreateOrUpdateResponse> | async beginCreateOrUpdateAndWait(
resourceGroupName: string,
cloudServiceName: string,
options?: CloudServicesCreateOrUpdateOptionalParams
): Promise<CloudServicesCreateOrUpdateResponse> |
The following table compares LROPoller and PollerLike:
| operation | LROPoller |
PollerLike |
|---|---|---|
| return final results | pollUntilFinished() |
pollUntilDone() |
| poll | poll() |
poll() |
| access the current state after receiving the response of each poll request | N/A | onProgress() |
| check whether the operation finished | isFinished() / isFinalStatusAcceptable() |
isDone() |
| stop polling | N/A | stopPolling() |
| check if the polling stopped | N/A | isStopped() |
| cancel the operation | N/A | cancelOperation() |
| get the current operation state | getPollState() |
getOperationState() |
| access the final result | getOperationResponse() |
getResult() |
| serialize the poller state | N/A | toString() |
| get the most recent response | getMostRecentResponse() |
can be accessed using the onResponse callback in the operation options |
| get the current operation status | getOperationStatus() |
can be accessed using the onResponse callback in the operation options |
And here are examples of how to commonly use one of the beginCreateOrUpdateAndWait LROs found in @azure/compute:
| Operations that returns a poller. | |
|---|---|
| Current Libraries | Next Generation |
| const computeClient = new compute.ComputeManagementClient(credential, subscriptionId); | |
| const poller = await computeClient.dedicatedHosts.beginCreateOrUpdate( | |
| resourceGroupName, | |
| hostGroupName, | |
| hostName, | |
| parameter | |
| ); | |
console.log(The current status? ${poller.getPollState().state"}) |
|
| const result = await poller.pollUntilFinished().then((response) => { | |
| console.log(response); | |
| }); | const computeClient = new compute.ComputeManagementClient(credential, subscriptionId); |
| const poller = await computeClient.dedicatedHosts.beginCreateOrUpdate( | |
| resourceGroupName, | |
| hostGroupName, | |
| hostName, | |
| parameter | |
| ); | |
| poller.onProgress((state) => { | |
| console.log(`Are we done yet? ${Boolean(state.isCompleted)}); | |
| }) | |
| const result = await poller.pollUntilDone().then((response) => { | |
| console.log(response); | |
| }); | |
| Operations that will poll until finish and return the result directly | |
| Current Libraries | Next Generation |
| const computeClient = new compute.ComputeManagementClient(credential, subscriptionId); | |
| await computeClient.dedicatedHosts | |
| .createOrUpdate(resourceGroupName, hostGroupName, hostName, parameter) | |
| .then((response) => { |
console.log(response); }); | const computeClient = new compute.ComputeManagementClient(credential, subscriptionId);
await computeClient.dedicatedHosts
.beginCreateOrUpdateAndWait(resourceGroupName, hostGroupName, hostName, parameter)
.then((response) => {
console.log(response);
}); |
Please refer to @azure/core-lro for more details.
Additional Samples
We also provide some samples here for customers on how to use the next generation of Azure JavaScript/TypeScript management libraries.
Need help
If you have encountered an issue during migration, please file an issue via Github Issues and make sure you add the 'Preview' label to the issue.
Documentation Quickstart On How To Write Tests (`documentation/Quickstart-on-how-to-write-tests.md`)
Javascript Codegen Quick Start for Test
This page is to help you write and run tests quickly for Javascript Codegen SDK including high-level and rest-level clients. We firstly show how to run in record and playback mode and then guide on how to add test cases.
Table of contents
- Javascript Codegen Quick Start for Test
- Table of contents
- Background
- Prerequisites
- How to run test
- How to add tests
Background
The Azure SDK test framework uses the test-recorder library, which in turn rests upon on a HTTP recording system (testproxy) that enables tests dependent on network interaction to be run offline.
Please note that this quickstart is based on version 4.x of the recorder tool (@azure-tools/test-recorder), which uses the language-agnostic test proxy server and the asset-sync workflow.
Prerequisites
- pnpm
- Install/update pnpm globally via pnpm installation instructions
- Any of the LTS versions of Node.js
- A C++ compiler toolchain and Python (for compiling machine-code modules)
- Refer here for more details
To be able to leverage the asset-sync workflow
- Install Powershell
- Make sure "pwsh" command works at this step (If you follow the above link, "pwsh" is typically added to the system environment variables by default)
- Add
dev-toolto thedevDependenciesin thepackage.json.
How to run test
This section describes how to run the SDK tests. If you want to run the tests of a specific project, go to that project's folder and execute pnpm test. All of the tests will automatically run both in NodeJS and in the browser. To target these environments individually, you can run pnpm test:node and pnpm test:browser. Let's take purview-datamap-rest as an example.
To select a subset of test files and/or test cases:
pnpm run test:node -- test/myTest.spec.ts -t "should handle basic operations"Some shells (e.g. PowerShell) process command-line options differently and require double --:
pnpm run test:node -- -- test/myTest.spec.ts -t "should handle basic operations"If you have no concepts of recording, playback or TEST_MODE we'll highly recommend you to read this doc. We'll touch upon these concepts in below content.
Code structure
If this is your first time generating an SDK, you can enable the config generate-test: true in README.md or tspconfig.yaml. For management plane packages, generate-test is always enabled. Then we'll generate simple utils and a sample test file for you.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// import type { Recorder } from "@azure-tools/test-recorder";
// import { createRecorder } from "./utils/recordedClient.js";
import {
assert,
// beforeEach,
// afterEach,
it,
describe,
} from "vitest";
describe("My test", () => {
// let recorder: Recorder;
// beforeEach(async function (ctx) {
// recorder = await createRecorder(ctx);
// });
// afterEach(async function () {
// await recorder.stop();
// });
it("sample test", async function () {
assert.equal(1, 1);
});
});This only contains basics for testing, we comment out some lines except license header. If you want to update to your own utility and test cases. The overall structure will be similar to below:
Note: the structure of the test folder has slight differences between high-level, rest-level and Modular clients. In HLC, we only have one file under the test folder which contains all contents. But in RLC and Modular, we separate the sample test and utils.
sdk/
├─ purview/
│ ├─ purview-datamap-rest/
│ │ ├─ src/
│ │ │ ├─ ...
│ │ ├─ test/
│ │ │ ├─ public/
│ │ │ | ├─ utils/
│ │ │ | | ├─ recordedClient.ts
│ │ │ | ├─ sampleTest.spec.tsYou could also refer here to add your test cases.
After writing your test cases you need to run your test cases and record the test recordings. See here
Run tests in record mode
Before running tests, it's advised to update the dependencies and build our project by running the command pnpm install && pnpm turbo build --filter=... --token 1. Please notice this command is time-consuming and it will take around 10 mins, you could refer here for more details.
> pnpm install
> pnpm turbo build --filter=@azure-rest/purview-datamap... --token 1Then, we could go to the project folder to run the tests. By default, if you don't specify TEST_MODE, it will run previously recorded tests.
> cd sdk/purview/purview-datamap-rest
sdk/purview/purview-datamap-rest> pnpm testIf you are the first time to run tests you may fail with below message because there is no any recordings found.
[test-info] ===TEST_MODE=undefined===
...
[node-tests] 2 failing
[node-tests]
[node-tests] 1) My test
[node-tests] "before each" hook for "sample test":
[node-tests] RecorderError: Start request failed.To record or update our recordings, we need to set the environment variable TEST_MODE to record. Then, run pnpm test.
After running the test cases, you need to push your recordings into assets repo. See here
# Windows with CMD
> set TEST_MODE=record
> pnpm test
# Windows with PowerShell
> $env:TEST_MODE="record"
> pnpm test
# Linux / Mac
> export TEST_MODE=record
> pnpm testThis time we could get following similar logs. After pushing, recording files will be stored in the azure-sdk-assets repo and referenced via your assets.json.
[test-info] ===TEST_MODE="record"===
...
[node-tests] My test
[node-tests] √ sample test
[node-tests]
[node-tests] 1 passing (223ms)Run tests in playback mode
If we have existing recordings, then the tests have been run against generated the HTTP recordings, we can run your tests in playback mode.
# Windows with CMD
> set TEST_MODE=playback
> pnpm test
# Windows with PowerShell
> $env:TEST_MODE="playback"
> pnpm test
# Linux / Mac
> export TEST_MODE=playback
> pnpm testHow to push test recordings to assets repo
We need to push test recording files to asset repo after testing your test cases.
Notice: Before pushing your recording file, you must confirm that you are able to push recordings to the azure-sdk-assets repo, you need write-access to the assets repo. Permissions to Azure/azure-sdk-assets
Push test recording
New Package - No recorded tests
This section assumes that your package is new to the JS repo and that you're trying to onboard your tests with recorder, and the asset-sync workflow.
Generate an sdk/<service-folder>//assets.json file by running the following command.
npx dev-tool test-proxy initNote: If you install dev-tool globally, you don't need npx prefix in the above command
This command would generate an assets.json file with an empty tag.
Example assets.json with an empty tag:
{
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/network/arm-network",
"Tag": ""
}After init the assets.json file, run your test with record mode
Notice: If you have already run tests in record mode before, you need to re-run the tests again to make sure that your records can be pushed later.
Then, go to the next step to Existing package - Tests have been pushed before.
Existing package - Tests have been pushed before
At this point, you should have an assets.json file under your SDK. sdk/<service-folder>//assets.json.
With asset sync enabled, there is one extra step that must be taken before you create a PR with changes to recorded tests: you must push the new recordings to the assets repo. This is done with the following command:
Notice: the tests have to be recorded using the TEST_MODE=record environment variable in order for the recording files to be generated, then you can push them to assets repo
npx dev-tool test-proxy pushThis command will:
- Push your local recordings to a tag in the
Azure/azure-sdk-assetsrepo, and - Update the
assets.jsonin your package root to reference the newly created tag.
You should stage and commit the assets.json update as part of your PR. If you don't run the push command before creating a PR, the CI (and anyone else who tries to run your recorded tests) will use the old recordings, which will cause failures.
How to find recording files
Find local recording files
You can find your recording files in ./azure-sdk-for-js/.assets
If you want to search your recording quickly, you can open .breadcrumb file and search your package in which folder.
Find recording files in assets repo
You can get the tag in assets.json in your package root, which is a tag pointing to your recordings in the Azure/azure-sdk-assets repo.
Example assets.json from arm-network SDK:
{
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/network/arm-network",
"Tag": "js/network/arm-network_bec01aa795"
}The recordings are located at https://github.com/Azure/azure-sdk-assets/tree/js/network/arm-network_bec01aa795.
How to add tests
Adding runnable tests requires both a good understanding of the service, and the knowledge of the client and test framework. Feel free to contact SDK developers, if you encountered issues on client or test framework.
Before adding tests
Client authentication
There are several ways to authenticate to Azure and most common ways are AzureAD OAuth2 authentication and API key authentication. Before adding tests, you are expected to know what your services support and ensure you or service principal have rights to perform actions in test.
AzureAD OAuth2 Authentication
If your service uses AzureAD OAuth2 token for authentication, a common solution is to provide an application and its service principal and to provide RBAC to the service principal for the access to the Azure resource of your service.
Client requires following three variables for the service principal using client ID/secret for authentication:
AZURE_TENANT_ID
AZURE_CLIENT_ID
AZURE_CLIENT_SECRETThe recommended practice is to store these three values in environment variables called AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET. To set an environment variable, use the following commands:
# Windows
> set AZURE_TENANT_ID=<value>
# Linux / Mac
> export AZURE_TENANT_ID=<value>To ensure our recorder could record OAuth traffic, we have to leverage the createTestCredential helper to prepare test credential. So, please follow below code snippet to create your client.
import { createTestCredential } from "@azure-tools/test-credential";
const credential = createTestCredential();
// Create your client using the test credential.
new MyServiceClient(<endpoint>, credential);To avoid storing the sensitive info in the recordings like authenticating with your Azure endpoints, keys, secrets, etc, we use the sanitizers to mask the values with the fake ones or remove them, RecorderStartOptions helps us here. In our generated sample file, we have below sanitizers' code:
const envSetupForPlayback: Record<string, string> = {
ENDPOINT: "https://endpoint",
AZURE_CLIENT_ID: "azure_client_id",
AZURE_CLIENT_SECRET: "azure_client_secret",
AZURE_TENANT_ID: "88888888-8888-8888-8888-888888888888",
AZURE_SUBSCRIPTION_ID: "azure_subscription_id",
};
const recorderEnvSetup: RecorderStartOptions = {
envSetupForPlayback,
};
//...
await recorder.start(recorderEnvSetup);API Key Authentication
API key authentication would hit the service's endpoint directly so these traffic will be recorded. It doesn't require any customization in tests. However, we must secure the sensitive data and not leak into our recordings, so add a sanitizer to replace your API keys. You could read more on how to add sanitizer at here.
Example 1: Basic RLC test interaction and recording for Azure data-plane service
At the code structure section, we described we'll generate sample file for you. If you are the first time to write test cases, you could grow up your own based on them.
This simple test creates a resource and checks that the service handles it correctly in the project purview-datamap-rest. Below are the steps:
- Step 1: Create your test file and add one test case with resource creation, here we have purview datamap glossary test file
glossary.spec.tsand one case namedShould create a glossary. Or rename thesampleTest.spec.tsfile and its casesample test. - Step 2: Add the utility method
createClientinpublic/utils/recordedClient.tsto share thePurviewDataMapClientcreation.- Call
createTestCredentialto init your credential and refer here for more details. - Wrap the
optionwith test options by callingrecorder.configureClientOptions(options).
- Call
- Step 3: In
glossary.spec.tsfile, callcreateClientto prepare the client and callclient.path("/atlas/v2/glossary").post()to create our glossary resource under our caseShould create a glossary. - Step 4: Specify environment variables that would be faked in the recordings in map
envSetupForPlaybackunder the filepublic/utils/recordedClient.ts. - Step 5: In
glossary.spec.tsfile, add necessary assertions in your test case. - Step 6: Run and record your test cases.
`glossary.spec.ts`
import { Recorder } from "@azure-tools/test-recorder";
import { describe, it, assert, beforeEach, afterEach } from "vitest";
import type { PurviewDataMapClient } from "../../src/index.js";
import { createClient, createRecorder } from "./utils/recordedClient.js";
describe("My test", () => {
let recorder: Recorder;
// Step 3: Declare your own variables
let client: PurviewDataMapClient;
let glossaryName: string;
beforeEach(async (ctx) => {
recorder = await createRecorder(ctx);
// Step 3: Create your client
client = await createClient(recorder);
glossaryName = "js-testing";
});
afterEach(async () => {
await recorder.stop();
});
// Step 1: Create your test case
it("Should create a glossary", async () => {
// Step 3: Add your test cases
const glossaryResponse = await client.path("/atlas/v2/glossary").post({
body: {
name: glossaryName,
shortDescription: "Example Short Description",
longDescription: "Example Long Description",
language: "en",
usage: "Example Glossary",
},
});
if (isUnexpected(glossaryResponse)) {
throw new Error(glossaryResponse.body?.error.message);
}
// Step 5: Add your assertions
assert.strictEqual(glossaryResponse.status, "200");
});
});`utils/recordedClient.ts`
/* Detailed source-code truncated for AI context efficiency. */Example 2: Basic HLC test interaction and recording for Azure management service
At the code structure section, we described if your SDK is generated based on HLC, we'll generate a sample test named sampleTest.ts for you.
Next, we'll take the package @azure/arm-monitor as an example to guide you how to add your own test case. Below are the steps:
- Step 1: Create your test file and add one test case with resource creation, here we have monitor test file
monitor.spec.tsand one case namedShould create diagnosticSettings. Or rename thesampleTest.spec.tsfile and its casesample test. - Step 2: Add declarations for common variables e.g monitor client, its diagnostic name and subscription id.
- Step 3: Create the monitor client in
beforeEachand callclient.diagnosticSettings.createOrUpdatein test case.- Read the
subscriptionIdfromenv. - Call
createTestCredentialto init your credential and refer here for more details. - Wrap the
optionwith test options by callingrecorder.configureClientOptions(options).
- Read the
- Step 4[Optional]: Specify environment variables that would be faked in the recordings in map
envSetupForPlayback. - Step 5: Add necessary assertions in your test case.
- Step 6: Run and record your test cases.
`monitor.spec.ts`
/* Detailed source-code truncated for AI context efficiency. */Documentation RLC Customization (`documentation/RLC-customization.md`)
Customization on the RLC rest-level client libraries
Generate RLC Client
Follow quickstart to generate the rest-level client from OpenAPI specs.
It's advised to put the generated code into the folder generated, add your customization code under the folder src and then export or re-export them as needed.
source-code-folder-path: ./generatedCustom authentication
Before you customize the code, you should run npx dev-tool customization apply to sync the generated src code from ./generated into ./src
Some services require a custom authentication flow. For example, a service might use Key Authentication requiring 2 headers for key authentication (e.g., Ocp-Apim-Subscription-Key and x-api-key), which is different from the usual key authentication which only requires a single key.
In this case, we customize as follows:
- Hand author a
PipelinePolicythat takes values for both keys and sign the request - Hand author a wrapping client factory function
- In the wrapping factory, we create a new client with the generated factory
- Inject the new policy to the client
- Return the client
- Only expose the wrapping factory and hide the generated factory.
Here is an example implementation.
The wrapping function looks like:
import MyServiceClient from "./generatedClient";
import { isTokenCredential, TokenCredential } from "@azure/core-auth";
import { ClientOptions } from "@azure-rest/core-client";
import {
createMyServiceKeyCredentialPolicy,
MyServiceKeyCredential,
} from "./myServiceKeyCredentialPolicy";
export default function createClient(
endpoint: string,
credential: TokenCredential | MyServiceKeyCredential,
options: ClientOptions = {}
): GeneratedClient {
if (isTokenCredential(credential)) {
return MyServiceClient(endpoint, credential, options);
} else {
const client = MyServiceClient(endpoint, undefined as any, options);
const authPolicy = createMyServiceKeyCredentialPolicy(credential);
client.pipeline.addPolicy(authPolicy);
return client;
}
}And in myServiceKeyCredentialPolicy.ts file, we have the customized policy and createMyServiceKeyCredentialPolicy function to create that policy.
import {
PipelinePolicy,
PipelineRequest,
PipelineResponse,
SendRequest,
} from "@azure/core-rest-pipeline";
import { KeyCredential } from "@azure/core-auth";
export const API_KEY_HEADER_NAME = "Ocp-Apim-Subscription-Key";
export const X_API_KEY_HEADER_NAME = "x-api-key";
/**
* Interface parameters for updateKey function
*/
export interface MyServiceKeyCredential extends KeyCredential {
/** API key from the service web portal */
// key?: string; // extended from KeyCredential
/** Subscription access key from the Azure portal */
subscriptionKey?: string;
}
/**
* Creates an HTTP pipeline policy to authenticate a request
* using an `MyServiceKeyCredential`
*/
export function createMyServiceKeyCredentialPolicy(
credential: MyServiceKeyCredential
): PipelinePolicy {
return {
name: "myServiceKeyCredentialPolicy",
sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
if (!request) {
throw new Error("webResource cannot be null or undefined");
}
request.headers.set(API_KEY_HEADER_NAME, credential.subscriptionKey || "");
request.headers.set(X_API_KEY_HEADER_NAME, credential.key);
return next(request);
},
};
}With this user experience is the same as it is with any other RLC, as they just need to create a new client from the default exported factory function.
import MyServiceClient, { paginate } from "@azure-rest/my-service";
const client = MyServiceClient("https://<endpoint>", {
key: "<apiKey>",
subscriptionKey: "<subscriptionKey>",
});Custom paging helper
Eventhough the code generator provides a pagination helper for RLCs, there are services that implement their own pagination pattern, different to the standard specification of x-ms-pageable.
Some services implement a pagination pattern in which getting the next page can be called with GET or POST depending on the resource.
The standard pagination pattern, assumes GET for getting the next pages. In this case, we can implement a custom paginate helper that has the same public interface as the generated helper but under the hoods has an additional pagination implementation to use POST. Also this custom helper has an internal map that indicates which operations need POST and which need GET.
Here is an example implementation. Remember to replace the paginationMapping with your service's specific endpoints. The generated paging helper is hidden and the custom paginate helper is exposed.
/* Detailed source-code truncated for AI context efficiency. */The example code to call the helper.
import MyServiceClient, { paginate } from "@azure-rest/my-service";
import { DefaultAzureCredential } from "@azure/identity";
const client = MyServiceClient("https://<endpoint>", new DefaultAzureCredential());
const initResponse = await client.listItems({
queryParameters: {
itemName: "test-",
$skip: 1,
$maxpagesize: 1,
},
});
const items = paginate(client, initResponse);
for await (const item of items) {
console.log(item);
}Custom data transform helpers
There may be times in which transforming the data from the service would be beneficial. When a transformation is common for our customers we may decide to expose helper transformation functions. These helper transformations are optional and customers can decide to use them or not, the calls maintain the original data form from the Service.
If we export toItemDetailResponse, which may convert the REST model to a common one so that the customers could call this way:
import MyServiceClient, { toItemDetailResponse } from "@azure-rest/my-service";
import { DefaultAzureCredential } from "@azure/identity";
const client = MyServiceClient("https://<endpoint>", new DefaultAzureCredential());
const listResponse = await client.listItems(<parameter>);
if (listResponse.status != "201") {
throw new Error("Error");
}
// Transforms service data into a more useful shape
const formattedItem = toItemDetailResponse(listResponse);Multi-client packages
There are cases where 2 services are closely related that most users will need to use both in the same application. In this case, we may opt for multi-client packages. Each client can be imported individually without a top-level client, this is to work nicely with bundler TreeShaking.
We could leverage the autorest batch option and enable multi-client flag in our README.md to generate two or more service clients.
Here is an example where we have two clients MyServiceClient and MyServiceAdministrationClient.
Use multi-client flag and batch option
Add the multi-client flag in our readme and use the batch autorest option to create the two clients:
batch:
- my-service: true
- my-service-admin: trueSpecify configurations for each individual clients
For each individual clients, specify your client name and swagger file. Make sure that you don't have one Swagger with operations that are designed to be in two different clients so that clients should correspond to a clear set of Swagger files.
Normally, the folder structure would be something like sdk/{servicename}/{servicename}-{modulename}-rest. For example, we have sdk/agrifood/agrifood-farming-rest folder for Farmbeats account modules. That folder will be your ${PROJECT_ROOT} folder.
title: MyServiceClient
description: My Service Client
output-folder: ${PROJECT_ROOT}/src
source-code-folder-path: ./client
input-file: /your/swagger/folder/myservice.jsontitle: MyServiceAdministrationClient
description: My Service Admin Client
output-folder: ${PROJECT_ROOT}/src
source-code-folder-path: ./admin
input-file: /your/swagger/folder/myservice-admin.jsonGenerate code with `--multi-client`
When generating the code, specify that what we want is multi-client so append the flag in command line --multi-client. After generation, the folder structure would be like below:
${PROJECT_ROOT}/
├─ src/
│ ├─ client/
│ │ ├─ MyServiceClient.ts
│ │ ├─ index.ts
│ ├─ admin/
│ │ ├─ MyServiceAdministrationClient.ts
│ │ ├─ index.ts
│ ├─ index.tsExample code to call any client
import {
MyServiceAdministrationClient,
MyServiceClient,
} from "@azure-rest/my-service";
const adminClient = MyServiceAdministrationClient.createClient(endpoint, credential);
// call any admin operation
const createdResponse = await adminClient.createResource(`<parameter>`);
const serviceClient = MyServiceClient.createClient(endpoint, credential);
// call any non-admin operation
const listedResponse = await serviceClient.listItems(`<parameter>`);RLC Customization Considerations
Our customization strategy has the following principles:
- Expose custom functionality as helper functions that users can opt-in
- Never force customers to use a customized function or operation
- The only exception is if we need to add custom policies to the client. It is okay to wrap the generated client factory and expose the wrapped factory instead of the generated one.
Documentation Troubleshoot Ci Failure (`documentation/Troubleshoot-ci-failure.md`)
Overview
This doc shows some common problems and resolution in CI.
Broken links
Add the broken links into eng/ignore-links.txt file to bypass this verification or you could update the broken links to valid ones, see example PR here.
Check spelling (cspell)
For new service the error usually happens, fix spelling in code or in markdown at file .vscode/cspell.json. See an example in devcenter PR.
Push failure\
Spawn pwsh ENOENT
Install Powershell. Make sure pwsh command works at this step (If you follow the above link, pwsh is typically added to the system environment variables by default.)
Authorization issue
If you are from service team, External to azure-sdk, you can follow these steps:
To request write access, join an appropriate team from this list(same list on MS OpenSource portal) that corresponds with the product or org that you work in. Be sure to join only one team.
If you don't see your team in the list? Contact Scott Kurtzeborn : [email protected] to create a new one for your service team.
For more details, join a partner write team
Note
For merge permissions to main branches, you must be a member of an azure-sdk-push team. These permissions are assigned on case-by-case basis only. Reach out to Scott Kurtzeborn : [email protected] if you think that your team needs that permission.
Documentation Case Study Investigating A Pipeline That Hangs (`documentation/case-study-investigating-a-pipeline-that-hangs.md`)
Case Study: Investigating a Pipeline that Hangs
Note: This is a historical case study. It describes the tooling and environment in use at the time of the investigation (mocha test runner, Node.js 8). The repository has since migrated to vitest and requires LTS versions of Node.js.
Problem
We have encountered a problem where the pipelines hung right after the identity tests were run [only(majorly) on Windows].
Here is a screenshot.
The issue is time sensitive as it was blocking several PRs.
Observations / Tips
- We can't use "why-is-node-running" tool because we would have to kill the process cleanly to generate the logs, which isn't typically possible with the CI.
- The issue doesn't reproduce in any of our local work machines or laptops - doesn't matter which OS, doesn't matter which node version.
- On the CI, from more runs of the pipeline, [Windows - node 8] combination seemed more pronounced(almost always fails). [Windows - other node versions] failed about half the time. We have rearely observed the same failure with macOS and Linux.
- "--exit" flag from mocha can be used to exit the mocha run from a hanging state. Though there is relief, it would be temporary, the problem has to be fixed eventually.
The Story
The next attempt, creating a Windows 32-bit VM on Azure helped reproduce the problem. Just a tip, the VM can also be RDP-ed into, if you need to open VS Code and just debug the whole thing.
Next steps
Clone JS repo in the VM.
Running just the identity tests suite fails (Though succeeds at about 10% of the time).
At this point, we know that one/subset of the tests is/are causing the problem.
We marched a binary search and isolated the problem to be consistently appearing when we ran a couple of tests in unison.
- And interestingly, both the tests succeed independently.
- And the issue doesn't repro if we enable logger verbose.
Now that we have the problem isolated at least, the next steps are to locate the problem.
I have used wtfnode to find out what's been causing the issue, it helps by providing active handles to investigate stalled node apps. (Another mocha team's resource which also helped)
This helped locate the spot where it goes wrong. The InteractiveBrowserCredential test is the one that hangs(from the above screenshot).
Workflow of the test is that opening the browser would throw and the test is supposed to end, however, it doesn't.
- If the server is closed even before, it ends fine.
Now that we have located the problem, we tried understanding and fixing it altogether.
Since what was hanging wasn’t an exception, since no 'unhandled exception' appeared in the logs… We checked that all of the catches were reached, attempting to find some code path that was being ignored and eventually found that all the code paths were following accordingly, so our code "worked". We were closing everything we wanted to close.
So we tried adding a delay before throwing the error we were testing (remember, this specific test checks the edge case in which there is no browser available). That delay fixed it, so it was something added in the stack out of order. So, though we resolved and wrapped up everything, Node was adding something into the stack right after. The delay would be a bad solution since the time needed order the stack appropriately would be variable.
We assumed what was taking a while to load was the server setting things up to listen for requests.
The issue was this:
- We were allowing Node to close everything and resolve all promises before the server was ready to listen for requests.
- Our "closing" dependency/code assumed that it's always closing something that is at least already listening.
- As soon as we added the step to wait for the server to begin listening before we allowed anything to resolve, then by the time the exception was thrown, everything was able to close properly.
Here's the PR from @sadasant for more details on the fix - https://github.com/Azure/azure-sdk-for-js/pull/15497
Documentation Core Client Additional Policy For Debug Logging (`documentation/core-client-additional-policy-for-debug-logging.md`)
Additional Policy for Debug Logging
| :boom: | This is meant to be used by Azure SDK developers investigating bugs. The request policy on this page WILL log sensitive information to the Node.js console. |
|---|
Default Azure SDK logging systems do not log request bodies, even in verbose mode. This is to prevent the logging of sensitive information from the request body.
Sometimes, it's useful to be able to dump a request trace to the console anyway. You may need to do so to investigate a bug or to accurately report an issue. For those cases, an "additional policy" may be used in the pipeline that logs the request contents.
Such policies must ONLY be used for debugging, as the information in the request and response must be assumed to be sensitive.
Here's one that logs the request headers and body as well as the response information (status code, headers, and body).
/* Detailed source-code truncated for AI context efficiency. */This may be installed into a client during construction using the additionalPolicies option:
// Assuming the options bag inherits from CommonClientOptions as it should
const client = new Client(endpoint, credential, {
additionalPolicies: [debugLoggingPolicy],
});Note that the policy runs after the default policies but before any policies that are appended to the pipeline by the client itself, so for example it runs after the retry policy, but before an appended KeyCredentialPolicy or BearerTokenAuthenticationPolicy.
Documentation Credscan Process (`documentation/credscan-process.md`)
Guide for monitoring CredScan checks
This guide describes how package owners can monitor their package's Credential Scanner (CredScan) status and correct
any warnings.
General information about CredScan can be found in the overview documentation at aka.ms/credscan. The
Azure SDK's motivation and methodology for running CredScan is documented here.
Table of Contents
Check CredScan status
CredScan is run each night over the entire azure-sdk-for-js repository as part of the
js - aggregate-reports pipeline. The scan produces a list of active warnings in the "Post
Analysis" task of the "ComplianceTools" job (example output).
Each warning will begin with the path to the file containing a potential credential, as well as the row and column where
the credential string begins. For example, for a potential credential that starts in row 29 and column 13 of a
particular file:
##[error]sdk/{service}/{package}/{path-to-file}.ts:sdk/{service}/{package}/{path-to-file}.ts(29,13):The warning will then list an error code and description of why the potential credential was flagged.
Correct active warnings
If you find any warnings listed for a package you own, the next step is to determine if the potential credential found
by CredScan is an actual credential (a true positive), or a fake credential/false flag (a false positive).
True positives
If CredScan discovers an actual credential, please contact the EngSys team at [email protected] so any
remediation can be done.
False positives
If CredScan flags something that's not actually a credential or secret, we can suppress the warning to shut off the
false alarm. CredScan allows you to suppress fake credentials by either suppressing a string value or by suppressing
warnings for a whole file. Files that contain more than just fake credentials shouldn't be suppressed.
Credential warnings are suppressed in eng/CredScanSuppression.json. Suppressed string values are in
the "placeholder" list, and suppressed files are in the "file" lists under "suppressions".
If you have a fake credential flagged by CredScan, try one of the following (listed from most to least preferable):
- Import and use a suitable credential from a file that's already suppressed in eng/CredScanSuppression.json. If the fake credential will be used by multiple packages, consider adding and exporting it in the
test-utilspackage. - If the test credentials are specific to your package, create a
fakeTestSecrets.tsfile under yourtestfolder, add and export the fake secrets from this file, and add the file path to the list of suppressed files if necessary. - Replace the credential with a string value that's already suppressed in eng/CredScanSuppression.json. "SecretPlaceholder" is a good one to use.
- Add the string that uses credential to the list of suppressed string values. For examples,
"password: fakeTestSecretPlaceholder,"
Ideally, fake credential files -- which contain nothing but fake secrets -- should be suppressed and their fake
credentials shouldn't appear in any other files. Sanitizers should be used to keep fake credentials out of test
recordings when possible. String value suppression should be avoided unless the string appears in many files.
Suppressing string values will disable warnings no matter where the string comes up during a scan, but is inefficient
and inconvenient for lengthy strings. Note due to current limitation of the CredScan tool, a whole text line is suppress if the line matches one suppression pattern, which means if a real secret and a suppressed string value are in the same line, the real secret are not reported! We should be very careful when adding a suppressing string value.
Suppressing warnings in a file is convenient for fake credential files, but strings in that file will still trigger warnings if present in another unsuppressed file.
Documentation Dependency Management (`documentation/dependency-management.md`)
Dependency Management
The Azure SDK for JS client libraries include a host of dependencies, both internal and external. In general, our policy allows for some flexibility in dependencies and we generally pin to a caret version - allowing for patches, security fixes, and any non-breaking changes in our dependency ranges.
There are times when a dependency needs to be updated, such as when a security vulnerability has been found and patched in the dependency's codebase. While we will address security vulnerabilities in dependencies by updating our minimum version to the latest patched version, our SemVer policy allows customers to update to the latest version of a transitive dependency without waiting for our next releases and without requiring a hotfix.
In this document we'll outline a few options available to you when a transitive dependency must be updated.
SDK authors: for the policy on taking a new third-party runtime
dependency in a shipped library, see
Adding a new third-party runtime dependency.
Example scenario
Let's assume that a vulnerability has been found in node-fetch version 2.6.6 and that you use @azure/keyvault-keys directly. Your security scan identified this vulnerability and asks to upgrade to 2.6.7 or higher.
The outputs below assume you're using npm v8.1.2 with a lockfile, but we will provide instructions for Yarn as well.
Identifying the dependency tree
First, let's see how node-fetch is pulled into our dependency tree.
> npm ls node-fetch
[email protected] /home/user/my-app
└─┬ @azure/[email protected]
└─┬ @azure/[email protected]
└── [email protected]If you are using Yarn you can use
yarn why node-fetchwith similar results.
It looks like node-fetch, a dependency of @azure/[email protected] is being pulled in transitively via @azure/[email protected].
Because I use a lockfile, running npm install again will not help me here. But npm provides a few utilities that can.
Using `npm audit fix`
First, it's possible that you were alerted to a security vulnerability thanks to npm audit. In this case, the simplest solution might be to run npm audit fix. Let's see what that looks like:
> npm audit fix
changed 1 package, and audited 51 packages in 421ms
2 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
keyvault main % npm ls node-fetch
[email protected] /home/user/my-app
└─┬ @azure/[email protected]
└─┬ @azure/[email protected]
└── [email protected]As you can see node-fetch has been updated to 2.6.7, without having to wait for a new version of @azure/core-http.
For more information on npm audit please refer to the npm-audit documentation.
If you are using Yarn you can use
yarn npm auditwith similar results.
Using `npm update`
Not all updates are due to security vulnerabilities. Sometimes you just want to update a transitive dependency without deleting your lockfile. In that case, npm update node-fetch can help. Let's see what that looks like:
> npm update node-fetch
changed 1 package, and audited 51 packages in 320ms
2 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
keyvault main % npm ls node-fetch
[email protected] /home/user/my-app
└─┬ @azure/[email protected]
└─┬ @azure/[email protected]
└── [email protected]Awesome!
If you are using Yarn you can use
yarn up node-fetchwith similar results.
Note: In some previous versions of
npm,npm updatewould only update top-level dependencies. If you're using npm 6.x for example you can provide the--depthargument to achieve similar results.
For more information on npm update please refer to the npm-update documentation.
I read this guide and still have no idea what to do
Feel free to file an issue and start a discussion, we're here to help and will try to do so to the best of our abilities!
Documentation Dual Emitting Using Warp (`documentation/dual-emitting-using-warp.md`)
Dual Emitting Using Warp
The Azure SDK for JS client libraries are built for both CommonJS and ESM using @microsoft/warp.
While the two formats are largely compatible, a few caveats apply.
Usage of __dirname and __filename
When a module is required in commonJS, __dirname and __filename are available via the module system; however, in ESM the same globals are not available.
Likewise, in CommonJS the globals import.meta.* are not available for usage.
Our build tool, warp, provides a polyfill substitution mechanism for automatically substituting the correct source file depending on the build target.
In order to properly fetch the current filename or current dirname you could do the following:
// myFile.ts - ESM flavor, use import.meta
const currentDir = dirname(fileURLToPath(import.meta.url));
// myFile-cjs.cts - CommonJS flavor, use __dirname and __filename
const currentDir = __dirname;Note that the file naming convention is important! The CommonJS polyfill must:
- Begin with the same file name as the ESM flavor
- Add the
-cjspostfix to the file name - Use
.ctsextension
For more details on polyfill substitution and the full configuration options, see the warp README.
Documentation Golden Testing Commands (`documentation/golden-testing-commands.md`)
Useful Commands while Testing your packages
If you're not familiar with the recorder refer to recorder-readme.
If you're looking to be onboarded to the asset-sync workflow to push out the test recordings to Azure/azure-sdk-assets repository, refer to asset-sync-workflow.
`pnpm` Commands
| script name | What does it do? |
|---|---|
pnpm install |
Updates dependencies |
pnpm -F {./}... build |
Expected to be run from inside your package(sdk/service-name/package-name). Builds the whole dependency tree. |
Commands - to run the tests inside the package directory
| script name | command(usually) |
|---|---|
pnpm test:browser |
dev-tool run test:vitest --browser |
pnpm test:node |
dev-tool run test:vitest |
After migrating to asset-sync
Expects that you have dev-tool among your devDependencies.
Expects that you have powershell installed.
| script command | What does it do? |
|---|---|
npx dev-tool test-proxy init |
[Only once per package] Generates the assets.json with an empty tag. After init, run test commands such as pnpm test:node to generate test recordings and push them. |
npx dev-tool test-proxy push |
To push the test recordings to the assets repo. |
npx dev-tool test-proxy reset |
Reverts the local changes to recordings and resets to what is currently checked in to the assets repo. This is a destructive operation. |
npx dev-tool test-proxy restore |
Pulls the recordings from the assets repo that are referenced in your assets.json. Typically this will be done automatically when you first run tests in playback if the recordings haven't been downloaded already. But you can run this command in advance if you'd like to download them earlier, for example for offline work. |
Debug logs from test-proxy tool
| script command | What does it do? |
|---|---|
dev-tool run test:vitest --test-proxy-debug |
Provides helpful test-proxy debug logs that include sanitization modifications for both the central sanitizers and the user-specified ones. |
Run a single test instead of the whole test suite
[Method 1] The .only way
- Update the vitest.config.ts as follows to include the test file you're interested in.
export default mergeConfig(
{
...viteConfig,
test: {
include: ["test/public/node/analysis.spec.ts"],
},
},
defineConfig({
test: {
testTimeout: 1200000,
hookTimeout: 1200000,
},
}),
);- Set
.onlyon the test you're interested in
it.only("test title...")Run the test command such as pnpm unit-test:node to run the test
[Method 2] --testNamePattern
pnpm integration-test:node -- --testNamePattern "simple"Reference - Filtering Tests — Introduction to Testing JavaScript with Vitest
[Method 3] -- test file
pnpm test:node -- test/public/metricsClient.spec.tsDocumentation How To Use (`documentation/how-to-use.md`)
How to Use the Azure SDK for JavaScript
In this document, we will give a brief introduction on how to use the JavaScript SDK for new users.
Prepare your environment.
NodeJS: can be installed from https://nodejs.org/en/download/
typescript: install it withnpm install typescript.Create a empty folder and
cdthis folder.textmkdir jstest cd jstestInitialize a new node project.
textnpm initThis step will create a
package.jsonfile in current folder.Install dependencies.
text// install @azure/identity, you can use @azure/identity to do the authentication work. npm install @azure/identity // Then install your target try out package, you can install the latest published with npm install @azure/arm-XXX // or install it from your local JS SDK artifact file. npm install D:\\dev\\test\\test-compute\\azure-arm-XXX-1.0.0.tgzIn the case of verifying the unpublished packages, you may download the artifact from either rest api specs CI pipeline or from the release request issue that we provided.
Create a ts file (free name and copy follow code into this file), eg:
test_1.ts.
Eg:tsimport { DefaultAzureCredential } from "@azure/identity"; import{ TargetManagementClient } from "@azure/arm-target"; const subscriptionId = process.env.SUBSCRIPTION_ID || ''; const credentials=new DefaultAzureCredential(); async function test() { const client = new TargetManagementClient(credentials, subscriptionId); const result = await client.operations.list(); // you can test that you need test operation console.log("The result is:\n"); console.log(result); } test();In the example, we only add
client.operations.list(), you may change them into other resources CRUD function per your need.
Eg:tsconst client = new ComputeManagementClient(credentials, subscriptionID); const result= await client.galleries.beginCreateOrUpdateAndWait(resourceGroupName, galleryName, gallery); const result= await client.galleryImages.begincreateOrUpdateAndWait(resourceGroupName, galleryName, galleryImageName, galleryImage);Install all the dependencies
textnpm install // need to make sure package.json exists and has contained at step 4.Compile the ts file.
texttsc test_1.tsit will generate a
test_1.jsfile in current folder.Run the code.
textnode test_1.jsNow, you can see expected response.
Documentation Linting (`documentation/linting.md`)
We use recommended rules from eslint, typescript-eslint, and a set of our custom rules to ensure our code adhere to the Azure SDK design guidelines and have high quality.
In this document, we describe how to address common linting issues for a package.
Azure SDK eslint plugin
Our custom linting rules and recommended configurations is hosted in the @azure/eslint-plugin-azure-sdk package. You MUST build it first before linting any SDK packages.
pnpm installpnpm turbo build --filter=@azure/eslint-plugin-azure-sdk... --token 1
It also gets built as a dependency of any SDK packages.
Linting a package
This is done by running pnpm lint under the package directory.
cd sdk/<service-directory>/if your current directory is not the package directory yet.pnpm lint
Fixing linting issues
You should NEVER turn off a rule in @azure/eslint-plugin-azure-sdk to resolve linting issues.
Some linting rules provide auto fixer. To use them, run pnpm lint:fix under the package directory.
For linting issues that lint:fix script could not resolve, you will need to examine the code and fix the issues accordingly.
For documentation on eslint rules, refer to https://eslint.org/docs/latest/rules/.
For documentation on typescript-eslint rules, refer to https://typescript-eslint.io/rules/
Cross-package `instanceof` checks
Do not use instanceof with a class from another package. The check compares constructor identity.
It can return false when an application loads two copies of the package.
Use the structural type guard from the package that defines the class:
- Use
isRestErrorforRestError. - Use
isKeyCredentialforAzureKeyCredential. - Use
isNamedKeyCredentialforAzureNamedKeyCredential. - Use
isSASCredentialforAzureSASCredential. - Use
isPipelineLikeforPipeline.
If the current package defines the class, keep the instanceof check inside its type guard.
Disable no-restricted-syntax only on that line. Add a comment that explains why the check is safe.
For a generated package whose name starts with @azure/arm- or @azure-rest/, there might be "tsdoc/syntax" warnings because generated code files often contains some characters in reference docs that are not recommended in TSDoc. We should NEVER fix auto-generated files. You can add rules to suppress the "tsdoc/syntax" rules in the package's ESLint configuration file eslint.config.mjs. Create that file by using one from other packages as a template but do not copy rules that don't apply.
Documentation Mgmt Release Rotation Runbook (`documentation/mgmt-release-rotation-runbook.md`)
JavaScript mgmt. SDK release rotation runbook
Follow these steps for generated management SDK PRs affecting sdk/*/arm-* packages, ending with PR approval. Review the mgmt-review findings and the Next Steps to Merge comment rather than repeating their API, tooling, and CI checks. Route handwritten or generator changes to a JavaScript management SDK maintainer.
Step 1. Start from the dashboard
Use the Azure SDK JS Dashboard as the starting point for the daily rotation:
- Open the dashboard link and sign in with your Microsoft work account if prompted. The link opens the SDK PRs view.
- Check the unread activities for management SDK PRs, including new PRs and updates to PRs awaiting follow-up.
- Open the relevant PR on GitHub and continue with Step 2 to read the workflow results and determine the next action. Use the existing PR conversation to avoid duplicating reviews or follow-up requests.
- Once you have reviewed an activity, return to the dashboard and click Mark read.
The dashboard data refreshes every morning. It may not reflect changes made since that refresh, so use the live PR status for review and approval decisions.
If the dashboard is unavailable or appears stale, contact the JS owner and use the live management AutoPR query as a fallback. A missing dashboard update does not mean the queue is empty.
Step 2. Open the PR and find both workflow results
Read the PR description or release plan for the package, target version, and service-team contact. Check existing human decisions, holds, and the latest commit, then find these two distinct outputs:
| Output | Trigger and where to look |
|---|---|
mgmt-review result |
Adding mgmt-review-needed requests an API/tooling review. Find the submitted PR review, its summary, and any inline findings. |
| Next Steps to Merge | The separate mgmt-guidance workflow posts this conversation comment once all CI checks finish. It reports CI/merge blockers and any new-package pipeline setup action. It does not require mgmt-review to have finished. |
If the mgmt-review result is missing and no review is running, apply mgmt-review-needed. The request label is consumed when review starts, so its absence alone does not mean a review needs to be requested again. mgmt-review-in-progress indicates an active review; mgmt-review-added indicates completion, not approval.
If Next Steps to Merge is missing, first check whether CI is still running. Automatic guidance applies to PRs with the Mgmt label and AutoPR in the title. If CI has finished but guidance is missing or failed, ask the JS owner to investigate or manually rerun mgmt-guidance. Missing output is not a clean result.
Step 3. Act on the findings and merge guidance
Use the workflows' findings and links to identify the next action rather than reconstructing their checks.
| Finding or guidance | Reviewer action |
|---|---|
mgmt-review reports API design issues |
Ask the service team to fix the source specification or supported customization and regenerate. Escalate unclear findings or intentional API changes to the JS owner for a decision. |
mgmt-review reports tooling issues |
Send the finding and affected package to the JS owner to coordinate a tooling fix. Do not patch regenerated output just to silence a finding. |
mgmt-review reports no API design issues |
Continue checking merge guidance and human blockers. This is advisory, not approval. |
| Next Steps to Merge lists failed CI checks | Relay the listed fixes and log links to the service team. Escalate pipeline infrastructure or generator failures to the JS owner. Wait for updated checks and guidance after fixes. |
PR has merge conflicts, especially in pnpm-lock.yaml |
No manual resolution is needed; daily automation resolves these conflicts. Wait for the automation update and the resulting CI checks before proceeding. |
| Next Steps to Merge requests new-package pipeline setup | Either the PR author or reviewer can post /azp run prepare-pipelines as a separate PR comment. Check existing requests and pipeline responses before requesting it again; a request alone does not prove setup succeeded. |
| Next Steps to Merge says all CI checks passed and the PR is ready for review | Continue with Step 4. This confirms CI readiness, not that management review is complete or that a human has approved. |
Keep the next action on the existing PR thread. Do not approve while a confirmed issue remains unresolved, and do not bypass required checks or human decisions based on an automated suggestion.
General Copilot comments
Copilot may also review the PR separately from mgmt-review. Some suggestions do not apply to generated management SDKs. Focus this rotation review on the mgmt-review workflow findings; there is no need to implement every general Copilot suggestion or treat each one as a release blocker.
If a general Copilot comment identifies a real issue, route it to the service team or JS owner as appropriate. If the suggestion is inapplicable, resolve the comment thread rather than modifying generated code to satisfy it. Existing blocking human reviews still require resolution.
Step 4. Approve
Approve when the current mgmt-review findings have been addressed or have a justified maintainer disposition, Next Steps to Merge and the live checks show no remaining CI or merge blockers, any requested onboarding setup has succeeded, and no hold or blocking human review remains.
Common service-team questions
| Question | Response |
|---|---|
| "CI is green. Can you approve now?" | Check the current mgmt-review result and remaining human or onboarding actions. Green CI alone is not approval readiness. |
| "Why is this waiting, and can we unblock it?" | Link the specific workflow finding, required service-team action, or JS-owner escalation in Step 3. |
| "Must we make every Copilot change?" | No. Focus on mgmt-review findings and real issues; inapplicable general Copilot suggestions do not require generated-code changes. |
Documentation Migrating To Typespec (`documentation/migrating-to-typespec.md`)
Migrating to TypeSpec
This guide helps service teams migrate their JavaScript SDK generation from OpenAPI specifications and AutoRest to TypeSpec. The migration process involves updating how your SDK is generated, but should not result in breaking changes to the public API surface of your SDK.
Target Audience
This guide is written for service teams who are migrating a high-level client (i.e. a client library with a hand-authored convenience layer) from OpenAPI to TypeSpec.
Overview
TypeSpec is Microsoft's new API specification language that provides better tooling, type safety, and developer experience compared to OpenAPI specifications. When you migrate from AutoRest to TypeSpec:
- Your public SDK API should remain the same - this is not a breaking change for your customers
- The generation process changes - you'll use TypeSpec definitions instead of OpenAPI/Swagger files
- Build scripts and configuration update - new tooling replaces AutoRest
- Internal generated code structure changes - but your hand-written client code adapts with minimal changes
- Customization workflow - generated code is placed in
generated/and then copied tosrc/with merge capabilities
Prerequisites
Before starting the migration, ensure you have:
- TypeSpec definitions ready: Your service's TypeSpec definitions should be complete and merged into the main branch of the Azure REST API specs repository
- Local development environment:
- Node.js LTS version
- Local clone of your fork of azure-sdk-for-js
- Local clone of your fork of azure-rest-api-specs
- Understanding of your current SDK: Know which packages in azure-sdk-for-js belong to your service
Step-by-Step Migration Process
Step 1: Install Required Tools
Install the TypeSpec client generator CLI globally:
npm install @azure-tools/typespec-client-generator-cliFor more information on tsp-client, see the TypeSpec Client Generator CLI documentation
Step 2: Add TypeSpec Configuration
In your package directory (e.g., sdk/your-service/your-package), add the tsp-location.yaml file that points to your TypeSpec definitions in the azure-rest-api-specs repository.
An example tsp-location.yaml file looks like this:
directory: specification/ai/Azure.AI.Projects
commit: a720ec94da68a0d77a691ddd563a4528883638ee
repo: Azure/azure-rest-api-specs
additionalDirectories:
- specification/common-types/resource-managementStep 3: Understand the New Generated Structure
After migration, your package will have this structure:
sdk/your-service/your-package/
├── generated/ # Generated TypeScript code (temporary)
│ ├── api/
│ ├── models/
│ ├── static-helpers/
│ ├── index.ts
│ └── restorePollerHelpers.ts (if LRO is used)
├── src/ # Your source code (generated + customizations)
│ ├── index.ts # Your public exports
│ ├── yourClient.ts # Your hand-written client code
│ ├── api/ # Copied from generated folder by customization (if any are needed)
| └── ...
├── tsp-location.yaml # TypeSpec configuration
└── package.jsonKey differences from AutoRest:
- Generated code initially goes to
generated/(package root) - The customization tool copies files from
generated/tosrc/ tsp-location.yamlreplaces AutoRest configuration- Generated models and client interfaces follow modern TypeScript conventions
Step 4: Update package.json Scripts
Replace your AutoRest generation script with TypeSpec generation and customization scripts:
Before (AutoRest):
{
"scripts": {
"generate": "autorest --typescript swagger/README.md"
}
}After (TypeSpec):
{
"scripts": {
"generate:client": "tsp-client update -d && npm run format && dev-tool customization apply --skip index.ts",
"build": "npm run clean && dev-tool run build-package && dev-tool run extract-api",
"test": "npm run test:node && npm run test:browser",
"test:node": "dev-tool run build-test --no-browser-test && dev-tool run test:vitest"
}
}Step 5: Update Dependencies
Update your package.json dependencies to use the new core packages:
Key dependency changes:
{
"dependencies": {
"@azure-rest/core-client": "^2.3.2",
"@azure/core-lro": "^3.1.0",
"@azure/core-paging": "^1.6.2",
"@azure/core-rest-pipeline": "^1.19.0",
"@azure/core-util": "^1.11.0"
},
"engines": {
"node": ">=22.0.0"
}
}Replace:
@azure/core-client→@azure-rest/core-client@azure/[email protected]→@azure/[email protected]
Step 6: Generate Initial TypeSpec Code
Run the TypeSpec client generation:
cd sdk/your-service/your-package
npm run generate:clientStep 7: Apply Customizations
Use the dev-tool customization command to copy generated files to src/ and merge with existing customizations:
npx dev-tool customization apply --skip index.tsThis command:
- Copies all files from
generated/tosrc/ - Performs 3-way merges with any existing files in
src/ - Preserves your customizations from previous versions
Step 8: Add Helper Functions (If Needed)
For packages with pagination
Add a mapPagedAsyncIterable function to handle pagination mapping:
// src/mappings.ts
export function mapPagedAsyncIterable<T, U>(
iterable: PagedAsyncIterableIterator<T>,
mapper: (item: T) => U
): PagedAsyncIterableIterator<U> {
return {
next() {
return iterable.next().then(({ done, value }) => ({
done,
value: done ? (value as any) : mapper(value),
}));
},
[Symbol.asyncIterator]() {
return this;
},
byPage: (settings) => {
return mapPagedAsyncIterable(iterable.byPage(settings), (page) => ({
...page,
value: page.value.map(mapper),
}));
},
};
}Step 9: Update ESLint Configuration
Update your eslint.config.mjs to handle the new structure:
import azsdkEslint from "@azure/eslint-plugin-azure-sdk";
export default azsdkEslint.config([
{
rules: {
"@typescript-eslint/no-empty-object-type": "warn",
"@azure/azure-sdk/ts-naming-options": "warn",
},
},
{
files: [
"src/api/**/*.ts",
"src/classic/**/*.ts",
"src/models/**/*.ts",
"src/static-helpers/**/*.ts",
"src/restorePollerHelpers.ts", // if LRO is used
],
rules: {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/no-unsafe-function-type": "off",
"tsdoc/syntax": "off",
"spaced-comment": "off",
"no-useless-escape": "off",
"no-unused-expressions": "off",
},
},
]);Step 10: Remove AutoRest Configuration Files
Delete the following files that are no longer needed:
swagger/README.md(or similar AutoRest configuration)- Any custom AutoRest configuration files
- The old
src/generated/directory
Step 11: Test and Validate
Generate and apply customizations:
bashnpm run generate:clientBuild the package:
bashpnpm turbo build --filter=<your-package-name>... --token 1Run tests:
bashpnpm testValidate the API surface: Use API Extractor to ensure your public API hasn't changed unexpectedly.
Best Practices
Using the Customization Workflow
- Always generate to
generated/first - never modify files in this directory directly - Use the customization tool to copy files to
src/and preserve customizations - Keep customizations minimal - prefer TypeSpec definition changes over post-generation modifications
- Test the customization merge - the 3-way merge can help but may require manual conflict resolution
Ongoing Development
After migration, your development workflow becomes:
- Update TypeSpec definitions in azure-rest-api-specs
- Generate new code:
npm run generate:client - Build and test:
pnpm turbo build --filter=<your-package-name>... --token 1 && pnpm test
Version Management
- Update your package version according to Azure SDK versioning guidelines
- Add changelog entries describing the migration (usually marked as internal changes)
- Coordinate with the Azure SDK team for any breaking changes
Common Migration Issues and Solutions
Build Failures
Problem: TypeScript compilation errors after migration.
Solution:
- Run
pnpm installto ensure dependencies are installed - Check that the customization tool completed successfully
- Verify all imports are pointing to the correct files in
src/
Merge Conflicts During Customization
Problem: The customization tool reports merge conflicts.
Solution:
- Examine the conflicted files in
src/ - Resolve conflict markers manually
- Test that the resolution maintains your customizations
- Consider updating TypeSpec definitions to reduce future conflicts
API Surface Changes
Problem: Generated API doesn't match expectations.
Solution:
- Review your TypeSpec definitions and ensure they're complete
- Check emitter options in the generation command
- Use the customization workflow to add necessary adaptations
Getting Help
If you encounter issues during migration:
Check existing documentation:
Consult with the team:
- Post in the TypeSpec Discussion Teams channel
- Tag
@DPG TypeScriptfor JavaScript/TypeScript-specific questions
File issues:
Examples
For complete examples of migrated packages, see:
sdk/keyvault/keyvault-admin/sdk/keyvault/keyvault-keys/sdk/keyvault/keyvault-certificates/
These packages demonstrate the full migration including the customization workflow, helper functions, and updated configurations.
Documentation Migration Guide For Libraries Generated From TypeSpec (`documentation/migration-guide-for-libraries-generated-from-TypeSpec.md`)
Guide for migrating to code generation from TypeSpec (Azure JavaScript SDK)
Terminology used in this guide
- Libraries generated from TypeSpec: Client libraries produced by the TypeSpec Emitter (the new generation toolchain).
Previously referred to as “modular”/“modularized libraries.”- Libraries generated with AutoRest: Client libraries produced by the AutoRest Code Generator (previous generation).
Previously referred to as “HLC”, "Swagger generator", "OpenAPI Generator"
This guide helps developers transition their JavaScript/TypeScript applications to use the TypeSpec generated Azure SDK libraries.
For new customers of the JavaScript/TypeScript SDK (azure-sdk-for-js), please see quick start guide.
Current status
Several packages generated from TypeSpec have already reached General Availability (GA), including @azure/arm-avs, @azure/arm-fabric, @azure/arm-oracledatabase, @azure/keyvault-admin. We are actively working on releasing more packages and eventually cover all Azure services. Please find the latest version of those libraries in npm and give them a try.
Library improvements when generating from *TypeSpec*
We recommend reviewing the complete guide for full details. Compared to libraries generated with Autorest, TypeSpec code generation has following key benefits:
- Subpath exports: Libraries now leverage subpath exports(introduced in Node.js version 12.7) to provide layered APIs. This means developer can access the familiar
Clientat the root level while also using the/apisubpath for fine-grained, operation-level imports. - Bundle size optimization: By leveraging the new
/apisubpath export, developers can selectively import only the operations they need. This approach minimizes the overall library footprint in the application bundle, ensuring that only the required pieces are included. - Long-running operations: Based on customer feedback, we simplified the API to make it cleaner and more ergonomic. Previously, clients exposed two methods for each long-running operation (
beginDoSthandbeginDoSthAndWait), which often felt redundant and confusing. Libraries generated from TypeSpec now provide a single method (doSth) that supports both async and sync usage, reducing complexity while improving developer experience.
How to migrate to libraries generated from TypeSpec
If you’re updating an existing application from libraries generated with AutoRest to libraries generated from TypeSpec, focus on these key areas:
- Long-running operations (LROs) – Updated method signatures and poller behavior
- List operations (paging) – Simplified continuation token handling
Long-running Operations (LROs)
Based on customer feedback, we simplified LROs to make the API cleaner and more ergonomic. Three changes matter for migration:
- Method shape: two methods → one method
- Poller type:
SimplePollerLike→PollerLike(Promise‑like) - Rehydration: option‑based → helper function
Method signature changes
Previously (libraries generated with AutoRest), each LRO exposed two methods (e.g., beginStart and beginStartAndWait).
Now (libraries generated from TypeSpec), there’s a single method that behaves as a poller and can be directly awaited.
AutoRest‑generated (previous)
beginStart(
options?: IntegrationRuntimesStartOptionalParams,
): Promise<
SimplePollerLike<
OperationState<IntegrationRuntimesStartResponse>,
IntegrationRuntimesStartResponse
>
>;
beginStartAndWait(
options?: IntegrationRuntimesStartOptionalParams,
): Promise<IntegrationRuntimesStartResponse>;TypeSpec‑generated (current)
start(options?: IntegrationRuntimesStartOptionalParams): PollerLike<
OperationState<IntegrationRuntimesStartResponse>,
IntegrationRuntimesStartResponse
>;Migrate your usage
// Before (AutoRest-generated)
const result = await beginStartAndWait();
const poller = await beginStart();
const result2 = await poller.pollUntilDone();
// After (TypeSpec-generated)
const result = await start(); // awaiting returns the final result
const poller = start(); // direct access to the poller
await poller.submitted(); // optional: await initial submission
const result2 = await poller; // or: await poller.pollUntilDone()Poller type: `SimplePollerLike` → `PollerLike`
TypeSpec‑generated LROs return a PollerLike, which is also Promise‑like.
| Capability | AutoRest (SimplePollerLike) |
TypeSpec (PollerLike) |
|---|---|---|
| Return final results | pollUntilDone() |
pollUntilDone() |
| Poll | poll() |
poll() |
| Observe progress | onProgress() |
onProgress() |
| Check completion | getOperationState().isCompleted/isDone() |
isDone |
| Stop / check stopped | stopPolling() / isStopped() |
N/A |
| Read current state | getOperationState() |
operationState |
| Access final result | getResult() |
result |
| Serialize poller state | toString() |
serialize() |
| Await initial submission | N/A | submitted() |
Note:
getOperationState(): TStatebecomes the propertyoperationState?: TState. Guard forundefinedbefore access:
// Before
const status = poller.getOperationState().status;
// Now
const status = poller?.operationState?.status;Serialization change
// Before
const serialized = poller.toString();
// Now
const serialized = await poller.serialize();Rehydration (restoring a poller)
Rehydration moved from an operation option (resumeFrom) to a client‑level helper.
Before → After
// Before (AutoRest-generated)
const result = await client.beginStartAndWait({ resumeFrom: serializedState });
// After (TypeSpec-generated)
const result = await restorePoller(client, serializedState, client.start);For more detail, see the core‑lro migration guide:
https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-lro/docs/MIGRATION.md
Quick migration checklist
- Replace
beginXxxAndWait()→await xxx(). - Replace
await beginXxx()→const poller = xxx(). - Replace
poller.toString()→await poller.serialize(). - Replace
poller.getOperationState()→poller.operationState(guard forundefined). - If you previously used
resumeFrom, switch torestorePoller(client, serialized, client.xxx). - If you depended on
stopPolling()/isStopped(), revisit your control flow (these are not exposed onPollerLike).
List operations (paging)
Paging has been simplified in libraries generated from TypeSpec. Two main changes:
- Removed unsupported
maxpagesize - Replaced
getContinuationTokenhelper with directcontinuationTokenproperty
`maxpagesize` removed
The maxpagesize setting was never supported in AutoRest-generated clients, so it has been removed from PageSettings. No behavioral impact is expected.
Continuation token access simplified
Previously, you needed a helper function to extract the continuation token:
AutoRest-generated (previous)
const firstPage = await iter.byPage().next();
const continuationToken = getContinuationToken(firstPage);Now, the token is exposed directly on the page object:
TypeSpec-generated (current)
export type ContinuablePage<TElement, TPage = TElement[]> = TPage & {
/** Token to continue iteration */
continuationToken?: string;
};
const firstPage = await iter.byPage().next();
const continuationToken = firstPage.value.continuationToken;Need help
If you have encountered an issue during migration, please file an issue via GitHub Issues and make sure you add the 'Preview' label to the issue.
Documentation Modular Customization (`documentation/modular-customization.md`)
Modular SDK Customization Guide
This document outlines the merge-based workflow for customizing modular TypeScript SDKs generated by @azure-tools/typespec-ts using the JavaScript customization tool. The tool manages regeneration by applying your customizations to the new generated code using a three-way merge.
Before You Customize
Before customizing generated code, consider whether your change should be made in TypeSpec (client.tsp) instead. TypeSpec customizations are cleaner and survive regeneration. See the TypeSpec Client Customizations Reference for available decorators like @@clientName, @@access, etc.
Setting Up for Customization
If your package has not been set up for customization yet, run the following command from the package directory:
npx dev-tool customization initThis copies the contents of src/ into generated/, establishing the baseline for the customization workflow. It also adds a default customize script to the package's package.json when one is not already present. If generated/ already exists, the package is already set up and the command makes no changes to it.
Folder Structure
The customization tool assumes you have the following folder structure:
my-sdk-package/
generated/
<generated source files (NO src/ underneath this path!)>
src/
<generated code with hand-applied customizations>
test/
<tests run against code in src/>
README.md
package.json
(etc...)The original generated code and the customized code live side-by-side in the package. The modified source code in the src/ directory is the source of truth and is what is ultimately built and shipped to customers. The root-level generated/ directory is not compiled or included in the published package; it is retained as the regeneration baseline used to calculate and merge customizations.
Customization Workflow
Making customizations is straightforward. Directly edit the code in the src/ folder with the customizations you need (rename/add/remove/modify functions, methods, exports, files...). Just treat it as if you're refactoring any other code.
Tip: You can make any customization you want, but it's a good idea to minimize and consolidate the changes that you do make: no reformatting, reordering, etc., outside of the customizations you are making. This will make your life easier when it comes time to regenerate.
If you are making a large number of additions (e.g. helper functions and types), it may be worth separating these to a different file and re-exporting them in the customized file instead of making the additions inline.
Once you are happy with your customizations, check everything in as normal (both the generated/ folder and the src/ folder). You are done!
Regeneration Workflow
As the service specification changes, it will become necessary to update your library. Because the root-level generated/ directory exists, the TypeScript emitter regenerates the source code into that directory instead of src/. DO NOT commit the regenerated changes before applying the customizations.
Note: The customization tool relies on the committed version of
generated/as the previous generated baseline.
Once you've done this, you can now run:
npx dev-tool customization applyThis command will:
- Determine the diff between the customized version of your code (in
src/) and the previous version of the generated code (what was ingenerated/before you ran the command to regenerate) - Attempt a three way merge of the customized code into the new version of the generated code, with the previous version of the generated code as the common parent. The result of the merge attempt will be output into the
src/folder (including any merge conflict markers)
If the merge was successful, review the merged changes and run the package's normal formatting, build, and test checks before committing. If merge conflicts are present, this means that the changes in the generated code conflicted with your customizations. Resolve the conflicts as you would do for any other merge, taking parts from the new generated code and parts from the customized code as makes sense to you. After resolving the conflicts, the src/ folder should be the result of applying your customizations to the new version of the generated code.
When an SDK is generated through the SDK generation pipeline, the pipeline automatically runs the package's customize script (via npm run --if-present customize) after the emitter updates the root-level generated/ directory and before the package is built. Packages without a customize script are skipped. You only need to run the command yourself when regenerating locally.
Documentation Quickstart Guide For Libraries Generated From TypeSpec (`documentation/quickstart-guide-for-libraries-generated-from-TypeSpec.md`)
Getting started – Libraries generated from TypeSpec (Azure JavaScript/TypeScript SDK)
Terminology
- Libraries generated from TypeSpec: the latest Azure SDK libraries produced by the TypeSpec Emitter
(previously referred to as “modularized libraries” or “Modular SDKs”)- Libraries generated with AutoRest: the previous generation produced by the AutoRest Code Generator
Libraries generated from TypeSpec offer layered APIs, smaller bundles, and cleaner ergonomics—while remaining familiar to existing users of the Azure SDK for JavaScript/TypeScript.
Several packages are already GA, including @azure/arm-avs, @azure/arm-fabric, @azure/arm-oracledatabase, and @azure/keyvault-admin. Find the latest versions on npm: https://www.npmjs.com.
This quickstart walks you through managing Azure resources using a TypeSpec‑generated management library. For an overview, see:
https://devblogs.microsoft.com/azure-sdk/azure-sdk-modularized-libraries-for-javascript/
Migrating from older libraries
If you’re upgrading from libraries generated with AutoRest, use the migration guide:
https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/migration-guide-for-libraries-generated-from-TypeSpec.md
Prerequisites
There are several possible approaches to Azure authentication. In this document we would use the application secret authentication. You will need the following values:
- Subscription ID
- Client ID
- Client Secret
- Tenant ID
These values can be obtained from the portal. You could follow the instructions to get Subscription ID. And please refer to this document to get Client ID, Client Secret, and Tenant ID. After you obtain the values, you need to set the following values as your environment variables.
AZURE_CLIENT_IDAZURE_CLIENT_SECRETAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID
Install the package
As an example, to install the latest Azure VMware Solution module, you would run:
npm i @azure/arm-avs@latestWe also recommend installing the authentication package:
npm i @azure/identityAuthentication
Once the environment is setup, all you need to do is to create an authenticated client. The @azure/identity module provides facilities for various ways of authenticating with Azure including client/secret, certificate, managed identity, and more.
Our default option is to use DefaultAzureCredential which will make use of the environment variables we have set and take care of the authentication flow for us.
const credential = new DefaultAzureCredential();For more details on how authentication works in @azure/identity, please see the documentation for @azure/identity.
Creating a Resource Management Client
To begin, determine the target service and create a client to connect to it. In this example, we will use AzureVMwareSolutionAPI as the service. To manage a private cloud resource, you can instantiate the client as follows:
const client = new AzureVMwareSolutionAPI(credential, subscriptionId);Interacting with Azure Resources
Once authenticated and the client is created, you can use it to perform API operations. In resource management scenarios, common operations include creating, updating, reading, and deleting Azure resources. These operations are referred to as "management operations" in Azure.
After identifying the specific operation you want to perform, you can implement it using the management client initialized above.
We will walk through two examples:
- Example 1: Creating a private cloud resource using the Azure VMware Solution client.
- Example 2: Managing a private cloud with the Azure SDK for TypeScript.
Example 1: Create a private cloud resource
Import the packages
import { AzureVMwareSolutionAPI } from "@azure/arm-avs";
import { DefaultAzureCredential } from "@azure/identity";Define some global variables
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const client = new AzureVMwareSolutionAPI(credential, subscriptionId);Create a private cloud
async function privateCloudsCreateOrUpdate(resourceGroupName: string, privateCloudName: string): Promise<void> {
const result = await client.privateClouds.createOrUpdate(resourceGroupName, privateCloudName, {
location: "eastus2",
sku: { name: "AV36" },
identity: { type: "SystemAssigned" },
properties: {
networkBlock: "192.168.48.0/22",
managementCluster: { clusterSize: 4 },
},
tags: {},
});
console.log(result);
}Example 2: Manage a private cloud with the Azure SDK
Import the packages
import { AzureVMwareSolutionAPI } from "@azure/arm-avs";
import { DefaultAzureCredential } from "@azure/identity";Authentication and set up
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const client = new AzureVMwareSolutionAPI(credential, subscriptionId);Update a private cloud
async function privateCloudsUpdate(resourceGroupName: string, privateCloudName: string): Promise<void> {
const result = await client.privateClouds.update(resourceGroupName, privateCloudName, {
identity: { type: "None" },
properties: {
managementCluster: { clusterSize: 4 },
encryption: {
status: "Enabled",
keyVaultProperties: {
keyName: "keyname1",
keyVersion: "ver1.0",
keyVaultUrl: "https://keyvault1-kmip-kvault.vault.azure.net/",
},
},
},
});
console.log(result);
}List all private clouds
async function privateCloudsList(resourceGroupName: string): Promise<void> {
const resArray = new Array();
for await (const item of client.privateClouds.list(resourceGroupName)) {
resArray.push(item);
}
console.log(resArray);
}Get a private cloud
async function privateCloudsGet(resourceGroupName: string, privateCloudName: string): Promise<void> {
const result = await client.privateClouds.get(resourceGroupName, privateCloudName);
console.log(result);
}Delete a private cloud
async function privateCloudsDelete(resourceGroupName: string, privateCloudName: string): Promise<void> {
await client.privateClouds.delete(resourceGroupName, privateCloudName);
}Manage private clouds
async function main() {
const resourceGroupName = "your resource group";
const privateCloudName = "private cloud name";
await privateCloudsCreateOrUpdate(resourceGroupName, privateCloudName);
await privateCloudsList(resourceGroupName);
await privateCloudsGet(resourceGroupName, privateCloudName);
await privateCloudsUpdate(resourceGroupName, privateCloudName);
await privateCloudsGet(resourceGroupName, privateCloudName);
await privateCloudsDelete(resourceGroupName, privateCloudName);
await privateCloudsList(resourceGroupName);
}Code Samples
You can find additional code samples for using the JavaScript/TypeScript SDK in this repo. These samples are located in the samples folder alongside the SDK source code—for example, AVS-related samples are available at https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/avs/arm-avs/samples.
Need help?
- File an issue via GitHub
Issues
Contributing
For details on contributing to this repository, see the contributing
guide.
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 provided by the
bot. You will only need to do this once across all repositories using
our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For
more information see the Code of Conduct FAQ or contact
[email protected] with any additional questions or comments.
Documentation Resolve Pnpm Lock Merge Conflict (`documentation/resolve-pnpm-lock-merge-conflict.md`)
Resolve pnpm-lock Merge Conflicts
Here's the recommended process to resolve merge conflicts of pnpm-lock.yaml in your pull request (assuming your upstream remote is named "upstream"):
- Find out your upstream remote name for
Azure/azure-sdk-for-jsrepository:git remote -v - Get the latest changes from the upstream main branch:
git fetch upstream main - Merge the latest changes from main to local pull request branch:
git merge --no-edit upstream/main - Use your favorite IDE, editor, or Git Client to resolve conflicts for files other than
pnpm-lock.yaml - Check out the main branch version of pnpm-lock.yaml:
git checkout upstream/main -- pnpm-lock.yaml - Refresh dependencies:
pnpm install - Stage the updates:
git add pnpm-lock.yaml - Commit the merge
- Push the commit to your pull request branch, assuming "origin" is the git remote name of your fork,
git push origin
Step 4) to 6) can be combined and enhanced to run under any directory under the repo if you are using a *NIX environment:
git checkout upstream/main `git rev-parse --show-toplevel`/pnpm-lock.yaml && pnpm install && git add `git rev-parse --show-toplevel`/pnpm-lock.yamlDocumentation Rest Clients (`documentation/rest-clients.md`)
Typescript REST Clients
Our REST Clients provide simple, flexible, and reliable connections to REST APIs. Although these clients have raw access to the REST API of a service, we provide some functionality out-of-the-box such as Authentication with Azure Identity through our configurable REST pipeline. REST Clients also provide dev workflow benefits such as a strongly typed experience that helps to navigate the REST API through auto-complete and IntelliSense.
Even though our REST Clients are written in Typescript, JavaScript developers can also benefit from the types provided with the libraries when using modern editors such as VSCode, as they would consume the types to provide IntelliSense and auto-complete.
Here's how to get started:
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient("https://example.org/", new DefaultAzureCredential());
// Send a GET request to https://example.org/hello
const response = await client.path("/hello").get();
if(response.status !== "200") {
throw response.body.error;
}
console.log(response.body);
// {content: "Hello World"}What is a REST Client
Our REST clients provide a simple, reliable connection to raw HTTP. We provide a path function on the client to set the resource to target and methods for each verb it supports. Using path provides strong types to check for the validity of the path, parameters, and response types.
We also expose a pathUnchecked function which allows targeting an arbitrary path. We can provide some help for detecting required path parameters, but responses and body parameters are loosely typed.
Both methods fully harness the power of @azure/core-rest-pipeline and @azure/identity.
The basic structure of calls with REST clients is:
- Initialize your client
- Send the request
- Handle the response
We will go into each step in the following sections
1. Initialize the Client
First, import the client
import ExampleClient from "@azure-rest/example-client";Most clients require authenticating through their credential parameter. Depending on what authentication support your library is using, you can either authenticate with AAD or authenticate with an AzureKeyCredential.
Additionally, most of our clients accept an endpoint parameter at initialization, usually a link to your own resource.
Authenticating with AAD
Authenticating with AAD
Depending on your library, our clients support authenticating with an Azure Active Directory (AAD) token credential. We always recommend using a credential type obtained from the @azure/identity library for AAD authentication. For this example, we use the most common DefaultAzureCredential.
As an installation note, the @azure/identity library is not a dependency of this library. Please run npm install @azure/identity before using AAD authentication
The following code snippet shows you how to authenticate with a DefaultAzureCredential.
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient("https://example.org/", new DefaultAzureCredential());2. Send a request
Once the client has been initialized, we need to set a path to work with. For this, the REST client exposes 2 functions path and pathUnchecked
Path
The path function takes a string as the first parameter and accepts any path documented by the service, this function will help with autocomplete to discover all available paths. It also detects if the path needs parameters and makes them required positional parameters to path. Once the path is set, users can access functions for all the supported verbs on that path
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient ("https://example.org/", new DefaultAzureCredential());
// {name} is detected as a path parameter, so the path function gets a required parameter
const response = await client.path("/hello/{name}", "Brian").get();
// response.body is strongly typed
console.log(response.body);
// {content: "Hello Brian"}PathUnchecked
PathUnchecked function is similar to Path, it takes a path as the first parameter, this can be any arbitrary path. It also detects if the path needs a path parameter and requires them as positional parameters to pathUnchecked. Once the path is set, users can access functions for any verb on that path.
The main difference with path is that pathUnchecked doesn't have strongly typed payload, headers, or query parameters and has any as the response type.
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient ("https://example.org/", new DefaultAzureCredential());
const response = await client.pathUnchecked("/newPath/{resourceName}", "greeter").head();
// response.headers is not strongly typed
console.log(response.headers["content-length"]);
// 1024Payload
Our REST clients have types to help users auto-complete the paths and build request payloads for example
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient ("https://example.org/", new DefaultAzureCredential());
// This REST client has a type defined for the body property that '/hello' takes when
// calling a post. This way you get help from intellisense to find out which properties
// are required or optional in the body.
const response = await client.path("/hello").post({body: {content: "Brian"}});
console.log(response.status);
// 200Headers and Query Parameters
REST clients also have types to help users sending required and optional query parameters and headers
const client = ExampleClient("https://example.org/", new DefaultAzureCredential());
const hello = await client
.path("/hello")
// The defined types for headers and queryParameters enable intellisense
// to suggest required and optional properties and headers
.get({ queryParameters: { top: 5 }, headers: { "request-id": "1234" } });
console.log(hello.body);
// {content: "Hello"}3. Handle the Response
Responses are also strongly typed based on the path
import ExampleClient from "@azure-rest/example-client";
import { DefaultAzureCredential } from "@azure/identity";
const client = ExampleClient ("https://example.org/", new DefaultAzureCredential());
const response = await client.pathUnchecked("/hello/{name}", "Brian").get();
if (response.status !== "200") {
// Type will be narrowed down to the error type
// and auto complete will give you the properties of the
throw response.body.error;
}
// Response is narrowed down to the 200 response shape and users get autocomplete for
// the response body properties
console.log(response.body.content)
// "Hello Brian"Documentation Reviewer Agents (`documentation/reviewer-agents.md`)
Reviewer Agents
This repository includes eight specialized AI reviewer agents that can review
pull requests for specific quality dimensions. Each agent is an expert in its
domain and produces targeted, actionable feedback.
Available Agents
| Agent | Label | Focus |
|---|---|---|
| Archie | architecture-review-needed |
Public API surface — naming, exports, breaking changes, type safety |
| Dash | performance-review-needed |
Runtime performance — latency, memory, bundle size, async patterns |
| Dexter | dependency-review-needed |
Dependency management — version ranges, workspace protocol, new deps |
| Mgmt-review | mgmt-review-needed |
Management-plane SDKs — API design guidelines, tooling validation |
| Scribe | docs-review-needed |
Documentation — README, CHANGELOG, TSDoc, snippets, samples |
| Sentinel | security-review-needed |
Security — credential exposure, injection, unsafe patterns, supply chain |
| Tester | test-review-needed |
Testing — coverage, recorder setup, test quality, browser tests |
On-Demand Analysis Agent
| Agent | Focus |
|---|---|
| Mgmt-breaking-change-analysis | ARM SDK breaking change analysis — categorizes and traces root causes for PRs migrating from Swagger/AutoRest to TypeSpec/emitter. Invoked on-demand; does not trigger via a PR label. |
Using Agents on Pull Requests
Triggering a Review
On GitHub, repository collaborators with label permissions can add the
corresponding label to a pull request to request a review. The workflows
authorize the requester separately; human requesters need write, maintain,
or admin access.
All seven label-triggered reviewers use the shared intake flow described below.
Management review also accepts requests from the explicitly allowlistedazure-sdk-automation[bot]; that bot cannot request the other reviewers.
For example, to request an architecture review:
- Open your pull request on GitHub.
- If you have permission to manage labels, use the Labels sidebar to add
thearchitecture-review-neededlabel. - If you do not have label permissions (for example, you're an external
contributor), leave a comment asking a maintainer to add the label for you. - Once the label is applied, the Archie agent will run and post review
comments directly on the PR.
Collaborators with label permissions can apply multiple labels to trigger
several agents on the same PR. Each agent focuses only on its domain and will
not duplicate findings from other agents.
Shared Review Intake
Review requests use a split intake, routing, and review flow:
one or more review-needed labels
-> PR Review Intake (pull_request, no repository permissions or checkout)
-> PR Review Router (workflow_run, trusted code from the default branch)
-> Selected reviewers (workflow_dispatch, API-only reviews and constrained safe outputs)The router resolves the PR using GitHub's run metadata and commit-to-PR API,
including for fork PRs whose run metadata has an empty pull_requests array.
It does not download artifacts or execute PR code. It verifies the originating
workflow, repository, requester, current head SHA, and requested labels before
dispatching the matching reviewer workflows on the default branch. The intake'srequest job must succeed in the current run attempt; a successful workflow
conclusion with a skipped request job does not activate reviews. If multiple
current PRs share the commit and carry review labels, routing stops before any
dispatch and requires an explicit manual review instead. Labels are
mapped to fixed workflow names in eng/tools/pr-review/review-request.cjs, not
to workflow names supplied by the PR. A failed dispatch is reported without
preventing dispatch of the other requested reviewers.
The intake definition and its job results are not authorization: a PR can change
its workflow YAML. For each pending review label, the router reads GitHub's issue
event history and requires its latest label-change event to be a label addition
by the original run actor's immutable account ID. That event must be at most five
minutes before the run's original creation time, not its runner start or rerun
time. The recorded actor must have the required repository role or reviewer-specific
bot allowance. Stale labels and labels added by other requesters cannot piggyback
on the run. Each matching event ID is passed to its reviewer.
Each reviewer revalidates automatic requests against the same unique PR target
and the same current label-event ID before claiming the request. Removing or
reapplying a label invalidates an already dispatched request; a new label event
requires its own validation. If the intake was delayed beyond the five-minute
event window, reapply the label or use manual dispatch.
Because GitHub does not support conditional label deletion, the worker rechecks
the event immediately before removing the request label and verifies the recorded
removal afterward. If it consumed a replacement request, it restores the missing
label on the current open PR even if new commits arrived, and fails without
starting the review; the requester must reapply the label or
dispatch manually, since the bot's restoration is not review authorization.
When starting, it consumes its request label and replaces it with its in-progress
label. For example, Archie replacesarchitecture-review-needed with architecture-review-in-progress. Runs for the
same reviewer and PR are serialized, while different reviewers run independently.
Duplicate automatic requests skip once their label has been consumed. Review
output is rejected if the PR closes or its head changes during the review.
Reapply the request label or use a manual dispatch to retry a failed or stale
review.
For a manual retry, run the desired reviewer (for example, Architecture Review)
from the Actions UI on the default branch, supplying item_number. head_sha is
optional for manual reviews; when supplied, it must still be the current PR head. Leaverequest_run_id and request_event_id empty: the router supplies these inputs for automatic
requests. Manual reviews do not require the request label.
The intake, router, validation helper, and reviewer workflows must be on the
default branch before this flow can run. Fork workflow approval requirements
still apply to the intake, and pull_request events do not run for PRs with merge
conflicts. A maintainer can use manual dispatch in those cases.
All reviewers inspect PR content as data through the GitHub API. They do not
check out or execute PR code. In particular, Dash evaluates existing benchmark
evidence from unprivileged CI for the reviewed commit rather than running
PR-derived microbenchmarks inside the privileged reviewer. When evidence is
missing, it reports the performance claim as unverified.
To add another reviewer, extend the intake's label filter and the fixed routing
map in eng/tools/pr-review/review-request.cjs, then give that reviewer the same
validated dispatch contract. The bot allowlist is scoped to each reviewer.
What to Expect
Agents post inline review comments on the PR diff (up to 10 comments each).
Each comment includes a severity indicator:
- 🔴 Critical / Breaking / Missing — highest priority to address
- 🟡 Concern / Design issue — should address when feasible
- 🔵 Suggestion — nice to have, optional improvement
These severities are advisory and help authors and maintainers prioritize
follow-up; they do not by themselves block merging.If no issues are found, the agent posts a single summary comment noting that
no significant issues were identified.
Recommended Combinations
| Scenario | Labels to Apply |
|---|---|
| New client library feature | architecture-review-needed, test-review-needed, docs-review-needed |
| Dependency update | dependency-review-needed, security-review-needed |
| Management SDK changes | mgmt-review-needed |
| Performance-sensitive change | performance-review-needed, test-review-needed |
| Full review | architecture-review-needed, performance-review-needed, dependency-review-needed, docs-review-needed, security-review-needed, test-review-needed |
Using Agents Locally in VS Code
The same agents are available as custom agents in VS Code Copilot Chat
(via Copilot CLI). Their definitions live in .github/agents/*.agent.md.
Prerequisites
- VS Code with the
GitHub Copilot
and GitHub Copilot Chat
extensions installed. - The repository must be open as a workspace in VS Code.
Invoking an Agent
In the Copilot Chat panel, use the @ prefix to summon an agent by name:
@archie Review the changes in this branch for API design issues@sentinel Check the staged changes for security vulnerabilities@tester Are there any untested public methods in sdk/storage/storage-blob/src/BlobClient.ts?@dexter Review the dependency changes in sdk/keyvault/keyvault-keys/package.json@scribe Check that the README and CHANGELOG are consistent with the code changes@dash Look for performance regressions in the current diff@mgmt-review Review this management SDK PR for API design guideline complianceAvailable Agent Names
| Chat command | Agent |
|---|---|
@archie |
Architecture / API design review |
@dash |
Performance review |
@dexter |
Dependency review |
@mgmt-review |
Management SDK review |
@mgmt-breaking-change-analysis |
ARM SDK breaking change analysis (on-demand) |
@scribe |
Documentation review |
@sentinel |
Security review |
@tester |
Test coverage and quality review |
Tips
- Agents have access to file reading, code search, and bash tools — they can
explore the codebase to answer questions. - You can ask an agent to review specific files, a git diff, or the entire
branch. - Agents follow the detailed guidance and references in
.github/prompts/. - Agents respect the same scoping rules as in CI: they ignore
src/generated/,snippets.spec.ts, formatting issues, and domains outside their expertise.
Agent Configuration
Agent definitions and their detailed review guidelines are stored in:
.github/
├── agents/ # Agent identity and checklist
│ ├── archie.agent.md
│ ├── dash.agent.md
│ ├── dexter.agent.md
│ ├── mgmt-breaking-change-analysis.agent.md
│ ├── mgmt-review.agent.md
│ ├── scribe.agent.md
│ ├── sentinel.agent.md
│ └── tester.agent.md
├── prompts/ # Detailed review and analysis guidance
│ ├── architecture-review-guidelines.md
│ ├── performance-review-guidelines.md
│ ├── dependency-review-guidelines.md
│ ├── mgmt-breaking-change-analysis-guidelines.md
│ ├── mgmt-breaking-change-patterns.md
│ ├── mgmt-review-guidelines.md
│ ├── documentation-review-guidelines.md
│ ├── security-review-guidelines.md
│ └── test-review-guidelines.md
└── workflows/ # CI workflow triggers
├── pr-review-intake.yml # Unprivileged PR review signal
├── pr-review-router.yml # Trusted label-to-reviewer dispatcher
├── archie.md / archie.lock.yml
├── dash.md / dash.lock.yml
├── dexter.md / dexter.lock.yml
├── mgmt-review.md / mgmt-review.lock.yml
├── scribe.md / scribe.lock.yml
├── sentinel.md / sentinel.lock.yml
└── tester.md / tester.lock.yml.github/agents/*.agent.md— Defines the agent persona, checklist, scope,
and output format. Used by both VS Code Copilot Chat and CI workflows..github/prompts/*.md— Comprehensive review and analysis guidance plus
supporting references used by agents. Edit these to update review criteria or
analysis behavior..github/workflows/*.md— Agentic Workflow source files that define the
CI trigger, permissions, and tools. Compiled to.lock.ymlviagh aw compile.eng/tools/pr-review/review-request.cjs— Shared deterministic routing and
request validation for all seven label-triggered reviewers. Its tests run
locally withnode --test eng/tools/pr-review/review-request.test.cjs.eng/tools/pr-review/ci.yml— Tool-owned Azure Pipelines definition for
the routing tests, following the dev-tool CI pattern. Branch and PR triggers
are path-filtered to the routing tools and reviewer workflows, rather than
running in every SDK package's Analyze job. Register a pipeline against this
YAML file in Azure DevOps to enable automatic runs.
To modify an agent's behavior, edit the corresponding .agent.md and/or
guidelines file. For CI trigger changes, edit the workflow .md file and
recompile with:
gh aw compile <agent-name>Declare the publishing job's validation dependency underjobs.safe_outputs.needs, not safe-outputs.needs: validation runs before the
agent, whereas the latter marks dependencies as handler-only in the pinned
compiler and clears their values from the agent-side safe-output config.
The routing tests check that both the agent-side and publishing configs retain
the same validated PR number and head SHA.
Documentation Run Your Azure Ts Js Samples With Multiple Node Versions (`documentation/run-your-azure-ts-js-samples-with-multiple-node-versions.md`)
Run Azure SDK Samples with Multiple Node Versions
dev-tool is a great resource for Azure SDK in JS developers to automate many of the testing/debugging workflows. This post shows how to use it to run SDK samples using different node versions. This is especially useful when you want to validate the artifact built by the release pipeline against different node versions.
- make sure
dockeris installed on your machine. If you are using a UNIX-based OS, you might want to make your user account part of the docker admin group so you do not have to usesudoto invoke it. You can do so by executingusermod -aG docker. - make sure the docker daemon is up and running.
- download the artifact created by the release pipeline and save the path for it.
- navigate to your package directory in the command line and make sure your samples directory and
.envfile exist - run
npx dev-tool samples check-node-versions --artifact-path <artifact path> --node-versions "20,22"or you can use any other combination of node versions including specifying the full semver for it.
The tool will spin up docker containers based on images where each contain one of the requested node versions. Each container will install all samples dependencies and then run all TS and JS samples. If an error occurs in one of the samples, it will be shown in red.
Documentation Sdk Breaking Changes Guide Migration (`documentation/sdk-breaking-changes-guide-migration.md`)
Azure JavaScript SDK Breaking Changes Review and Resolution Guide for TypeSpec Migration
The Azure JavaScript SDK generally prohibits breaking changes unless they result from service behavior modifications. This guide helps you identify, review, and resolve breaking changes that may occur in new SDK versions due to migration of service specifications from Swagger to TypeSpec.
Breaking changes can be resolved by:
- Client Customizations
Client customizations should be implemented in a file named client.tsp located in the service's specification directory alongside the main entry point main.tsp.
import "./main.tsp";
import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
// Add your customizations here- TypeSpec Configuration Changes
TypeSpec configuration changes should be made in the tspconfig.yaml file located in the service's specification directory. This file is used to configure the TypeSpec compiler and client generator options. For example:
options:
"@azure-tools/typespec-ts":Breaking Changes That Can Be Resolved with Client Customizations or Configurations
1. Client Naming Changes
Changelog Pattern:
- Deleted Class AdvisorManagementClientSpec Pattern:
The client name is derived from the service namespace, not from the @service title.
@service(#{ title: "AdvisorManagementClient" })
namespace Microsoft.Advisor;Breaking: The generated client class is named after the namespace (e.g. AdvisorClient), so the previously released client (AdvisorManagementClient) is deleted, breaking any code that imported or constructed it.
Reason: Naming convention difference. TypeSpec derives the client class name from the namespace name rather than the @service title that the Swagger emitter used.
Resolution:
Use the client name from the removal entry (the name after Deleted Class) as the target name to restore:
@@clientName(Microsoft.Advisor, "AdvisorManagementClient", "javascript");2. Renamed Model
Changelog Pattern:
A model emits a single TypeScript interface, so a rename is a paired removal/addition of an interface:
- Removed Interface Vm
- Added Interface VMSpec Pattern:
Find the model using the added name; casing or namespace differences cause the rename.
model VM extends Common.Resource {}Breaking: A model is renamed (e.g. Vm → VM), so code referencing the old exported interface name breaks.
Reason: Naming convention difference. The TypeSpec emitter preserves the original spec casing (VM), while the Swagger emitter produced the de-cased Vm. This also applies to models renamed by namespace changes.
Resolution:
@@clientName(VM, "Vm", "javascript");3. Renamed Enum
Changelog Pattern:
An extensible enum emits both a string-typed type alias and a Known* values enum, so a rename is a paired removal/addition of both:
- Removed Type Alias IpFamily
- Removed Enum KnownIpFamily
- Added Type Alias IPFamily
- Added Enum KnownIPFamilySpec Pattern:
Find the enum using the added name; casing or namespace differences cause the rename.
union IPFamily {
string,
IPv4: "IPv4",
IPv6: "IPv6",
}Breaking: An enum (and its paired Known* type) is renamed (e.g. IpFamily → IPFamily), so code referencing the old exported name breaks.
Reason: Naming convention difference. The TypeSpec emitter preserves the original spec casing (IPFamily), while the Swagger emitter produced the de-cased IpFamily. This also applies to enums renamed by namespace changes.
Resolution:
@@clientName(Microsoft.ContainerService.IPFamily, "IpFamily", "javascript");4. Naming Changes with Numbers
Changelog Pattern:
Enum values whose names are numbers get an emitter-generated prefix instead of the word-based name:
- Enum KnownAlertSeverity no longer has value Zero
- Enum KnownAlertSeverity no longer has value One
- Enum KnownAlertSeverity added value AlertSeverity0
- Enum KnownAlertSeverity added value AlertSeverity1Spec Pattern:
union AlertSeverity {
int32,
`0`: 0,
`1`: 1,
}Breaking: Numeric enum member names (0, 1) are not valid identifiers, so the emitter prefixes them with the enum type name (AlertSeverity0, AlertSeverity1), replacing the word-based names (Zero, One) the Swagger emitter produced.
Reason: Emitter change. The Swagger emitter converted numeric names to words; the TypeSpec emitter preserves the numeric source name and prefixes it to make a valid identifier.
Resolution:
@@clientName(AlertSeverity.`0`, "Zero", "javascript");
@@clientName(AlertSeverity.`1`, "One", "javascript");5. Operation Naming Changes
Changelog Pattern:
An operation is renamed: the released name is removed (breaking) and the new emitter-generated name is added (Features Added), confirming a rename rather than a deletion:
# Breaking Changes
- Removed operation ManagementGroups.listDescendants
# Features Added
- Added operation ManagementGroups.getDescendantsSpec Pattern:
The operation is a list operation (marked with @list), but the spec op name starts with get:
interface ManagementGroups {
@list
@get
@action("descendants")
getDescendants is Extension.ActionSync<...>;
}Breaking: The released SDK exposed the operation as listDescendants; the TypeSpec emitter names it after the spec op (getDescendants), so callers of ManagementGroups.listDescendants break.
Reason: Emitter change. The TypeSpec emitter generates the operation name from the spec op name, which may differ from the name the Swagger-based SDK exposed.
Resolution:
Restore the released operation name (from the removal entry) with @@clientName:
@@clientName(ManagementGroups.getDescendants, "listDescendants", "javascript");6. Removal of Flattened Properties
Changelog Pattern:
A model loses several direct properties (which move under a properties bag):
- Interface Identity no longer has parameter clientId
- Interface Identity no longer has parameter principalId
- Interface Identity no longer has parameter tenantId
- Interface Identity has a new required parameter propertiesSpec Pattern:
model Identity {
properties?: IdentityProperties;
}
model IdentityProperties {
clientId?: string;
principalId?: string;
tenantId?: string;
}Breaking: Convenience properties that used to be flattened onto the model (e.g. identity.clientId) are now nested under identity.properties.clientId, so direct property access breaks.
Reason: Emitter/spec-structure change. TypeSpec preserves the real REST API hierarchy instead of flattening the properties bag as the Swagger emitter did.
Resolution:
Restore the flattened surface with Legacy.flattenProperty:
#suppress "@azure-tools/typespec-azure-core/no-legacy-usage" "SDK backward compatibility"
@@Azure.ClientGenerator.Core.Legacy.flattenProperty(Identity.properties, "javascript");7. Removal of `begin*` Long-Running Operation Methods
Changelog Pattern:
Each long-running operation loses its released begin<Op> and begin<Op>AndWait methods:
- Removed operation ApiDefinitions.beginExportSpecification
- Removed operation ApiDefinitions.beginExportSpecificationAndWait
- Removed operation ApiDefinitions.beginImportSpecification
- Removed operation ApiDefinitions.beginImportSpecificationAndWait
- Removed operation Services.beginExportMetadataSchema
- Removed operation Services.beginExportMetadataSchemaAndWaitBreaking: The released SDK exposed each long-running operation through the begin<Op> / begin<Op>AndWait method pair (returning SimplePollerLike<OperationState<T>, T> and Promise<T>). By default the TypeSpec emitter generates only the new single-method form (e.g. exportMetadataSchema), so callers of the begin* methods break.
Reason: Emitter change. The TypeSpec (@azure-tools/typespec-ts) emitter no longer emits the begin* poller methods by default.
Resolution:
Set compatibility-lro: true in the @azure-tools/typespec-ts options of tspconfig.yaml. This re-emits the deprecated begin<Op> / begin<Op>AndWait methods (and beginList<Op>AndWait for LRO + paging operations) alongside the new method, restoring the released API surface:
options:
"@azure-tools/typespec-ts":
compatibility-lro: trueBreaking Changes That Can Be Accepted
These generally cannot (or should not) be resolved through client customizations or configurations and are shipped in a new major version.
1. Common Types Upgrade
Changelog Pattern:
- Interface Resource no longer has parameter etag
- Interface Resource no longer has parameter location
- Interface Resource no longer has parameter tags
- Type of parameter error of interface ErrorResponse is changed from ErrorDefinition to ErrorDetailBreaking: Common infrastructure types (Resource, Operation, OperationDisplay, error models, etc.) are replaced by their latest ARM common-types equivalents. Some properties move to a different base type (e.g. etag/location/tags move from Resource to TrackedResource), and some type names change (e.g. ErrorDefinition → ErrorDetail, Display → OperationDisplay).
Reason: Common types are upgraded to their latest versions during TypeSpec migration.
Resolution: Accept these breaking changes.
2. Removal of Unreferenced Models
Changelog Pattern:
- Removed Interface Resource
- Removed Interface ProxyResource
- Removed Interface ErrorResponse
- Removed Interface ErrorAdditionalInfoBreaking: Public interfaces that are not referenced by any operation or model are removed.
Reason: Spec-structure change. Unreferenced types are dropped during TypeSpec migration.
Resolution: Accept these breaking changes.
3. Removal of List-Result (Pageable) Models
Changelog Pattern:
- Removed Interface MeshRevisionProfileList
- Removed Interface OutboundEnvironmentEndpointCollectionBreaking: Response-wrapper models for list/paging operations (whose only properties are value and optionally nextLink) are no longer generated. In addition, maxPageSize is removed from PageSettings, and the continuation token is now returned directly from the byPage() result.
Reason: Emitter change. The new JS SDK exposes list operations via PagedAsyncIterableIterator and does not emit the wrapper model.
Resolution: Accept these breaking changes.
4. Options Interface Renamed (`OptionalParams` → `Options`)
Changelog Pattern:
- Removed Interface XxxCreateOptionalParamsBreaking: An operation's options interface is renamed from XxxOptionalParams to XxxOptions.
Reason: Emitter change.
Resolution: Accept these breaking changes.
5. Response / Header Wrapper Removal
Changelog Pattern:
- Removed Interface XxxResponse
- Removed Interface XxxHeaders
- Removed Interface XxxNextResponseBreaking: The old SDK generated XxxResponse wrapper types (including response headers) plus XxxHeaders and XxxNextResponse types. The new SDK returns the raw model directly.
Reason: Emitter change.
Resolution: Accept these breaking changes.
6. Discriminator Inline Union → `string` / Named Type
Changelog Pattern:
- Type of parameter type of interface Activity is changed from "Container" | "Execution" | ... to string
- Type alias "ActivityUnion" has been changedBreaking: Two related effects share one root cause. First, the old SDK generated inline literal unions for discriminator properties (e.g., type: "KeyVault" | "AppConfig"); the new SDK generates string or a named type alias (e.g., type: AzureResourceType where AzureResourceType = string). This also applies when Swagger had type: string without x-ms-enum but AutoRest inferred a fixed union from discriminator subtypes. Second, cascading from that change, every union type alias that includes the affected discriminator base as a constituent (e.g. ActivityUnion) also changes, surfacing as Type alias "XxxUnion" has been changed.
Reason: Emitter change.
Resolution: Accept these breaking changes.
7. Removal of `string`-Backed Type Aliases and Their `Known*` Enums
Changelog Pattern:
- Removed Type Alias AmazonRdsForOraclePartitionOption
- Removed Type Alias AvroCompressionCodec
- Removed Enum KnownAmazonRdsForOraclePartitionOption
- Removed Enum KnownAvroCompressionCodecBreaking: The old SDK generated a named type alias (e.g. type XxxOption = string) for constrained string properties, plus a companion KnownXxx enum listing the known values. The new SDK inlines these properties as string directly, so both the type alias and its paired Known* enum are removed together.
Reason: Emitter change. The TypeSpec emitter represents extensible/constrained string values as plain string rather than a named alias + Known* enum pair.
Resolution: Accept these breaking changes.
Documentation Sdk And Ai (`documentation/sdk_and_ai.md`)
Using the Azure SDK for JavaScript/TypeScript with AI tools
AI-powered coding tools can help you write, understand, and debug applications
that use the Azure SDK. This page lists common options and integrations available
today.
AI coding tools
Several tools support AI-assisted development with the Azure SDK:
| Tool | Description |
|---|---|
| VS Code | Code editor with built-in AI features and Copilot integration |
| GitHub Copilot | AI code completion and chat inside VS Code, Visual Studio, JetBrains IDEs, and GitHub.com |
| Copilot CLI | AI assistance for shell commands and Azure CLI usage |
| Claude Code | Terminal-based AI coding agent from Anthropic |
| Cursor | AI-first code editor with chat and inline editing |
| Aider | Terminal-based AI pair programming tool |
This is not an exhaustive list — most AI coding tools that support chat or code
generation can work with the Azure SDK.
Azure MCP Server
The Azure MCP Server
exposes Azure resource operations to AI tools through the
Model Context Protocol (MCP). This lets an AI coding agent
query, create, and manage Azure resources directly during a conversation.
See the getting started guide
for setup instructions.
Azure SDK skills
The Microsoft skills marketplace provides Azure SDK skills that give AI agents
context about Azure SDK conventions, code generation, and package management.
These skills work with CLI-based AI tools that support plugins, such as Claude Code
and Copilot CLI.
Install the Microsoft skills marketplace:
/plugin marketplace add Microsoft/skillsInstall the Azure SDK skills plugin:
/plugin install azure-sdk-typescript@skillsVerify installation:
/plugin listUpdate the plugin:
/plugin update azure-sdk-typescript@skillsSkills provide domain-specific context that helps AI tools generate more
accurate code to use Azure SDK.
For operational Azure tasks (managing resources, querying services), see the
Azure skills repository.
Further reading
- Azure SDK for JavaScript documentation
- Azure SDK design guidelines for TypeScript
- Azure MCP Server
- Agent guidance for this repo (
AGENTS.md)
Feedback
If you have feedback on your AI experience with the Azure SDK for JavaScript,
open an issue on the repository.
Documentation Steps After Generations (`documentation/steps-after-generations.md`)
What to do after generating the SDK code
The generated code is not enough to release at once and you need to update it for better user experience. Please follow the steps to check the code:
Build your project
After this finishes, you will see the generated code in src folder in your {PROJECT_ROOT}. Refer the development workflows guide for more details if you'd like to know more.
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1Customizing the generated code
If the generated code does not fit your needs, you can use the JavaScript customization workflow to make edits. This should only be done in coordination with the JavaScript architects. For more information, see the Modular (DPG) Customization Guide.
Generate CHANGELOG.md
Install and build js-sdk-release-tools from the repository root:
package_manager=$(node -p "require('./package.json').packageManager")
npm_registry=$(npm config get registry)
npm install "$package_manager" --registry="$npm_registry"
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1After you build your package, run
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js <your-package-path>Here is the example
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js sdk/advisor/arm-advisorImprove README.md document
A minimal README.md is generated by TypeScript emitter and you could improve README.md file per your service. To learn more about README, see below example README file.
- RLC example: Maps Route README.md
- DPG example: OpenAI README.md
How to write test for DPG/RLC/MPG
In order to release it, we need to add some tests for it to make sure we are delivering high quality packages. After generation you will see a sampleTest.spec.ts file in your {PROJECT_ROOT}/test/public folder, which has an empty test and you could add/update test cases against your own services.
See the Javascript Codegen Quick Start for Test for information on how to write and run tests for the Javascript SDK.
Write the test
You could follow the basic RLC test interaction and recording example to write your test step by step.
Also, you could refer to the below examples for more cases:
- RLC example: Maps Route Testing
- DPG example: OpenAI Testing
- MPG example: Containerservice Fleet Testing
Run the test
Now, you can run the test like this. If you are the first time to run test, you need to set the environment variable
TEST_MODEtorecord. This will generate recordings for your test they could be used inplaybackmode.On Linux, you could use
exportto set env variable:shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 export TEST_MODE=record && pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.On Windows, you could use
SET:shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 SET TEST_MODE=record&& pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.You can also run the
playbackmode test if your apis don't have breaking changes and you've already done the recording before.On Linux, you could use below commands:
shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 export TEST_MODE=playback && pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.On Windows, you can use:
shellpnpm turbo build --filter=${PACKAGE_NAME}... --token 1 SET TEST_MODE=playback&& pnpm test # this will run live test and generate a recordings folder, you will need to submit it in the PR.
How to write samples
We highly encourage you to write some valid samples for your customer to get start your service with libraries. You may author TypeScript samples under the samples-dev folder. For quick start, you can use sample-dev template as reference and update the relevant information for your service such as package-name, sample code, description, etc.
To learn more, you could refer to the below samples:
- DPG sample: the samples of OpenAIClient
- RLC sample: the samples of MapsRouteClient
After the samples-dev folder change is finished, you will need to change the tsconfig.json to make sure the dev samples can be compiled and build correctly.
You will need to add this part to the compilerOptions of your tsconfig.json file so that the Samples engine could resolve the sample-dev package against the source code of the SDK.
"paths": { "@azure/agrifood-farming": ["./src/index"] }And change the "include" part to
"include": ["./src/**/*.ts", "./test/**/*.ts", "samples-dev/**/*.ts"],Then, we provide tools to automatically change it into workable samples in both TypeScript and JavaScript. And, you just need to add a sampleConfiguration in your package.json.
You will need to add a sample configuration section in your package.json file and put the following content into it.
"//sampleConfiguration": {
"productName": "A description of your services",
"productSlugs": ["azure"],
"disableDocsMs": true,
"apiRefLink": "<the-link-to-your-service-on-learn.microsoft.com>"
}Now, you can generate both JavaScript and TypeScript workable samples with the following commands.
cd ${PROJECT_ROOT}
npx dev-tool samples publish -fYou will see the workable samples in the ${PROJECT_ROOT}/samples folder.
Format both the generated code and manual code
After you have finished the generation and added your own tests or samples, you can use the following command to format the code.
cd ${PROJECT_ROOT} && pnpm formatAlso, we'll recommend you to run lint command to analyze your code and quickly find any problems.
cd ${PROJECT_ROOT} && pnpm lintAnd we could use lint:fix if there are any errors.
cd ${PROJECT_ROOT} && pnpm lint:fixAdding a new third-party runtime dependency
Shipped client and management libraries (those with "sdk-type": "client" or"sdk-type": "mgmt" in package.json) may not take a new third-party runtime
dependency without approval. This is enforced in CI by the@azure/azure-sdk/ts-package-json-approved-dependencies ESLint rule (part ofpnpm lint), which validates the dependencies field of every in-scopepackage.json against a central allow-list. devDependencies andpeerDependencies are not checked, and samples, tests, perf tests, and
utility/tooling packages are out of scope.
First-party dependencies are always allowed and do not need to be listed:@azure/, @azure-rest/, @azure-tools/, @microsoft/, and @typespec/.
To take a new third-party runtime dependency:
- Get the dependency approved (size, license, maintenance, and supply-chain
considerations — see the dependency review guidance). - Add it to
eng/approved-third-party-dependencies.yml:Add it under
allowedif every package should be able to use it.Add a scoped entry under
exceptionsif only specific packages should be
able to use it:yamlexceptions: - dependency: some-package packages: - "@azure/your-package"
Entries are normally exact package names. A scope wildcard of the form@scope/* is also supported (in both allowed and exceptions) and matches
every package in that npm scope — for example, "@opentelemetry/*" underallowed approves the entire OpenTelemetry ecosystem for every package.
If a package declares a runtime dependency that is neither first-party nor
approved, pnpm lint (and CI) will fail with a message pointing at the
allow-list.
How to create package
Now, we can use the exact same steps to build a releasable artifact.
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1
cd <your-sdk-folder>
export TEST_MODE=record && pnpm test
pnpm packYou may send this artifact to your customer if your services are still in private preview and some customers want to try it out.
Create/Update the ci.yaml
Now, if everything looks good to you, you can submit a PR in azure-sdk-for-js repo with all the changes you made above. Before you do that, you need to add/update the ci.yml file. Depends on whether there's already one in your package folder.
If there's no such file, then you can add the following template.
# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file.
trigger:
branches:
include:
- main
- release/*
- hotfix/*
paths:
include:
- sdk/purview/
pr:
branches:
include:
- main
- feature/*
- release/*
- hotfix/*
exclude:
- feature/v4
paths:
include:
- sdk/purview/
extends:
template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml
parameters:
ServiceDirectory: purview
Artifacts:
- name: azure-agrifood-farming # azure-agrifood-farming for DPG; azure-rest-agrifood-farming for RLC
safeName: azureagrifoodfarming # azureagrifoodfarming for DPG; azurerestagrifoodfarming for RLCPlease change the paths.include value as your own project path, and change the Artifacts name and safeName into yours.
If there's already a ci.yml file in your project path, then the only thing you need to do is to add the Artifacts name and safeName of yours into that ci.yml.
Please notice the Artifacts name should align with your package name. Here, the package name is @azure/agrifood-farming so the relevant Artifacts name is azure-agrifood-farming.
Prepare PR
TypeScript emitter can only help you generate SDK code, there is something you need to update manually.
CHANGELOG.md
CHANGELOG can help customers know the change of new version quickly, so you need to update it according to the change of this new version. It is also necessary to update release date like 1.0.0-beta.1 (2022-11-11) (rough time is fine and no need to be very accurate).
Version Number
You shall update the version number according to semantic versioning rule.
Test recordings
After writing and running test cases, you need to push the recordings to assets repo. Please refer to push recording guide to push recordings.
Fix CI for PR
You may meet the CI failures after submitting the PR, so please refer to Troubleshoot CI Failure to fix it.
Resolve conflicts for PR
Latest changes to the main branch may introduce merge conflicts of pnpm-lock.yaml file to the PR, so please try to follow these steps to resolve conflicts:
- checkout to main branch and pull the latest code
- checkout the PR's branch
- run
git merge main - run
git checkout origin/main pnpm-lock.yaml - run
pnpm install - push your changes into your PR
If you meet other files' conflicts, you need to resolve them case by case.
Steps to check out the `AutoPR` branch locally
AutoPR is created by a bot based on the azure-sdk/azure-sdk-for-js repo. Since this is a different fork from Azure/azure-sdk-for-js, you cannot directly check out the branch or push changes to it via origin. Here are three solutions to check out the AutoPR branch locally.
Note for Options 2 & 3: Replace
{REMOTE_NAME}with any name you prefer (e.g.,azure-sdk), and{BRANCH_NAME}with the branch name shown in the AutoPR (e.g.,sdkauto/@azure-arm-connectedcache-5699155).
Option 1: Use VS Code GitHub Pull Requests extension ⭐ (recommended)
This is the easiest way — no git commands needed. The GitHub Pull Requests extension can check out any PR branch directly in VS Code, including PRs from forks.
- Install the GitHub Pull Requests extension in VS Code if you haven't already.
- Open the GitHub Pull Requests panel from the Activity Bar (the GitHub icon).
- Find the AutoPR by its PR number or search by branch name.
- Click the Checkout button next to the PR.
VS Code will automatically fetch the branch from the fork and create a local tracking branch for you.
Option 2: Fetch the branch and create a local tracking branch
This option lets you make local commits and easily push them back to the remote branch.
- Add the remote (skip if already added):bash
git remote add {REMOTE_NAME} https://github.com/azure-sdk/azure-sdk-for-js.git - Fetch the specific branch:bash
git fetch {REMOTE_NAME} {BRANCH_NAME} - Create a local branch that tracks the remote branch:bash
git checkout -b {LOCAL_BRANCH_NAME} --track {REMOTE_NAME}/{BRANCH_NAME}
Full example:
git remote add azure-sdk https://github.com/azure-sdk/azure-sdk-for-js.git
git fetch azure-sdk sdkauto/@azure-arm-connectedcache-5699155
git checkout -b pr/azure-sdk/36933 --track azure-sdk/sdkauto/@azure-arm-connectedcache-5699155Option 3: Fetch all branches and checkout
This option fetches all branches from the remote, which takes longer but is useful if you need to access multiple branches.
- Add the remote (skip if already added):bash
git remote add {REMOTE_NAME} https://github.com/azure-sdk/azure-sdk-for-js.git - Fetch all branches from the remote:bash
git fetch {REMOTE_NAME} - Checkout the branch:bash
git checkout {BRANCH_NAME}
Full example:
git remote add azure-sdk https://github.com/azure-sdk/azure-sdk-for-js.git
git fetch azure-sdk
git checkout sdkauto/@azure-arm-connectedcache-5699155CC dpg-devs for review
Please add below comment in your PR to include dpg-devs to review your PR timely.
cc @Azure/dpg-devs for awarenessRelease
Prepare your SDK for release. The necessary approvals, guidance for testing, documentation, and release pipelines is described in your release plan. More information about the Azure SDK Release Tool is here
Documentation Using Azure Identity (`documentation/using-azure-identity.md`)
Using @azure/identity with Microsoft Entra ID
This document intends to demystify the configuration and use of Microsoft
Entra ID with the Azure SDK libraries.
Microsoft Entra ID implements the OAuth 2.0 and OpenID Connect
standards
to provide authentication for users and services who may be granted access to
Azure services.
Table of Contents
- Getting Started
- Understanding the Credential Types
- Choosing a Credential Type
- Permissions and Consent
- Credential Types in @azure/identity
Getting Started
Any application that must support authentication through Microsoft Entra ID needs two things: a tenant and an app registration created for that
tenant.
A "tenant" is basically instance of Microsoft Entra ID associated with your
Azure account. You can follow the instructions on this quickstart guide for
setting up a
tenant
to check if you have a Microsoft Entra tenant already or, if not, create one.
Once you have a tenant, you can create an app registration by following this
quickstart guide for app
registrations.
Your app registration holds the configuration for how your application will
authenticate users and services, so it's very important to it set up correctly
before using any of the credential types below. The section on each credential
will indicate which configuration settings it needs and how to use them.
Should my App be Single or Multi Tenant?
One decision you will need to make up front when registering your app is whether
it will be single or multi-tenant, and more importantly, if the multi-tenant app
registration also supports personal Microsoft accounts. The primary deciding
factor is whether your application will be used only by users and services
inside of your Microsoft Entra tenant or if you'd like other organizations and individuals
to use it.
The app registration quickstart
guide
gives a helpful breakdown for the various tenancy options in the "Supported
account types" documentation.
You can easily change your application from single to multi-tenant and vice
versa after it is created, but you cannot currently change it to support
personal Microsoft accounts after it's already created.
Understanding the Credential Types
Microsoft Entra ID provides a variety of authentication flows that
serve different use cases and application types. A primary differentiator
between these flows is whether the "client" that initiates the flow is running
on a user device or on a system managed by the application developer (like a web
server). The Microsoft Authentication
Library
documentation describes this distinction as public versus confidential
clients.
Most of the credential types are strictly public or confidential as they serve a
specific purpose, like authenticating a backend service for use with storage
APIs. Some credentials may be both public or confidential depending on how you
configure them. For example, the authorization code
flow
can be initiated from a mobile application or from within a web application
running in a server.
Choosing a Credential Type
Here's a high level decision tree to help with choosing an appropriate
credential for your application:
Is the application deployed to a server?
Do the Azure services you want to use support authentication with managed identities?
If so, use the
ManagedIdentityCredentialIf not, use the
EnvironmentCredential
Do you want your application to pick the appropriate credential type based on the
environment?- Use the
DefaultAzureCredential
- Use the
Is the application deployed to a user device or running in the browser?
Can the user's device display an authentication site in a browser window
or web control?If so, use the
AuthorizationCodeCredentialorInteractiveBrowserCredentialIf not, use the
DeviceCodeCredential
Permissions and Consent
The identity platform provides an authorization model for Azure services with
two types of
permissions:
- Application permissions authorize an application to access resources
directly. Administrator consent must be granted to your application. - Delegated permissions authorize an application to access resources on
behalf of a specific user. The user may grant permission to your application
unless the permission requires administrator consent.
If you are only using confidential credentials, you should only need to be
concerned with application permissions. If you will be authenticating users
with a public credential, you must configure API permissions for the Azure
service you need to access (Key Vault, Storage, etc) so that user accounts can
be authorized to use them through your application. The quickstart guide for
configuring API
permissions
explains how to do this in detail.
User-Granted Consent
When a user is being authenticated to access a service that is configured with
delegated permissions, they may be presented with a consent screen that asks
whether they want to grant your application permission to access resources on
their behalf. An example of this consent flow can be found in the consent
framework documentation
page.
An administrator can also grant consent for your application on behalf of all
users. In this case, users may never see a consent screen. If you'd like to
make it easy for an administrator to grant access to all users, follow the
instructions in the admin consent endpoint request
documentation.
There are some cases where a user may not be allowed to grant consent to an
application. When this occurs, the user may have to speak with an administrator
to have the permissions granted on their behalf. The user consent
troubleshooting
page
provides more details on the consent errors a user might encounter.
Credential Types in @azure/identity
ClientSecretCredential and ClientCertificateCredential
The ClientSecretCredential implements the client credentials
flow
to enable confidential clients, like web services, to access Azure resources.
To use this credential, you will need to create a client secret using the
"Certificates & secrets" page for your app registration.
The ClientCertificateCredential implements the same client credentials flow,
but instead uses a certificate as the means to authenticate the client. You must
generate your own PEM-formatted certificate for use in this flow and then
register
it
in the "Certificates & secrets" page for your app registration. Using a
certificate to authenticate is recommended as it is generally more secure than
using a client secret.
NOTE: At this time,
@azure/identityonly supports PEM certificates.
For both of these credentials, tenantId and clientId are required parameters.
The clientSecret or certificatePath parameters are also required depending
on which credential you are using.
UsernamePasswordCredential
The UsernamePasswordCredential follows the resource owner password credential
flow
to authenticate public or confidential clients. To use this credential, you
will need the tenantId and clientId of your app and a username andpassword of the user you are authenticating.
Since this credential uses a user account to authenticate, the user or an
administrator will need to grant consent to your application before they can
successfully authenticate.
This credential type supports multi-tenant app registrations so you may passorganizations as the tenantId to enable users from any organizational tenant
to authenticate.
Generally speaking, we do not recommend using this credential type when other
more secure credential types are available. Handling the user's password
directly is a major security risk.
NOTE: This credential type does not work with personal Microsoft accounts or
multi-factor authentication at this time. See the
documentation
for more information.
EnvironmentCredential
The EnvironmentCredential looks for well-known environment variable names to
determine how it should authenticate. It effectively acts as a wrapper for theClientSecretCredential, ClientCertificateCredential orUsernamePasswordCredential depending on which environment variables are
present.
This credential is meant to be used either on the developer's local machine or
in applications that are deployed to Azure in VMs, App Service, Functions,
Containers, etc.
In all cases, the AZURE_TENANT_ID and AZURE_CLIENT_ID environment variables
are expected to be present to use this credential as they identify your
application. The following environment variables will then be tried in order:
AZURE_CLIENT_SECRET- A client secret to be used withClientSecretCredentialAZURE_CLIENT_CERTIFICATE_PATH- The path to a PEM-formatted certificate file
in the deployment environment to be used with theClientCertificateCredentialAZURE_USERNAMEandAZURE_PASSWORD- The username and password pair to be
used with theUsernamePasswordCredential
Consult the documentation for the Azure service to which you deploy your
application to learn how to configure environment variables for your deployment.
ManagedIdentityCredential
The ManagedIdentityCredential takes advantage of authentication endpoints that
are hosted within the virtual network of applications deployed to Azure virtual
machines, App Services, Functions, Container Services, and
more.
One important distinction of this credential compared to the others is that it
does not require an app registration. This authentication scheme relates to
the actual Azure resources to which your code is deployed rather than the
application itself.
To enable your application to authenticate with this credential, you will need
to grant one of two types of managed identity to the resource that runs your
code:
- A system-assigned
identity
which uniquely identifies your resource - A user-assigned
identity
which can be assigned to your resource (and others)
Once your resource has an identity assigned, that identity can be granted access
to other Azure services through role assignments.
If you have configured your resource to use a system-assigned identity, you can
just create an instance of ManagedIdentityCredential without any
configuration. For user-assigned identities, you must provide the clientId of
the managed identity you wish to use for authentication.
More information on configuring and using managed identities can be found in the
Managed identities for Azure
resources
documentation. There is also a list of Azure
services
that have been tested to confirm support for managed identity authentication.
InteractiveBrowserCredential
The InteractiveBrowserCredential follows the authorization code
flow
with PKCE to enable interactive authentication. In the browser it uses a popup
window, and in Node.js it opens the system browser and listens on a local
redirect URI. It is useful for single-page web applications (SPAs) and
desktop/CLI applications that need to authenticate users to access Azure
resources and APIs directly.
To use this credential successfully, your app registration will need to be
configured with a redirect URI in the Redirect URIs section of the
Authentication page.
For browser-based apps, check both the Access tokens and ID tokens options under Implicit grant, and set redirectUri to match your app's redirect URI.
For Node.js apps, configure a "Mobile and desktop applications" redirect URI (see https://learn.microsoft.com/entra/identity-platform/scenario-desktop-app-registration#redirect-uris). The redirectUri option is not required in Node.js unless you need to override the default.
DeviceCodeCredential
The DeviceCodeCredential follows the device code authorization
flow
which enables input-constrained devices, like TVs or IoT devices, to
authenticate by having the user enter a provided "device code" into an
authorization site that the user visits on another device.
This credential is treated as a public client and must have the Treat
application as a public client setting enabled in the Default client type
section of the Authentication page of your app registration.
AuthorizationCodeCredential
The AuthorizationCodeCredential follows the authorization code
flow
which enables server-hosted web applications, native desktop and mobile
applications, and web APIs to access resources on the user's behalf.
This credential requires that the developer have an HTTP(S) endpoint exposed
which can receive the authentication response redirect. The URI at which you
host this endpoint must be added to the Redirect URIs list on the
Authentication page of your app registration. If you are developing
locally, you can also add a redirect URI for your development endpoint
(e.g. http://localhost:8080/authresponse).
A complete example of hosting your own authentication response endpoint can be
found in the authorization code manual test.
DefaultAzureCredential
The DefaultAzureCredential is a specialization of the ChainedTokenCredential
which tries each of the following credential types in order until one of them
succeeds:
EnvironmentCredentialWorkloadIdentityCredentialManagedIdentityCredentialVisualStudioCodeCredentialAzureCliCredentialAzurePowerShellCredentialAzureDeveloperCliCredentialBrokerCredential(requires@azure/identity-broker)
This credential type is ideal when one of the credentials in the chain will work
in the current environment, whether it's your local development or a production
server.
When you use this credential you will need to make sure that you've
properly configured your deployment environment to support one of the chained
credential types above otherwise authentication will fail.
Eng Tools Js Sdk Release Tools Docs Automation Pipeline (`eng/tools/js-sdk-release-tools/docs/automation-pipeline.md`)
JS SDK Automation Pipeline
This document describes how the JS SDK release automation pipeline works, covering the entry point, SDK type and run mode determination, folder cleanup logic per mode, and detailed generation steps for each SDK type.
Table of Contents
- Overall Architecture
- Overall Flow & Utilities
- SDK Generation Steps
- Folder Cleanup Logic
- Changelog & Version Bump (Common)
- Output JSON Structure
1. Overall Architecture
CLI Entry Points
The package exposes the following CLI commands (defined in package.json bin):
AutoPR / Release Pipeline
| Command | Parameters | Description |
|---|---|---|
code-gen-pipeline |
--inputJsonPath, --outputJsonPath, --use, --typespecEmitter, --sdkGenerationType, --local |
Main automation entry point; used by the AutoPR release pipeline to generate and package SDK code end-to-end |
hlc-code-gen-for-pipeline |
(same as above) | Alias for code-gen-pipeline (legacy HLC-specific name) |
HLC generation is disabled for non-local runs through both pipeline commands. An HLC configuration causes an error and exit code 1, with guidance to migrate to TypeSpec-based modular client generation. For services that already have a TypeSpec project, configure the @azure-tools/typespec-ts emitter in tspconfig.yaml and use that project instead of the Swagger README configuration. See the TypeSpec migration guidance.
Local HLC generation and builds remain supported when --local is supplied or the input JSON specifies "runMode": "local". The --local flag is for local development only; do not set it in automation pipelines. The standalone hlc-code-gen CLI is unchanged. RLC and MLC generation are unaffected by this restriction.
Dev Loop Experience
| Command | Parameters | Description |
|---|---|---|
update-changelog |
--sdkRepoPath, --packagePath |
Regenerates CHANGELOG.md only (does not bump version) |
update-version |
--sdkRepoPath, --packagePath, --releaseType, --version, --releaseDate |
Updates the version in package.json, updates version information in CHANGELOG.md without regenerating changelog content, and updates user-agent/version metadata in source |
generate-ci-yaml |
--sdkRepoPath, --packagePath |
Creates or updates the ci.yml / ci.mgmt.yml file for a package |
Local Code Generation
| Command | Parameters | Description |
|---|---|---|
hlc-code-gen |
see hlc.md | Local HLC (management-plane) code generation from swagger/README |
rlc-code-gen |
see llc.md | Local RLC (data-plane) code generation |
changelog-tool |
see changelog-tool.md | Generate changelog by comparing api.md against published npm package |
SDK Type Enum (`SDKType`)
| Type | Value | Description |
|---|---|---|
HighLevelClient |
'HighLevelClient' |
HLC — Management plane SDK (autorest-based, corresponds to @azure/arm-*) |
RestLevelClient |
'RestLevelClient' |
RLC — REST level client (autorest or TypeSpec based) |
ModularClient |
'ModularClient' |
MLC — Modular client (TypeSpec-based, typically management plane) |
Run Mode (`RunMode`)
| Mode | Value | Description |
|---|---|---|
Release |
'release' |
Official production release pipeline |
Local |
'local' |
Developer local run (skips node_modules backup) |
SpecPullRequest |
'spec-pull-request' |
Triggered by spec PR for automated validation |
Batch |
'batch' |
Bulk SDK generation across multiple packages |
2. Overall Flow & Utilities
Overall Flow Diagram
/* Detailed source-code truncated for AI context efficiency. */Utility Operations Summary
| Operation | Function | Required / Optional | Description |
|---|---|---|---|
| Backup node_modules | backupNodeModules() |
✅ Required (non-local) | Recursively rename node_modules → node_modules_backup |
| Restore node_modules | restoreNodeModules() |
✅ Required (non-local) | Recursively rename back to node_modules |
| Format code | formatSdk() |
✅ Required | npm run format |
| Update snippets | updateSnippets() |
✅ Required | dev-tool run update-snippets |
| Lint fix | lintFix() |
⚠️ Optional (Release/Local only) |
npm run lint:fix |
| Customize package | customizeCodes() |
⚠️ If present | npm run --if-present customize |
| Clean up package dir | cleanUpPackageDirectory() |
✅ Required | Cleanup strategy based on SDK type + RunMode |
| Specify API version | specifyApiVersionToGenerateSDKByTypeSpec() |
⚠️ Optional | Modify api-version field in tspconfig.yaml |
4. Folder Cleanup Logic
The cleanup behavior is determined by SDK type and run mode. The core function is cleanUpPackageDirectory().
Two run mode categories are used internally:
- Pipeline modes (
ReleaseorLocal): Perform targeted cleanup to preserve manually authored files. - Automation modes (
SpecPullRequestorBatch): Perform a full cleanup to ensure a pristine environment.
4.1 HighLevelClient (HLC) — Management Plane
HLC packages correspond to @azure/arm-* and are generated by the generateMgmt() entry point using autorest.
The pipeline entry point permits HLC generation only for local runs (--local or "runMode": "local"). Non-local runs fail before HLC generation or package cleanup.
| Run Mode | Cleanup Behavior | Details |
|---|---|---|
Local (or --local) |
No explicit cleanup | Autorest overwrites generated files in-place. No directory-level cleanup is performed by the tool. |
| Non-local | Not applicable | HLC generation is disabled; the pipeline entry point throws an error. |
Note: The HLC generation path does not perform any directory-level cleanup. The partial cleanup behavior that preserves
test/andassets.jsonapplies only when an MLC or RLC generation detects that the target package was previously generated as HLC (i.e., converting from HLC to MLC).
4.2 RestLevelClient (RLC) — Data Plane REST Client
RLC packages are identified as "sdk-type": "client" without modular markers.
| Run Mode | Cleanup Behavior | Details |
|---|---|---|
Release / Local |
Skip cleanup (tool-level) | The tool does not delete the package directory. Before writing new files, the emitter automatically empties the sources directory. |
SpecPullRequest / Batch |
Conditional cleanup | Performs full cleanup for standard packages. Packages with root generated/ skip cleanup to preserve merge-based customizations. |
Note on generation path: The emitter behavior described below applies only to the TypeSpec path (Path A in §3.2). When RLC packages are generated from Swagger via autorest (Path B), the TypeSpec emitter is not involved — autorest directly overwrites files under
--output-folderwithout the priority-based source directory selection described below.
What the emitter clears before writing (TypeSpec path only)
The emitter selects the sources directory using this priority order:
src/generated/— if it exists (customization pattern: hand-authored code insrc/, generated code insrc/generated/)generated/— if it exists (alternative customization pattern)src/— otherwise
The selected folder is fully emptied before new source files are written.
Files and folders preserved across emitter runs:
| File / Folder | Behavior |
|---|---|
test/ |
Preserved — if a test folder already exists, the emitter skips test file generation |
package.json |
Updated in-place (dependencies, exports, and version fields are merged) |
README.md |
Updated in-place; fully regenerated only if the client name changed |
CHANGELOG.md |
Not touched (managed by js-sdk-release-tools) |
assets.json |
Not touched |
swagger/ |
Not touched |
src/ (hand-authored files) |
Preserved only when the customization pattern is active (src/generated/ exists) — the emitter clears only src/generated/, leaving the rest of src/ intact |
samples-dev/ |
Cleared only when sample generation is enabled in tspconfig.yaml |
Note on
clearOutputFolder: Iftspconfig.yamlsetsclearOutputFolder: true, the emitter wipes the entire package directory (preserving onlyTempTypeSpecFiles/) before emptying the sources directory. This overrides the skip-cleanup behavior above.
4.3 ModularClient (MLC) — Modular Client
MLC packages are identified by is-modular-library: true in tspconfig.yaml.
Cleanup sub-types within MLC:
Management Plane MLC — Converting from HLC (existing HLC `package.json` found)
When a management plane package previously generated via autorest (HLC) is being regenerated as a ModularClient, the old package directory retains HLC markers in package.json. Cleanup is based on run mode:
| Run Mode | Cleanup Behavior | Details |
|---|---|---|
Release / Local |
Partial cleanup | Preserves test/ and assets.json. All other generated files, including src/, are removed and regenerated. |
SpecPullRequest / Batch |
Full cleanup | Removes the entire package directory. |
Management Plane MLC — New or existing ModularClient package
When generating a brand-new package (no directory yet), or regenerating a package that is already a ModularClient in its package.json:
| Run Mode | Cleanup Behavior | Details |
|---|---|---|
Release / Local |
Skip cleanup (tool-level) | The tool does not delete the package directory. The emitter empties the sources directory before writing new files (see §4.2 for details). |
SpecPullRequest / Batch |
Skip cleanup | Same — no directory-level cleanup by the tool; emitter handles source regeneration. |
Note: If the package directory does not exist yet, no cleanup action is taken.
Data Plane MLC
| Run Mode | Cleanup Behavior | Details |
|---|---|---|
Release / Local |
Skip cleanup (tool-level) | Same as RLC: the tool does not delete the package directory; the emitter empties the sources directory. See §4.2 for the full file-level breakdown. |
SpecPullRequest / Batch |
Conditional cleanup | Performs full cleanup unless root generated/ exists, in which case cleanup is skipped to preserve merge-based customizations. |
4.4 Summary Table
| SDK Type | Plane | Source State | Release / Local |
SpecPullRequest / Batch |
|---|---|---|---|---|
HighLevelClient |
Management | N/A | Local only: no cleanup; Release: error | Error: HLC generation disabled |
RestLevelClient |
Data | N/A | Skip (emitter cleans src/) |
Full cleanup; skip for root generated/ |
ModularClient |
Management | Converting from HLC | Partial: keep test/, assets.json |
Full cleanup |
ModularClient |
Management | New or already MLC | Skip (emitter handles) | Skip (emitter handles) |
ModularClient |
Data | N/A | Skip (emitter cleans src/) |
Full cleanup; skip for root generated/ |
For HLC, --local also enables local generation regardless of the input runMode; it must not be used in automation pipelines.
3. SDK Generation Steps
3.1 HighLevelClient (HLC) — Management Plane SDK
These steps apply only to local HLC generation. The pipeline CLI requires --local or "runMode": "local"; otherwise, it throws an error directing service teams to TypeSpec-based modular client generation. The standalone hlc-code-gen CLI continues to call generateMgmt() directly and is not affected by the pipeline restriction.
Processing Steps
| Step | Required | Operation | Command / Details | Code Link |
|---|---|---|---|---|
| 1. Code Generation | ✅ Required (unless skipGeneration) |
Run autorest to generate code | autorest --version=3.9.7 --typescript --modelerfour.lenient-model-deduplication --azure-arm --head-as-boolean=true --license-header=MICROSOFT_MIT_NO_VERSION --generate-test --typescript-sdks-folder={sdkRepo} {readmeMd} + optional --tag=package-{apiVersion} --use={use} |
[generateMgmt.ts#L50] |
| 2. Find Changed Packages | ✅ Required | getChangedPackageDirectory() |
Uses git diff to find changed package directories after generation |
[generateMgmt.ts#L73] |
| 3. Modify Test/Sample Config | ✅ Required | changeConfigOfTestAndSample() |
Modify tsconfig.json to skip compiling test/ and sample/ directories |
[generateMgmt.ts#L88] |
4. Write _meta.json |
✅ Required (non-skipGeneration) | Write code generation metadata | Contains commit, readme, autorest_command, repository_url, release_tool, etc. |
[generateMgmt.ts#L90] |
| 5. Generate/Modify CI YAML | ✅ Required (non-skipGeneration) | modifyOrGenerateCiYml() |
Create or update ci.mgmt.yml |
[generateMgmt.ts#L105] |
| 6. Install Dependencies | ✅ Required | pnpm | pnpm install |
[generateMgmt.ts#L124] |
| 7. Lint Fix | ⚠️ Optional | lintFix() — only in Release / Local mode |
npm run lint:fix |
[generateMgmt.ts#L139] |
| 8. Build | ✅ Required | Compile package (excluding test/sample) | pnpm build --filter {packageName}... |
[generateMgmt.ts#L127] |
| 9. Generate Changelog & Bump Version | ✅ Required (non-skipGeneration) | generateChangelogAndBumpVersion() |
Compare api.md between npm published version and local; detect breaking changes; generate changelog; bump version |
[generateMgmt.ts#L130] |
| 10. Pack | ✅ Required | Generate .tgz package |
pnpm run --filter {packageName}... pack |
[generateMgmt.ts#L133] |
| 11. Update Snippets | ✅ Required | updateSnippets() |
dev-tool run update-snippets |
[generateMgmt.ts#L152] |
| 12. Modify README | ✅ Required (non-skipGeneration) | changeReadmeMd() |
Update package README.md |
[generateMgmt.ts#L155] |
| 13. Add ApiView Info | ✅ Required | addApiViewInfo() |
Find temp/**/*.api.json file path and add to outputJson |
[generateMgmt.ts#L182] |
| 14. Restore Config | ✅ Required (non-skipGeneration) | changeConfigOfTestAndSample(Revert) |
Restore original tsconfig.json configuration |
[generateMgmt.ts#L203] |
3.2 RestLevelClient (RLC) — REST Level Client
There are two generation paths based on the source: TypeSpec project or Swagger/README project.
Path A: TypeSpec Project (`options.typespecProject` exists)
| Step | Required | Operation | Command / Details | Code Link |
|---|---|---|---|---|
| 1. Get Target Package Dir | ✅ Required | getGeneratedPackageDirectory() |
Parse emitter-output-dir / service-dir + package-dir from tspconfig.yaml |
[generateRLCInPipeline.ts#L46] |
| 2. Clean Up Package Dir | ✅ Required | cleanUpPackageDirectory() |
Cleanup strategy based on runMode and SDK type (see Section 4) |
[generateRLCInPipeline.ts#L48] |
| 3a. Code Gen (command mode) | ✅ Conditional (sdkGenerationType = command) |
TypeSpec direct compile | ① Copy emitter-package.json → ② npm install → ③ Update tspconfig.yaml → ④ npx tsp compile {source} --emit @azure-tools/typespec-ts --arg "js-sdk-folder={sdkRepo}" |
[generateRLCInPipeline.ts#L68] |
| 3b. Code Gen (script mode) | ✅ Conditional (sdkGenerationType = script) |
tsp-client generation | npm --prefix eng/common/tsp-client exec -- tsp-client init --update-if-exists --debug --tsp-config {tspconfig.yaml} --local-spec-repo {tspDefDir} --repo {repoUrl} --commit {commitId} |
[generateRLCInPipeline.ts#L82] |
Note: Steps 3a and 3b are mutually exclusive alternatives. The pipeline executes only one of them per run, based on
sdkGenerationType.
Path B: Swagger Project (no `typespecProject`)
| Step | Required | Operation | Command / Details | Code Link |
|---|---|---|---|---|
| 1. Find Autorest Config | ✅ Required | Search SDK repo for existing autorest config | Scans sdk/{RP}/{package}-rest/swagger/README.md files looking for a matching require URL or input-file path that references the incoming spec. The PR-comment-based config generation path was removed as a security fix (#14743). |
[generateRLCInPipeline.ts#L93] |
| 2. Code Generation | ✅ Required | Run autorest | autorest --version=3.9.7 {README.md} --output-folder={packagePath} + optional --use --multi-client=true --tag=package-{apiVersion} |
[generateRLCInPipeline.ts#L149] |
Common Post-generation Steps (both paths)
| Step | Required | Operation | Command / Details | Code Link |
|---|---|---|---|---|
| 4. Generate/Modify CI YAML | ✅ Required | modifyOrGenerateCiYml() |
Create or update ci.yml |
[generateRLCInPipeline.ts#L180] |
| 5. Modify Test/Sample Config | ✅ Required | changeConfigOfTestAndSample() |
Skip test/sample compilation | [generateRLCInPipeline.ts#L186] |
| 6. Install Dependencies | ✅ Required | pnpm | pnpm install |
[generateRLCInPipeline.ts#L204] |
| 7. Customize Package | ⚠️ If present | customizeCodes() |
npm run --if-present customize (runs the package customize script when present, no-op otherwise) |
[generateRLCInPipeline.ts#L215] |
| 8. Lint Fix | ⚠️ Optional | lintFix() — Release / Local mode only |
npm run lint:fix |
[generateRLCInPipeline.ts#L218] |
| 9. Build | ✅ Required | Compile package | pnpm build --filter {packageName}... (Release/Local) or pnpm run --filter {packageName}... build (other modes) |
[generateRLCInPipeline.ts#L208] |
| 10. Pack | ✅ Required | Generate .tgz |
pnpm run --filter {packageName}... pack |
[generateRLCInPipeline.ts#L210] |
| 11. Format Code | ✅ Required | formatSdk() |
npm run format |
[generateRLCInPipeline.ts#L239] |
| 12. Update Snippets | ✅ Required | updateSnippets() |
dev-tool run update-snippets |
[generateRLCInPipeline.ts#L240] |
| 13. Generate Changelog & Bump Version | ✅ Required | generateChangelogAndBumpVersion() |
Same as HLC | [generateRLCInPipeline.ts#L249] |
| 14. Add ApiView Info | ✅ Required | addApiViewInfo() |
Find *.api.json files |
[generateRLCInPipeline.ts#L260] |
| 15. Restore Config | ✅ Required | changeConfigOfTestAndSample(Revert) |
Restore original tsconfig.json |
[generateRLCInPipeline.ts#L279] |
3.3 ModularClient (MLC) — Modular Client
Processing Steps
| Step | Required | Operation | Command / Details | Code Link |
|---|---|---|---|---|
| 1. Get Target Package Dir | ✅ Required | getGeneratedPackageDirectory() |
Parse emitter-output-dir / service-dir + package-dir from tspconfig.yaml |
[modularClientPackageGenerator.ts#L28] |
| 2. Generate ignore-links | ⚠️ Optional | ignoreLinkGenerator() |
For first-time published packages: update eng/ignore-links.txt |
[modularClientPackageGenerator.ts#L30] |
| 3. Record Original Version | ✅ Required | getNpmPackageInfo() |
Read existing package.json version to restore after code generation |
[modularClientPackageGenerator.ts#L33] |
| 4. Clean Up Package Dir | ✅ Required | cleanUpPackageDirectory() |
Cleanup strategy based on runMode + SDK type (see Section 4) |
[modularClientPackageGenerator.ts#L35] |
| 5. Specify API Version | ⚠️ Optional | specifyApiVersionToGenerateSDKByTypeSpec() |
Modify api-version field in tspconfig.yaml if apiVersion is specified |
[modularClientPackageGenerator.ts#L37] |
| 6. Code Generation | ✅ Required | generateTypeScriptCodeFromTypeSpec() |
npm --prefix eng/common/tsp-client exec -- tsp-client init --update-if-exists --debug --tsp-config {tspconfig.yaml} --local-spec-repo {typeSpecDir} --repo {repoUrl} --commit {commitId} |
[typeSpecUtils.ts#L13] |
| 7. Restore Version | ✅ Required | updatePackageVersion() |
Restore package.json version to the pre-generation original to avoid version drift |
[typeSpecUtils.ts#L49] |
| 8. Build Package | ✅ Required | buildPackage() — contains multiple sub-steps |
See sub-steps below | [modularClientPackageGenerator.ts#L41] |
| 9. Generate Changelog & Bump Version | ✅ Required | generateChangelogAndBumpVersion() |
Same as HLC; skipped for Data Plane packages | [modularClientPackageGenerator.ts#L47] |
| 10. Try Build Samples | ⚠️ Conditional | tryBuildSamples() |
dev-tool run build:samples. Blocking rules: Management plane — failure is a hard error in Release mode only; treated as a warning in all other modes (SpecPullRequest, Batch, Local). Data plane — always treated as a warning (never blocks). Known gap (#14610): sample failures are not caught during spec PR validation (SpecPullRequest mode), so a package that passes spec PR checks can still fail at release time. |
[modularClientPackageGenerator.ts#L49] |
| 11. Update Package Result | ✅ Required | updateNpmPackageResult() |
Read package.json name/version into PackageResult |
[modularClientPackageGenerator.ts#L56] |
| 12. Create Release Artifact | ✅ Required | createArtifact() |
pnpm run --filter {packageName}... pack, generates .tgz |
[modularClientPackageGenerator.ts#L63] |
| 13. Create/Update CI YAML | ✅ Required | createOrUpdateCiYaml() |
Create or update ci.mgmt.yml |
[modularClientPackageGenerator.ts#L67] |
`buildPackage()` Sub-steps Detail
| Sub-step | Required | Command / Operation | Code Link |
|---|---|---|---|
| pnpm install | ✅ Required | pnpm install |
[rushUtils.ts#L127] |
| Customize package | ⚠️ If present | npm run --if-present customize — runs the package customize script when present, no-op otherwise |
[rushUtils.ts#L139] |
| Lint fix | ⚠️ Optional | npm run lint:fix — only in Release / Local mode |
[rushUtils.ts#L142] |
| turbo build | ✅ Required | pnpm turbo build --filter {packageName}... --token 1 (build errors are warnings for Data Plane) |
[rushUtils.ts#L150] |
| Extract ApiView info | ✅ Required | Find temp/**/*-node.api.json or temp/**/*.api.json |
[rushUtils.ts#L157] |
| Test package | ⚠️ Optional | pnpm run test:node — TEST_MODE=record; failure does not block |
[rushUtils.ts#L169] |
| Format | ✅ Required | npm run format |
[rushUtils.ts#L170] |
| Update snippets | ✅ Required | dev-tool run update-snippets |
[rushUtils.ts#L171] |
5. Changelog & Version Bump (Common)
Note: Changelog generation is skipped for Data Plane (
ModularClient/DataPlane) packages.
Core Logic
1. Query npm registry for published package info (tryGetNpmView)
2. Determine if first release (shouldTreatAsFirstRelease)
├── First Release:
│ → makeChangesForFirstRelease(): use initial changelog template,
│ set version to 1.0.0-beta.1 or 1.0.0
│
└── Non-first Release:
├── Download published stable version (npm pack {packageName}@{stableVersion})
├── Determine if track2 or track1:
│
├── Track2 Previously Released:
│ ├── Compare old and new api.md (using DifferenceDetector)
│ ├── Generate changelog (using ChangelogGenerator)
│ ├── Calculate new version (getNewVersion):
│ │ - Has breaking change → bump minor/major
│ │ - No changes → bump patch
│ │ - Beta version → bump preview
│ └── makeChangesForReleasingTrack2(): write CHANGELOG.md and update package.json version
│
└── Track1 Previously Released:
└── makeChangesForMigrateTrack1ToTrack2(): generate migration changelogKey Sub-operations
| Operation | Command |
|---|---|
| Download and extract npm package | npm pack {packageName}@{version} → tar -xzf {tgz} |
| Get original version | git show HEAD:{package.json path} |
| Clean up temp files | Delete changelog-temp/ directory |
6. Output JSON Structure
Final structure written to --outputJsonPath:
{
"packages": [
{
"packageName": "@azure/arm-xxx",
"version": "1.0.0",
"language": "JavaScript",
"path": ["sdk/xxx/arm-xxx", "ci.mgmt.yml"],
"packageFolder": "sdk/xxx/arm-xxx",
"typespecProject": ["specification/xxx/XXX"],
"readmeMd": ["specification/xxx/resource-manager/readme.md"],
"artifacts": ["sdk/xxx/arm-xxx/azure-arm-xxx-1.0.0.tgz"],
"apiViewArtifact": "sdk/xxx/arm-xxx/temp/arm-xxx.api.json",
"changelog": {
"content": "### Breaking Changes\n...",
"hasBreakingChange": true,
"breakingChangeItems": ["..."]
},
"result": "succeeded",
"installInstructions": { "full": "npm install ..." }
}
],
"language": "JavaScript"
}Eng Tools Js Sdk Release Tools Docs Changelog Tool (`eng/tools/js-sdk-release-tools/docs/changelog-tool.md`)
Overview - How Changelog Tool Works
Note: changelog tool only supports hlc now.
In order to compute this changelog, we do the following steps:
- Input the package path.
- Get the package name and download the latest release from NPM.
- If not found it in NPM, it's first release and generate changelog for first release. If found it, check whether package in npm is track2 whem releasing mgmt sdk.
- If the npm package is mgmt track1, generated changelog for migrating track1 to track2. If not, generate change log by compare api.md extracted by API Extractor.
- Changelog Tool parses the typescript blocks in the api.md to ASTs, and extracts useful information from the ASTs.
- Changelog Tool compares extracted information and generates the changelog.
Convert the TypeScript Block in api.md to AST And Extract Useful information from the AST
The data structure of the extracted information:
export class TSExportedMetaData {
public typeAlias = {};
public operationInterface = {};
public modelInterface = {};
public enums = {};
public classes = {};
}This is an example of the extracted information:
{
"typeAlias": {
"AttachedDatabaseConfiguration": {
"type": {
"inherits": ["ProxyResource"],
"typeLiteralDeclarations": [
{
"properties": [
{
"name": "location",
"visibility": 2,
"type": "string",
"isOptional": true,
"isStatic": false,
"start": 290,
"end": 308
}
],
"methods": [],
"intersectionDeclarations": [],
"typeLiteralDeclarations": [],
"name": "",
"start": 284,
"end": 606
}
],
"name": "",
"start": 268,
"end": 606
},
"methods": [],
"name": "AttachedDatabaseConfiguration",
"isExported": true,
"start": 224,
"end": 607
}
}
}Compare Extracted Information And Generate the Changelog
we have a set of rules that we apply for each entry in the diff. Currently template rules are:
## Features
Added operation group {}
Added operation {}.{}
Added Interface {}
Added Class {}
Added Type Alias {}
Interface {} has a new optional parameter {}
Class {} has a new optional parameter {}
Add parameters of {} to TypeAlias {}
Type Alias {} has a new parameter {}
Added Enum {}
Enum {} has a new value {}
## Breaking Changes
Removed operation group {}
Removed operation {}.{}
Operation {}.{} has a new signature
Class {} has a new signature
Interface {} no longer has parameter {}
Interface {} has a new required parameter {}
Parameter {} of interface {} is now required
Class {} no longer has parameter {}
Class {} has a new required parameter {}
Parameter {} of class {} is now required
Delete parameters of {} in TypeAlias {}
Type Alias {} no longer has parameter {}
Type Alias {} has a new parameter {}
Parameter {} of Type Alias {} is now required
Removed Enum {}
Enum {} no longer has value NoneEng Tools Js Sdk Release Tools Docs Hlc (`eng/tools/js-sdk-release-tools/docs/hlc.md`)
Azure Track2 JS SDK Tools
This package contains 3 tools used for azure track2 mgmt js sdk:
- Use Track2 Codegen to generate codes by inputting swagger, and then run
pnpm install && pnpm build. - Use Track2 Codegen to generate codes in swagger pipeline.
- Generate changelog and bump version for Azure Track2 JS SDK.
Install
From the azure-sdk-for-js repository root:
package_manager=$(node -p "require('./package.json').packageManager")
npm_registry=$(npm config get registry)
npm install "$package_manager" --registry="$npm_registry"
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1Tools
Use Track2 Codegen to generate codes by inputting swagger
You can use this command to generate codes, and then run pnpm install, pnpm build, and pack the generated codes.
cd azure-sdk-for-js
node eng/tools/js-sdk-release-tools/dist/hlcCodeGenCli.js --use=@autorest/[email protected] --readme=../azure-rest-api-specs/specification/iotspaces/resource-manager/readme.mdIf you want to add more parameters to autorest command, use can use parameter additionalArgs. For example:
cd azure-sdk-for-js
node eng/tools/js-sdk-release-tools/dist/hlcCodeGenCli.js --use=@autorest/[email protected] --readme=../azure-rest-api-specs/specification/iotspaces/resource-manager/readme.md --additional-args="--package-name=@azure/arm-sample"hint: you can change the readme to yours, and get codegen version from NPM.
Use Track2 Codegen to generate codes in swagger pipeline
This command is only used in swagger pipeline.
cd azure-sdk-for-js
node eng/tools/js-sdk-release-tools/dist/autoGenerateInPipeline.js --inputJsonPath=a.json --outputJsonPath=b.json --use=@autorest/[email protected]hint: you can get codegen version from NPM.
Generate changelog and bump version for Azure Track2 JS SDK
This tool is used to generate changelog and bump version automatically.
Prerequisites
This tools is based on the api.md file generated by API Extractor. So make sure the sdk package contains the api.md file
How to Use
cd azure-sdk-for-js
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js ${package-path}example:
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js sdk/compute/arm-computeEng Tools Js Sdk Release Tools Docs Llc (`eng/tools/js-sdk-release-tools/docs/llc.md`)
Azure JS RLC Tool
Azure JS RLC Tool can help you get llc codes by one command in few minutes.
Note: Currently, this tools only supports generating dataplane sdk, and only can run in powershell/bash.
Install
From the azure-sdk-for-js repository root:
Bash
package_manager=$(node -p "require('./package.json').packageManager")
npm_registry=$(npm config get registry)
npm install "$package_manager" --registry="$npm_registry"
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1PowerShell
$packageManager = node -p "require('./package.json').packageManager"
$npmRegistry = npm config get registry
npm install $packageManager --registry=$npmRegistry
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1How to use
Prerequisites
- Install Any of the LTS versions of Node.js.
- Install autorest:
npm install autorest. - use Git to clone azure-sdk-for-js or azure-sdk-for-js-pr. Then go to the root of sdk repository and run
pnpm install. For example:
cd azure-sdk-for-js
pnpm installSteps
Step 1. Run command:
node eng/tools/js-sdk-release-tools/dist/rlcCodegenCli.js --package-name=<your package name>Note:
- Please replace with your package name, for example:
node eng/tools/js-sdk-release-tools/dist/rlcCodegenCli.js --package-name=@azure-rest/purview-account. If you don't provide it, the tool will ask you to input it interactively. - You also can use command
node eng/tools/js-sdk-release-tools/dist/rlcCodegenCli.js --readme=to generate codes ifswagger/README.mdexists. Then it will skip Step 2 and go to Step 3 directly. - If you run command with
--interactive, then the tool asks to re-check some values inswagger/README.mdif it exists.
Step 2. Generate swagger/README.md
If your package is first release or doesn't contain swagger/README.md, the tool will ask you to input some necessary information. If not, please skip this step.
For example:
Which resource provider do you want to store your package in sdk folder? Please input it: purviewYou need to provide following information. You can refer to swagger/README.md as an example.
| parameter | description |
|---|---|
| service-name | The folder name under sdk. If the service name you provided is not under sdk, we will create a new one for you. |
| package-name | The package name of the sdk, which should be in format @azure-rest/xxxxx |
| title | The title of the sdk. |
| description | The description of the sdk. |
| input-file | The swagger files to generate sdk. If you have multi input files, please use semicolons to separate. |
| package-version | The sdk version you want to generate. |
| credential-scopes | The credential scopes of your service. |
You also can provide the information when running the command, such as node eng/tools/js-sdk-release-tools/dist/rlcCodegenCli.js --package-name=<your package name> --title=<your title> --description=<your description>.
Step 3. The tool will generate codes and build generated codes automatically. Finally, you can get RLC codes.
- If you want to write test, please start from sample test from
testfolder.
Please refer to template as a good start.
Contribute
If you want to contribute to this tool, please start from [src/rlcCodegenCli.ts].
Also, you need to know what the tool does:
Generate Codes:
- Generate
swagger/README.md - Generate source code by codegen
- Add/update ci.yml
- Add/update ci.yml
Build Codes:
- Run
pnpm install - Run
pnpm build --filter <your package name>... - Generate changelog(Because released package doesn't include review folder, so we generate an initial release changelog if there is no existing CHANGELOG.md.)
- Clean compiled codes
Sdk Core Core Lro Docs LROEngine (`sdk/core/core-lro/docs/LROEngine.md`)
Modular Support for Long-Running Operations
Long-running operations (LROs) are operations that the service could take a long time to finish processing and they follow a common convention:
- the customer first send an initiation request to the service, which in turn sends back a response, from which the customer can learn how to poll for the status of the operation, if it has not been completed already,
- using their learnings, the customer polls the status of the operation until it is done,
- again, using their learnings, the customer can now get the desired result of the operation once its status says it is done.
Ideally, we can write an algorithm that implements this convention once and use it in all Azure clients for LRO APIs, however, in reality, this convention is implemented differently across Azure services. The good news is that the TypeScript Autorest extension is AFAIK able to generate code that implements those different ones, but this implementation has a couple of limitations:
- it is located in a few files that the code generator copies into every generated package that has LROs. So if in the future there is a bug fix needed in the LRO logic, the package has to be regenerated with the fix.
- it works only with clients that use
@azure/core-client, so clients that use@azure-rest/core-clientor@azure/core-httpcan not use this implementation as-is.
To fix limitation #1, the most straightforward thing to do is to move those files into @azure/core-lro, but without fixing limitation #2 first, @azure/core-lro will have to depend on @azure/core-client in this case which will force clients that depend on @azure/core-lro but not necessarily depend on @azure/core-client to transitively depend on the latter, posing concerns about unnecessarily larger bundle sizes.
This document presents a design that fixes limitation #2 and naturally fixes limitation #1 too.
Things to know before reading
- Some details not related to the high-level concept are not illustrated; the scope of this is limited to the high-level shape and paradigms for the feature area.
Terminology
- Azure Async Operation, Body, and Location are names for the LRO implementations currently supported in the TypeScript Autorest extension. They vary in how to calculate the path to poll from, the algorithm for detecting whether the operation has finished, and the location to retrieve the desired results from. Currently, these pieces of information can be calculated from the response received after sending the initiation request.
Why this is needed
The China team is currently waiting for fixing limitation #1 which they regard as a blocker for GAing the TypeScript Autorest extension. Furthermore, having this LRO implementation being part of @azure/core-lro and not tied to @azure/core-client will significantly help streamline the underway effort to add convenience helpers for LROs in @azure-rest clients.
Design
This document presents a design of an LRO engine to be part of @azure/core-lro and could be used by any client regardless of how it is implemented. Furthermore, specific implementations of the engine are also provided to be auto-generated by Autorest.
The design consists of three main pieces:
- an interface, named
LongRunningOperation<T>which groups various primitives needed to implement LROs - a class, named
LroEngine, that implements the LRO engine and its constructor takes as input an object that implementsLongRunningOperation<T> - a class that implement
LongRunningOperation<T>that works with clients that use either@azure/core-httpand@azure/core-client. @joheredi also created one for@azure-rest/core-clientin https://github.com/Azure/azure-sdk-for-js/pull/15898
`LongRunningOperation`
This interface contains two methods: sendInitialRequest and sendPollRequest.
`sendInitialRequest`
This method should be implemented to send the initial request to start the operation and it has the following signature:
sendInitialRequest: () => Promise<LroResponse<T>>The method does not take the path or the HTTP request method as parameters because they're members of the interface since they're needed to control many aspects of subsequent polling. This is how this method can be implemented:
public async sendInitialRequest(): Promise<LroResponse<T>> {
return this.sendOperation(this.args, this.spec); // the class will have sendOperation, args, and spec as private fields
}`sendPollRequest`
This method should be implemented to send a polling (GET) request, a request the service should respond to with the current status of the operation, and it has the following signature:
sendPollRequest: (path: string) => Promise<LroResponse<T>>;This method takes the polling path as input and here is what a simplified implementation would look like:
public async sendPollRequest(path: string): Promise<LroResponse<T>> {
return this.sendOperationFn(this.args, { // the class will have sendOperation, args, and spec as private fields
...this.spec,
path,
httpMethod: "GET")
});
}`LroEngine`
This class implements the PollerLike interface and does the heavy lifting for LROs and has the following type signature:
class LroEngine<TResult, TState extends PollOperationState<TResult>> extends Poller<TState, TResult>The class also has the following constructor:
constructor(lro: LongRunningOperation<TResult>, options?: LroEngineOptions);Currently options have intervalInMs to control the polling interval, resumeFrom to enable resuming from a serialized state, and lroResourceLocationConfig which could determine where to find the results of the LRO after the operation is finished. Typically, Autorest figures out the value for LroResourceLocationConfig from the x-ms-long-running-operation-options swagger extension. If there are new arguments to be added to the class, they could be added to the options type.
`LroImpl`
This class implements the LongRunningOperation<T> interface and is auto-generated by Autorest. LroImpl needs access to a few pieces: operation specification and operation arguments and a primitive function that can take them as input to send a request and converts the received response into one of type LroResponse<T> which has both the flattened and the raw responses.
Usage examples
Create an object of `LroImpl`
const directSendOperation = async (
args: OperationArguments,
spec: OperationSpec
): Promise<unknown> => {
return this.client.sendOperationRequest(args, spec);
};
const sendOperation = async (
args: OperationArguments,
spec: OperationSpec
) => {
let currentRawResponse: FullOperationResponse | undefined = undefined;
const providedCallback = args.options?.onResponse;
const callback: RawResponseCallback = (
rawResponse: FullOperationResponse,
flatResponse: unknown
) => {
currentRawResponse = rawResponse;
providedCallback?.(rawResponse, flatResponse);
};
const updatedArgs = {
...args,
options: {
...args.options,
onResponse: callback
}
};
const flatResponse = await directSendOperation(updatedArgs, spec);
return {
flatResponse,
rawResponse: {
statusCode: currentRawResponse!.status,
body: currentRawResponse!.parsedBody,
headers: currentRawResponse!.headers.toJSON()
}
};
};
const lro = new LroImpl(
sendOperation,
{ options }, // arguments are just the operation options
spec
);Using `LroEngine`
const pollerEngine = new LroEngine(lro, { intervalInMs: 2000 }); // lro was instantiated in the previous section
const result = pollerEngine.pollUntilDone();Testing
We have extensive test suite for LROs in the TypeScript code generator repo. I both added those tests here and re-implemented the lro routes in the Autorest test server. For this to work, I created a fairly low-level instantiation for LongRunningOperation<T> with just @azure/core-rest-pipeline.
Sdk Core Core Lro Docs MIGRATION (`sdk/core/core-lro/docs/MIGRATION.md`)
Migration Guide
This document shows the customers of v2 LRO (Long Running Operations) on how to migrate their code to use the v3 libraries.
Main difference between v2 and v3
In v2, you have to await the LRO to get this poller. But await doesn't give you the final result, it gives you the poller to track the operation's status. This has confused some users who thought await would give them the final result.
To make things clearer and easier to understand, we've changed how this works in v3. Now, our poller interface blends the need to track long-running operations with the usual way of handling asynchronous programming. And this is the main difference between them.
Except that the LroEngine is no longer supported and you could use createHttpPoller to create a poller instead.
If you have an existing application that uses our v2 libaries and you're interested in updating your application to use the latest one, here are the things that you need to do for the migration:
Creation of Poller
In v2 we have two implementations for LRO: LroEngine and SimplePollerLike with createHttpPoller. In v3 we deprecate the former and keep the PollerLike with createHttpPoller to build the poller. And the return type is changed from SimplePollerLike to PollerLike. The creation code would be changed as below.
From
const pollerSetting: LongRunningOperation<TResult> = {
requestMethod: "{http-method}",
requestPath: "{path}",
sendInitialRequest: async () => {
// your code to send initial request
},
sendPollRequest: async (path) => {
// your code to trigger polling request
},
};
const httpPoller = await createHttpPoller(pollerSetting, options);To
const pollerSetting: RunningOperation<TResult> = {
sendInitialRequest: async () => {
// your code to send initial request
},
sendPollRequest: async (path) => {
// your code to trigger polling request
},
};
const httpPoller = createHttpPoller(pollerSetting, options);Now please notice if you await the helper you would get the final result.
const result = await httpPoller;Poller API
Also the return type for createHttpPoller is renamed from SimplePollerLike to PollerLike.The following table compares SimplePollerLike and PollerLike:
| operation | SimplePollerLike |
PollerLike |
|---|---|---|
| return final results | pollUntilDone() |
pollUntilDone() |
| poll | poll() |
poll() |
| access the current state after receiving the response of each poll request | onProgress() |
onProgress() |
| check whether the operation finished | isDone() |
isDone |
| stop polling | stopPolling() |
N/A |
| check if the polling stopped | isStopped() |
N/A |
| get the current operation state | getOperationState() |
operationState |
| access the final result | getResult() |
result |
| serialize the poller state | toString() |
serialize() |
| wait the poller submitted successfully | N/A | submitted |
Please note the operation getOperationState(): TState is changed to attribute operationState: TState | undefined, so the value could be undefined if the poller is not initialized yet.
const status = poller.getOperationState().status;now
const status = poller?.operationState?.status;If you want to serialize a poller, use the serialize instead.
const serializeState = poller.toString();now
const serializeState = await poller.serialize();`core-lro-shim`
There are situations where we don't want to break existing code and want to keep backward compatibility. Here we prepare core-lro-shim scripts for you, in which we implemented v2 SimplePollerLike interfaces with v3 interfaces.
Once you copy them into your environment and the only thing you need to do is to update your lro references to that shim.
/* Detailed source-code truncated for AI context efficiency. */Sdk Core Core Lro Docs Implementing Lros With Createhttppoller (`sdk/core/core-lro/docs/implementing-lros-with-createhttppoller.md`)
Implementing Long-Running Operations
Implementing LROs is a daunting task because of the complexity of managing transitions between states and in the same time taking care of behavioral details such as dynamic polling spacing and aborting. To make it easier for client authors to implement LROs, @azure/core-lro exports a function named createHttpPoller that abstracts away all LRO-specific implementation details.
Overview
createHttpPoller constructs a poller from a few pieces, mainly,
- a way to initialize the operation,
- a way to poll it,
- a way to convert the last polling response into a result object, and
- a way to update the poller state from a polling response.
The first two pieces are captured by the first parameter of the function which is of type LongRunningOperation:
export interface LongRunningOperation<T = unknown> {
requestPath?: string;
requestMethod?: string;
sendInitialRequest: () => Promise<LroResponse<unknown>>;
sendPollRequest: (path: string, options?: { abortSignal?: AbortSignalLike }) => Promise<LroResponse<T>>;
}More specifically, those two pieces are the values of sendInitialRequest and sendPollRequest respectively. Note that requestPath and requestMethod are optional properties that are needed by PUT LROs only, in which case, requestPath is the URL path of the resource, and requestMethod is "PUT".
Moreover, the last two pieces are options in the options bag which is defined as follows:
export interface CreateHttpPollerOptions<TResult, TState> {
processResult?: (result: unknown, state: TState) => TResult;
updateState?: (state: TState, response: LroResponse) => void;
intervalInMs?: number;
restoreFrom?: string;
resourceLocationConfig?: LroResourceLocationConfig;
withOperationLocation?: (operationLocation: string) => void;
resolveOnUnsuccessful?: boolean;
}More specifically, those two pieces are the values of processResult and updateState respectively. processResult takes the last polling response and the state as input and returns a TResult, which will eventually be returned to the customer when they call poller.pollUntilDone(). updateState can mutate the state with specific information from every polling response such as updating metadata fields (e.g. many operations return a lastUpdatedDateTime field in their polling responses to tell the customer when last time their operation had an activity).
These are the main four pieces of information needed to construct the poller but there are other options that can be used to adjust the poller behavior:
intervalInMs: controls the time interval between polling requestsrestoreFrom: enables creating a poller for an existing operation from a serialized stateresourceLocationConfig: controls the polling behavior and typically used only when the LRO swagger extension is used in the swaggerwithOperationLocation: a callback that gets called every time the operation location header is first returned or updated. This callback is useful to save the operation URL for later use, e.g. get pages from it if the result is pageableresolveOnUnsuccessful: controls whether to throw exceptions when the operation is canceled or failed. Typically needed by REST-level clients (RLCs) only
Example
Please see @azure/ai-language-text for an example of how to use createHttpPoller.
Benefits of using `createHttpPoller`
- it is plug-in and forget it setup, with zero maintenance cost on the client author side
- it provides a good logging experience
- it is feature-complete
- it is battle-tested with a comprehensive test suite that has many customer-reported cases and no major gh issue currently open against it
- it is being used in all types of clients, REST-level, management-plane, data-plane, etc
Migrating existing LROs
Please note that createHttpPoller returns an object of type SimplePollerLike which is slightly different from PollerLike that many libraries still use. There are a couple of differences between the two interfaces:
PollerLikehasisCanceled,isFailed, andisCompletedboolean fields butSimplePollerLikehasstatuswhich is a closed enum ofnotStarted,running,succeeded,failed, andcanceledvalues. Usingstatusimproves the customer experience because it can be consumed by a simple switch statement and typically corresponds directly to a field in the response object with the same name, so no need for the customer to be familiar with how the client interprets the operation's status.PollerLikehas acancelOperationmethod that is not implemented by the majority of LROs, which is misleading to our customers.SimplePollerLikedoesn't have such method but you can always add it to your poller object if one is needed.
To avoid breaking changes, you may want to update the return object from createHttpPoller to be compatible with PollerLike as follows:
- utilize
updateStateto addisCanceled,isFailed, andisCompletedfields to your poller state - tack on a
cancelOperationmethod
Common Tools Eslint Plugin Azure Sdk Docs Rules Github Source Headers (`common/tools/eslint-plugin-azure-sdk/docs/rules/github-source-headers.md`)
github-source-headers
Requires copyright headers in every source file.
Specifically, a source file is defined as any TypeScript file and the expected header text is as follows:
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.This rule is fixable using the --fix option.
Examples
Good
These must be located at the top of every source file.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license./**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*/// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// Other comment text here.Bad
// Copyright (c) Microsoft Corporation.class Class {
/* source code here */
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.When to turn off
Only if rule breaks.
[Source](https://azure.github.io/azure-sdk/policies_opensource.html#github-source-headers)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Apisurface Standardized Verbs (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-apisurface-standardized-verbs.md`)
ts-apisurface-standardized-verbs
Requires all public-facing client methods to use a verb from the approved list of prefixes and suffixes where possible.
The list of approved verbs and their uses is as follows:
| Verb | Parameters | Returns | Comments |
|---|---|---|---|
create\<Noun> |
key, item | Created item | Create new item. Fails if item already exists. |
upsert\<Noun> |
key, item | Updated or created item | Create new item, or update existing item. Verb is primarily used in database-like services |
set\<Noun> |
key, item | Updated or created item | Create new item, or update existing item. Verb is primarily used for dictionary-like properties of a service |
update\<Noun> |
key, partial item | Updated item | Fails if item doesn't exist. |
replace\<Noun> |
key, item | Replace existing item | Completely replaces an existing item. Fails if the item doesn't exist. |
append\<Noun> |
item | Appended item | Add item to a collection. Item will be added last. |
add\<Noun> |
index, item | Added item | Add item to a collection. Item will be added at the given index. |
get\<Noun> |
key | Item | Will return null if item doesn't exist |
list\<Noun>s |
PagedAsyncIterableIterator<TItem, TPage> |
Return list of items. Returns empty list if no items exist | |
\<noun>Exists |
key | bool |
Return true if the item exists. |
delete\<Noun> |
key | None | Delete an existing item. Will succeed even if item didn't exist. |
remove\<Noun> |
key | None or removed item | Remove item from a collection. |
In usage, this rule forbids the usage of synonyms of these approved verb prefixes and suffixes, with the following prefixes being the current banned set:
erasefetchgetAllinsertOrUpdatemakepoppushupdateOrInsert
Examples
Good
// ignore parameter and return types for the purposes of this rule
class ServiceClient {
createItem(): void {}
upsertItem(): void {}
setItem(): void {}
updateItem(): void {}
replaceItem(): void {}
appendItem(): void {}
addItem(): void {}
getItem(): void {}
listItems(): void {}
itemExists(): void {}
deleteItem(): void {}
removeItem(): void {}
}// private methods are ignored
class ServiceClient {
private makeItem(): void {}
}// classes not suffixed with "Client" are ignored
class ServiceItem {
makeItem(): void {}
}Bad
class ServiceClient {
makeItem(): void {}
}When to turn off
Only if rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-apisurface-standardized-verbs)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Apisurface Supportcancellation (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-apisurface-supportcancellation.md`)
ts-apisurface-supportcancellation
Requires all asynchronous public-facing client methods to accept an AbortSignalLike parameter or option.
Examples
Good
class ServiceClient {
async createItem(cancelToken: AbortSignalLike);
}interface ServiceOptions {
cancelToken: AbortSignalLike;
}
class ServiceClient {
async createItem(options: ServiceOptions);
}// private methods are ignored
class ServiceClient {
private createItem(): void {}
}Bad
class ServiceClient {
async createItem();
}When to turn off
If your SDK doesn't intend to support cancellation.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-apisurface-supportcancellation)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Consistent Type Exports (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-consistent-type-exports.md`)
ts-consistent-type-exports
Enforces consistent usage of type exports, requiring export type for type-only named exports.
This rule wraps @typescript-eslint/consistent-type-exports but ignores export * declarations. The original rule flags export * from '...' when all re-exported symbols are types, suggesting export type * from '...'. This wrapper skips that check because:
export *re-exports everything from a module. If value exports are later added to the source module,export type *would silently drop them.- Changing
export *toexport type *alters the module contract and can cause unexpected breakage downstream.
Examples
Good
// Named type export with `type` keyword ✅
type Foo = string;
export type { Foo };
// export * is always allowed, even if all exports are types ✅
export * from "./types";
// Value export ✅
const bar = 42;
export { bar };Bad
// Named export of a type without `type` keyword ❌
type Foo = string;
export { Foo };Options
| Option | Type | Default | Description |
|---|---|---|---|
fixMixedExportsWithInlineTypeSpecifier |
boolean |
false |
Whether the rule will autofix "mixed" export cases using TS inline type specifiers. |
When Not To Use It
If you intentionally want export * declarations to be flagged when re-exporting only types, use the original @typescript-eslint/consistent-type-exports rule directly.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Doc Internal Private Member (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-doc-internal-private-member.md`)
ts-doc-internal-private-member
Prevents the usage of the @internal tag in TSDoc comments for private members.
Internal objects are defined as classes, interfaces, or standalone functions that are not exported from the main entrypoint to the package and are not members of any exports from the main entrypoint (defined recursively). Because private members are already ignored by the docs generation tools, we do not need to use @internal in the TSDoc comments Files that are specified as excluded in a typedoc.json file are ignored by this rule.
Examples
Good
/**
* Class documentation
*/
class ExampleClass {
private x = 0;
private y: number;
private [s: string]: boolean | ((s: string) => boolean);
/**
* Method documentation
*/
private testMethod(private x: number, private y: number) {}
}/**
* Class documentation
*/
class ExampleClass {
/**
* @internal
*/
x = 0;
/**
* @internal
*/
y: number;
/**
* @internal
*/
[s: string]: boolean | ((s: string) => boolean);
/**
* Method documentation
* @internal
*/
testMethod(private x: number, private y: number) {}
}Bad
/**
* Class documentation
*/
class ExampleClass {
/**
* @internal
*/
private x = 0;
/**
* @internal
*/
private y: number;
/**
* @internal
*/
private [s: string]: boolean | ((s: string) => boolean);
/**
* Method documentation
* @internal
*/
private testMethod(private x: number, private y: number) {}
}When to turn off
If you are not using TypeDoc.
Source
Suggestion by @deyaaeldeen.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Doc Internal (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-doc-internal.md`)
ts-doc-internal
Requires any TSDoc comments on internal objects to include either an @internal or an @hidden tag.
Internal objects are defined as classes, interfaces, or standalone functions that are not exported from the main entrypoint to the package and are not members of any exports from the main entrypoint (defined recursively). Files that are specified as excluded in a typedoc.json file are ignored by this rule.
Background
We used to use either @hidden or @ignore to hide definitions from showing up in our docs but none of them is supported by TSDoc. However, we opted to allow @hidden with TSDoc ESLint plugin and replaced all uses of @ignore with @hidden so we can stick with just one of them.
When to use either tags
TSDoc supports @internal and it is confusing to decide whether to use that or @hidden. @internal, unlike @hidden, has compilation implications. In particular, using the former can instruct the compiler to not emit declarations for the annotated definitions (see here). So it is advised to use @internal if you want this specific behavior along with the compiler flag. However, unfortunately, you still need to also insert @hidden because our docs generator tool (TypeDoc) does not understand the stripInternal flag. This is why you can find many definitions in our code base annotated with both. If the definition is supposed to be exported but you do not want to have it in the docs, then use @hidden only.
Configuration
By default, the main entrypoint is assumed to be src/index.ts. However, if your package's main entrypoint is elsewhere, you'll need to specify so in your .eslintrc configuration file as follows (for example, if the entrypoint is index.ts):
{
"settings": {
"main": "index.ts"
}
}This rule exists to prevent inclusion of internal objects in documentation generated by tools such as API Extractor and TypeDoc.
Examples
Assume all objects mentioned are not exported from the main entrypoint to the package.
Good
/**
* Other documentation
* @internal
*/
class ExampleClass {}/**
* Other documentation
* @hidden
*/
interface ExampleInterface {}/**
* Other documentation
* @internal
*/
function exampleFunction(): void {}Bad
/**
* Other documentation
*/
class ExampleClass {}/**
* Other documentation
*/
interface ExampleInterface {}/**
* Other documentation
* @ignore
*/
function exampleFunction(): void {}When to turn off
If you are not using TypeDoc.
Source
Suggestion by @chradek.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Error Handling (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-error-handling.md`)
ts-error-handling
Limits thrown errors to ECMAScript built-in error types (TypeError, RangeError, Error).
Allows exceptions for catch blocks where the type of the caught error is unknown.
Examples
Good
throw new Error("error");const err = new TypeError("invalid type");
throw err;try {
/* code here */
} catch (err) {
throw err;
}Bad
throw 1;throw "error";throw new OtherError("error");try {
/* code here */
} catch (err) {
throw err;
}When to turn off
If you have good reason to include other error types.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-error-handling)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Modules Only Named (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-modules-only-named.md`)
ts-modules-only-named
Requires all exports from the main entrypoint to the package to be named exports.
By default, the main entrypoint is assumed to be src/index.ts. However, if your package's main entrypoint is elsewhere, you'll need to specify so in your .eslintrc configuration file as follows (for example, if the entrypoint is index.ts):
{
"settings": {
"main": "index.ts"
}
}Examples
All examples are representative of the main entrypoint.
Good
export /* package contents */ {};export /* package contents */ {};export const package = {/* package contents */};Bad
export default {/* package contents */};When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-modules-only-named)
Also encompasses ts-config-no-default, as the rules are similar enough to not exist separately for linting purposes.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Naming Drop Noun (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-naming-drop-noun.md`)
ts-naming-drop-noun
Requires client methods that return instances of the client to drop the client name from the method name.
Examples
Good
class ServiceClient {
create(): ServiceClient {
/* code to return instance of ServiceClient */
}
}// private methods are ignored
class ServiceClient {
private _createService(): ServiceClient {
/* code to return instance of ServiceClient */
}
}Bad
class ServiceClient {
createService(): ServiceClient {
/* code to return instance of ServiceClient */
}
}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-naming-drop-noun)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Naming Options (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-naming-options.md`)
ts-naming-options
Requires client method options parameter types to be suffixed with Options and prefixed with the method name.
Exception: OperationOptions type is allowed when an operation doesn't need any custom options.
Examples
Good
class ServiceClient {
constructor(options: ServiceClientOptions) {
/* code */
}
createItem(options: CreateItemOptions): Item {
/* code to return instance of Item */
}
upsertItem(options: UpsertItemOptions): Item {
/* code to return instance of Item */
}
deleteItem(options: OperationOptions): void {
/* code to return instance of Item */
}
}Bad
class ServiceClient {
constructor(options: Options) {
/* code */
}
createItem(options: Options): Item {
/* code to return instance of Item */
}
}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-naming-options)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Naming Subclients (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-naming-subclients.md`)
ts-naming-subclients
Requires client methods returning a subclient to have names prefixed suffixed with "get" and suffixed with "client".
Examples
Good
class ServiceClient {
getSubClient(): SubClient {
/* code to return instance of SubClient */
}
}// private methods are ignored
class ServiceClient {
private _get(): SubClient {
/* code to return instance of SubClient */
}
}Bad
class ServiceClient {
get(): SubClient {
/* code to return instance of SubClient */
}
}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-naming-subclients)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts No Const Enums (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-no-const-enums.md`)
ts-no-const-enums
Recommends against the usage of TypeScript's const enums.
This rule is fixable using the --fix option.
Examples
Good
enum Directions {
North,
East,
South,
West,
}Bad
const enum Directions {
North,
East,
South,
West,
}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-no-const-enums)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts No Direct Child Process (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-no-direct-child-process.md`)
Require safe process execution
External commands in SDK source must use @azure/core-process instead of
loading Node's child_process module directly. The core package keeps commands
and arguments separate, prevents callers from enabling a shell, and applies a
restricted Windows batch-file policy.
Type-only imports and the named fork API remain allowed:
import { fork, type ChildProcess } from "node:child_process";Use the core package for external executables:
import { execFile } from "@azure/core-process";
const result = await execFile("git", ["status", "--short"], {
encoding: "utf8",
});Common Tools Eslint Plugin Azure Sdk Docs Rules Ts No Invalid Test Imports (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-no-invalid-test-imports.md`)
ts-no-invalid-test-imports
Disallow relative imports from src/ in test files.
- Category: Problem
- Recommended: yes (for Azure SDK repos)
Why
Tests should exercise the public API surface of the package (or explicit $internal entrypoints) rather than reaching into implementation files under src/ via relative paths. This maintains clean package boundaries and prevents brittle coupling to internal structure.
Rule details
This rule flags relative imports in test files that climb out of the test directory and enter a top-level src folder, such as:
import { Client } from "../src/client";import { Helper } from "../../src/helpers/helper";
A file is considered a test file if one of the following is true:
- Filename matches
*.test.ts,*.test.tsx,*.spec.ts, or*.spec.tsx - Path contains a
test/ortests/segment
The rule is cross-platform and does not depend on the current working directory.
Examples
Incorrect
// tests/client.test.ts
import { Client } from "../src/client";
import { helper } from "../../src/helpers/helper";Correct
// tests/client.test.ts
import { Client } from "@azure/<package>"; // from the package entrypoint
import { something } from "$internal"; // explicit internal entrypoint
import { helper } from "./helpers"; // local test utilitiesOptions
This rule does not have any options.
When not to use it
If your project intentionally allows tests to import from src/ using relative paths, you should not enable this rule.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts No Window (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-no-window.md`)
ts-no-window
Recommends against the usage of window in favor of self.
This rule is fixable using the --fix option.
Examples
Good
const navigator = self.navigator as NavigatorEx;Bad
const navigator = window.navigator as NavigatorEx;When to turn off
Only if the rule breaks.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Approved Dependencies (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-approved-dependencies.md`)
ts-package-json-approved-dependencies
Prevents shipped libraries from taking unapproved third-party runtime
dependencies.
Packages with "sdk-type": "client", "mgmt", or "provisioning" are checked.
Samples, tests, perf tests, and utility/tooling packages are out of scope, and
only the dependencies field is checked — devDependencies andpeerDependencies are ignored.
A runtime dependency is allowed when it is either:
- first-party — its name starts with
@azure/,@azure-rest/,@azure-tools/,@microsoft/, or@typespec/; or - listed in the central allow-list at
eng/approved-third-party-dependencies.yml,
either underallowed(available to every package) or under anexceptions
entry that names the package.
Allow-list entries are usually exact package names, but a scope wildcard of
the form @scope/* is also supported and matches every package published under
that npm scope. Scope wildcards work in both allowed and exceptions. For
example, "@opentelemetry/*" under allowed approves every @opentelemetry/...
package for every shipped library.
To take a new third-party runtime dependency, get it approved and then add it to
the allow-list. See
Adding a new third-party runtime dependency.
Examples
Good
{
"name": "@azure/example",
"sdk-type": "client",
"dependencies": {
"@azure/core-rest-pipeline": "^1.0.0",
"tslib": "^2.6.2"
}
}Bad
{
"name": "@azure/example",
"sdk-type": "client",
"dependencies": {
"lodash": "^4.17.21"
}
}When to turn off
This rule should not be turned off. New third-party runtime dependencies in
shipped libraries require approval.
Options
"configPath": overrides the location of the allow-list file. Intended for
testing; in the monorepo the file is resolved relative to the repository root
automatically.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Author (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-author.md`)
ts-package-json-author
Requires author in package.json to be set to "Microsoft Corporation".
This rule is fixable using the --fix option.
Examples
Good
{
"author": "Microsoft Corporation"
}Bad
{
"author": "Microsoft"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-author)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Bugs (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-bugs.md`)
ts-package-json-bugs
Requires bugs in package.json to be set to "https://github.com/Azure/azure-sdk-for-js/issues".
This rule is fixable using the --fix option.
Examples
Good
{
"bugs": {
"url": "https://github.com/Azure/azure-sdk-for-js/issues"
}
}Bad
{
"bugs": {
"url": "https://github.com/Azure/azure-sdk-for-java/issues"
}
}{
"bugs": {}
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-bugs)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Engine Is Present (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-engine-is-present.md`)
ts-package-json-engine-is-present
Requires support for all Node LTS version.
Currently, this requires engines in package.json to contain an entry for node set to ">=12.0.0" unless a nodeVersionOverride value is present.
This rule is fixable using the --fix option.
Examples
Good
{
"engines": {
"node": ">=14.0.0"
}
}Bad
{
"engine": {
"node": ">=14.0.0"
}
}{
"engine": ">=6.0.0"
}{
"engine": ">=10.0.0"
}{}When to turn off
Only if the rule breaks.
Options
This rule as an object option:
"nodeVersionOverride": allow providing a custom supported node version if an external dependency enforces it
nodeVersionOverride
Example of correct code for this rule with the { "nodeVersionOverride": ">=10.0.0" } option:
{
"engines": {
"node": ">=10.0.0"
}
}[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-engine-is-present)
Also encompasses ts-node-support, as the rules are similar enough to not exist separately for linting purposes.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Files Required (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-files-required.md`)
ts-package-json-files-required
Requires files in package.json to contain paths to the package contents.
Specifically, this rule looks for inclusion of dist, dist-esm/src, and src as either just those directories or specific subdirectories
This rule is fixable using the --fix option.
Examples
Good
{
"files": ["dist", "dist-esm/src"]
}{
"files": ["./dist", "./dist-esm/src"]
}{
"files": ["dist/", "dist-esm/"]
}{
"files": ["dist/lib", "dist-esm/src/lib"]
}Bad
{
"files": ["dist", "src"]
}{
"files": ["dist"]
}{
"files": []
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-files-required)
Also encompasses ts-include-cjs, ts-include-esm, and ts-include-original-source
, as the rules are similar enough to not exist separately for linting purposes.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Homepage (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-homepage.md`)
ts-package-json-homepage
Requires homepage in package.json to be set to the library's readme.
Examples
Good
{
"homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/servicebus/service-bus/README.md"
}{
"homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/servicebus/service-bus/"
}Bad
{
"homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/README.md"
}{
"homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main"
}{
"homepage": "https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/servicebus/service-bus/README.md"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-homepage)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Keywords (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-keywords.md`)
ts-package-json-keywords
Requires keywords in package.json to include "azure" and "cloud".
This rule is fixable using the --fix option.
Examples
Good
{
"keywords": ["azure", "cloud"]
}{
"keywords": ["azure", "cloud", "sdk"]
}Bad
{
"keywords": ["azure"]
}{
"keywords": []
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-keywords)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Main Is Cjs (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-main-is-cjs.md`)
ts-package-json-main-is-cjs
Requires main in package.json to be point to a CommonJS or UMD module. In this case, its assumed that it points to "dist/index.js" or "dist/index.cjs".
This rule is fixable using the --fix option.
Examples
Good
{
"main": "dist/index.js"
}Good
{
"main": "dist/index.cjs"
}Bad
{
"main": "dist/src/index.js"
}{
"main": "dist-esm/src/index.js"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-main-is-cjs)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Name (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-name.md`)
ts-package-json-name
Requires name in package.json to be set to "@azure/<service-name>", with the service name in kebab-case.
Examples
Good
{
"name": "@azure/service-bus"
}Bad
{
"name": "@microsoft/service-bus"
}{
"name": "service-bus"
}{
"name": "@azure/serviceBus"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-name)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Repo (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-repo.md`)
ts-package-json-repo
Requires repository in package.json to be set to "github:Azure/azure-sdk-for-js".
This rule is fixable using the --fix option.
Examples
Good
{
"repository": "github:Azure/azure-sdk-for-js"
}Bad
{
"repository": "github:Azure/azure-sdk-for-java"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-repo)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Required Scripts (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-required-scripts.md`)
ts-package-json-required-scripts
Requires scripts in package.json to be contain "build" and "test".
Examples
Good
{
"scripts": {
"build": "...",
"test": "..."
}
}{
"scripts": {
"build": "...",
"lint": "...",
"test": "..."
}
}Bad
{
"scripts": {
"build": "..."
}
}{
"scripts": {}
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-required-scripts)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Sdktype (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-sdktype.md`)
ts-package-json-sdktype
Requires the existence of the sdk-type field and be one of client, mgmt, provisioning, perf-test, or utility.
Examples
Good
{
"sdk-type": "client"
}{
"sdk-type": "mgmt"
}{
"sdk-type": "provisioning"
}{
"sdk-type": "utility"
}Bad
{
"sdk-type": "invalid"
}{
"sdk-type": 1
}{
"sdk-type": true
}{
"not-sdk-type": "client"
}{
"outer": {
"sdk-type": "client"
}
}{}When to turn off
Only if the rule breaks.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Sideeffects (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-sideeffects.md`)
ts-package-json-sideeffects
Requires sideEffects in package.json to be true for sdk-type: provisioning and false for other SDK types. Provisioning resources register constructors and shapes at load time.
This rule is fixable using the --fix option.
Examples
Good
{
"sdk-type": "client",
"sideEffects": false
}{
"sdk-type": "provisioning",
"sideEffects": true
}Bad
{
"sdk-type": "client",
"sideEffects": true
}{
"sdk-type": "provisioning",
"sideEffects": false
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-sideeffects)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Package Json Types (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-package-json-types.md`)
ts-package-json-types
Requires inclusion of type declarations in the package.
In practice, this means types in package.json must be set to a path pointing to a .d.ts file.
Examples
Good
{
"types": "types/index.d.ts"
}Bad
{
"types": "types/index.ts"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-package-json-types)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Pagination List (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-pagination-list.md`)
ts-pagination-list
Requires client list methods to return a PagedAsyncIterableIterator.
Examples
Good
class ServiceClient {
listItems(): PagedAsyncIterableIterator<Item> {
/* code to return instance of PagedAsyncIterableIterator<Item> */
}
}Bad
// no return type
class ServiceClient {
listItems() {
/* code here */
}
}// different return type
class ServiceClient {
listItems(): AsyncIterator<Item> {
/* code to return instance of AsyncIterator<Item> */
}
}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-pagination-list)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Use Cjs Polyfill (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-use-cjs-polyfill.md`)
ts-use-cjs-polyfill
Requires the use of a polyfill when using CommonJS concepts in ESM files according to tshy conventions.
Examples
Good
// filename: example.ts
import { dirname, fileURLToPath } from "node:path";
const currentDir = dirname(fileURLToPath(import.meta.url));// filename: example-cjs.cts
import path from "node:path";
const currentDir = __dirname;Bad
// filename: example.ts
const currentDir = __dirname; // This will cause an error in ESM filesWhen to turn off
Only if the rule breaks.
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Use Interface Parameters (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-use-interface-parameters.md`)
ts-use-interface-parameters
Recommends the usage of interfaces instead of classes as function parameters in source files.
This includes arrays and member properties of parameter types.
However, exceptions are made for private class methods, overloads where at least one function signature does not contain and violating parameters, optional parameters, and class types which originate from another library.
Source files are defined as files in the src directory.
Examples
The following interfaces and classes are to be used as example types:
import ExternalClass from "example-library";
class BasicClass {}
interface BasicInterface {}
interface NestedInterface {
interface: BasicInterface;
}
interface ClassInInterface {
class: BasicClass;
}Good
function example(param: BasicInterface) {
/* code here */
}function example(param: NestedInterface) {
/* code here */
}function example(param?: BasicClass) {
/* code here */
}class Example {
constructor(param: BasicClass);
constructor(param: BasicInterface);
}class Example {
private _example(param: BasicClass);
}Bad
function example(param: BasicClass) {
/* code here */
}function example(param1: BasicInterface, param2: BasicClass) {
/* code here */
}function example(param: BasicClass[]) {
/* code here */
}function example(param: ClassInInterface) {
/* code here */
}class Example {
constructor(param: BasicClass);
constructor(param1: BasicInterface, param2: BasicClass);
}When to turn off
Only if the rule breaks (more likely for this rule).
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-use-interface-parameters)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Use Promises (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-use-promises.md`)
ts-use-interface-parameters
Requires the usage of in-built Promises instead of those from libraries or polyfills.
Examples
Good
const promise = (): Promise<string> => {
return new Promise((resolve) => resolve("hi"));
};Bad
import Promise from "bluebird"; // or any such library
const promise = (): Promise<string> => {
return new Promise((resolve) => resolve("example"));
};When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_design.html#ts-use-promises)
Common Tools Eslint Plugin Azure Sdk Docs Rules Ts Versioning Semver (`common/tools/eslint-plugin-azure-sdk/docs/rules/ts-versioning-semver.md`)
ts-versioning-semver
Requires version in package.json to be in SemVer.
Additionally, the following rules are checked:
- Major versions of
0are not permitted, - Alpha versions must be in the format
<version>-alpha.<date>.<alpha-version>wheredateis an integer representing a particular day (e.g. 20200728 is July 28th, 2020), andalpha-versionis an integer. - Beta versions must be in the format
<version>-beta., wherebeta-versionis an integer. - Preview versions must be in the format
<version>-preview., wherepreview-versionis an integer. - Development versions must be in the format
<version>-dev.<date>.<dev-version>wheredateis an integer representing a particular day (e.g. 20200728 is July 28th, 2020), anddev-versionis an integer.
Examples
Good
{
"version": "1.0.0"
}{
"version": "1.0.0-alpha.20200728.1"
}{
"version": "1.0.0-beta.1"
}Bad
{
"version": "1.0.01"
}{
"version": "1.0"
}{
"version": "1"
}{
"version": "0.0.1"
}{
"version": "1.0.0.0"
}{
"version": "1.0.0-preview.1"
}{
"version": "1.0.0-dev.20200728.1"
}{}When to turn off
Only if the rule breaks.
[Source](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-versioning-semver)
Also encompasses ts-versioning-no-version-0, as the rules are similar enough to not exist separately for linting purposes.
--- METRICS ---
- Files Extracted: 73
- Estimated Token Budget: ~93891 tokens
- Canonical Reference: https://codewiki.google/github.com/Azure/azure-sdk-for-js