# Technical Documentation: Azure/azure-sdk-for-js

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/Azure/azure-sdk-for-js)

### Overview (README.md)

# Azure SDK for JavaScript

[](https://azure.github.io/azure-sdk/releases/latest/js.html) [](https://azuresdkartifacts.blob.core.windows.net/azure-sdk-for-js/dependencies/dependencies.html) [](https://azuresdkartifacts.blob.core.windows.net/azure-sdk-for-js/dependencies/dependencyGraph/dagre.html)

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](https://www.npmjs.com/).
- The API reference documentation of the latest versions of these packages, can be found at our [public developer docs](https://learn.microsoft.com/javascript/azure/).
- The API reference documentation of older versions, can be found in our [versioned developer docs](https://azure.github.io/azure-sdk-for-js).

Each service might have a number of libraries available from each of the following categories:

- [Client](#client)
- [Management](#management)

> 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](https://azure.github.io/azure-sdk/typescript_introduction.html) 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](https://azure.github.io/azure-sdk/releases/latest/js.html).

### Management

Management libraries enable you to provision and manage Azure resources via the [Azure Resource Manager i.e. ARM](https://learn.microsoft.com/azure/azure-resource-manager/management/overview). 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](https://azure.github.io/azure-sdk/typescript_introduction.html). These new versions provide a number of core capabilities that are shared amongst all Azure SDKs, including the intuitive Azure Identity library, an HTTP Pipeline with custom policies, error-handling, distributed tracing, and much more. A few helpful resources to get started with these are:

- [List of management libraries that follow the new guidelines](https://azure.github.io/azure-sdk/releases/latest/mgmt/js.html)
- [Documentation and code samples](https://aka.ms/azsdk/js/mgmt).
- [Migration guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md) that shows how to transition from older versions of libraries.

> NOTE: If you are experiencing authentication issues with the management libraries after upgrading certain packages, it's possible that you upgraded to the new versions of SDK without changing the authentication code, please refer to the migration guide mentioned above for proper instructions.

## Need help?

- For detailed documentation, visit our [Azure SDK for JavaScript documentation](https://aka.ms/js-docs)
- File an issue via [GitHub Issues](https://github.com/Azure/azure-sdk-for-js/issues)
- Check [previous questions](https://stackoverflow.com/questions/tagged/azure-sdk-js) or ask new ones on StackOverflow using `azure-sdk-js` tag.
- Read our [Support documentation](https://github.com/Azure/azure-sdk-for-js/blob/main/SUPPORT.md).

## 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](https://go.microsoft.com/fwlink/?LinkID=824704). For more information on the data collected by the Azure SDK, please visit the [Telemetry Guidelines](https://azure.github.io/azure-sdk/general_azurecore.html#telemetry-policy) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/SUPPORT.md#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) <secure@microsoft.com>. 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](https://www.microsoft.com/msrc/faqs-report-an-issue).

## Contributing

For details on contributing to this repository, see the [contributing guide](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md).

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](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) 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](https://github.com/Azure/azure-rest-api-specs) 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](https://teams.microsoft.com/l/channel/19%3a906c1efbbec54dc8949ac736633e6bdf%40thread.skype/TypeSpec%2520Discussion%2520%25F0%259F%2590%25AE?groupId=3e17dcb0-4257-4a30-b843-77f47f1d4121&tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47) channel. Tag `@DPG TypeScript` for JavaScript/TypeScript-specific inquiries.

Join the [JavaScript - Reviews](https://teams.microsoft.com/l/channel/19%3a408c5f1322ee4303b02b5da9c5ff6137%40thread.skype/Language%2520-%2520JavaScript%2520-%2520Reviews?groupId=3e17dcb0-4257-4a30-b843-77f47f1d4121&tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47) channel for assistance with the API review process.

## Prerequisites

- [LTS versions of Node.js](https://nodejs.org/en/about/releases/)
- Install pnpm with the [pnpm installation instructions](https://pnpm.io/installation).
- Install tsp-client dependencies with `npm --prefix eng/common/tsp-client ci`

# Set up your development environment

Follow the [setup guide](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) 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](https://www.npmjs.com/). 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](https://github.com/Azure/azure-sdk-for-js) repo, so fork and clone it in your local then the absolute path is called **${SDK_REPO_ROOT} folder**.

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

1. **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](https://github.com/Azure/azure-sdk-tools/blob/main/doc/common/TypeSpec-Project-Scripts.md#tsp-locationyaml) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/emitter-package.json) and relevant lock file [emitter-package-lock.json](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) for the environment set up prerequisites in `azure-sdk-for-js` repository. We highly recommend to read [this blog](https://devblogs.microsoft.com/azure-sdk/azure-rest-libraries-for-javascript/) to get familiar with REST libraries for JavaScript.

:warning: Note: if you’re generating from TypeSpec with RLC, please read [this doc](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/RLC-quickstart.md) 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**.
1. 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`:

    ~~~

    # 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](https://github.com/Azure/autorest/blob/main/docs/generate/authentication.md).

    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](https://www.npmjs.com/package/@autorest/typescript) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/maps/maps-route-rest/README.md).


# 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md) 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](https://github.com/PowerShell/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.
    ```

1. **Write the test**

    You could follow the [basic RLC test interaction and recording example](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service) to write your test step by step. Also you could refer [the test of MapsRouteClient](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/maps/maps-route-rest/test/public) 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.
    ```

3. **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`](https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/785/Externalizing-Recordings-(Asset-Sync)?anchor=permissions-to-%60azure/azure-sdk-assets%60)


    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](https://github.com/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](https://github.com/Azure/azure-sdk-assets/tree/js/keyvault/keyvault-certificates_43821e21b3).


# 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](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/maps/maps-route-rest/samples-dev) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/RLC-customization.md) 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](https://semver.org/).

## 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Troubleshoot-ci-failure.md) to fix it.

## CC dpg-devs for review

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

```
cc @Azure/dpg-devs for awareness
```

# Create API View

When submitting a PR, our pipeline would automatically prepare the API view in [API View Website](https://apiview.dev/). You could see an [example link](https://github.com/Azure/azure-sdk-for-js/pull/23866#issuecomment-1316259448) 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](https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/8/Release-Checklist?anchor=prepare-release-script) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/DPG-quickstart.md).

:warning: Ensure that your TypeSpec definition has been merged into the main branch of the [Azure REST API specs repository](https://github.com/Azure/azure-rest-api-specs) 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](https://teams.microsoft.com/l/channel/19%3a906c1efbbec54dc8949ac736633e6bdf%40thread.skype/TypeSpec%2520Discussion%2520%25F0%259F%2590%25AE?groupId=3e17dcb0-4257-4a30-b843-77f47f1d4121&tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47) channel. Tag `@DPG TypeScript` for JavaScript/TypeScript-specific inquiries.

Join the [JavaScript - Reviews](https://teams.microsoft.com/l/channel/19%3a408c5f1322ee4303b02b5da9c5ff6137%40thread.skype/Language%2520-%2520JavaScript%2520-%2520Reviews?groupId=3e17dcb0-4257-4a30-b843-77f47f1d4121&tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47) channel for assistance with the API review process.

## Prerequisites

- [LTS versions of Node.js](https://nodejs.org/en/about/releases/)
- Install pnpm via the [pnpm installation instructions](https://pnpm.io/installation).
- Install tsp-client dependencies with `npm --prefix eng/common/tsp-client ci`

# Set up your development environment

Follow the [setup guide](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) 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](https://www.npmjs.com/). 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](https://github.com/Azure/azure-sdk-for-js) repo, so fork and clone it in your local. Then, the absolute path is called **${SDK_REPO_ROOT} folder**.

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

1. **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](https://github.com/Azure/azure-sdk-tools/blob/main/doc/common/TypeSpec-Project-Scripts.md#tsp-locationyaml) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/emitter-package.json) and relevant lock file [emitter-package-lock.json](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md) 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](https://azure.github.io/azure-sdk/typescript_design.html) 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](https://www.npmjs.com) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md) 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](https://learn.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal)

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

    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](https://www.npmjs.com) 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`](https://www.npmjs.com/package/@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

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

JavaScript

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

The following example shows how to delete a Virtual Machine

**_Deleting a Virtual Machine_**  
TypeScript or JavaScript

```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](https://aka.ms/azsdk/js/mgmt/samples)

_Please Note that these samples provided in JS/TS SDK Code Samples are written in TypeScript_

## Need help?

- File an issue via [Github
  Issues](https://github.com/Azure/azure-sdk-for-js/issues)

## Contributing

For details on contributing to this repository, see the contributing
guide.

This project welcomes contributions and suggestions. Most contributions
require you to agree to a Contributor License Agreement (CLA) declaring
that you have the right to, and actually do, grant us the rights to use
your contribution. For details, visit <https://cla.microsoft.com>.

When you submit a pull request, a CLA-bot will automatically determine
whether you need to provide a CLA and decorate the PR appropriately
(e.g., label, comment). Simply follow the instructions provided by the
bot. You will only need to do this once across all repositories using
our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For
more information see the Code of Conduct FAQ or contact
<opencode@microsoft.com> 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](https://www.npmjs.com/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](https://www.npmjs.com/package/@azure/identity)  
1. Callbacks: Method overloads that use callbacks have been removed and please use Promise instead. You may find out the examples [here](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md#callbacks)  
1. 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md#list-operations) 
1. 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md#long-running-operations)  
1. The ClientContext class has been removed and all its properties could be found in Client class itself.  
1. 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/MIGRATION-guide-for-next-generation-management-libraries.md).**  
1. **To get started, please visit the [quickstart guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/next-generation-quickstart.md).**  
1. **For more sample code, please visit our [samples repo](https://github.com/Azure-Samples/azure-sdk-for-js-samples).**

---

### 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](https://azure.github.io/azure-sdk/typescript_introduction.html) is the overall design guideline of the client SDK.

## Prerequisites

- [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule)
- [Git](https://git-scm.com/downloads)
- Local Clone of Rest API Spec Repo Fork
  - If you don't already have a fork, [Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#forking-a-repository) the [Rest API Spec Repo](https://github.com/Azure/azure-rest-api-specs).
  - Clone your fork of the repo.
    ```
      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](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#forking-a-repository) the [JS Repo](https://github.com/Azure/azure-sdk-for-js).
  - Clone your fork of the repo.

    ```
      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](https://github.com/Azure/azure-rest-api-specs). Please refer to [these guidelines](https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/Getting-started-with-TypeSpec-specifications.md).

### How to configure tspconfig.yaml
You can reference these two config files to configure the Modular or RLC package:
- [Modular tspconfig.yaml](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml)
- [RLC tspconfig.yaml](https://github.com/Azure/azure-rest-api-specs/blob/main/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml)

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](#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
```
  {
    "dryRun": false,
    "specFolder": <your-local-spec-repo-path>,
    "headSha": <commit-id-you-want-to-generate-from>,
    "repoHttpsUrl": "https://github.com/Azure/azure-rest-api-specs",
    "relatedTypeSpecProjectFolder": [
    "specification/SERVICE_DIRECTORY_NAME/PACKAGE_DIRECTORY_NAME/"
    ]
  }
```

Run the command
```
node eng/tools/js-sdk-release-tools/dist/autoGenerateInPipeline.js --inputJsonPath=<path-to-generatedInput.json> --outputJsonPath=<path-to-generatedOutput.json> --typespecEmitter=@azure-tools/typespec-ts --local
```

> path-to-generatedOutput.json is the detailed information of generated package, you can ignore it without pipeline. [generateOutput.json](https://github.com/Azure/azure-rest-api-specs/blob/main/documentation/sdkautomation/GenerateOutputSchema.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](#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

```
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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md#createupdate-the-ciyaml)

#### Build

See [steps-after-generations](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md).

After all the steps finished, you can prepare the release PullRequest for this generation. See [Prepare Release](#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](https://eng.ms/docs/products/azure-developer-experience/plan/release-plan).

---

### 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](https://github.com/Azure/azure-sdk-for-js)), please see [quick start for next generation](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/next-generation-quickstart.md).**

## 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](https://www.npmjs.com) 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](https://www.npmjs.com/package/@azure/identity) instead. Select a credential from Azure Identity examples based on the authentication method of your choice.
1. Callbacks: Method overloads that use callbacks have been replaced to use Promise instead.
1. 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.
1. 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`.
1. 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](https://www.npmjs.com/package/@azure/identity) for more details about `@azure/identity` and [migration guide from @azure/ms-rest-nodeauth to @azure/identity](https://github.com/Azure/ms-rest-nodeauth/blob/master/migrate-to-identity-v2.md) 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


<table>
  <tr>
    <th>Current Libraries</th>
    <th>Next Generation</th>
  </tr>
  <tr>
    <td>
      <pre lang="typescript">
getInstanceView(
  resourceGroupName: string,
  cloudServiceName: string,
  options?: msRest.RequestOptionsBase
): Promise&lt;Models.CloudServicesGetInstanceViewResponse>
      </pre>
      <pre lang="typescript">
getInstanceView(
  resourceGroupName: string,
  cloudServiceName: string,
  callback: msRest.ServiceCallback&lt;Models.CloudServiceInstanceView>
): void;
      </pre>
      <pre lang="typescript">
getInstanceView(
  resourceGroupName: string,
  cloudServiceName: string,
  options: msRest.RequestOptionsBase,
  callback: msRest.ServiceCallback&lt;Models.CloudServiceInstanceView>
): void;
      </pre>
      <pre lang="typescript">
getInstanceView(
  resourceGroupName: string,
  cloudServiceName: string,
  options?: msRest.RequestOptionsBase |
              msRest.ServiceCallback&lt;Models.CloudServiceInstanceView>,
  callback?: msRest.ServiceCallback&lt;Models.CloudServiceInstanceView>
): Promise&lt;Models.CloudServicesGetInstanceViewResponse>
      </pre>
    </td>
    <td>
      <pre lang="typescript">
getInstanceView(
  resourceGroupName: string,
  cloudServiceName: string,
  options?: CloudServicesGetInstanceViewOptionalParams
): Promise&lt;CloudServicesGetInstanceViewResponse>
      </pre>
    </td>
  </tr>
</table>


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](https://www.npmjs.com/package/@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`](https://github.com/Azure/ms-rest-azure-js/blob/a9cee4480a8710d5c81890ae7cb7a1dea559ec65/lib/lroPoller.ts#L13), a class that is exported by `@azure/ms-rest-azure-js` but the new operations return an object that implements the [`PollerLike`](https://github.com/Azure/azure-sdk-for-js/blob/35f86a68def5022d4c03a32d4c8c0362a0158772/sdk/core/core-lro/src/poller.ts#L50) 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.


<table>
  <tr>
    <td colspan="2">
      <p>
        Operations that return a poller.
      </p>
    </td>
  </tr>
  <tr>
    <th>Current Libraries</th>
    <th>Next Generation</th>
  </tr>
  <tr>
    <td>
      <pre lang="typescript">
  beginCreateOrUpdate(
    resourceGroupName: string,
    cloudServiceName: string,
    options?: Models.CloudServicesBeginCreateOrUpdateOptionalParams
  ): Promise&lt;msRestAzure.LROPoller>
      </pre>
    </td>
    <td>
      <pre lang="typescript">
  async beginCreateOrUpdate(
    resourceGroupName: string,
    cloudServiceName: string,
    options?: CloudServicesCreateOrUpdateOptionalParams
  ): Promise&lt;PollerLike<
        PollOperationState&lt;CloudServicesCreateOrUpdateResponse>,
        CloudServicesCreateOrUpdateResponse
      >
    >
      </pre>
    </td>
  </tr>
  <tr>
    <td colspan="2">
      <p>
        Operations that will poll until finish and return the result directly
      </p>
    </td>
  </tr>
  <tr>
    <th>Current Libraries</th>
    <th>Next Generation</th>
  </tr>
  <tr>
    <td>
      <pre lang="typescript">
  createOrUpdate(
    resourceGroupName: string,
    cloudServiceName: string,
    options?: Models.CloudServicesCreateOrUpdateOptionalParams
  ): Promise&lt;Models.CloudServicesCreateOrUpdateResponse>
      </pre>
    </td>
    <td>
      <pre lang="typescript">
  async beginCreateOrUpdateAndWait(
    resourceGroupName: string,
    cloudServiceName: string,
    options?: CloudServicesCreateOrUpdateOptionalParams
  ): Promise&lt;CloudServicesCreateOrUpdateResponse>
      </pre>
    </td>
  </tr>
</table>


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`](https://github.com/Azure/azure-sdk-for-js/blob/35f86a68def5022d4c03a32d4c8c0362a0158772/sdk/core/core-client/src/interfaces.ts#L115) callback in the operation options |
| get the current operation status                                           | `getOperationStatus()`                       | can be accessed using the [`onResponse`](https://github.com/Azure/azure-sdk-for-js/blob/35f86a68def5022d4c03a32d4c8c0362a0158772/sdk/core/core-client/src/interfaces.ts#L115) callback in the operation options |

And here are examples of how to commonly use one of the `beginCreateOrUpdateAndWait` LROs found in `@azure/compute`:


<table>
  <tr>
    <td colspan="2">
      <p>
        Operations that returns a poller.
      </p>
    </td>
  </tr>
  <tr>
    <th>Current Libraries</th>
    <th>Next Generation</th>
  </tr>
  <tr>
    <td>
      <pre lang="typescript">
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);
});
      </pre>
    </td>
    <td>
      <pre lang="typescript">
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);
});
      </pre>
    </td>
  </tr>
  <tr>
    <td colspan="2">
      <p>
        Operations that will poll until finish and return the result directly
      </p>
    </td>
  </tr>
  <tr>
    <th>Current Libraries</th>
    <th>Next Generation</th>
  </tr>
  <tr>
    <td>
      <pre lang="typescript">
const computeClient = new compute.ComputeManagementClient(credential, subscriptionId);
await computeClient.dedicatedHosts
  .createOrUpdate(resourceGroupName, hostGroupName, hostName, parameter)
  .then((response) => {
    console.log(response);
  });
      </pre>
    </td>
    <td>
      <pre lang="typescript">
const computeClient = new compute.ComputeManagementClient(credential, subscriptionId);
await computeClient.dedicatedHosts
  .beginCreateOrUpdateAndWait(resourceGroupName, hostGroupName, hostName, parameter)
  .then((response) => {
    console.log(response);
  });
      </pre>
    </td>
  </tr>
</table>


Please refer to [@azure/core-lro](https://www.npmjs.com/package/@azure/core-lro) for more details.

## Additional Samples

We also provide some samples [here](https://github.com/Azure-Samples/azure-samples-js-management) 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](https://github.com/Azure/azure-sdk-for-js/issues) and make sure you add the 'Preview' label to the issue.

---

### Documentation Quickstart On How To Write Tests (`documentation/Quickstart-on-how-to-write-tests.md`)

# Javascript Codegen Quick Start for Test

This page is to help you write and run tests quickly for Javascript Codegen SDK including high-level and rest-level clients. We firstly show how to run in `record` and `playback` mode and then guide on how to add test cases.

# Table of contents

- [Javascript Codegen Quick Start for Test](#javascript-codegen-quick-start-for-test)
- [Table of contents](#table-of-contents)
- [Background](#background)
- [Prerequisites](#prerequisites)
- [How to run test](#how-to-run-test)
  - [Code structure](#code-structure)
  - [Run tests in record mode](#run-tests-in-record-mode)
  - [Run tests in playback mode](#run-tests-in-playback-mode)
  - [How to push test recordings to assets repo](#how-to-push-test-recordings-to-assets-repo)
    - [Push test recording](#push-test-recording)
      - [New Package - No recorded tests](#new-package---no-recorded-tests)
      - [Existing package - Tests have been pushed before](#existing-package---tests-have-been-pushed-before)
    - [How to find recording files](#how-to-find-recording-files)
      - [Find local recording files](#find-local-recording-files)
      - [Find recording files in assets repo](#find-recording-files-in-assets-repo)
- [How to add tests](#how-to-add-tests)
  - [Before adding tests](#before-adding-tests)
    - [Client authentication](#client-authentication)
      - [AzureAD OAuth2 Authentication](#azuread-oauth2-authentication)
      - [API Key Authentication](#api-key-authentication)
  - [Example 1: Basic RLC test interaction and recording for Azure data-plane service](#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service)
    - [`glossary.spec.ts`](#glossaryspects)
    - [`utils/recordedClient.ts`](#utilsrecordedclientts)
  - [Example 2: Basic HLC test interaction and recording for Azure management service](#example-2-basic-hlc-test-interaction-and-recording-for-azure-management-service)
    - [`monitor.spec.ts`](#monitorspects)

# Background

The Azure SDK test framework uses the [`test-recorder`](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md) library, which in turn rests upon on a HTTP recording system ([testproxy](https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy)) 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](https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy) and the [asset-sync workflow](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/ASSET_SYNC_WORKFLOW.md).

# Prerequisites

- pnpm
  - Install/update pnpm globally via [pnpm installation instructions](https://pnpm.io/installation)
- Any of [the LTS versions of Node.js](https://github.com/nodejs/release#release-schedule)
- A C++ compiler toolchain and Python (for compiling machine-code modules)
  - Refer [here](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) for more details

To be able to leverage the asset-sync workflow

- Install [Powershell](https://github.com/PowerShell/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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md#test_mode) we'll highly recommend you to read this [doc](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md#key-concepts). We'll touch upon these concepts in below content.

## Code structure

If this is your first time generating an SDK, you can enable the config `generate-test: true` in `README.md` or `tspconfig.yaml`. For management plane packages, `generate-test` is always enabled. Then we'll generate simple utils and a sample test file for you.

```
  // Copyright (c) Microsoft Corporation.
  // Licensed under the MIT License.

  // import type { Recorder } from "@azure-tools/test-recorder";
  // import { createRecorder } from "./utils/recordedClient.js";
  import {
    assert,
    // beforeEach,
    // afterEach,
    it,
    describe,
  } from "vitest";

  describe("My test", () => {
    // let recorder: Recorder;

    // beforeEach(async function (ctx) {
    //   recorder = await createRecorder(ctx);
    // });

    // afterEach(async function () {
    //   await recorder.stop();
    // });

    it("sample test", async function () {
      assert.equal(1, 1);
    });
  });
```

This only contains basics for testing, we comment out some lines **except** license header. If you want to update to your own utility and test cases. The overall structure will be similar to below:

_Note: the structure of the `test` folder has slight differences between high-level, rest-level and Modular clients. In HLC, we only have one file under the `test` folder which contains all contents. But in RLC and Modular, we separate the sample test and utils._

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

You could also refer [here](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md#how-to-write-test-for-dpgrlcmpg) 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)

## 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=<package-name>... --token 1`. Please notice this command is time-consuming and it will take around 10 mins, you could refer [here](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#resolving-dependency-version-conflicts) 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.

```
[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](#how-to-push-test-recordings-to-assets-repo)

```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](https://github.com/Azure/azure-sdk-assets) repo and referenced via your `assets.json`.

```
[test-info] ===TEST_MODE="record"===
...
[node-tests]   My test
[node-tests]     √ sample test
[node-tests]
[node-tests]   1 passing (223ms)
```

## Run tests in playback mode

If we have existing recordings, then the tests have been run against generated the HTTP recordings, we can run your tests in `playback` mode.

```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](https://github.com/Azure/azure-sdk-assets) 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`](https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/785/Externalizing-Recordings-(Asset-Sync)?anchor=permissions-to-%60azure/azure-sdk-assets%60)

### 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>/<package-name>/assets.json` file by running the following command.

```bash
npx dev-tool test-proxy init
```

Note: If you [install `dev-tool` globally](https://github.com/Azure/azure-sdk-for-js/tree/main/common/tools/dev-tool#installation), 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](#run-tests-in-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).

#### Existing package - Tests have been pushed before

At this point, you should have an `assets.json` file under your SDK. `sdk/<service-folder>/<package-name>/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
1. 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](https://learn.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals) and to provide RBAC to the service principal for the access to the Azure resource of your service.

Client requires following three variables for the service principal using client ID/secret for authentication:

```
AZURE_TENANT_ID
AZURE_CLIENT_ID
AZURE_CLIENT_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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md).

## Example 1: Basic RLC test interaction and recording for Azure data-plane service

At the code structure [section](#code-structure), 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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md#azure-toolstest-credential-package-and-the-noopcredential) 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`

```
/* 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](#code-structure), 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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md#azure-toolstest-credential-package-and-the-noopcredential) 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`

```
/* 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](https://aka.ms/azsdk/rlc/js) 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.

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

```
${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.

# Broken links



Add the broken links into [eng/ignore-links.txt](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/ignore-links.txt) file to bypass this verification or you could update the broken links to valid ones, see [example PR here](https://github.com/Azure/azure-sdk-for-js/pull/23429/commits/1a7b74c4bdad27e423a355a4c7f3dde4ac3c83bc).


# Check spelling (cspell)

For new service the error usually happens, fix spelling in code or in markdown at file [.vscode/cspell.json](https://github.com/Azure/azure-sdk-for-js/blob/main/.vscode/cspell.json). See an example in [devcenter PR](https://github.com/chrissmiller/azure-sdk-for-js/commit/ef18dccae59e98185e3854f8b087230b65735744).

# Push failure\

## Spawn pwsh ENOENT



Install [Powershell](https://github.com/PowerShell/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](https://github.com/orgs/Azure/teams?query=azure-sdk-write-)([same list](https://repos.opensource.microsoft.com/teams?q=azure-sdk-write-) 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** : <scotk@microsoft.com> to create a new one for your service team.

For more details, join [a partner write team](https://eng.ms/docs/products/azure-developer-experience/onboard/access-teams)

**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** : <scotk@microsoft.com> 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](https://github.com/myndzi/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](https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843) which also helped)
  - This helped locate the spot where it goes wrong. The InteractiveBrowserCredential test is the one that hangs(from the above screenshot).  
    
    Workflow of the test is that opening the browser would throw and the test is supposed to end, however, it doesn't.
    - If the server is closed even before, it ends fine.
- Now that we have located the problem, we tried understanding and fixing it altogether.

  - Since what was hanging wasn’t an exception, since no 'unhandled exception' appeared in the logs… We checked that all of the catches were reached, attempting to find some code path that was being ignored and eventually found that all the code paths were following accordingly, so our code "worked". We were closing everything we wanted to close.
  - So we tried adding a delay before throwing the error we were testing (remember, this specific test checks the edge case in which there is no browser available). That delay fixed it, so it was something added in the stack out of order. So, though we resolved and wrapped up everything, Node was adding something into the stack right after. The delay would be a bad solution since the time needed order the stack appropriately would be variable.
  - We assumed what was taking a while to load was the server setting things up to listen for requests.

    The issue was this:

    - We were allowing Node to close everything and resolve all promises _before_ the server was ready to listen for requests.
    - Our "closing" dependency/code assumed that it's always closing something that is at least already listening.
    - As soon as we added the step to wait for the server to begin listening before we allowed anything to resolve, then by the time the exception was thrown, everything was able to close properly.

  - Here's the PR from @sadasant for more details on the fix - https://github.com/Azure/azure-sdk-for-js/pull/15497

---

### Documentation Core Client Additional Policy For Debug Logging (`documentation/core-client-additional-policy-for-debug-logging.md`)

# Additional Policy for Debug Logging

| :boom: | **This is meant to be used by Azure SDK developers investigating bugs. The request policy on this page ***WILL*** log sensitive information to the Node.js console.** |
|-----------|-------|

Default Azure SDK logging systems do not log request bodies, even in _verbose_ mode. This is to prevent the logging of sensitive information from the request body.

Sometimes, it's useful to be able to dump a request trace to the console anyway. You may need to do so to investigate a bug or to accurately report an issue. For those cases, an "additional policy" may be used in the pipeline that logs the request contents.

Such policies must _ONLY_ be used for debugging, as the information in the request and response must be assumed to be sensitive.

Here's one that logs the request headers and body as well as the response information (status code, headers, and body).

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

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

```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][credscan_doc]. The
Azure SDK's motivation and methodology for running CredScan is documented [here][devops_doc].

## Table of Contents
- [Check CredScan status](#check-credscan-status)
- [Correct active warnings](#correct-active-warnings)
  - [True positives](#true-positives)
  - [False positives](#false-positives)

## Check CredScan status

CredScan is run each night over the entire `azure-sdk-for-js` repository as part of the
[js - aggregate-reports][aggregate_reports] pipeline. The scan produces a list of active warnings in the "Post
Analysis" task of the "ComplianceTools" job ([example output][credscan_output]).

Each warning will begin with the path to the file containing a potential credential, as well as the row and column where
the credential string begins. For example, for a potential credential that starts in row 29 and column 13 of a
particular file:
```
##[error]sdk/{service}/{package}/{path-to-file}.ts:sdk/{service}/{package}/{path-to-file}.ts(29,13):
```

The warning will then list an error code and description of why the potential credential was flagged.

## Correct active warnings

If you find any warnings listed for a package you own, the next step is to determine if the potential credential found
by CredScan is an actual credential (a true positive), or a fake credential/false flag (a false positive).

### True positives

If CredScan discovers an actual credential, please contact the EngSys team at azuresdkengsysteam@microsoft.com 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][suppression_file]. 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][suppression_file]. 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][suppression_file]. "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.

[aggregate_reports]: https://dev.azure.com/azure-sdk/internal/_build?definitionId=1394&_a=summary
[credscan_doc]: https://aka.ms/credscan
[credscan_output]: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=1326164&view=logs&j=3b141548-98d7-5be1-7ef8-eeb08ca02972&t=7989ab4d-bdd3-5239-37e1-e3681bbc7025&l=49
[devops_doc]: https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/413/Credential-Scan-Step-in-Pipeline
[suppression_file]: https://github.com/Azure/azure-sdk-for-js/blob/main/eng/CredScanSuppression.json

---

### 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md#adding-a-new-third-party-runtime-dependency).

## Example scenario

Let's assume that a vulnerability has been found in [node-fetch] version 2.6.6 and that you use `@azure/keyvault-keys` directly. Your security scan identified this vulnerability and asks to upgrade to 2.6.7 or higher.

The outputs below assume you're using `npm v8.1.2` with a lockfile, but we will provide instructions for Yarn as well.

### Identifying the dependency tree

First, let's see how `node-fetch` is pulled into our dependency tree.

```
> npm ls node-fetch

keyvault@1.0.0 /home/user/my-app
└─┬ @azure/keyvault-keys@4.3.0
  └─┬ @azure/core-http@2.2.3
    └── node-fetch@2.6.6
```

> If you are using Yarn you can use `yarn why node-fetch` with similar results.

It looks like `node-fetch`, a dependency of `@azure/core-http@2.2.3` is being pulled in _transitively_ via `@azure/keyvault-keys@4.3.0`.

Because I use a lockfile, running `npm install` again will not help me here. But `npm` provides a few utilities that can.

### Using `npm audit fix`

First, it's possible that you were alerted to a security vulnerability thanks to `npm audit`. In this case, the simplest solution might be to run `npm audit fix`. Let's see what that looks like:

```
> npm audit fix

changed 1 package, and audited 51 packages in 421ms

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
keyvault main % npm ls node-fetch
keyvault@1.0.0 /home/user/my-app
└─┬ @azure/keyvault-keys@4.3.0
  └─┬ @azure/core-http@2.2.3
    └── node-fetch@2.6.7
```

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][npm-audit].

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

```
> 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
keyvault@1.0.0 /home/user/my-app
└─┬ @azure/keyvault-keys@4.3.0
  └─┬ @azure/core-http@2.2.3
    └── node-fetch@2.6.7
```

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][npm-update].

### I read this guide and still have no idea what to do

Feel free to [file an issue][file-an-issue] and start a discussion, we're here to help and will try to do so to the best of our abilities!

[caret]: https://docs.npmjs.com/cli/v6/using-npm/semver#caret-ranges-123-025-004
[node-fetch]: https://www.npmjs.com/package/node-fetch
[npm-audit]: https://docs.npmjs.com/cli/v8/commands/npm-audit
[npm-update]: https://docs.npmjs.com/cli/v8/commands/npm-update
[file-an-issue]: https://github.com/Azure/azure-sdk-for-js/issues/new/choose

---

### 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](https://github.com/Azure/azure-sdk-for-js/blob/main/common/tools/warp/README.md).

While the two formats are largely compatible, a few caveats apply.

## Usage of __dirname and __filename

When a module is `require`d 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](https://github.com/Azure/azure-sdk-for-js/blob/main/common/tools/warp/README.md), provides a [polyfill substitution](https://github.com/Azure/azure-sdk-for-js/blob/main/common/tools/warp/README.md#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](https://github.com/Azure/azure-sdk-for-js/blob/main/common/tools/warp/README.md).

---

### 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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md).

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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/ASSET_SYNC_WORKFLOW.md).

## `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,
    },
  }),
);
```
2. 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

[powershell]: https://github.com/PowerShell/PowerShell

## [Method 2] --testNamePattern
```bash
pnpm integration-test:node -- --testNamePattern "simple"
```
Reference - [Filtering Tests — Introduction to Testing JavaScript with Vitest](https://stevekinney.net/courses/testing/filtering-tests)

## [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`.

1. Create a empty folder and `cd` this folder.

    ```
    mkdir jstest
    cd jstest
    ```

1. Initialize a new node project. 

    ```
    npm init
    ```

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

1. Install dependencies.

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

1. 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);
    ```
    
1. Install all the dependencies

   ```
   npm install // need to make sure package.json exists and has contained at step 4.
   ```

1. Compile the ts file.

   ```
   tsc test_1.ts
   ```

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

1. Run the code. 
   
   ```
   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>/<package-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](https://ca-sdk-js-worker.ambitiouspond-79d04e69.eastus2.azurecontainerapps.io/#/sdk-prs) 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](https://github.com/Azure/azure-sdk-for-js/pulls?q=is%3Apr+is%3Aopen+label%3AMgmt+AutoPR+in%3Atitle) 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](https://github.com/Azure/azure-rest-api-specs)
2. **Local development environment**:
   - [Node.js LTS version](https://nodejs.org/en/about/releases/)
   - Local clone of your fork of [azure-sdk-for-js](https://github.com/Azure/azure-sdk-for-js)
   - Local clone of your fork of [azure-rest-api-specs](https://github.com/Azure/azure-rest-api-specs)
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](https://aka.ms/azsdk/tsp-client)

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

```
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:**

- `@azure/core-client` → `@azure-rest/core-client`
- `@azure/core-lro@2.x` → `@azure/core-lro@3.x`

### 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:**
   - [Generate code from TypeSpec](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Generate-code-from-TypeSpec.md)
   - [Azure SDK TypeScript Guidelines](https://azure.github.io/azure-sdk/typescript_introduction.html)

2. **Consult with the team:**
   - Post in the TypeSpec Discussion Teams channel
   - Tag `@DPG TypeScript` for JavaScript/TypeScript-specific questions

3. **File issues:**
   - [Azure SDK for JS Issues](https://github.com/Azure/azure-sdk-for-js/issues)
   - [TypeSpec Issues](https://github.com/microsoft/typespec/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](https://github.com/Azure/azure-sdk-for-js)), please see [quick start guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/quickstart-guide-for-libraries-generated-from-TypeSpec.md).**

## 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](https://www.npmjs.com) and give them a try.

## Library improvements when generating from *TypeSpec*

We recommend reviewing the [complete guide](https://devblogs.microsoft.com/azure-sdk/azure-sdk-modularized-libraries-for-javascript/) for full details. Compared to libraries generated with *Autorest*, *TypeSpec code generation* has following key benefits:

1. Subpath exports: Libraries now leverage [subpath exports](https://nodejs.org/api/packages.html#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.
1. 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.
1. 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](https://github.com/Azure/azure-sdk-for-js/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](https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/knowledge/customizing-client-tsp.md) 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:

```
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](https://learn.microsoft.com/azure/azure-portal/get-subscription-tenant-id) to get Subscription ID. And please refer to [this document](https://learn.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal) 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`](https://www.npmjs.com/package/@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?

- File an issue via [GitHub
  Issues](https://github.com/Azure/azure-sdk-for-js/issues)

## Contributing

For details on contributing to this repository, see the contributing
guide.

This project welcomes contributions and suggestions. Most contributions
require you to agree to a Contributor License Agreement (CLA) declaring
that you have the right to, and actually do, grant us the rights to use
your contribution. For details, visit <https://cla.microsoft.com>.

When you submit a pull request, a CLA-bot will automatically determine
whether you need to provide a CLA and decorate the PR appropriately
(e.g., label, comment). Simply follow the instructions provided by the
bot. You will only need to do this once across all repositories using
our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For
more information see the Code of Conduct FAQ or contact
<opencode@microsoft.com> 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](https://github.com/Azure/azure-sdk-for-js/issues/8461). 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.

### Recommended Combinations

| Scenario | Labels to Apply |
|----------|----------------|
| New client library feature | `architecture-review-needed`, `test-review-needed`, `docs-review-needed` |
| Dependency update | `dependency-review-needed`, `security-review-needed` |
| Management SDK changes | `mgmt-review-needed` |
| Performance-sensitive change | `performance-review-needed`, `test-review-needed` |
| Full review | `architecture-review-needed`, `performance-review-needed`, `dependency-review-needed`, `docs-review-needed`, `security-review-needed`, `test-review-needed` |

## Using Agents Locally in VS Code

The same agents are available as **custom agents** in VS Code Copilot Chat
(via Copilot CLI). Their definitions live in `.github/agents/*.agent.md`.

### Prerequisites

- [VS Code](https://code.visualstudio.com/) with the
  [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot)
  and [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat)
  extensions installed.
- The repository must be open as a workspace in VS Code.

### Invoking an Agent

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

```
@archie Review the changes in this branch for API design issues
```

```
@sentinel Check the staged changes for security vulnerabilities
```

```
@tester Are there any untested public methods in sdk/storage/storage-blob/src/BlobClient.ts?
```

```
@dexter Review the dependency changes in sdk/keyvault/keyvault-keys/package.json
```

```
@scribe Check that the README and CHANGELOG are consistent with the code changes
```

```
@dash Look for performance regressions in the current diff
```

```
@mgmt-review Review this management SDK PR for API design guideline 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:

```
.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 <username>`.
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
```

2. 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](https://code.visualstudio.com/) | Code editor with built-in AI features and Copilot integration |
| [GitHub Copilot](https://github.com/features/copilot) | AI code completion and chat inside VS Code, Visual Studio, JetBrains IDEs, and GitHub.com |
| [Copilot CLI](https://docs.github.com/en/copilot/github-copilot-in-the-cli/about-github-copilot-in-the-cli) | AI assistance for shell commands and Azure CLI usage |
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Terminal-based AI coding agent from Anthropic |
| [Cursor](https://www.cursor.com/) | AI-first code editor with chat and inline editing |
| [Aider](https://aider.chat/) | 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](https://learn.microsoft.com/azure/developer/azure-mcp-server/get-started)
exposes Azure resource operations to AI tools through the
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/). This lets an AI coding agent
query, create, and manage Azure resources directly during a conversation.

See the [getting started guide](https://learn.microsoft.com/azure/developer/azure-mcp-server/get-started)
for setup instructions.

## Azure SDK skills

The Microsoft skills marketplace provides Azure SDK skills that give AI agents
context about Azure SDK conventions, code generation, and package management.
These skills work with CLI-based AI tools that support plugins, such as Claude Code
and Copilot CLI.

Install the Microsoft skills marketplace:

```
/plugin marketplace add Microsoft/skills
```

Install the [Azure SDK skills plugin](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript):

```
/plugin install azure-sdk-typescript@skills
```

Verify installation:

```
/plugin list
```

Update the plugin:

```
/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](https://github.com/microsoft/azure-skills) repository.

## Further reading

- [Azure SDK for JavaScript documentation](https://learn.microsoft.com/javascript/api/overview/azure/)
- [Azure SDK design guidelines for TypeScript](https://azure.github.io/azure-sdk/typescript_introduction.html)
- [Azure MCP Server](https://learn.microsoft.com/azure/developer/azure-mcp-server/get-started)
- [Agent guidance for this repo (`AGENTS.md`)](https://github.com/Azure/azure-sdk-for-js/blob/main/AGENTS.md)

## Feedback

If you have feedback on your AI experience with the Azure SDK for JavaScript,
[open an issue](https://github.com/Azure/azure-sdk-for-js/issues/new/choose) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#development-workflows) 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](https://aka.ms/azsdk/js/customization).

# Generate CHANGELOG.md

Install and build `js-sdk-release-tools` from the repository root:

```
package_manager=$(node -p "require('./package.json').packageManager")
npm_registry=$(npm config get registry)
npm install "$package_manager" --registry="$npm_registry"
pnpm install --frozen-lockfile
pnpm turbo build --filter=@azure-tools/js-sdk-release-tools... --token 1
```

After you build your package, run

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

Here is the example

```
node eng/tools/js-sdk-release-tools/dist/changelogToolCli.js sdk/advisor/arm-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.
- RLC example: [Maps Route README.md](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/maps/maps-route-rest/README.md)
- DPG example: [OpenAI README.md](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/openai/openai/README.md)

# How to write test for DPG/RLC/MPG

In order to release it, we need to add some tests for it to make sure we are delivering high quality packages. After generation you will see a `sampleTest.spec.ts` file in your `{PROJECT_ROOT}/test/public` folder, which has an empty test and you could add/update test cases against your own services.

See the [Javascript Codegen Quick Start for Test](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service) to write your test step by step.

    Also, you could refer to the below examples for more cases:
    - RLC example: [Maps Route Testing](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/maps/maps-route-rest/test/public)
    - DPG example: [OpenAI Testing](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/openai/openai/test/public)
    - MPG example: [Containerservice Fleet Testing](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/containerservice/arm-containerservicefleet/test/public/)

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](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/template/template/samples-dev) as reference and update the relevant information for your service such as package-name, sample code, description, etc.

To learn more, you could refer to the below samples:
- DPG sample: [the samples of OpenAIClient](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/openai/openai/samples-dev)
- RLC sample: [the samples of MapsRouteClient](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/maps/maps-route-rest/samples-dev)

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`](https://github.com/Azure/azure-sdk-for-js/blob/main/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](https://semver.org/).

## Test recordings

After [writing and running test cases](#how-to-write-test-for-dpgrlcmpg), you need to push the recordings to [assets repo](https://github.com/Azure/azure-sdk-assets). Please refer to [push recording guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md#how-to-push-test-recordings-to-assets-repo) to push recordings.

## Fix CI for PR

You may meet the CI failures after submitting the PR, so please refer to [Troubleshoot CI Failure](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Troubleshoot-ci-failure.md) 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](https://github.com/azure-sdk/azure-sdk-for-js/). Since this is a different fork from `Azure/azure-sdk-for-js`, you cannot directly check out the branch or push changes to it via `origin`. Here are three solutions to check out the AutoPR branch locally.

> **Note for Options 2 & 3:** Replace `{REMOTE_NAME}` with any name you prefer (e.g., `azure-sdk`), and `{BRANCH_NAME}` with the branch name shown in the AutoPR (e.g., `sdkauto/@azure-arm-connectedcache-5699155`).

### Option 1: Use VS Code GitHub Pull Requests extension ⭐ (recommended)

This is the easiest way — no git commands needed. The [GitHub Pull Requests](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) extension can check out any PR branch directly in VS Code, including PRs from forks.

1. Install the [GitHub Pull Requests](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) 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.

```
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](https://eng.ms/docs/products/azure-developer-experience/plan/release-plan)

---

### 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](https://learn.microsoft.com/azure/active-directory/develop/) with the Azure SDK libraries.
Microsoft Entra ID implements the [OAuth 2.0 and OpenID Connect
standards](https://learn.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols)
to provide authentication for users and services who may be granted access to
Azure services.

## Table of Contents

- [Getting Started](#getting-started)
- [Understanding the Credential Types](#understanding-the-credential-types)
- [Choosing a Credential Type](#choosing-a-credential-type)
- [Permissions and Consent](#permissions-and-consent)
- [Credential Types in @azure/identity](#credential-types-in-azureidentity)

## 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](https://learn.microsoft.com/azure/active-directory/develop/quickstart-create-new-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](https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app).
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](https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app#register-a-new-application-using-the-azure-portal)
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](https://learn.microsoft.com/azure/active-directory/develop/msal-client-applications)
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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-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`

## Permissions and Consent

The identity platform provides an authorization model for Azure services with
[two types of
permissions](https://learn.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#troubleshooting-permissions-and-consent):

- **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](https://learn.microsoft.com/azure/active-directory/develop/quickstart-configure-app-access-web-apis)
explains how to do this in detail.

### User-Granted Consent

When a user is being authenticated to access a service that is configured with
delegated permissions, they may be presented with a consent screen that asks
whether they want to grant your application permission to access resources on
their behalf. An example of this consent flow can be found in the [consent
framework documentation
page](https://learn.microsoft.com/azure/active-directory/develop/consent-framework).

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](https://learn.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#request-the-permissions-from-a-directory-admin).

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](https://learn.microsoft.com/azure/active-directory/manage-apps/application-sign-in-unexpected-user-consent-error)
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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-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](https://learn.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad)
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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth-ropc)
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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth-ropc)
> 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](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/services-support-managed-identities).

One important distinction of this credential compared to the others is that it
_does not require an app registration_. This authentication scheme relates to
the actual Azure resources to which your code is deployed rather than the
application itself.

To enable your application to authenticate with this credential, you will need
to grant one of two types of managed identity to the resource that runs your
code:

- A [system-assigned
  identity](https://learn.microsoft.com/azure/app-service/overview-managed-identity#adding-a-system-assigned-identity)
  which uniquely identifies your resource
- A [user-assigned
  identity](https://learn.microsoft.com/azure/app-service/overview-managed-identity#adding-a-user-assigned-identity)
  which can be assigned to your resource (and others)

Once your resource has an identity assigned, that identity can be granted access
to other Azure services through role assignments.

If you have configured your resource to use a system-assigned identity, you can
just create an instance of `ManagedIdentityCredential` without any
configuration. For user-assigned identities, you must provide the `clientId` of
the managed identity you wish to use for authentication.

More information on configuring and using managed identities can be found in the
[Managed identities for Azure
resources](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview)
documentation. There is also a [list of Azure
services](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/services-support-managed-identities#azure-services-that-support-azure-ad-authentication)
that have been tested to confirm support for managed identity authentication.

### InteractiveBrowserCredential

The `InteractiveBrowserCredential` follows the [authorization code
flow](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-device-code)
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](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-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`](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity/test/manual/authorization-code-credential).

### 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](#1-overall-architecture)
2. [Overall Flow & Utilities](#2-overall-flow--utilities)
3. [SDK Generation Steps](#3-sdk-generation-steps)
   - [3.1 HighLevelClient (HLC)](#31-highlevelclient-hlc--management-plane-sdk)
   - [3.2 RestLevelClient (RLC)](#32-restlevelclient-rlc--rest-level-client)
   - [3.3 ModularClient (MLC)](#33-modularclient-mlc--modular-client)
4. [Folder Cleanup Logic](#4-folder-cleanup-logic)
5. [Changelog & Version Bump (Common)](#5-changelog--version-bump-common)
6. [Output JSON Structure](#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](../../../../documentation/migrating-to-typespec.md).

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](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/tools/js-sdk-release-tools/docs/hlc.md)                       | Local HLC (management-plane) code generation from swagger/README     |
| `rlc-code-gen`   | see [llc.md](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/tools/js-sdk-release-tools/docs/llc.md)                       | Local RLC (data-plane) code generation                               |
| `changelog-tool` | see [changelog-tool.md](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/tools/js-sdk-release-tools/docs/changelog-tool.md) | Generate changelog by comparing api.md against published npm package |

### SDK Type Enum (`SDKType`)

| Type              | Value               | Description                                                                |
| ----------------- | ------------------- | -------------------------------------------------------------------------- |
| `HighLevelClient` | `'HighLevelClient'` | HLC — Management plane SDK (autorest-based, corresponds to `@azure/arm-*`) |
| `RestLevelClient` | `'RestLevelClient'` | RLC — REST level client (autorest or TypeSpec based)                       |
| `ModularClient`   | `'ModularClient'`   | MLC — Modular client (TypeSpec-based, typically management plane)          |

### Run Mode (`RunMode`)

| Mode              | Value                 | Description                                     |
| ----------------- | --------------------- | ----------------------------------------------- |
| `Release`         | `'release'`           | Official production release pipeline            |
| `Local`           | `'local'`             | Developer local run (skips node_modules backup) |
| `SpecPullRequest` | `'spec-pull-request'` | Triggered by spec PR for automated validation   |
| `Batch`           | `'batch'`             | Bulk SDK generation across multiple packages    |

---

## 2. Overall Flow & Utilities

### Overall Flow Diagram

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

### Utility Operations Summary

| Operation            | Function                                     | Required / Optional                  | Description                                               |
| -------------------- | -------------------------------------------- | ------------------------------------ | --------------------------------------------------------- |
| Backup node_modules  | `backupNodeModules()`                        | ✅ Required (non-local)              | Recursively rename `node_modules` → `node_modules_backup` |
| Restore node_modules | `restoreNodeModules()`                       | ✅ Required (non-local)              | Recursively rename back to `node_modules`                 |
| Format code          | `formatSdk()`                                | ✅ Required                          | `npm run format`                                          |
| Update snippets      | `updateSnippets()`                           | ✅ Required                          | `dev-tool run update-snippets`                            |
| Lint fix             | `lintFix()`                                  | ⚠️ Optional (`Release`/`Local` only) | `npm run lint:fix`                                        |
| Customize package    | `customizeCodes()`                           | ⚠️ If present                        | `npm run --if-present customize`                          |
| Clean up package dir | `cleanUpPackageDirectory()`                  | ✅ Required                          | Cleanup strategy based on SDK type + `RunMode`            |
| Specify API version  | `specifyApiVersionToGenerateSDKByTypeSpec()` | ⚠️ Optional                          | Modify `api-version` field in `tspconfig.yaml`            |

---

## 4. Folder Cleanup Logic

The cleanup behavior is determined by **SDK type** and **run mode**. The core function is `cleanUpPackageDirectory()`.

Two run mode categories are used internally:

- **Pipeline modes** (`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](#4-folder-cleanup-logic))                                                                                              | [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](https://github.com/Azure/azure-sdk-tools/pull/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](#4-folder-cleanup-logic))                                                                                                                                                                                                                                                                                                                                                                                                                | [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](#buildpackage-sub-steps)                                                                                                                                                                                                                                                                                                                                                                                                                                                           | [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](https://github.com/Azure/azure-sdk-tools/issues/14610)): sample failures are not caught during spec PR validation (`SpecPullRequest` mode), so a package that passes spec PR checks can still fail at release time. | [modularClientPackageGenerator.ts#L49] |
| **11. Update Package Result**            | ✅ Required    | `updateNpmPackageResult()`                     | Read `package.json` name/version into `PackageResult`                                                                                                                                                                                                                                                                                                                                                                                                                                                    | [modularClientPackageGenerator.ts#L56] |
| **12. Create Release Artifact**          | ✅ Required    | `createArtifact()`                             | `pnpm run --filter {packageName}... pack`, generates `.tgz`                                                                                                                                                                                                                                                                                                                                                                                                                                              | [modularClientPackageGenerator.ts#L63] |
| **13. Create/Update CI YAML**            | ✅ Required    | `createOrUpdateCiYaml()`                       | Create or update `ci.mgmt.yml`                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | [modularClientPackageGenerator.ts#L67] |

#### `buildPackage()` Sub-steps Detail

| Sub-step             | Required      | Command / Operation                                                                                  | Code Link           |
| -------------------- | ------------- | ---------------------------------------------------------------------------------------------------- | ------------------- |
| pnpm install         | ✅ Required   | `pnpm install`                                                                                       | [rushUtils.ts#L127] |
| Customize package    | ⚠️ If present | `npm run --if-present customize` — runs the package `customize` script when present, no-op otherwise | [rushUtils.ts#L139] |
| Lint fix             | ⚠️ Optional   | `npm run lint:fix` — only in `Release` / `Local` mode                                                | [rushUtils.ts#L142] |
| turbo build          | ✅ Required   | `pnpm turbo build --filter {packageName}... --token 1` (build errors are warnings for Data Plane)    | [rushUtils.ts#L150] |
| Extract ApiView info | ✅ Required   | Find `temp/**/*-node.api.json` or `temp/**/*.api.json`                                               | [rushUtils.ts#L157] |
| Test package         | ⚠️ Optional   | `pnpm run test:node` — `TEST_MODE=record`; failure does not block                                    | [rushUtils.ts#L169] |
| Format               | ✅ Required   | `npm run format`                                                                                     | [rushUtils.ts#L170] |
| Update snippets      | ✅ Required   | `dev-tool run update-snippets`                                                                       | [rushUtils.ts#L171] |

---

## 5. Changelog & Version Bump (Common)

> **Note**: Changelog generation is **skipped** for Data Plane (`ModularClient` / `DataPlane`) packages.

### Core Logic

```
1. Query npm registry for published package info (tryGetNpmView)
2. Determine if first release (shouldTreatAsFirstRelease)
   ├── First Release:
   │   → makeChangesForFirstRelease(): use initial changelog template,
   │     set version to 1.0.0-beta.1 or 1.0.0
   │
   └── Non-first Release:
       ├── Download published stable version (npm pack {packageName}@{stableVersion})
       ├── Determine if track2 or track1:
       │
       ├── Track2 Previously Released:
       │   ├── Compare old and new api.md (using DifferenceDetector)
       │   ├── Generate changelog (using ChangelogGenerator)
       │   ├── Calculate new version (getNewVersion):
       │   │   - Has breaking change → bump minor/major
       │   │   - No changes → bump patch
       │   │   - Beta version → bump preview
       │   └── makeChangesForReleasingTrack2(): write CHANGELOG.md and update package.json version
       │
       └── Track1 Previously Released:
           └── makeChangesForMigrateTrack1ToTrack2(): generate migration 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:

```
## 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/typescript@6.0.0-beta.5 --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/typescript@6.0.0-beta.5 --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](https://www.npmjs.com/package/@autorest/typescript).

### 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/typescript@6.0.0-beta.5
```

hint: you can get codegen version from [NPM](https://www.npmjs.com/package/@autorest/typescript).

### 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](https://api-extractor.com/). 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:

```
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](https://nodejs.org/en/about/releases/).
2. Install autorest: `npm install autorest`.
3. use [Git](https://git-scm.com/) to clone [azure-sdk-for-js](https://github.com/Azure/azure-sdk-for-js) or [azure-sdk-for-js-pr](https://github.com/Azure/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:

```
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=<the path of swagger/README.md>` 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:

```
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](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk). If the service name you provided is not under [sdk](https://github.com/Azure/azure-sdk-for-js/tree/main/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](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/template/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<T>`

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](https://github.com/Azure/autorest.typescript/blob/main/packages/autorest.typescript/test/integration/lro.spec.ts) in the TypeScript code generator repo. I both added those tests here and re-implemented the [lro routes](https://github.com/Azure/autorest.testserver/blob/main/legacy/routes/lros.js) 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`](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-lro/test/utils/coreRestPipelineLro.ts).

---

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

```
/* 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`](https://github.com/Azure/azure-sdk-for-js/blob/f8e83621a047113ae8d408e552d7d7f32cd82075/sdk/cognitivelanguage/ai-language-text/src/textAnalysisClient.ts#L690-L703) 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](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-lro/test/public/lro.spec.ts) 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<TResult>` which is slightly different from `PollerLike<TResult>` 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`](https://typescript-eslint.io/rules/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](https://github.com/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](https://www.typescriptlang.org/zh/tsconfig#stripInternal)). 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](https://api-extractor.com/) and [TypeDoc](https://typedoc.org/).

## 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](https://github.com/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](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-modules-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`](https://github.com/Azure/azure-sdk-for-js/blob/main/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](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/steps-after-generations.md#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](https://azure.github.io/azure-sdk/typescript_design.html#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](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-include-cjs), [ts-include-esm](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-include-esm), and [ts-include-original-source](https://azure.github.io/azure-sdk/typescript_implementation.html#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](https://github.com/isaacs/tshy?tab=readme-ov-file#commonjs-dialect-polyfills).

## 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](https://semver.org/).

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.<beta-version>`, where `beta-version` is an integer.
- Preview versions must be in the format `<version>-preview.<preview-version>`, 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](https://azure.github.io/azure-sdk/typescript_implementation.html#ts-versioning-no-version-0), as the rules are similar enough to not exist separately for linting purposes.

---

--- METRICS ---
- Files Extracted: 73
- Estimated Token Budget: ~93891 tokens
- Canonical Reference: https://codewiki.google/github.com/Azure/azure-sdk-for-js
