azure-sdk-for-js Documentation

This repository is for active development of the Azure SDK for JavaScript (NodeJS & Browser). For consumers of the SDK we recommend visiting our public...

2,301 stars TypeScript TypeScript 73 files · ~93,975 tokens #azure#azure-sdk#browser#hacktoberfest#javascript#nodejs#typescript
RAW Doc

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 /sdk folder 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:

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?

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.

javascript
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

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

  1. 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.

  2. 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.

  3. Package Name Convention: follow the format @azure/{service-name}-{module}, like @azure/storage-blob.

Steps to generate DPG

  1. Configure tspconfig.yaml in spec repository

    In your specs repository, update or create tspconfig.yaml to configure the TypeScript emitter. Replace YOUR_SERVICE_DIRECTORY, YOUR_SERVICE_FOLDER and YOUR_PACKAGE_NAME with your specific details.

    yaml
    parameters:
      "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"
  2. Generate code

    Initialize a new TypeScript DPG library

    For initial set up, use the tsp-client CLI tool to initialize the generation process. From the root of your local azure-sdk-for-js repository clone, run the following command, replacing YOUR_REMOTE_TSPCONFIG_URL with the URL to your TypeSpec configuration file:

    sh
    npm --prefix eng/common/tsp-client exec --no -- tsp-client init -c YOUR_REMOTE_TSPCONFIG_URL

    If 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:

    sh
    npm --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.yaml

    This command sets up your local SDK repository with the necessary structure and files based on your tspconfig.yaml file 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.

    yaml
    directory: specification/agrifood/DataPlane
    commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 # the commit id you'd like to refer for generation
    repo: Azure/azure-rest-api-specs

    Run the update command from SDK directory (i.e sdk/agrifood/agrifood-farming) to re-generate the code:

    sh
    npm --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.

  1. Project Folder Structure.
    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.
  2. 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.

  1. 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 the swagger/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-scopes into your own service's package-name, title, description etc.

    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
    yaml
    security: AADToken
    security-scopes: https://yourendpoint.azure.com/.default
    • Support key authentication
    yaml
    security: AzureKey
    security-header-name: Your-Subscription-Key
    • Support both credentials
    yaml
    security:
      - AADToken
      - AzureKey
    security-header-name: Your-Subscription-Key
    security-scopes: https://yourendpoint.azure.com/.default
    • Disable neither authentications
    yaml
    add-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-file is followed by an .md file, you need to replace the input-file with require. If it is a JSON file, 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: false to non-block your process.

    After the first generation, you need to switch generate-metadata: false as we have some manual changes in this file and don't want them get overwritten by generated ones.


  2. Run autorest to generate the SDK

    Now, you can run this command in swagger folder you just created.

    shell
    autorest --typescript ./README.md

    After this finishes, you will see the generated code in ${PROJECT_ROOT}/src folder.
    After that, you can get a workable package, and run the following commands to get a artifact if you like.

    shell
    pnpm install
    pnpm turbo build --filter=<your-package-name>... --token 1
    cd <your-sdk-folder>
    pnpm pack

    But, 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.

  1. Prerequisites

    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-tool to the devDependencies in the package.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.json file. If your package is new or has never been pushed before, you could use below commands:

    shell
    npx dev-tool test-proxy init # this will generate assets.json file, you will get some info in this file.
  2. 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.

  3. 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_MODE to record. This will generate recordings for your test they could be used in playback mode.

    On Linux, you could use export to set env variable:

    shell
    pnpm 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:

    shell
    pnpm 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 playback mode test if your apis don't have breaking changes and you've already done the recording before.

    On Linux, you could use below commands:

    shell
    pnpm 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:

    shell
    pnpm 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.
  4. 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-assets repo, you need write-access to the assets repo.
      Permissions to Azure/azure-sdk-assets

    Here is the command to push:

    shell
    npx dev-tool test-proxy push

    After 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 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 keyvault-certificates SDK.

    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.json and package.json.
  • Generate a collection of TypeScript sample files (based on x-ms-examples in OpenAPI specs) under samples-dev folder.

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.

shell
cd ${PROJECT_ROOT}
npx dev-tool samples publish -f

You 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.

shell
cd ${PROJECT_ROOT} && pnpm format

Also we'll recommend you to run lint command to analyze your code and quickly find any problems.

shell
cd ${PROJECT_ROOT} && pnpm lint

And we could use lint:fix if there are any errors.

shell
cd ${PROJECT_ROOT} && pnpm lint:fix

How 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.

shell
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1
cd <your-sdk-folder>
export TEST_MODE=record && pnpm test
pnpm pack

You 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.

yaml
# 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: azurerestagrifoodfarming

Please 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.

text
cc @Azure/dpg-devs for awareness

Create 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

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

  1. 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.

  2. 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.

  3. Package Name Convention: follow the format @azure-rest/{service-name}-{module}, like @azure-rest/agrifood-farming.

Steps to generate RLC

  1. Configure tspconfig.yaml in spec repository

    In your specs repository, update or create tspconfig.yaml to configure the TypeScript emitter. Replace YOUR_SERVICE_DIRECTORY, YOUR_SERVICE_FOLDER and YOUR_PACKAGE_NAME with your specific details.

    yaml
    parameters:
      "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"
  2. Generate code

    Initialize a new TypeScript RLC library

    For initial set up, use the tsp-client CLI tool to initialize the generation process. From the root of your local azure-sdk-for-js repository clone, run the following command, replacing YOUR_REMOTE_TSPCONFIG_URL with the URL to your TypeSpec configuration file:

    sh
    npm --prefix eng/common/tsp-client exec --no -- tsp-client init -c YOUR_REMOTE_TSPCONFIG_URL

    If 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:

    shell
    npm --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.yaml

    This command sets up your local SDK repository with the necessary structure and files based on your tspconfig.yaml file 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.

    yaml
    directory: specification/agrifood/DataPlane
    commit: b646a42aa3b7a0ce488d05f1724827ea41d12cf1 # the commit id you'd like to refer for generation
    repo: Azure/azure-rest-api-specs

    Run the update command from SDK directory (i.e sdk/agrifood/agrifood-farming) to re-generate the code:

    shell
    npm --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

  1. Login into your Azure account
  2. Select Subscriptions in the left sidebar
  3. Select whichever subscription is needed
  4. Click on Overview
  5. 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_ID
  • AZURE_CLIENT_SECRET
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID

To set the following environment variables on your development system:

Windows (Note: Administrator access is required)

  1. Open the Control Panel
  2. Click System Security, then System
  3. Click Advanced system settings on the left
  4. Inside the System Properties window, click the Environment Variables… button.
  5. Click on the property you would like to change, then click the Edit… button. If the property name is not listed, then click the New… button.

Linux-based OS :

text
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 :

sh
npm i @azure/arm-compute@latest

You 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 :

sh
npm i @azure/identity

Authentication

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.

typescript
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:

typescript
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

typescript
import { ResourceManagementClient, ResourceGroup } from "@azure/arm-resources";
import { DefaultAzureCredential } from "@azure/identity";

JavaScript

javascript
const resources = require("@azure/arm-resources");
const identity = require("@azure/identity");

Define some global variables
TypeScript

typescript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);

JavaScript

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

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

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

typescript
import {
  ResourceManagementClient,
  ResourceGroup,
  ResourceGroupPatchable,
} from "@azure/arm-resources";
import { DefaultAzureCredential } from "@azure/identity";

JavaScript

javascript
const resources = require("@azure/arm-resources");
const identity = require("@azure/identity");

Authentication and Setup
TypeScript

typescript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);

JavaScript

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

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

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

typescript
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

typescript
async function getResourceGroup(resourceGroupName: string) {
  const get_result = await resourcesClient.resourceGroups.get(resourceGroupName);
  console.log(get_result);
}

JavaScript

javascript
async function getResourceGroup(resourceGroupName) {
  const get_result = await resourcesClient.resourceGroups.get(resourceGroupName);
  console.log(get_result);
}

Delete a resource group
TypeScript

typescript
async function deleteResourceGroup(resourceGroupName: string) {
  await resourcesClient.resourceGroups.delete(resourceGroupName).then((result) => {
    console.log(result);
  });
}

JavaScript

javascript
async function deleteResourceGroup(resourceGroupName) {
  await resourcesClient.resourceGroups.delete(resourceGroupName).then((result) => {
    console.log(result);
  });
}

Manage Resource Group
TypeScript or JavaScript

typescript
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

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

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

typescript
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

typescript
const credential = new DefaultAzureCredential();
const computeClient = new ComputeManagementClient(credential, subscriptionId);
const networkClient = new NetworkManagementClient(credential, subscriptionId);
const resourcesClient = new ResourceManagementClient(credential, subscriptionId);

JavaScript

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

typescript
async function createResourceGroup() {
  const parameter: ResourceGroup = {
    location: "eastus",
    tags: {
      tag1: "value1",
    },
  };
  await resourcesClient.resourceGroups
    .createOrUpdate(resourceGroupName, parameter)
    .then((result) => {
      console.log(result);
    });
}

JavaScript

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

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

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

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

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

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

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

text
/* Detailed source-code truncated for AI context efficiency. */

JavaScript

text
/* 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

typescript
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?

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:

  1. Authentication: The packages @azure/ms-rest-nodeauth or @azure/ms-rest-browserauth are 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
  2. Callbacks: Method overloads that use callbacks have been removed and please use Promise instead. You may find out the examples here
  3. List operations now return an iterable result that follows the PagedAsyncIterableIterator interface 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
  4. 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 use pollUntilDone instead of pollUntilFinished. To get the final result directly, use the method with the suffix AndWait e.g.beginCreateOrUpdateAndWait. You may find out the LRO examples here
  5. The ClientContext class has been removed and all its properties could be found in Client class itself.
  6. The SDK only supports ECMAScript 2015 (ES6) and beyond, all projects that referenced this SDK should be upgraded to use ES6.

Tips:

  1. For more details on how to migrate the next-generation libraries, please visit the migration guide.
  2. To get started, please visit the quickstart guide.
  3. 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
    • If you don't already have a fork, Fork the JS Repo.

    • Clone your fork of the repo.

      text
      git clone https://github.com/{YOUR_GITHUB_USERNAME}/azure-sdk-for-js.git

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,

ps
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 1

Create a local json file named generatedInput.json with content similar to that shown below

text
{
    "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

text
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 --local

path-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:

  1. Generate package code with TypeSpec.
  2. Build the package.
  3. Generate and run tests (optional, with warnings displayed if they fail).
  4. Generate samples, if enabled.
  5. Create or update the CHANGELOG.md.
  6. Bump the version according to the Azure SDK for JS policy.
  7. Generate or update ci.mgmt.yml or ci.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

ps
npm --prefix eng/common/tsp-client ci

For initial set up, from the root of the SDK repo, call

text
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

ps
npm --prefix ../../../eng/common/tsp-client exec --no -- tsp-client update

Notice
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

See steps-after-generations.

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:

  1. Authentication: The packages @azure/ms-rest-nodeauth or @azure/ms-rest-browserauth are no longer supported. Use package @azure/identity instead. Select a credential from Azure Identity examples based on the authentication method of your choice.
  2. Callbacks: Method overloads that use callbacks have been replaced to use Promise instead.
  3. You could iterate the result of List operations by using the PagedAsyncIterableIterator interface, compared with in previous model, you have to make a new request using the link to the next page.
  4. 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 use pollUntilDone instead of pollUntilFinished. To get the final result directly, use the method with the suffix AndWait e.g.beginCreateOrUpdateAndWait.
  5. 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

typescript
import * as msRestNodeAuth from "@azure/ms-rest-nodeauth";
const credentials = msRestNodeAuth.loginWithServicePrincipalSecret(
  clientId,
  clientSecret,
  tenantId
);

into

typescript
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

typescript
const callback = function handle(...args) {
  // callback function body
};
getInstanceView(resourceGroupName, cloudServiceName, callback);

You may change it into a promise based

typescript
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:

typescript
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.

typescript
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.

typescript
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(
text
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) => {
text
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

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

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-tool to the devDependencies in the package.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:

shell
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 --:

shell
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.

text
// 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.

text
sdk/
├─ purview/
│  ├─ purview-datamap-rest/
│  │  ├─ src/
│  │  │  ├─ ...
│  │  ├─ test/
│  │  │  ├─ public/
│  │  │  |  ├─ utils/
│  │  │  |  |  ├─ recordedClient.ts
│  │  │  |  ├─ sampleTest.spec.ts

You 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.

shell
> pnpm install
> pnpm turbo build --filter=@azure-rest/purview-datamap... --token 1

Then, 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.

shell
> cd sdk/purview/purview-datamap-rest
sdk/purview/purview-datamap-rest> pnpm test

If you are the first time to run tests you may fail with below message because there is no any recordings found.

text
[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

shell
# 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 test

This 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.

text
[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.

shell
# 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 test

How 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.

bash
npx dev-tool test-proxy init

Note: 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:

json
{
  "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

bash
npx dev-tool test-proxy push

This command will:

  1. Push your local recordings to a tag in the Azure/azure-sdk-assets repo, and
  2. Update the assets.json in 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:

json
{
  "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:

text
AZURE_TENANT_ID
AZURE_CLIENT_ID
AZURE_CLIENT_SECRET

The 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:

shell
# 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.

typescript
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:

typescript
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.ts and one case named Should create a glossary. Or rename the sampleTest.spec.ts file and its case sample test.
  • Step 2: Add the utility method createClient in public/utils/recordedClient.ts to share the PurviewDataMapClient creation.
    • Call createTestCredential to init your credential and refer here for more details.
    • Wrap the option with test options by calling recorder.configureClientOptions(options).
  • Step 3: In glossary.spec.ts file, call createClient to prepare the client and call client.path("/atlas/v2/glossary").post() to create our glossary resource under our case Should create a glossary.
  • Step 4: Specify environment variables that would be faked in the recordings in map envSetupForPlayback under the file public/utils/recordedClient.ts.
  • Step 5: In glossary.spec.ts file, add necessary assertions in your test case.
  • Step 6: Run and record your test cases.

`glossary.spec.ts`

typescript
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`

text
/* 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.ts and one case named Should create diagnosticSettings. Or rename the sampleTest.spec.ts file and its case sample 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 beforeEach and call client.diagnosticSettings.createOrUpdate in test case.
    • Read the subscriptionId from env.
    • Call createTestCredential to init your credential and refer here for more details.
    • Wrap the option with test options by calling recorder.configureClientOptions(options).
  • 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`

text
/* 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.

yaml
source-code-folder-path: ./generated

Custom 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:

  1. Hand author a PipelinePolicy that takes values for both keys and sign the request
  2. Hand author a wrapping client factory function
  3. In the wrapping factory, we create a new client with the generated factory
  4. Inject the new policy to the client
  5. Return the client
  6. Only expose the wrapping factory and hide the generated factory.

Here is an example implementation.

The wrapping function looks like:

typescript
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.

typescript
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.

typescript
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.

text
/* Detailed source-code truncated for AI context efficiency. */

The example code to call the helper.

typescript
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:

typescript
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:

yaml $(multi-client)
batch:
  - my-service: true
  - my-service-admin: true

Specify 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.

yaml $(my-service) == true
title: MyServiceClient
description: My Service Client
output-folder: ${PROJECT_ROOT}/src
source-code-folder-path: ./client
input-file: /your/swagger/folder/myservice.json
yaml $(my-service-admin) == true
title: MyServiceAdministrationClient
description: My Service Admin Client
output-folder: ${PROJECT_ROOT}/src
source-code-folder-path: ./admin
input-file: /your/swagger/folder/myservice-admin.json

Generate 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:

text
${PROJECT_ROOT}/
├─ src/
│  ├─ client/
│  │  ├─ MyServiceClient.ts
│  │  ├─ index.ts
│  ├─ admin/
│  │  ├─ MyServiceAdministrationClient.ts
│  │  ├─ index.ts
│  ├─ index.ts

Example code to call any client

typescript
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.

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:

  1. 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.

  2. 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).

text
/* Detailed source-code truncated for AI context efficiency. */

This may be installed into a client during construction using the additionalPolicies option:

ts
// 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:

text
##[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-utils package.
  • If the test credentials are specific to your package, create a fakeTestSecrets.ts file under your test folder, 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.

text
> 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-fetch with 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:

text
> 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 audit with 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:

text
> 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-fetch with similar results.

Note: In some previous versions of npm, npm update would only update top-level dependencies. If you're using npm 6.x for example you can provide the --depth argument 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:

ts
// 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:

  1. Begin with the same file name as the ESM flavor
  2. Add the -cjs postfix to the file name
  3. Use .cts extension

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

  1. Update the vitest.config.ts as follows to include the test file you're interested in.
ts
export default mergeConfig(
  {
    ...viteConfig,
    test: {
      include: ["test/public/node/analysis.spec.ts"],
    },
  },
  defineConfig({
    test: {
      testTimeout: 1200000,
      hookTimeout: 1200000,
    },
  }),
);
  1. Set .only on the test you're interested in
ts
it.only("test title...")

Run the test command such as pnpm unit-test:node to run the test

[Method 2] --testNamePattern

bash
pnpm integration-test:node -- --testNamePattern "simple"

Reference - Filtering Tests — Introduction to Testing JavaScript with Vitest

[Method 3] -- test file

bash
pnpm test:node -- test/public/metricsClient.spec.ts

Documentation 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.

  1. Prepare your environment.
    NodeJS: can be installed from https://nodejs.org/en/download/
    typescript: install it with npm install typescript.

  2. Create a empty folder and cd this folder.

    text
    mkdir jstest
    cd jstest
  3. Initialize a new node project.

    text
    npm init

    This step will create a package.json file in current folder.

  4. 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.tgz

    In 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.

  5. Create a ts file (free name and copy follow code into this file), eg: test_1.ts.
    Eg:

    ts
    import { 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:

    ts
    const client = new ComputeManagementClient(credentials, subscriptionID);
    const result= await client.galleries.beginCreateOrUpdateAndWait(resourceGroupName, galleryName, gallery);
    const result= await client.galleryImages.begincreateOrUpdateAndWait(resourceGroupName, galleryName, galleryImageName, galleryImage);
  6. Install all the dependencies

    text
    npm install // need to make sure package.json exists and has contained at step 4.
  7. Compile the ts file.

    text
    tsc test_1.ts

    it will generate a test_1.js file in current folder.

  8. Run the code.

    text
    node test_1.js

    Now, 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 install
  • pnpm 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.

  1. cd sdk/<service-directory>/ if your current directory is not the package directory yet.
  2. 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 isRestError for RestError.
  • Use isKeyCredential for AzureKeyCredential.
  • Use isNamedKeyCredential for AzureNamedKeyCredential.
  • Use isSASCredential for AzureSASCredential.
  • Use isPipelineLike for Pipeline.

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:

  1. Open the dashboard link and sign in with your Microsoft work account if prompted. The link opens the SDK PRs view.
  2. Check the unread activities for management SDK PRs, including new PRs and updates to PRs awaiting follow-up.
  3. 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.
  4. 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 to src/ with merge capabilities

Prerequisites

Before starting the migration, ensure you have:

  1. TypeSpec definitions ready: Your service's TypeSpec definitions should be complete and merged into the main branch of the Azure REST API specs repository
  2. Local development environment:
  3. 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:

bash
npm install @azure-tools/typespec-client-generator-cli

For 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:

yaml
directory: specification/ai/Azure.AI.Projects
commit: a720ec94da68a0d77a691ddd563a4528883638ee
repo: Azure/azure-rest-api-specs
additionalDirectories:
- specification/common-types/resource-management

Step 3: Understand the New Generated Structure

After migration, your package will have this structure:

text
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.json

Key differences from AutoRest:

  • Generated code initially goes to generated/ (package root)
  • The customization tool copies files from generated/ to src/
  • tsp-location.yaml replaces 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):

json
{
  "scripts": {
    "generate": "autorest --typescript swagger/README.md"
  }
}

After (TypeSpec):

json
{
  "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:

json
{
  "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:

Step 6: Generate Initial TypeSpec Code

Run the TypeSpec client generation:

bash
cd sdk/your-service/your-package
npm run generate:client

Step 7: Apply Customizations

Use the dev-tool customization command to copy generated files to src/ and merge with existing customizations:

bash
npx dev-tool customization apply --skip index.ts

This command:

  1. Copies all files from generated/ to src/
  2. Performs 3-way merges with any existing files in src/
  3. 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:

typescript
// 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:

javascript
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

  1. Generate and apply customizations:

    bash
    npm run generate:client
  2. Build the package:

    bash
    pnpm turbo build --filter=<your-package-name>... --token 1
  3. Run tests:

    bash
    pnpm test
  4. Validate 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:

  1. Update TypeSpec definitions in azure-rest-api-specs
  2. Generate new code: npm run generate:client
  3. 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:

  1. Run pnpm install to ensure dependencies are installed
  2. Check that the customization tool completed successfully
  3. Verify all imports are pointing to the correct files in src/

Merge Conflicts During Customization

Problem: The customization tool reports merge conflicts.
Solution:

  1. Examine the conflicted files in src/
  2. Resolve conflict markers manually
  3. Test that the resolution maintains your customizations
  4. Consider updating TypeSpec definitions to reduce future conflicts

API Surface Changes

Problem: Generated API doesn't match expectations.
Solution:

  1. Review your TypeSpec definitions and ensure they're complete
  2. Check emitter options in the generation command
  3. Use the customization workflow to add necessary adaptations

Getting Help

If you encounter issues during migration:

  1. Check existing documentation:

  2. Consult with the team:

    • Post in the TypeSpec Discussion Teams channel
    • Tag @DPG TypeScript for JavaScript/TypeScript-specific questions
  3. 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:

  1. 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 Client at the root level while also using the /api subpath for fine-grained, operation-level imports.
  2. Bundle size optimization: By leveraging the new /api subpath 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.
  3. 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 (beginDoSth and beginDoSthAndWait), 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:

  1. Long-running operations (LROs) – Updated method signatures and poller behavior
  2. 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)

ts
beginStart(
    options?: IntegrationRuntimesStartOptionalParams,
  ): Promise<
    SimplePollerLike<
      OperationState<IntegrationRuntimesStartResponse>,
      IntegrationRuntimesStartResponse
    >
  >;
beginStartAndWait(
    options?: IntegrationRuntimesStartOptionalParams,
  ): Promise<IntegrationRuntimesStartResponse>;

TypeSpec‑generated (current)

ts
start(options?: IntegrationRuntimesStartOptionalParams): PollerLike<
      OperationState<IntegrationRuntimesStartResponse>,
      IntegrationRuntimesStartResponse
    >;

Migrate your usage

ts
// 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(): TState becomes the property operationState?: TState. Guard for undefined before access:

ts
// Before
const status = poller.getOperationState().status;

// Now
const status = poller?.operationState?.status;

Serialization change

ts
// 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

ts
// 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 for undefined).
  • If you previously used resumeFrom, switch to restorePoller(client, serialized, client.xxx).
  • If you depended on stopPolling()/isStopped(), revisit your control flow (these are not exposed on PollerLike).

List operations (paging)

Paging has been simplified in libraries generated from TypeSpec. Two main changes:

  • Removed unsupported maxpagesize
  • Replaced getContinuationToken helper with direct continuationToken property

`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)

ts
const firstPage = await iter.byPage().next();
const continuationToken = getContinuationToken(firstPage);

Now, the token is exposed directly on the page object:

TypeSpec-generated (current)

ts
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:

bash
npx dev-tool customization init

This 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:

text
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:

bash
npx dev-tool customization apply

This command will:

  • Determine the diff between the customized version of your code (in src/) and the previous version of the generated code (what was in generated/ 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_ID
  • AZURE_CLIENT_SECRET
  • AZURE_TENANT_ID
  • AZURE_SUBSCRIPTION_ID

Install the package

As an example, to install the latest Azure VMware Solution module, you would run:

sh
npm i @azure/arm-avs@latest

We also recommend installing the authentication package:

sh
npm i @azure/identity

Authentication

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.

typescript
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:

typescript
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

typescript
import { AzureVMwareSolutionAPI } from "@azure/arm-avs";
import { DefaultAzureCredential } from "@azure/identity";

Define some global variables

typescript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const client = new AzureVMwareSolutionAPI(credential, subscriptionId);

Create a private cloud

typescript
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

typescript
import { AzureVMwareSolutionAPI } from "@azure/arm-avs";
import { DefaultAzureCredential } from "@azure/identity";

Authentication and set up

typescript
const subscriptionId = process.env.AZURE_SUBSCRIPTION_ID;
const credential = new DefaultAzureCredential();
const client = new AzureVMwareSolutionAPI(credential, subscriptionId);

Update a private cloud

typescript
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

typescript
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

typescript
async function privateCloudsGet(resourceGroupName: string, privateCloudName: string): Promise<void> {
  const result = await client.privateClouds.get(resourceGroupName, privateCloudName);
  console.log(result);
}

Delete a private cloud

typescript
async function privateCloudsDelete(resourceGroupName: string, privateCloudName: string): Promise<void> {
  await client.privateClouds.delete(resourceGroupName, privateCloudName);
}

Manage private clouds

typescript
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?

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"):

  1. Find out your upstream remote name for Azure/azure-sdk-for-js repository: git remote -v
  2. Get the latest changes from the upstream main branch: git fetch upstream main
  3. Merge the latest changes from main to local pull request branch: git merge --no-edit upstream/main
  4. Use your favorite IDE, editor, or Git Client to resolve conflicts for files other than pnpm-lock.yaml
  5. Check out the main branch version of pnpm-lock.yaml: git checkout upstream/main -- pnpm-lock.yaml
  6. Refresh dependencies: pnpm install
  7. Stage the updates: git add pnpm-lock.yaml
  8. Commit the merge
  9. 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:

shell
git checkout upstream/main `git rev-parse --show-toplevel`/pnpm-lock.yaml && pnpm install && git add `git rev-parse --show-toplevel`/pnpm-lock.yaml

Documentation 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:

typescript
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:

  1. Initialize your client
  2. Send the request
  3. Handle the response

We will go into each step in the following sections

1. Initialize the Client

First, import the client

typescript
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.

typescript
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

typescript
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.

typescript
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"]);
// 1024

Payload

Our REST clients have types to help users auto-complete the paths and build request payloads for example

typescript
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);
// 200

Headers and Query Parameters

REST clients also have types to help users sending required and optional query parameters and headers

typescript
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

typescript
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 allowlisted
azure-sdk-automation[bot]; that bot cannot request the other reviewers.

For example, to request an architecture review:

  1. Open your pull request on GitHub.
  2. If you have permission to manage labels, use the Labels sidebar to add
    the architecture-review-needed label.
  3. 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.
  4. 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:

text
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's
request 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 replaces
architecture-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. Leave
request_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.

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

Invoking an Agent

In the Copilot Chat panel, use the @ prefix to summon an agent by name:

text
@archie Review the changes in this branch for API design issues
text
@sentinel Check the staged changes for security vulnerabilities
text
@tester Are there any untested public methods in sdk/storage/storage-blob/src/BlobClient.ts?
text
@dexter Review the dependency changes in sdk/keyvault/keyvault-keys/package.json
text
@scribe Check that the README and CHANGELOG are consistent with the code changes
text
@dash Look for performance regressions in the current diff
text
@mgmt-review Review this management SDK PR for API design guideline compliance

Available 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:

text
.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.yml via
    gh 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 with node --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:

bash
gh aw compile <agent-name>

Declare the publishing job's validation dependency under
jobs.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.

  1. make sure docker is 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 use sudo to invoke it. You can do so by executing usermod -aG docker .
  2. make sure the docker daemon is up and running.
  3. download the artifact created by the release pipeline and save the path for it.
  4. navigate to your package directory in the command line and make sure your samples directory and .env file exist
  5. 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:

  1. 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.

tsp
import "./main.tsp";
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core;

// Add your customizations here
  1. 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:

yaml
options:
  "@azure-tools/typespec-ts":

Breaking Changes That Can Be Resolved with Client Customizations or Configurations

1. Client Naming Changes

Changelog Pattern:

md
- Deleted Class AdvisorManagementClient

Spec Pattern:

The client name is derived from the service namespace, not from the @service title.

tsp
@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:

tsp
@@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:

md
- Removed Interface Vm
- Added Interface VM

Spec Pattern:

Find the model using the added name; casing or namespace differences cause the rename.

tsp
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:

tsp
@@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:

md
- Removed Type Alias IpFamily
- Removed Enum KnownIpFamily
- Added Type Alias IPFamily
- Added Enum KnownIPFamily

Spec Pattern:

Find the enum using the added name; casing or namespace differences cause the rename.

tsp
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:

tsp
@@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:

md
- Enum KnownAlertSeverity no longer has value Zero
- Enum KnownAlertSeverity no longer has value One
- Enum KnownAlertSeverity added value AlertSeverity0
- Enum KnownAlertSeverity added value AlertSeverity1

Spec Pattern:

tsp
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:

tsp
@@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:

md
# Breaking Changes
- Removed operation ManagementGroups.listDescendants

# Features Added
- Added operation ManagementGroups.getDescendants

Spec Pattern:

The operation is a list operation (marked with @list), but the spec op name starts with get:

tsp
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:

tsp
@@clientName(ManagementGroups.getDescendants, "listDescendants", "javascript");

6. Removal of Flattened Properties

Changelog Pattern:

A model loses several direct properties (which move under a properties bag):

md
- 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 properties

Spec Pattern:

tsp
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:

tsp
#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:

md
- Removed operation ApiDefinitions.beginExportSpecification
- Removed operation ApiDefinitions.beginExportSpecificationAndWait
- Removed operation ApiDefinitions.beginImportSpecification
- Removed operation ApiDefinitions.beginImportSpecificationAndWait
- Removed operation Services.beginExportMetadataSchema
- Removed operation Services.beginExportMetadataSchemaAndWait

Breaking: 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:

yaml
options:
  "@azure-tools/typespec-ts":
    compatibility-lro: true

Breaking 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:

md
- 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 ErrorDetail

Breaking: 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:

md
- Removed Interface Resource
- Removed Interface ProxyResource
- Removed Interface ErrorResponse
- Removed Interface ErrorAdditionalInfo

Breaking: 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:

md
- Removed Interface MeshRevisionProfileList
- Removed Interface OutboundEnvironmentEndpointCollection

Breaking: 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:

md
- Removed Interface XxxCreateOptionalParams

Breaking: 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:

md
- Removed Interface XxxResponse
- Removed Interface XxxHeaders
- Removed Interface XxxNextResponse

Breaking: 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:

md
- Type of parameter type of interface Activity is changed from "Container" | "Execution" | ... to string
- Type alias "ActivityUnion" has been changed

Breaking: 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:

md
- Removed Type Alias AmazonRdsForOraclePartitionOption
- Removed Type Alias AvroCompressionCodec
- Removed Enum KnownAmazonRdsForOraclePartitionOption
- Removed Enum KnownAvroCompressionCodec

Breaking: 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:

text
/plugin marketplace add Microsoft/skills

Install the Azure SDK skills plugin:

text
/plugin install azure-sdk-typescript@skills

Verify installation:

text
/plugin list

Update the plugin:

text
/plugin update azure-sdk-typescript@skills

Skills 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

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.

shell
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1

Customizing 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:

text
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 1

After you build your package, run

text
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js <your-package-path>

Here is the example

text
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js sdk/advisor/arm-advisor

Improve 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.

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.

  1. 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:

  2. 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_MODE to record. This will generate recordings for your test they could be used in playback mode.

    On Linux, you could use export to set env variable:

    shell
    pnpm 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:

    shell
    pnpm 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 playback mode test if your apis don't have breaking changes and you've already done the recording before.

    On Linux, you could use below commands:

    shell
    pnpm 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:

    shell
    pnpm 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:

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.

json
"paths": { "@azure/agrifood-farming": ["./src/index"] }

And change the "include" part to

json
"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.

json
"//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.

shell
cd ${PROJECT_ROOT}
npx dev-tool samples publish -f

You 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.

shell
cd ${PROJECT_ROOT} && pnpm format

Also, we'll recommend you to run lint command to analyze your code and quickly find any problems.

shell
cd ${PROJECT_ROOT} && pnpm lint

And we could use lint:fix if there are any errors.

shell
cd ${PROJECT_ROOT} && pnpm lint:fix

Adding 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 of
pnpm lint), which validates the dependencies field of every in-scope
package.json against a central allow-list. devDependencies and
peerDependencies 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:

  1. Get the dependency approved (size, license, maintenance, and supply-chain
    considerations — see the dependency review guidance).
  2. Add it to eng/approved-third-party-dependencies.yml:
    • Add it under allowed if every package should be able to use it.

    • Add a scoped entry under exceptions if only specific packages should be
      able to use it:

      yaml
      exceptions:
        - 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/*" under
allowed 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.

shell
pnpm install
pnpm turbo build --filter=<your-package-name>... --token 1
cd <your-sdk-folder>
export TEST_MODE=record && pnpm test
pnpm pack

You 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.

yaml
# 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 RLC

Please 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:

  1. checkout to main branch and pull the latest code
  2. checkout the PR's branch
  3. run git merge main
  4. run git checkout origin/main pnpm-lock.yaml
  5. run pnpm install
  6. 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).

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.

  1. Install the GitHub Pull Requests extension in VS Code if you haven't already.
  2. Open the GitHub Pull Requests panel from the Activity Bar (the GitHub icon).
  3. Find the AutoPR by its PR number or search by branch name.
  4. 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.

  1. Add the remote (skip if already added):
    bash
    git remote add {REMOTE_NAME} https://github.com/azure-sdk/azure-sdk-for-js.git
  2. Fetch the specific branch:
    bash
    git fetch {REMOTE_NAME} {BRANCH_NAME}
  3. Create a local branch that tracks the remote branch:
    bash
    git checkout -b {LOCAL_BRANCH_NAME} --track {REMOTE_NAME}/{BRANCH_NAME}

Full example:

bash
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-5699155

Option 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.

  1. Add the remote (skip if already added):
    bash
    git remote add {REMOTE_NAME} https://github.com/azure-sdk/azure-sdk-for-js.git
  2. Fetch all branches from the remote:
    bash
    git fetch {REMOTE_NAME}
  3. Checkout the branch:
    bash
    git checkout {BRANCH_NAME}

Full example:

bash
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-5699155

CC dpg-devs for review

Please add below comment in your PR to include dpg-devs to review your PR timely.

text
cc @Azure/dpg-devs for awareness

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 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

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 ManagedIdentityCredential

      • If not, use the EnvironmentCredential

    • Do you want your application to pick the appropriate credential type based on the
      environment?

      • Use the DefaultAzureCredential
  • 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 AuthorizationCodeCredential or
        InteractiveBrowserCredential

      • If not, use the DeviceCodeCredential

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.

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/identity only 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 and
password 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 pass
organizations 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 the
ClientSecretCredential, ClientCertificateCredential or
UsernamePasswordCredential 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 with
    ClientSecretCredential
  • AZURE_CLIENT_CERTIFICATE_PATH - The path to a PEM-formatted certificate file
    in the deployment environment to be used with the
    ClientCertificateCredential
  • AZURE_USERNAME and AZURE_PASSWORD - The username and password pair to be
    used with the UsernamePasswordCredential

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:

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:

  • EnvironmentCredential
  • WorkloadIdentityCredential
  • ManagedIdentityCredential
  • VisualStudioCodeCredential
  • AzureCliCredential
  • AzurePowerShellCredential
  • AzureDeveloperCliCredential
  • BrokerCredential (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

  1. Overall Architecture
  2. Overall Flow & Utilities
  3. SDK Generation Steps
  4. Folder Cleanup Logic
  5. Changelog & Version Bump (Common)
  6. 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

text
/* 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 (Release or Local): Perform targeted cleanup to preserve manually authored files.
  • Automation modes (SpecPullRequest or Batch): 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/ and assets.json applies 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-folder without 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:

  1. src/generated/ — if it exists (customization pattern: hand-authored code in src/, generated code in src/generated/)
  2. generated/ — if it exists (alternative customization pattern)
  3. 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: If tspconfig.yaml sets clearOutputFolder: true, the emitter wipes the entire package directory (preserving only TempTypeSpecFiles/) 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

text
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 changelog

Key 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:

json
{
  "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:

  1. Input the package path.
  2. Get the package name and download the latest release from NPM.
  3. 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.
  4. 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.
  5. Changelog Tool parses the typescript blocks in the api.md to ASTs, and extracts useful information from the ASTs.
  6. 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:

typescript
export class TSExportedMetaData {
  public typeAlias = {};
  public operationInterface = {};
  public modelInterface = {};
  public enums = {};
  public classes = {};
}

This is an example of the extracted information:

json
{
  "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:

text
## 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 None

Eng 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:

  1. Use Track2 Codegen to generate codes by inputting swagger, and then run pnpm install && pnpm build.
  2. Use Track2 Codegen to generate codes in swagger pipeline.
  3. Generate changelog and bump version for Azure Track2 JS SDK.

Install

From the azure-sdk-for-js repository root:

shell script
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 1

Tools

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.

shell script
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

If you want to add more parameters to autorest command, use can use parameter additionalArgs. For example:

shell script
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.

shell script
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

shell script
cd azure-sdk-for-js
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js ${package-path}

example:

text
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js sdk/compute/arm-compute

Eng 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

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 1

PowerShell

powershell
$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 1

How to use

Prerequisites

  1. Install Any of the LTS versions of Node.js.
  2. Install autorest: npm install autorest.
  3. 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:
shell
cd azure-sdk-for-js
pnpm install

Steps

Step 1. Run command:

text
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 if swagger/README.md exists. 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 in swagger/README.md if 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:

text
Which resource provider do you want to store your package in sdk folder? Please input it: purview

You 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 test folder.

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:

  1. Generate swagger/README.md
  2. Generate source code by codegen
  3. Add/update ci.yml
  4. Add/update ci.yml

Build Codes:

  1. Run pnpm install
  2. Run pnpm build --filter <your package name>...
  3. Generate changelog(Because released package doesn't include review folder, so we generate an initial release changelog if there is no existing CHANGELOG.md.)
  4. 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:

  1. 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.
  2. it works only with clients that use @azure/core-client, so clients that use @azure-rest/core-client or @azure/core-http can 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 implements LongRunningOperation<T>
  • a class that implement LongRunningOperation<T> that works with clients that use either @azure/core-http and @azure/core-client. @joheredi also created one for @azure-rest/core-client in 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:

ts
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:

ts
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:

ts
sendPollRequest: (path: string) => Promise<LroResponse<T>>;

This method takes the polling path as input and here is what a simplified implementation would look like:

ts
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:

ts
class LroEngine<TResult, TState extends PollOperationState<TResult>> extends Poller<TState, TResult>

The class also has the following constructor:

ts
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`

ts
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`

ts
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

typescript
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

typescript
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.

typescript
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.

ts
const status = poller.getOperationState().status;

now

ts
const status = poller?.operationState?.status;

If you want to serialize a poller, use the serialize instead.

ts
const serializeState = poller.toString();

now

ts
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.

text
/* 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:

ts
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:

ts
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 requests
  • restoreFrom: enables creating a poller for an existing operation from a serialized state
  • resourceLocationConfig: controls the polling behavior and typically used only when the LRO swagger extension is used in the swagger
  • withOperationLocation: 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 pageable
  • resolveOnUnsuccessful: 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:

  • PollerLike has isCanceled, isFailed, and isCompleted boolean fields but SimplePollerLike has status which is a closed enum of notStarted, running, succeeded, failed, and canceled values. Using status improves 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.
  • PollerLike has a cancelOperation method that is not implemented by the majority of LROs, which is misleading to our customers. SimplePollerLike doesn'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 updateState to add isCanceled, isFailed, and isCompleted fields to your poller state
  • tack on a cancelOperation method

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:

fundamental
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.

ts
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
ts
/**
 * Copyright (c) Microsoft Corporation.
 * Licensed under the MIT license.
 */
ts
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// Other comment text here.

Bad

ts
// Copyright (c) Microsoft Corporation.
ts
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:

  • erase
  • fetch
  • getAll
  • insertOrUpdate
  • make
  • pop
  • push
  • updateOrInsert

Examples

Good

ts
// 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 {}
}
ts
// private methods are ignored
class ServiceClient {
  private makeItem(): void {}
}
ts
// classes not suffixed with "Client" are ignored
class ServiceItem {
  makeItem(): void {}
}

Bad

ts
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

ts
class ServiceClient {
  async createItem(cancelToken: AbortSignalLike);
}
ts
interface ServiceOptions {
  cancelToken: AbortSignalLike;
}

class ServiceClient {
  async createItem(options: ServiceOptions);
}
ts
// private methods are ignored
class ServiceClient {
  private createItem(): void {}
}

Bad

ts
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 * to export type * alters the module contract and can cause unexpected breakage downstream.

Examples

Good

ts
// 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

ts
// 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

ts
/**
 * 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) {}
}
ts
/**
 * 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

ts
/**
 * 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):

json
{
  "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

ts
/**
 * Other documentation
 * @internal
 */
class ExampleClass {}
ts
/**
 * Other documentation
 * @hidden
 */
interface ExampleInterface {}
ts
/**
 * Other documentation
 * @internal
 */
function exampleFunction(): void {}

Bad

ts
/**
 * Other documentation
 */
class ExampleClass {}
ts
/**
 * Other documentation
 */
interface ExampleInterface {}
ts
/**
 * 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

ts
throw new Error("error");
ts
const err = new TypeError("invalid type");
throw err;
ts
try {
  /* code here */
} catch (err) {
  throw err;
}

Bad

ts
throw 1;
ts
throw "error";
ts
throw new OtherError("error");
ts
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):

json
{
  "settings": {
    "main": "index.ts"
  }
}

Examples

All examples are representative of the main entrypoint.

Good

ts
export /* package contents */ {};
ts
export /* package contents */ {};
ts
export const package = {/* package contents */};

Bad

ts
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

ts
class ServiceClient {
  create(): ServiceClient {
    /* code to return instance of ServiceClient */
  }
}
ts
// private methods are ignored
class ServiceClient {
  private _createService(): ServiceClient {
    /* code to return instance of ServiceClient */
  }
}

Bad

ts
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

ts
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

ts
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

ts
class ServiceClient {
  getSubClient(): SubClient {
    /* code to return instance of SubClient */
  }
}
ts
// private methods are ignored
class ServiceClient {
  private _get(): SubClient {
    /* code to return instance of SubClient */
  }
}

Bad

ts
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

ts
enum Directions {
  North,
  East,
  South,
  West,
}

Bad

ts
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:

ts
import { fork, type ChildProcess } from "node:child_process";

Use the core package for external executables:

ts
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/ or tests/ segment

The rule is cross-platform and does not depend on the current working directory.

Examples

Incorrect

ts
// tests/client.test.ts
import { Client } from "../src/client";
import { helper } from "../../src/helpers/helper";

Correct

ts
// 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 utilities

Options

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

ts
const navigator = self.navigator as NavigatorEx;

Bad

ts
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 and
peerDependencies 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 under allowed (available to every package) or under an exceptions
    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

json
{
  "name": "@azure/example",
  "sdk-type": "client",
  "dependencies": {
    "@azure/core-rest-pipeline": "^1.0.0",
    "tslib": "^2.6.2"
  }
}

Bad

json
{
  "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

json
{
  "author": "Microsoft Corporation"
}

Bad

json
{
  "author": "Microsoft"
}
json
{}

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

json
{
  "bugs": {
    "url": "https://github.com/Azure/azure-sdk-for-js/issues"
  }
}

Bad

json
{
  "bugs": {
    "url": "https://github.com/Azure/azure-sdk-for-java/issues"
  }
}
json
{
  "bugs": {}
}
json
{}

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

json
{
  "engines": {
    "node": ">=14.0.0"
  }
}

Bad

json
{
  "engine": {
    "node": ">=14.0.0"
  }
}
json
{
  "engine": ">=6.0.0"
}
json
{
  "engine": ">=10.0.0"
}
json
{}

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:

json
{
  "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

json
{
  "files": ["dist", "dist-esm/src"]
}
json
{
  "files": ["./dist", "./dist-esm/src"]
}
json
{
  "files": ["dist/", "dist-esm/"]
}
json
{
  "files": ["dist/lib", "dist-esm/src/lib"]
}

Bad

json
{
  "files": ["dist", "src"]
}
json
{
  "files": ["dist"]
}
json
{
  "files": []
}
json
{}

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

json
{
  "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/servicebus/service-bus/README.md"
}
json
{
  "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/servicebus/service-bus/"
}

Bad

json
{
  "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/README.md"
}
json
{
  "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main"
}
json
{
  "homepage": "https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/servicebus/service-bus/README.md"
}
json
{}

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

json
{
  "keywords": ["azure", "cloud"]
}
json
{
  "keywords": ["azure", "cloud", "sdk"]
}

Bad

json
{
  "keywords": ["azure"]
}
json
{
  "keywords": []
}
json
{}

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

json
{
  "main": "dist/index.js"
}

Good

json
{
  "main": "dist/index.cjs"
}

Bad

json
{
  "main": "dist/src/index.js"
}
json
{
  "main": "dist-esm/src/index.js"
}
json
{}

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

json
{
  "name": "@azure/service-bus"
}

Bad

json
{
  "name": "@microsoft/service-bus"
}
json
{
  "name": "service-bus"
}
json
{
  "name": "@azure/serviceBus"
}
json
{}

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

json
{
  "repository": "github:Azure/azure-sdk-for-js"
}

Bad

json
{
  "repository": "github:Azure/azure-sdk-for-java"
}
json
{}

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

json
{
  "scripts": {
    "build": "...",
    "test": "..."
  }
}
json
{
  "scripts": {
    "build": "...",
    "lint": "...",
    "test": "..."
  }
}

Bad

json
{
  "scripts": {
    "build": "..."
  }
}
json
{
  "scripts": {}
}
json
{}

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

json
{
  "sdk-type": "client"
}
json
{
  "sdk-type": "mgmt"
}
json
{
  "sdk-type": "provisioning"
}
json
{
  "sdk-type": "utility"
}

Bad

json
{
  "sdk-type": "invalid"
}
json
{
  "sdk-type": 1
}
json
{
  "sdk-type": true
}
json
{
  "not-sdk-type": "client"
}
json
{
  "outer": {
    "sdk-type": "client"
  }
}
json
{}

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

json
{
  "sdk-type": "client",
  "sideEffects": false
}
json
{
  "sdk-type": "provisioning",
  "sideEffects": true
}

Bad

json
{
  "sdk-type": "client",
  "sideEffects": true
}
json
{
  "sdk-type": "provisioning",
  "sideEffects": false
}
json
{}

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

json
{
  "types": "types/index.d.ts"
}

Bad

json
{
  "types": "types/index.ts"
}
json
{}

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

ts
class ServiceClient {
  listItems(): PagedAsyncIterableIterator<Item> {
    /* code to return instance of PagedAsyncIterableIterator<Item> */
  }
}

Bad

ts
// no return type
class ServiceClient {
  listItems() {
    /* code here */
  }
}
ts
// 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

ts
// filename: example.ts
import { dirname, fileURLToPath } from "node:path";
const currentDir = dirname(fileURLToPath(import.meta.url));
ts
// filename: example-cjs.cts
import path from "node:path";
const currentDir = __dirname;

Bad

ts
// filename: example.ts
const currentDir = __dirname; // This will cause an error in ESM files

When 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:

ts
import ExternalClass from "example-library";

class BasicClass {}

interface BasicInterface {}

interface NestedInterface {
  interface: BasicInterface;
}

interface ClassInInterface {
  class: BasicClass;
}

Good

ts
function example(param: BasicInterface) {
  /* code here */
}
ts
function example(param: NestedInterface) {
  /* code here */
}
ts
function example(param?: BasicClass) {
  /* code here */
}
ts
class Example {
  constructor(param: BasicClass);
  constructor(param: BasicInterface);
}
ts
class Example {
  private _example(param: BasicClass);
}

Bad

ts
function example(param: BasicClass) {
  /* code here */
}
ts
function example(param1: BasicInterface, param2: BasicClass) {
  /* code here */
}
ts
function example(param: BasicClass[]) {
  /* code here */
}
ts
function example(param: ClassInInterface) {
  /* code here */
}
ts
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

ts
const promise = (): Promise<string> => {
  return new Promise((resolve) => resolve("hi"));
};

Bad

ts
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 0 are not permitted,
  • Alpha versions must be in the format <version>-alpha.<date>.<alpha-version> where date is an integer representing a particular day (e.g. 20200728 is July 28th, 2020), and alpha-version is an integer.
  • Beta versions must be in the format <version>-beta., where beta-version is an integer.
  • Preview versions must be in the format <version>-preview., where preview-version is an integer.
  • Development versions must be in the format <version>-dev.<date>.<dev-version> where date is an integer representing a particular day (e.g. 20200728 is July 28th, 2020), and dev-version is an integer.

Examples

Good

json
{
  "version": "1.0.0"
}
json
{
  "version": "1.0.0-alpha.20200728.1"
}
json
{
  "version": "1.0.0-beta.1"
}

Bad

json
{
  "version": "1.0.01"
}
json
{
  "version": "1.0"
}
json
{
  "version": "1"
}
json
{
  "version": "0.0.1"
}
json
{
  "version": "1.0.0.0"
}
json
{
  "version": "1.0.0-preview.1"
}
json
{
  "version": "1.0.0-dev.20200728.1"
}
json
{}

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 ---