From 747c807429572f605f979457d069b575c1c4431d Mon Sep 17 00:00:00 2001 From: azure-sdk Date: Fri, 9 Jan 2026 22:04:57 +0000 Subject: [PATCH] Configurations: 'specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml', API Version: 2022-12-01, SDK Release Type: stable, and CommitSHA: 'ea9e2ff730d93906cb1671950e2415fcec64da51' in SpecRepo: 'https://github.com/Azure/azure-rest-api-specs' Pipeline run: https://dev.azure.com/azure-sdk/internal/_build/results?buildId=5738343 Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release. --- .github/CODEOWNERS | 3 + eng/ignore-links.txt | 1 + pnpm-lock.yaml | 82 ++++++ sdk/contosowidgetmanager/ci.mgmt.yml | 35 +++ .../contoso-widgetmanager/LICENSE | 21 ++ .../contoso-widgetmanager/README.md | 109 +++++++ .../contoso-widgetmanager/api-extractor.json | 1 + .../contoso-widgetmanager/eslint.config.mjs | 14 + .../contoso-widgetmanager/metadata.json | 24 ++ .../contoso-widgetmanager/package.json | 189 ++++++++++++ .../contoso-widgetmanager-api-node.api.md | 26 ++ ...toso-widgetmanager-api-widgets-node.api.md | 52 ++++ .../contoso-widgetmanager-models-node.api.md | 40 +++ .../review/contoso-widgetmanager-node.api.md | 118 ++++++++ .../contoso-widgetmanager/src/api/index.ts | 8 + .../src/api/widgetManagerContext.ts | 59 ++++ .../src/api/widgets/index.ts | 17 ++ .../src/api/widgets/operations.ts | 268 ++++++++++++++++++ .../src/api/widgets/options.ts | 25 ++ .../src/classic/index.ts | 4 + .../src/classic/widgets/index.ts | 77 +++++ .../contoso-widgetmanager/src/index.ts | 28 ++ .../contoso-widgetmanager/src/logger.ts | 5 + .../contoso-widgetmanager/src/models/index.ts | 10 + .../src/models/models.ts | 117 ++++++++ .../src/restorePollerHelpers.ts | 156 ++++++++++ .../src/static-helpers/pagingHelpers.ts | 246 ++++++++++++++++ .../src/static-helpers/pollingHelpers.ts | 127 +++++++++ .../src/static-helpers/urlTemplate.ts | 227 +++++++++++++++ .../src/widgetManagerClient.ts | 37 +++ .../test/public/sampleTest.spec.ts | 28 ++ .../test/public/utils/recordedClient.ts | 24 ++ .../test/snippets.spec.ts | 25 ++ .../tsconfig.browser.config.json | 10 + .../contoso-widgetmanager/tsconfig.json | 13 + .../tsconfig.snippets.json | 3 + .../contoso-widgetmanager/tsconfig.src.json | 3 + .../contoso-widgetmanager/tsconfig.test.json | 14 + .../tsconfig.test.node.json | 10 + .../contoso-widgetmanager/tsp-location.yaml | 5 + .../vitest.browser.config.ts | 6 + .../contoso-widgetmanager/vitest.config.ts | 6 + .../vitest.esm.config.ts | 8 + 43 files changed, 2281 insertions(+) create mode 100644 sdk/contosowidgetmanager/ci.mgmt.yml create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/LICENSE create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/README.md create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/api-extractor.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/eslint.config.mjs create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/metadata.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/package.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-node.api.md create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-widgets-node.api.md create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-models-node.api.md create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-node.api.md create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/api/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgetManagerContext.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/operations.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/options.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/widgets/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/logger.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/models/index.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/models/models.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/restorePollerHelpers.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pagingHelpers.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pollingHelpers.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/urlTemplate.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/src/widgetManagerClient.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/test/public/sampleTest.spec.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/test/public/utils/recordedClient.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/test/snippets.spec.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.browser.config.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.snippets.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.src.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.node.json create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/tsp-location.yaml create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/vitest.browser.config.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/vitest.config.ts create mode 100644 sdk/contosowidgetmanager/contoso-widgetmanager/vitest.esm.config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dff0313f8f2d..5f8e2c0d41ac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1644,6 +1644,9 @@ sdk/compute/arm-computerecommender/ @qiaozha @MaryGao @JialinHuang803 # PRLabel: %Mgmt sdk/computelimit/arm-computelimit/ @qiaozha @MaryGao @JialinHuang803 +# PRLabel: %Mgmt +sdk/contosowidgetmanager/contoso-widgetmanager/ @qiaozha @MaryGao @JialinHuang803 + ########### # Config ########### diff --git a/eng/ignore-links.txt b/eng/ignore-links.txt index 7491206b3b10..20b8f06d30f3 100644 --- a/eng/ignore-links.txt +++ b/eng/ignore-links.txt @@ -53,3 +53,4 @@ https://learn.microsoft.com/javascript/api/@azure/arm-computerecommender?view=az https://learn.microsoft.com/javascript/api/@azure/arm-computelimit?view=azure-node-preview https://learn.microsoft.com/javascript/api/@azure/arm-certificateregistration?view=azure-node-preview https://learn.microsoft.com/javascript/api/@azure/arm-domainregistration?view=azure-node-preview +https://learn.microsoft.com/javascript/api/@azure/contoso-widgetmanager?view=azure-node-preview diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8593520a5504..419551ad46b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7665,6 +7665,88 @@ importers: specifier: catalog:testing version: 4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(@vitest/browser-playwright@4.0.16)(jsdom@16.7.0)(msw@2.7.3(@types/node@20.19.27)(typescript@5.9.3))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.2) + sdk/contosowidgetmanager/contoso-widgetmanager: + dependencies: + '@azure-rest/core-client': + specifier: ^2.3.1 + version: link:../../core/core-client-rest + '@azure/abort-controller': + specifier: ^2.1.2 + version: link:../../core/abort-controller + '@azure/core-auth': + specifier: ^1.9.0 + version: link:../../core/core-auth + '@azure/core-lro': + specifier: ^3.1.0 + version: link:../../core/core-lro + '@azure/core-rest-pipeline': + specifier: ^1.20.0 + version: link:../../core/core-rest-pipeline + '@azure/core-util': + specifier: ^1.12.0 + version: link:../../core/core-util + '@azure/logger': + specifier: ^1.2.0 + version: link:../../core/logger + tslib: + specifier: ^2.8.1 + version: 2.8.1 + devDependencies: + '@azure-tools/test-credential': + specifier: workspace:^ + version: link:../../test-utils/test-credential + '@azure-tools/test-recorder': + specifier: workspace:^ + version: link:../../test-utils/recorder + '@azure-tools/test-utils-vitest': + specifier: workspace:^ + version: link:../../test-utils/test-utils-vitest + '@azure/dev-tool': + specifier: workspace:^ + version: link:../../../common/tools/dev-tool + '@azure/eslint-plugin-azure-sdk': + specifier: workspace:^ + version: link:../../../common/tools/eslint-plugin-azure-sdk + '@azure/identity': + specifier: catalog:internal + version: 4.13.0 + '@types/node': + specifier: 'catalog:' + version: 20.19.27 + '@vitest/browser-playwright': + specifier: catalog:testing + version: 4.0.16(msw@2.7.3(@types/node@20.19.27)(typescript@5.9.3))(playwright@1.57.0)(vite@7.3.0(@types/node@20.19.27)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.16) + '@vitest/coverage-istanbul': + specifier: catalog:testing + version: 4.0.16(vitest@4.0.16) + cross-env: + specifier: 'catalog:' + version: 10.1.0 + dotenv: + specifier: catalog:testing + version: 16.6.1 + eslint: + specifier: 'catalog:' + version: 9.39.2 + playwright: + specifier: catalog:testing + version: 1.57.0 + prettier: + specifier: 'catalog:' + version: 3.7.4 + rimraf: + specifier: 'catalog:' + version: 6.1.2 + tshy: + specifier: 'catalog:' + version: 3.1.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: catalog:testing + version: 4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(@vitest/browser-playwright@4.0.16)(jsdom@16.7.0)(msw@2.7.3(@types/node@20.19.27)(typescript@5.9.3))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.2) + sdk/core/abort-controller: dependencies: tslib: diff --git a/sdk/contosowidgetmanager/ci.mgmt.yml b/sdk/contosowidgetmanager/ci.mgmt.yml new file mode 100644 index 000000000000..e7b2969f0fec --- /dev/null +++ b/sdk/contosowidgetmanager/ci.mgmt.yml @@ -0,0 +1,35 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - feature/* + - release/* + - hotfix/* + exclude: + - feature/v4 + paths: + include: + - sdk/contosowidgetmanager/contoso-widgetmanager + - sdk/contosowidgetmanager/ci.mgmt.yml +pr: + branches: + include: + - main + - feature/* + - release/* + - hotfix/* + exclude: + - feature/v4 + paths: + include: + - sdk/contosowidgetmanager/contoso-widgetmanager + - sdk/contosowidgetmanager/ci.mgmt.yml +extends: + template: /eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: contosowidgetmanager + Artifacts: + - name: azure-contoso-widgetmanager + safeName: azurecontosowidgetmanager diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/LICENSE b/sdk/contosowidgetmanager/contoso-widgetmanager/LICENSE new file mode 100644 index 000000000000..63447fd8bbbf --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/README.md b/sdk/contosowidgetmanager/contoso-widgetmanager/README.md new file mode 100644 index 000000000000..444cab5fadc4 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/README.md @@ -0,0 +1,109 @@ +# Azure WidgetManager client library for JavaScript + +This package contains an isomorphic SDK (runs both in Node.js and in browsers) for Azure WidgetManager client. + + + +Key links: + +- [Source code](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/contosowidgetmanager/contoso-widgetmanager) +- [Package (NPM)](https://www.npmjs.com/package/@azure/contoso-widgetmanager) +- [API reference documentation](https://learn.microsoft.com/javascript/api/@azure/contoso-widgetmanager) + +## Getting started + +### Currently supported environments + +- [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule) +- Latest versions of Safari, Chrome, Edge and Firefox. + +See our [support policy](https://github.com/Azure/azure-sdk-for-js/blob/main/SUPPORT.md) for more details. + +### Prerequisites + +- An [Azure subscription][azure_sub]. + +### Install the `@azure/contoso-widgetmanager` package + +Install the Azure WidgetManager client library for JavaScript with `npm`: + +```bash +npm install @azure/contoso-widgetmanager +``` + +### Create and authenticate a `WidgetManagerClient` + +To create a client object to access the Azure WidgetManager API, you will need the `endpoint` of your Azure WidgetManager resource and a `credential`. The Azure WidgetManager client can use Azure Active Directory credentials to authenticate. +You can find the endpoint for your Azure WidgetManager resource in the [Azure Portal][azure_portal]. + +You can authenticate with Azure Active Directory using a credential from the [@azure/identity][azure_identity] library or [an existing AAD Token](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/identity/identity/samples/AzureIdentityExamples.md#authenticating-with-a-pre-fetched-access-token). + +To use the [DefaultAzureCredential][defaultazurecredential] provider shown below, or other credential providers provided with the Azure SDK, please install the `@azure/identity` package: + +```bash +npm install @azure/identity +``` + +You will also need to **register a new AAD application and grant access to Azure WidgetManager** by assigning the suitable role to your service principal (note: roles such as `"Owner"` will not grant the necessary permissions). + +For more information about how to create an Azure AD Application check out [this guide](https://learn.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal). + +Using Node.js and Node-like environments, you can use the `DefaultAzureCredential` class to authenticate the client. + +```ts snippet:ReadmeSampleCreateClient_Node +import { WidgetManagerClient } from "@azure/contoso-widgetmanager"; +import { DefaultAzureCredential } from "@azure/identity"; + +const client = new WidgetManagerClient("", new DefaultAzureCredential()); +``` + +For browser environments, use the `InteractiveBrowserCredential` from the `@azure/identity` package to authenticate. + +```ts snippet:ReadmeSampleCreateClient_Browser +import { InteractiveBrowserCredential } from "@azure/identity"; +import { WidgetManagerClient } from "@azure/contoso-widgetmanager"; + +const credential = new InteractiveBrowserCredential({ + tenantId: "", + clientId: "", +}); +const client = new WidgetManagerClient("", credential); +``` + + +### JavaScript Bundle +To use this client library in the browser, first you need to use a bundler. For details on how to do this, please refer to our [bundling documentation](https://aka.ms/AzureSDKBundling). + +## Key concepts + +### WidgetManagerClient + +`WidgetManagerClient` is the primary interface for developers using the Azure WidgetManager client library. Explore the methods on this client object to understand the different features of the Azure WidgetManager service that you can access. + +## Troubleshooting + +### Logging + +Enabling logging may help uncover useful information about failures. In order to see a log of HTTP requests and responses, set the `AZURE_LOG_LEVEL` environment variable to `info`. Alternatively, logging can be enabled at runtime by calling `setLogLevel` in the `@azure/logger`: + +```ts snippet:SetLogLevel +import { setLogLevel } from "@azure/logger"; + +setLogLevel("info"); +``` + +For more detailed instructions on how to enable logs, you can look at the [@azure/logger package docs](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/logger). + + +## Contributing + +If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md) to learn more about how to build and test the code. + +## Related projects + +- [Microsoft Azure SDK for JavaScript](https://github.com/Azure/azure-sdk-for-js) + +[azure_sub]: https://azure.microsoft.com/free/ +[azure_portal]: https://portal.azure.com +[azure_identity]: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity +[defaultazurecredential]: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity#defaultazurecredential diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/api-extractor.json b/sdk/contosowidgetmanager/contoso-widgetmanager/api-extractor.json new file mode 100644 index 000000000000..870d6d399477 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/api-extractor.json @@ -0,0 +1 @@ +{ "extends": "../../../api-extractor-base.json" } diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/eslint.config.mjs b/sdk/contosowidgetmanager/contoso-widgetmanager/eslint.config.mjs new file mode 100644 index 000000000000..6d2f8a5914c3 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/eslint.config.mjs @@ -0,0 +1,14 @@ +import azsdkEslint from "@azure/eslint-plugin-azure-sdk"; + +export default azsdkEslint.config([ + { + rules: { + "@azure/azure-sdk/ts-modules-only-named": "warn", + "@azure/azure-sdk/ts-package-json-types": "warn", + "@azure/azure-sdk/ts-package-json-engine-is-present": "warn", + "@azure/azure-sdk/ts-package-json-files-required": "off", + "@azure/azure-sdk/ts-package-json-main-is-cjs": "off", + "tsdoc/syntax": "warn", + }, + }, +]); diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/metadata.json b/sdk/contosowidgetmanager/contoso-widgetmanager/metadata.json new file mode 100644 index 000000000000..82a23a26de49 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/metadata.json @@ -0,0 +1,24 @@ +{ + "apiVersion": "2022-12-01", + "emitterVersion": "0.47.1", + "crossLanguageDefinitions": { + "CrossLanguagePackageId": "Azure.Contoso.WidgetManager", + "CrossLanguageDefinitionId": { + "@azure/contoso-widgetmanager!WidgetSuite:interface": "Azure.Contoso.WidgetManager.WidgetSuite", + "@azure/contoso-widgetmanager!FakedSharedModel:interface": "FakedSharedModel", + "@azure/contoso-widgetmanager!ErrorResponse:interface": "Azure.Core.Foundations.ErrorResponse", + "@azure/contoso-widgetmanager!Error:interface": "Azure.Core.Foundations.Error", + "@azure/contoso-widgetmanager!InnerError:interface": "Azure.Core.Foundations.InnerError", + "@azure/contoso-widgetmanager!ResourceOperationStatusWidgetSuiteWidgetSuiteError:interface": "Azure.Core.ResourceOperationStatus", + "@azure/contoso-widgetmanager!OperationStatusError:interface": "Azure.Core.Foundations.OperationStatus", + "@azure/contoso-widgetmanager!PagedWidgetSuite:interface": "Azure.Core.Foundations.CustomPage", + "@azure/contoso-widgetmanager!KnownOperationState:enum": "Azure.Core.Foundations.OperationState", + "@azure/contoso-widgetmanager!KnownVersions:enum": "Azure.Contoso.WidgetManager.Versions", + "@azure/contoso-widgetmanager!WidgetsOperations#listWidgets:member": "Azure.Contoso.WidgetManager.Widgets.listWidgets", + "@azure/contoso-widgetmanager!WidgetsOperations#deleteWidget:member": "Azure.Contoso.WidgetManager.Widgets.deleteWidget", + "@azure/contoso-widgetmanager!WidgetsOperations#createOrUpdateWidget:member": "Azure.Contoso.WidgetManager.Widgets.createOrUpdateWidget", + "@azure/contoso-widgetmanager!WidgetsOperations#getWidgetOperationStatus:member": "Azure.Contoso.WidgetManager.Widgets.getWidgetOperationStatus", + "@azure/contoso-widgetmanager!WidgetsOperations#getWidget:member": "Azure.Contoso.WidgetManager.Widgets.getWidget" + } + } +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/package.json b/sdk/contosowidgetmanager/contoso-widgetmanager/package.json new file mode 100644 index 000000000000..f6e09b265843 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/package.json @@ -0,0 +1,189 @@ +{ + "name": "@azure/contoso-widgetmanager", + "version": "1.0.0-beta.1", + "description": "A generated SDK for WidgetManagerClient.", + "engines": { + "node": ">=20.0.0" + }, + "sideEffects": false, + "autoPublish": false, + "tshy": { + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts", + "./api": "./src/api/index.ts", + "./api/widgets": "./src/api/widgets/index.ts", + "./models": "./src/models/index.ts" + }, + "dialects": [ + "esm", + "commonjs" + ], + "esmDialects": [ + "browser", + "react-native" + ], + "selfLink": false, + "project": "../../../tsconfig.src.build.json" + }, + "type": "module", + "browser": "./dist/browser/index.js", + "react-native": "./dist/react-native/index.js", + "keywords": [ + "node", + "azure", + "cloud", + "typescript", + "browser", + "isomorphic" + ], + "author": "Microsoft Corporation", + "license": "MIT", + "files": [ + "dist/", + "!dist/**/*.d.*ts.map", + "README.md", + "LICENSE" + ], + "sdk-type": "client", + "repository": "github:Azure/azure-sdk-for-js", + "bugs": { + "url": "https://github.com/Azure/azure-sdk-for-js/issues" + }, + "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/contosowidgetmanager/contoso-widgetmanager/README.md", + "prettier": "@azure/eslint-plugin-azure-sdk/prettier.json", + "//metadata": { + "constantPaths": [ + { + "path": "src/api/widgetManagerContext.ts", + "prefix": "userAgentInfo" + } + ] + }, + "dependencies": { + "@azure/core-util": "^1.12.0", + "@azure-rest/core-client": "^2.3.1", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-lro": "^3.1.0", + "@azure/core-rest-pipeline": "^1.20.0", + "@azure/logger": "^1.2.0", + "tslib": "^2.8.1" + }, + "devDependencies": { + "@azure-tools/test-credential": "workspace:^", + "@azure-tools/test-recorder": "workspace:^", + "@azure-tools/test-utils-vitest": "workspace:^", + "@azure/dev-tool": "workspace:^", + "tshy": "catalog:", + "@azure/eslint-plugin-azure-sdk": "workspace:^", + "@azure/identity": "catalog:internal", + "@types/node": "catalog:", + "cross-env": "catalog:", + "eslint": "catalog:", + "prettier": "catalog:", + "rimraf": "catalog:", + "@vitest/browser-playwright": "catalog:testing", + "@vitest/coverage-istanbul": "catalog:testing", + "dotenv": "catalog:testing", + "playwright": "catalog:testing", + "typescript": "catalog:", + "vitest": "catalog:testing" + }, + "scripts": { + "clean": "rimraf --glob dist dist-browser dist-esm test-dist temp types *.tgz *.log", + "extract-api": "rimraf review && dev-tool run extract-api", + "pack": "pnpm pack 2>&1", + "lint": "eslint package.json src test", + "lint:fix": "eslint package.json src test --fix --fix-type [problem,suggestion]", + "build:samples": "echo skipped", + "check-format": "prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.{ts,cts,mts}\" \"test/**/*.{ts,cts,mts}\" \"*.{js,cjs,mjs,json}\" ", + "execute:samples": "echo skipped", + "format": "prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.{ts,cts,mts}\" \"test/**/*.{ts,cts,mts}\" \"*.{js,cjs,mjs,json}\" ", + "generate:client": "echo skipped", + "test:browser": "dev-tool run build-test && dev-tool run test:vitest --browser", + "build": "npm run clean && dev-tool run build-package && dev-tool run extract-api", + "test:node": "dev-tool run test:vitest", + "test:node:esm": "dev-tool run test:vitest --esm", + "test": "npm run test:node && npm run test:browser", + "update-snippets": "dev-tool run update-snippets" + }, + "exports": { + "./package.json": "./package.json", + ".": { + "browser": { + "types": "./dist/browser/index.d.ts", + "default": "./dist/browser/index.js" + }, + "react-native": { + "types": "./dist/react-native/index.d.ts", + "default": "./dist/react-native/index.js" + }, + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./api": { + "browser": { + "types": "./dist/browser/api/index.d.ts", + "default": "./dist/browser/api/index.js" + }, + "react-native": { + "types": "./dist/react-native/api/index.d.ts", + "default": "./dist/react-native/api/index.js" + }, + "import": { + "types": "./dist/esm/api/index.d.ts", + "default": "./dist/esm/api/index.js" + }, + "require": { + "types": "./dist/commonjs/api/index.d.ts", + "default": "./dist/commonjs/api/index.js" + } + }, + "./api/widgets": { + "browser": { + "types": "./dist/browser/api/widgets/index.d.ts", + "default": "./dist/browser/api/widgets/index.js" + }, + "react-native": { + "types": "./dist/react-native/api/widgets/index.d.ts", + "default": "./dist/react-native/api/widgets/index.js" + }, + "import": { + "types": "./dist/esm/api/widgets/index.d.ts", + "default": "./dist/esm/api/widgets/index.js" + }, + "require": { + "types": "./dist/commonjs/api/widgets/index.d.ts", + "default": "./dist/commonjs/api/widgets/index.js" + } + }, + "./models": { + "browser": { + "types": "./dist/browser/models/index.d.ts", + "default": "./dist/browser/models/index.js" + }, + "react-native": { + "types": "./dist/react-native/models/index.d.ts", + "default": "./dist/react-native/models/index.js" + }, + "import": { + "types": "./dist/esm/models/index.d.ts", + "default": "./dist/esm/models/index.js" + }, + "require": { + "types": "./dist/commonjs/models/index.d.ts", + "default": "./dist/commonjs/models/index.js" + } + } + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-node.api.md b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-node.api.md new file mode 100644 index 000000000000..791ba4ea5f50 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-node.api.md @@ -0,0 +1,26 @@ +## API Report File for "@azure/contoso-widgetmanager" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { Client } from '@azure-rest/core-client'; +import type { ClientOptions } from '@azure-rest/core-client'; +import type { TokenCredential } from '@azure/core-auth'; + +// @public (undocumented) +export function createWidgetManager(endpointParam: string, credential: TokenCredential, options?: WidgetManagerClientOptionalParams): WidgetManagerContext; + +// @public +export interface WidgetManagerClientOptionalParams extends ClientOptions { + apiVersion?: string; +} + +// @public (undocumented) +export interface WidgetManagerContext extends Client { + apiVersion: string; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-widgets-node.api.md b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-widgets-node.api.md new file mode 100644 index 000000000000..f58d240c52e3 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-api-widgets-node.api.md @@ -0,0 +1,52 @@ +## API Report File for "@azure/contoso-widgetmanager" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { Client } from '@azure-rest/core-client'; +import type { ErrorModel } from '@azure-rest/core-client'; +import type { OperationOptions } from '@azure-rest/core-client'; +import type { OperationState } from '@azure/core-lro'; +import type { PollerLike } from '@azure/core-lro'; + +// @public +export function createOrUpdateWidget(context: WidgetManagerContext, widgetName: string, resource: WidgetSuite, options?: WidgetsCreateOrUpdateWidgetOptionalParams): PollerLike, WidgetSuite>; + +// @public +export function deleteWidget(context: WidgetManagerContext, widgetName: string, options?: WidgetsDeleteWidgetOptionalParams): PollerLike, WidgetSuite>; + +// @public +export function getWidget(context: WidgetManagerContext, widgetName: string, options?: WidgetsGetWidgetOptionalParams): Promise; + +// @public +export function getWidgetOperationStatus(context: WidgetManagerContext, widgetName: string, operationId: string, options?: WidgetsGetWidgetOperationStatusOptionalParams): Promise; + +// @public +export function listWidgets(context: WidgetManagerContext, options?: WidgetsListWidgetsOptionalParams): PagedAsyncIterableIterator; + +// @public +export interface WidgetsCreateOrUpdateWidgetOptionalParams extends OperationOptions { + updateIntervalInMs?: number; +} + +// @public +export interface WidgetsDeleteWidgetOptionalParams extends OperationOptions { + updateIntervalInMs?: number; +} + +// @public +export interface WidgetsGetWidgetOperationStatusOptionalParams extends OperationOptions { +} + +// @public +export interface WidgetsGetWidgetOptionalParams extends OperationOptions { +} + +// @public +export interface WidgetsListWidgetsOptionalParams extends OperationOptions { +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-models-node.api.md b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-models-node.api.md new file mode 100644 index 000000000000..63f41554e3fe --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-models-node.api.md @@ -0,0 +1,40 @@ +## API Report File for "@azure/contoso-widgetmanager" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ErrorModel } from '@azure-rest/core-client'; + +// @public +export interface FakedSharedModel { + createdAt: Date; + tag: string; +} + +// @public +export enum KnownVersions { + V20221201 = "2022-12-01" +} + +// @public +export type OperationState = "NotStarted" | "Running" | "Succeeded" | "Failed" | "Canceled"; + +// @public +export interface ResourceOperationStatusWidgetSuiteWidgetSuiteError { + error?: ErrorModel; + id: string; + result?: WidgetSuite; + status: OperationState; +} + +// @public +export interface WidgetSuite { + manufacturerId: string; + readonly name: string; + sharedModel?: FakedSharedModel; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-node.api.md b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-node.api.md new file mode 100644 index 000000000000..339ca05da208 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/review/contoso-widgetmanager-node.api.md @@ -0,0 +1,118 @@ +## API Report File for "@azure/contoso-widgetmanager" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { AbortSignalLike } from '@azure/abort-controller'; +import type { ClientOptions } from '@azure-rest/core-client'; +import type { ErrorModel } from '@azure-rest/core-client'; +import type { OperationOptions } from '@azure-rest/core-client'; +import type { OperationState as OperationState_2 } from '@azure/core-lro'; +import type { PathUncheckedResponse } from '@azure-rest/core-client'; +import type { Pipeline } from '@azure/core-rest-pipeline'; +import type { PollerLike } from '@azure/core-lro'; +import type { TokenCredential } from '@azure/core-auth'; + +// @public +export type ContinuablePage = TPage & { + continuationToken?: string; +}; + +// @public +export interface FakedSharedModel { + createdAt: Date; + tag: string; +} + +// @public +export enum KnownVersions { + V20221201 = "2022-12-01" +} + +// @public +export type OperationState = "NotStarted" | "Running" | "Succeeded" | "Failed" | "Canceled"; + +// @public +export interface PagedAsyncIterableIterator { + [Symbol.asyncIterator](): PagedAsyncIterableIterator; + byPage: (settings?: TPageSettings) => AsyncIterableIterator>; + next(): Promise>; +} + +// @public +export interface PageSettings { + continuationToken?: string; +} + +// @public +export interface ResourceOperationStatusWidgetSuiteWidgetSuiteError { + error?: ErrorModel; + id: string; + result?: WidgetSuite; + status: OperationState; +} + +// @public +export function restorePoller(client: WidgetManagerClient, serializedState: string, sourceOperation: (...args: any[]) => PollerLike, TResult>, options?: RestorePollerOptions): PollerLike, TResult>; + +// @public (undocumented) +export interface RestorePollerOptions extends OperationOptions { + abortSignal?: AbortSignalLike; + processResponseBody?: (result: TResponse) => Promise; + updateIntervalInMs?: number; +} + +// @public (undocumented) +export class WidgetManagerClient { + constructor(endpointParam: string, credential: TokenCredential, options?: WidgetManagerClientOptionalParams); + readonly pipeline: Pipeline; + readonly widgets: WidgetsOperations; +} + +// @public +export interface WidgetManagerClientOptionalParams extends ClientOptions { + apiVersion?: string; +} + +// @public +export interface WidgetsCreateOrUpdateWidgetOptionalParams extends OperationOptions { + updateIntervalInMs?: number; +} + +// @public +export interface WidgetsDeleteWidgetOptionalParams extends OperationOptions { + updateIntervalInMs?: number; +} + +// @public +export interface WidgetsGetWidgetOperationStatusOptionalParams extends OperationOptions { +} + +// @public +export interface WidgetsGetWidgetOptionalParams extends OperationOptions { +} + +// @public +export interface WidgetsListWidgetsOptionalParams extends OperationOptions { +} + +// @public +export interface WidgetsOperations { + createOrUpdateWidget: (widgetName: string, resource: WidgetSuite, options?: WidgetsCreateOrUpdateWidgetOptionalParams) => PollerLike, WidgetSuite>; + deleteWidget: (widgetName: string, options?: WidgetsDeleteWidgetOptionalParams) => PollerLike, WidgetSuite>; + getWidget: (widgetName: string, options?: WidgetsGetWidgetOptionalParams) => Promise; + getWidgetOperationStatus: (widgetName: string, operationId: string, options?: WidgetsGetWidgetOperationStatusOptionalParams) => Promise; + listWidgets: (options?: WidgetsListWidgetsOptionalParams) => PagedAsyncIterableIterator; +} + +// @public +export interface WidgetSuite { + manufacturerId: string; + readonly name: string; + sharedModel?: FakedSharedModel; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/index.ts new file mode 100644 index 000000000000..dfff21e925f7 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/index.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export { + createWidgetManager, + WidgetManagerContext, + WidgetManagerClientOptionalParams, +} from "./widgetManagerContext.js"; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgetManagerContext.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgetManagerContext.ts new file mode 100644 index 000000000000..aa18857a8f92 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgetManagerContext.ts @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { logger } from "../logger.js"; +import { KnownVersions } from "../models/models.js"; +import type { Client, ClientOptions } from "@azure-rest/core-client"; +import { getClient } from "@azure-rest/core-client"; +import type { TokenCredential } from "@azure/core-auth"; + +export interface WidgetManagerContext extends Client { + /** The API version to use for this operation. */ + /** Known values of {@link KnownVersions} that the service accepts. */ + apiVersion: string; +} + +/** Optional parameters for the client. */ +export interface WidgetManagerClientOptionalParams extends ClientOptions { + /** The API version to use for this operation. */ + /** Known values of {@link KnownVersions} that the service accepts. */ + apiVersion?: string; +} + +export function createWidgetManager( + endpointParam: string, + credential: TokenCredential, + options: WidgetManagerClientOptionalParams = {}, +): WidgetManagerContext { + const endpointUrl = options.endpoint ?? String(endpointParam); + const prefixFromOptions = options?.userAgentOptions?.userAgentPrefix; + const userAgentInfo = `azsdk-js-contoso-widgetmanager/1.0.0-beta.1`; + const userAgentPrefix = prefixFromOptions + ? `${prefixFromOptions} azsdk-js-api ${userAgentInfo}` + : `azsdk-js-api ${userAgentInfo}`; + const { apiVersion: _, ...updatedOptions } = { + ...options, + userAgentOptions: { userAgentPrefix }, + loggingOptions: { logger: options.loggingOptions?.logger ?? logger.info }, + credentials: { scopes: options.credentials?.scopes ?? ["https://contoso.azure.com/.default"] }, + }; + const clientContext = getClient(endpointUrl, credential, updatedOptions); + clientContext.pipeline.removePolicy({ name: "ApiVersionPolicy" }); + const apiVersion = options.apiVersion ?? "2022-12-01"; + clientContext.pipeline.addPolicy({ + name: "ClientApiVersionPolicy", + sendRequest: (req, next) => { + // Use the apiVersion defined in request url directly + // Append one if there is no apiVersion and we have one at client options + const url = new URL(req.url); + if (!url.searchParams.get("api-version")) { + req.url = `${req.url}${ + Array.from(url.searchParams.keys()).length > 0 ? "&" : "?" + }api-version=${apiVersion}`; + } + + return next(req); + }, + }); + return { ...clientContext, apiVersion } as WidgetManagerContext; +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/index.ts new file mode 100644 index 000000000000..b5c249efa6a3 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/index.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export { + listWidgets, + deleteWidget, + createOrUpdateWidget, + getWidgetOperationStatus, + getWidget, +} from "./operations.js"; +export { + WidgetsListWidgetsOptionalParams, + WidgetsDeleteWidgetOptionalParams, + WidgetsCreateOrUpdateWidgetOptionalParams, + WidgetsGetWidgetOperationStatusOptionalParams, + WidgetsGetWidgetOptionalParams, +} from "./options.js"; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/operations.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/operations.ts new file mode 100644 index 000000000000..88413254b968 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/operations.ts @@ -0,0 +1,268 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { WidgetManagerContext as Client } from "../index.js"; +import type { + WidgetSuite, + ResourceOperationStatusWidgetSuiteWidgetSuiteError, + _PagedWidgetSuite, +} from "../../models/models.js"; +import { + widgetSuiteSerializer, + widgetSuiteDeserializer, + resourceOperationStatusWidgetSuiteWidgetSuiteErrorDeserializer, + _pagedWidgetSuiteDeserializer, +} from "../../models/models.js"; +import type { PagedAsyncIterableIterator } from "../../static-helpers/pagingHelpers.js"; +import { buildPagedAsyncIterator } from "../../static-helpers/pagingHelpers.js"; +import { getLongRunningPoller } from "../../static-helpers/pollingHelpers.js"; +import { expandUrlTemplate } from "../../static-helpers/urlTemplate.js"; +import type { + WidgetsListWidgetsOptionalParams, + WidgetsDeleteWidgetOptionalParams, + WidgetsCreateOrUpdateWidgetOptionalParams, + WidgetsGetWidgetOperationStatusOptionalParams, + WidgetsGetWidgetOptionalParams, +} from "./options.js"; +import type { StreamableMethod, PathUncheckedResponse } from "@azure-rest/core-client"; +import { createRestError, operationOptionsToRequestParameters } from "@azure-rest/core-client"; +import type { PollerLike, OperationState } from "@azure/core-lro"; + +export function _listWidgetsSend( + context: Client, + options: WidgetsListWidgetsOptionalParams = { requestOptions: {} }, +): StreamableMethod { + const path = expandUrlTemplate( + "/widgets{?api%2Dversion}", + { + "api%2Dversion": context.apiVersion, + }, + { + allowReserved: options?.requestOptions?.skipUrlEncoding, + }, + ); + return context.path(path).get({ + ...operationOptionsToRequestParameters(options), + headers: { accept: "application/json", ...options.requestOptions?.headers }, + }); +} + +export async function _listWidgetsDeserialize( + result: PathUncheckedResponse, +): Promise<_PagedWidgetSuite> { + const expectedStatuses = ["200"]; + if (!expectedStatuses.includes(result.status)) { + throw createRestError(result); + } + + return _pagedWidgetSuiteDeserializer(result.body); +} + +/** List Widget resources */ +export function listWidgets( + context: Client, + options: WidgetsListWidgetsOptionalParams = { requestOptions: {} }, +): PagedAsyncIterableIterator { + return buildPagedAsyncIterator( + context, + () => _listWidgetsSend(context, options), + _listWidgetsDeserialize, + ["200"], + { itemName: "value", nextLinkName: "nextLink" }, + ); +} + +export function _deleteWidgetSend( + context: Client, + widgetName: string, + options: WidgetsDeleteWidgetOptionalParams = { requestOptions: {} }, +): StreamableMethod { + const path = expandUrlTemplate( + "/widgets/{widgetName}{?api%2Dversion}", + { + widgetName: widgetName, + "api%2Dversion": context.apiVersion, + }, + { + allowReserved: options?.requestOptions?.skipUrlEncoding, + }, + ); + return context.path(path).delete({ + ...operationOptionsToRequestParameters(options), + headers: { accept: "application/json", ...options.requestOptions?.headers }, + }); +} + +export async function _deleteWidgetDeserialize( + result: PathUncheckedResponse, +): Promise { + const expectedStatuses = ["202", "200", "201"]; + if (!expectedStatuses.includes(result.status)) { + throw createRestError(result); + } + + if (result?.body?.result === undefined) { + throw createRestError( + `Expected a result in the response at position "result.body.result"`, + result, + ); + } + + return widgetSuiteDeserializer(result.body.result); +} + +/** Delete a Widget asynchronously. */ +export function deleteWidget( + context: Client, + widgetName: string, + options: WidgetsDeleteWidgetOptionalParams = { requestOptions: {} }, +): PollerLike, WidgetSuite> { + return getLongRunningPoller(context, _deleteWidgetDeserialize, ["202", "200", "201"], { + updateIntervalInMs: options?.updateIntervalInMs, + abortSignal: options?.abortSignal, + getInitialResponse: () => _deleteWidgetSend(context, widgetName, options), + resourceLocationConfig: "operation-location", + }) as PollerLike, WidgetSuite>; +} + +export function _createOrUpdateWidgetSend( + context: Client, + widgetName: string, + resource: WidgetSuite, + options: WidgetsCreateOrUpdateWidgetOptionalParams = { requestOptions: {} }, +): StreamableMethod { + const path = expandUrlTemplate( + "/widgets/{widgetName}{?api%2Dversion}", + { + widgetName: widgetName, + "api%2Dversion": context.apiVersion, + }, + { + allowReserved: options?.requestOptions?.skipUrlEncoding, + }, + ); + return context.path(path).patch({ + ...operationOptionsToRequestParameters(options), + contentType: "application/merge-patch+json", + headers: { accept: "application/json", ...options.requestOptions?.headers }, + body: widgetSuiteSerializer(resource), + }); +} + +export async function _createOrUpdateWidgetDeserialize( + result: PathUncheckedResponse, +): Promise { + const expectedStatuses = ["201", "200", "202"]; + if (!expectedStatuses.includes(result.status)) { + throw createRestError(result); + } + + if (result?.body?.result === undefined) { + throw createRestError( + `Expected a result in the response at position "result.body.result"`, + result, + ); + } + + return widgetSuiteDeserializer(result.body.result); +} + +/** Creates or updates a Widget asynchronously. */ +export function createOrUpdateWidget( + context: Client, + widgetName: string, + resource: WidgetSuite, + options: WidgetsCreateOrUpdateWidgetOptionalParams = { requestOptions: {} }, +): PollerLike, WidgetSuite> { + return getLongRunningPoller(context, _createOrUpdateWidgetDeserialize, ["201", "200", "202"], { + updateIntervalInMs: options?.updateIntervalInMs, + abortSignal: options?.abortSignal, + getInitialResponse: () => _createOrUpdateWidgetSend(context, widgetName, resource, options), + resourceLocationConfig: "operation-location", + }) as PollerLike, WidgetSuite>; +} + +export function _getWidgetOperationStatusSend( + context: Client, + widgetName: string, + operationId: string, + options: WidgetsGetWidgetOperationStatusOptionalParams = { requestOptions: {} }, +): StreamableMethod { + const path = expandUrlTemplate( + "/widgets/{widgetName}/operations/{operationId}{?api%2Dversion}", + { + widgetName: widgetName, + operationId: operationId, + "api%2Dversion": context.apiVersion, + }, + { + allowReserved: options?.requestOptions?.skipUrlEncoding, + }, + ); + return context.path(path).get({ + ...operationOptionsToRequestParameters(options), + headers: { accept: "application/json", ...options.requestOptions?.headers }, + }); +} + +export async function _getWidgetOperationStatusDeserialize( + result: PathUncheckedResponse, +): Promise { + const expectedStatuses = ["200"]; + if (!expectedStatuses.includes(result.status)) { + throw createRestError(result); + } + + return resourceOperationStatusWidgetSuiteWidgetSuiteErrorDeserializer(result.body); +} + +/** Gets status of a Widget operation. */ +export async function getWidgetOperationStatus( + context: Client, + widgetName: string, + operationId: string, + options: WidgetsGetWidgetOperationStatusOptionalParams = { requestOptions: {} }, +): Promise { + const result = await _getWidgetOperationStatusSend(context, widgetName, operationId, options); + return _getWidgetOperationStatusDeserialize(result); +} + +export function _getWidgetSend( + context: Client, + widgetName: string, + options: WidgetsGetWidgetOptionalParams = { requestOptions: {} }, +): StreamableMethod { + const path = expandUrlTemplate( + "/widgets/{widgetName}{?api%2Dversion}", + { + widgetName: widgetName, + "api%2Dversion": context.apiVersion, + }, + { + allowReserved: options?.requestOptions?.skipUrlEncoding, + }, + ); + return context.path(path).get({ + ...operationOptionsToRequestParameters(options), + headers: { accept: "application/json", ...options.requestOptions?.headers }, + }); +} + +export async function _getWidgetDeserialize(result: PathUncheckedResponse): Promise { + const expectedStatuses = ["200"]; + if (!expectedStatuses.includes(result.status)) { + throw createRestError(result); + } + + return widgetSuiteDeserializer(result.body); +} + +/** Fetch a Widget by name. */ +export async function getWidget( + context: Client, + widgetName: string, + options: WidgetsGetWidgetOptionalParams = { requestOptions: {} }, +): Promise { + const result = await _getWidgetSend(context, widgetName, options); + return _getWidgetDeserialize(result); +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/options.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/options.ts new file mode 100644 index 000000000000..46e75008f329 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/api/widgets/options.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { OperationOptions } from "@azure-rest/core-client"; + +/** Optional parameters. */ +export interface WidgetsListWidgetsOptionalParams extends OperationOptions {} + +/** Optional parameters. */ +export interface WidgetsDeleteWidgetOptionalParams extends OperationOptions { + /** Delay to wait until next poll, in milliseconds. */ + updateIntervalInMs?: number; +} + +/** Optional parameters. */ +export interface WidgetsCreateOrUpdateWidgetOptionalParams extends OperationOptions { + /** Delay to wait until next poll, in milliseconds. */ + updateIntervalInMs?: number; +} + +/** Optional parameters. */ +export interface WidgetsGetWidgetOperationStatusOptionalParams extends OperationOptions {} + +/** Optional parameters. */ +export interface WidgetsGetWidgetOptionalParams extends OperationOptions {} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/index.ts new file mode 100644 index 000000000000..503fa2ec8bc4 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export { WidgetsOperations } from "./widgets/index.js"; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/widgets/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/widgets/index.ts new file mode 100644 index 000000000000..b6f31c1ab7f9 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/classic/widgets/index.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { WidgetManagerContext } from "../../api/widgetManagerContext.js"; +import { + listWidgets, + deleteWidget, + createOrUpdateWidget, + getWidgetOperationStatus, + getWidget, +} from "../../api/widgets/operations.js"; +import type { + WidgetsListWidgetsOptionalParams, + WidgetsDeleteWidgetOptionalParams, + WidgetsCreateOrUpdateWidgetOptionalParams, + WidgetsGetWidgetOperationStatusOptionalParams, + WidgetsGetWidgetOptionalParams, +} from "../../api/widgets/options.js"; +import type { + WidgetSuite, + ResourceOperationStatusWidgetSuiteWidgetSuiteError, +} from "../../models/models.js"; +import type { PagedAsyncIterableIterator } from "../../static-helpers/pagingHelpers.js"; +import type { PollerLike, OperationState } from "@azure/core-lro"; + +/** Interface representing a Widgets operations. */ +export interface WidgetsOperations { + /** List Widget resources */ + listWidgets: ( + options?: WidgetsListWidgetsOptionalParams, + ) => PagedAsyncIterableIterator; + /** Delete a Widget asynchronously. */ + deleteWidget: ( + widgetName: string, + options?: WidgetsDeleteWidgetOptionalParams, + ) => PollerLike, WidgetSuite>; + /** Creates or updates a Widget asynchronously. */ + createOrUpdateWidget: ( + widgetName: string, + resource: WidgetSuite, + options?: WidgetsCreateOrUpdateWidgetOptionalParams, + ) => PollerLike, WidgetSuite>; + /** Gets status of a Widget operation. */ + getWidgetOperationStatus: ( + widgetName: string, + operationId: string, + options?: WidgetsGetWidgetOperationStatusOptionalParams, + ) => Promise; + /** Fetch a Widget by name. */ + getWidget: (widgetName: string, options?: WidgetsGetWidgetOptionalParams) => Promise; +} + +function _getWidgets(context: WidgetManagerContext) { + return { + listWidgets: (options?: WidgetsListWidgetsOptionalParams) => listWidgets(context, options), + deleteWidget: (widgetName: string, options?: WidgetsDeleteWidgetOptionalParams) => + deleteWidget(context, widgetName, options), + createOrUpdateWidget: ( + widgetName: string, + resource: WidgetSuite, + options?: WidgetsCreateOrUpdateWidgetOptionalParams, + ) => createOrUpdateWidget(context, widgetName, resource, options), + getWidgetOperationStatus: ( + widgetName: string, + operationId: string, + options?: WidgetsGetWidgetOperationStatusOptionalParams, + ) => getWidgetOperationStatus(context, widgetName, operationId, options), + getWidget: (widgetName: string, options?: WidgetsGetWidgetOptionalParams) => + getWidget(context, widgetName, options), + }; +} + +export function _getWidgetsOperations(context: WidgetManagerContext): WidgetsOperations { + return { + ..._getWidgets(context), + }; +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/index.ts new file mode 100644 index 000000000000..699523920c63 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/index.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + PageSettings, + ContinuablePage, + PagedAsyncIterableIterator, +} from "./static-helpers/pagingHelpers.js"; + +export { WidgetManagerClient } from "./widgetManagerClient.js"; +export { restorePoller, RestorePollerOptions } from "./restorePollerHelpers.js"; +export { + WidgetSuite, + FakedSharedModel, + ResourceOperationStatusWidgetSuiteWidgetSuiteError, + OperationState, + KnownVersions, +} from "./models/index.js"; +export { WidgetManagerClientOptionalParams } from "./api/index.js"; +export { + WidgetsListWidgetsOptionalParams, + WidgetsDeleteWidgetOptionalParams, + WidgetsCreateOrUpdateWidgetOptionalParams, + WidgetsGetWidgetOperationStatusOptionalParams, + WidgetsGetWidgetOptionalParams, +} from "./api/widgets/index.js"; +export { WidgetsOperations } from "./classic/index.js"; +export { PageSettings, ContinuablePage, PagedAsyncIterableIterator }; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/logger.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/logger.ts new file mode 100644 index 000000000000..0eb476b68707 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/logger.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { createClientLogger } from "@azure/logger"; +export const logger = createClientLogger("contoso-widgetmanager"); diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/index.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/index.ts new file mode 100644 index 000000000000..e39242f7a2aa --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/index.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export { + WidgetSuite, + FakedSharedModel, + ResourceOperationStatusWidgetSuiteWidgetSuiteError, + OperationState, + KnownVersions, +} from "./models.js"; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/models.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/models.ts new file mode 100644 index 000000000000..e01063a30a5a --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/models/models.ts @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { ErrorModel } from "@azure-rest/core-client"; + +/** + * This file contains only generated model types and their (de)serializers. + * Disable the following rules for internal models with '_' prefix and deserializers which require 'any' for raw JSON input. + */ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/** A widget. */ +export interface WidgetSuite { + /** The widget name. */ + readonly name: string; + /** The ID of the widget's manufacturer. */ + manufacturerId: string; + /** The faked shared model. */ + sharedModel?: FakedSharedModel; +} + +export function widgetSuiteSerializer(item: WidgetSuite): any { + return { + manufacturerId: item["manufacturerId"], + sharedModel: !item["sharedModel"] + ? item["sharedModel"] + : fakedSharedModelSerializer(item["sharedModel"]), + }; +} + +export function widgetSuiteDeserializer(item: any): WidgetSuite { + return { + name: item["name"], + manufacturerId: item["manufacturerId"], + sharedModel: !item["sharedModel"] + ? item["sharedModel"] + : fakedSharedModelDeserializer(item["sharedModel"]), + }; +} + +/** Faked shared model */ +export interface FakedSharedModel { + /** The tag. */ + tag: string; + /** The created date. */ + createdAt: Date; +} + +export function fakedSharedModelSerializer(item: FakedSharedModel): any { + return { tag: item["tag"], createdAt: item["createdAt"].toISOString() }; +} + +export function fakedSharedModelDeserializer(item: any): FakedSharedModel { + return { + tag: item["tag"], + createdAt: new Date(item["createdAt"]), + }; +} + +/** Provides status details for long running operations. */ +export interface ResourceOperationStatusWidgetSuiteWidgetSuiteError { + /** The unique ID of the operation. */ + id: string; + /** The status of the operation */ + status: OperationState; + /** Error object that describes the error when status is "Failed". */ + error?: ErrorModel; + /** The result of the operation. */ + result?: WidgetSuite; +} + +export function resourceOperationStatusWidgetSuiteWidgetSuiteErrorDeserializer( + item: any, +): ResourceOperationStatusWidgetSuiteWidgetSuiteError { + return { + id: item["id"], + status: item["status"], + error: !item["error"] ? item["error"] : item["error"], + result: !item["result"] ? item["result"] : widgetSuiteDeserializer(item["result"]), + }; +} + +/** Enum describing allowed operation states. */ +export type OperationState = "NotStarted" | "Running" | "Succeeded" | "Failed" | "Canceled"; + +/** Paged collection of WidgetSuite items */ +export interface _PagedWidgetSuite { + /** The WidgetSuite items on this page */ + value: WidgetSuite[]; + /** The link to the next page of items */ + nextLink?: string; +} + +export function _pagedWidgetSuiteDeserializer(item: any): _PagedWidgetSuite { + return { + value: widgetSuiteArrayDeserializer(item["value"]), + nextLink: item["nextLink"], + }; +} + +export function widgetSuiteArraySerializer(result: Array): any[] { + return result.map((item) => { + return widgetSuiteSerializer(item); + }); +} + +export function widgetSuiteArrayDeserializer(result: Array): any[] { + return result.map((item) => { + return widgetSuiteDeserializer(item); + }); +} + +/** Versions info. */ +export enum KnownVersions { + /** The 2022-12-01 version. */ + V20221201 = "2022-12-01", +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/restorePollerHelpers.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/restorePollerHelpers.ts new file mode 100644 index 000000000000..d0acb06b14c7 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/restorePollerHelpers.ts @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { WidgetManagerClient } from "./widgetManagerClient.js"; +import { + _deleteWidgetDeserialize, + _createOrUpdateWidgetDeserialize, +} from "./api/widgets/operations.js"; +import { getLongRunningPoller } from "./static-helpers/pollingHelpers.js"; +import type { OperationOptions, PathUncheckedResponse } from "@azure-rest/core-client"; +import type { AbortSignalLike } from "@azure/abort-controller"; +import type { PollerLike, OperationState, ResourceLocationConfig } from "@azure/core-lro"; +import { deserializeState } from "@azure/core-lro"; + +export interface RestorePollerOptions< + TResult, + TResponse extends PathUncheckedResponse = PathUncheckedResponse, +> extends OperationOptions { + /** Delay to wait until next poll, in milliseconds. */ + updateIntervalInMs?: number; + /** + * The signal which can be used to abort requests. + */ + abortSignal?: AbortSignalLike; + /** Deserialization function for raw response body */ + processResponseBody?: (result: TResponse) => Promise; +} + +/** + * Creates a poller from the serialized state of another poller. This can be + * useful when you want to create pollers on a different host or a poller + * needs to be constructed after the original one is not in scope. + */ +export function restorePoller( + client: WidgetManagerClient, + serializedState: string, + sourceOperation: (...args: any[]) => PollerLike, TResult>, + options?: RestorePollerOptions, +): PollerLike, TResult> { + const pollerConfig = deserializeState(serializedState).config; + const { initialRequestUrl, requestMethod, metadata } = pollerConfig; + if (!initialRequestUrl || !requestMethod) { + throw new Error( + `Invalid serialized state: ${serializedState} for sourceOperation ${sourceOperation?.name}`, + ); + } + const resourceLocationConfig = metadata?.["resourceLocationConfig"] as + | ResourceLocationConfig + | undefined; + const { deserializer, expectedStatuses = [] } = + getDeserializationHelper(initialRequestUrl, requestMethod) ?? {}; + const deserializeHelper = options?.processResponseBody ?? deserializer; + if (!deserializeHelper) { + throw new Error( + `Please ensure the operation is in this client! We can't find its deserializeHelper for ${sourceOperation?.name}.`, + ); + } + return getLongRunningPoller( + (client as any)["_client"] ?? client, + deserializeHelper as (result: TResponse) => Promise, + expectedStatuses, + { + updateIntervalInMs: options?.updateIntervalInMs, + abortSignal: options?.abortSignal, + resourceLocationConfig, + restoreFrom: serializedState, + initialRequestUrl, + }, + ); +} + +interface DeserializationHelper { + deserializer: (result: PathUncheckedResponse) => Promise; + expectedStatuses: string[]; +} + +const deserializeMap: Record = { + "DELETE /widgets/{widgetName}": { + deserializer: _deleteWidgetDeserialize, + expectedStatuses: ["202", "200", "201"], + }, + "PATCH /widgets/{widgetName}": { + deserializer: _createOrUpdateWidgetDeserialize, + expectedStatuses: ["201", "200", "202"], + }, +}; + +function getDeserializationHelper( + urlStr: string, + method: string, +): DeserializationHelper | undefined { + const path = new URL(urlStr).pathname; + const pathParts = path.split("/"); + + // Traverse list to match the longest candidate + // matchedLen: the length of candidate path + // matchedValue: the matched status code array + let matchedLen = -1, + matchedValue: DeserializationHelper | undefined; + + // Iterate the responseMap to find a match + for (const [key, value] of Object.entries(deserializeMap)) { + // Extracting the path from the map key which is in format + // GET /path/foo + if (!key.startsWith(method)) { + continue; + } + const candidatePath = getPathFromMapKey(key); + // Get each part of the url path + const candidateParts = candidatePath.split("/"); + + // track if we have found a match to return the values found. + let found = true; + for (let i = candidateParts.length - 1, j = pathParts.length - 1; i >= 1 && j >= 1; i--, j--) { + if (candidateParts[i]?.startsWith("{") && candidateParts[i]?.indexOf("}") !== -1) { + const start = candidateParts[i]!.indexOf("}") + 1, + end = candidateParts[i]?.length; + // If the current part of the candidate is a "template" part + // Try to use the suffix of pattern to match the path + // {guid} ==> $ + // {guid}:export ==> :export$ + const isMatched = new RegExp(`${candidateParts[i]?.slice(start, end)}`).test( + pathParts[j] || "", + ); + + if (!isMatched) { + found = false; + break; + } + continue; + } + + // If the candidate part is not a template and + // the parts don't match mark the candidate as not found + // to move on with the next candidate path. + if (candidateParts[i] !== pathParts[j]) { + found = false; + break; + } + } + + // We finished evaluating the current candidate parts + // Update the matched value if and only if we found the longer pattern + if (found && candidatePath.length > matchedLen) { + matchedLen = candidatePath.length; + matchedValue = value; + } + } + + return matchedValue; +} + +function getPathFromMapKey(mapKey: string): string { + const pathStart = mapKey.indexOf("/"); + return mapKey.slice(pathStart); +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pagingHelpers.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pagingHelpers.ts new file mode 100644 index 000000000000..6f219e3c88cf --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pagingHelpers.ts @@ -0,0 +1,246 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { Client, PathUncheckedResponse } from "@azure-rest/core-client"; +import { createRestError } from "@azure-rest/core-client"; +import { RestError } from "@azure/core-rest-pipeline"; + +/** + * Options for the byPage method + */ +export interface PageSettings { + /** + * A reference to a specific page to start iterating from. + */ + continuationToken?: string; +} + +/** + * An interface that describes a page of results. + */ +export type ContinuablePage = TPage & { + /** + * The token that keeps track of where to continue the iterator + */ + continuationToken?: string; +}; + +/** + * An interface that allows async iterable iteration both to completion and by page. + */ +export interface PagedAsyncIterableIterator< + TElement, + TPage = TElement[], + TPageSettings extends PageSettings = PageSettings, +> { + /** + * The next method, part of the iteration protocol + */ + next(): Promise>; + /** + * The connection to the async iterator, part of the iteration protocol + */ + [Symbol.asyncIterator](): PagedAsyncIterableIterator; + /** + * Return an AsyncIterableIterator that works a page at a time + */ + byPage: (settings?: TPageSettings) => AsyncIterableIterator>; +} + +/** + * An interface that describes how to communicate with the service. + */ +export interface PagedResult< + TElement, + TPage = TElement[], + TPageSettings extends PageSettings = PageSettings, +> { + /** + * Link to the first page of results. + */ + firstPageLink?: string; + /** + * A method that returns a page of results. + */ + getPage: (pageLink?: string) => Promise<{ page: TPage; nextPageLink?: string } | undefined>; + /** + * a function to implement the `byPage` method on the paged async iterator. + */ + byPage?: (settings?: TPageSettings) => AsyncIterableIterator>; + + /** + * A function to extract elements from a page. + */ + toElements?: (page: TPage) => TElement[]; +} + +/** + * Options for the paging helper + */ +export interface BuildPagedAsyncIteratorOptions { + itemName?: string; + nextLinkName?: string; + nextLinkMethod?: "GET" | "POST"; +} + +/** + * Helper to paginate results in a generic way and return a PagedAsyncIterableIterator + */ +export function buildPagedAsyncIterator< + TElement, + TPage = TElement[], + TPageSettings extends PageSettings = PageSettings, + TResponse extends PathUncheckedResponse = PathUncheckedResponse, +>( + client: Client, + getInitialResponse: () => PromiseLike, + processResponseBody: (result: TResponse) => PromiseLike, + expectedStatuses: string[], + options: BuildPagedAsyncIteratorOptions = {}, +): PagedAsyncIterableIterator { + const itemName = options.itemName ?? "value"; + const nextLinkName = options.nextLinkName ?? "nextLink"; + const nextLinkMethod = options.nextLinkMethod ?? "GET"; + const pagedResult: PagedResult = { + getPage: async (pageLink?: string) => { + const result = + pageLink === undefined + ? await getInitialResponse() + : nextLinkMethod === "POST" + ? await client.pathUnchecked(pageLink).post() + : await client.pathUnchecked(pageLink).get(); + checkPagingRequest(result, expectedStatuses); + const results = await processResponseBody(result as TResponse); + const nextLink = getNextLink(results, nextLinkName); + const values = getElements(results, itemName) as TPage; + return { + page: values, + nextPageLink: nextLink, + }; + }, + byPage: (settings?: TPageSettings) => { + const { continuationToken } = settings ?? {}; + return getPageAsyncIterator(pagedResult, { + pageLink: continuationToken, + }); + }, + }; + return getPagedAsyncIterator(pagedResult); +} + +/** + * returns an async iterator that iterates over results. It also has a `byPage` + * method that returns pages of items at once. + * + * @param pagedResult - an object that specifies how to get pages. + * @returns a paged async iterator that iterates over results. + */ + +function getPagedAsyncIterator< + TElement, + TPage = TElement[], + TPageSettings extends PageSettings = PageSettings, +>( + pagedResult: PagedResult, +): PagedAsyncIterableIterator { + const iter = getItemAsyncIterator(pagedResult); + return { + next() { + return iter.next(); + }, + [Symbol.asyncIterator]() { + return this; + }, + byPage: + pagedResult?.byPage ?? + ((settings?: TPageSettings) => { + const { continuationToken } = settings ?? {}; + return getPageAsyncIterator(pagedResult, { + pageLink: continuationToken, + }); + }), + }; +} + +async function* getItemAsyncIterator( + pagedResult: PagedResult, +): AsyncIterableIterator { + const pages = getPageAsyncIterator(pagedResult); + for await (const page of pages) { + yield* page as unknown as TElement[]; + } +} + +async function* getPageAsyncIterator( + pagedResult: PagedResult, + options: { + pageLink?: string; + } = {}, +): AsyncIterableIterator> { + const { pageLink } = options; + let response = await pagedResult.getPage(pageLink ?? pagedResult.firstPageLink); + if (!response) { + return; + } + let result = response.page as ContinuablePage; + result.continuationToken = response.nextPageLink; + yield result; + while (response.nextPageLink) { + response = await pagedResult.getPage(response.nextPageLink); + if (!response) { + return; + } + result = response.page as ContinuablePage; + result.continuationToken = response.nextPageLink; + yield result; + } +} + +/** + * Gets for the value of nextLink in the body + */ +function getNextLink(body: unknown, nextLinkName?: string): string | undefined { + if (!nextLinkName) { + return undefined; + } + + const nextLink = (body as Record)[nextLinkName]; + + if (typeof nextLink !== "string" && typeof nextLink !== "undefined" && nextLink !== null) { + throw new RestError( + `Body Property ${nextLinkName} should be a string or undefined or null but got ${typeof nextLink}`, + ); + } + + if (nextLink === null) { + return undefined; + } + + return nextLink; +} + +/** + * Gets the elements of the current request in the body. + */ +function getElements(body: unknown, itemName: string): T[] { + const value = (body as Record)[itemName] as T[]; + if (!Array.isArray(value)) { + throw new RestError( + `Couldn't paginate response\n Body doesn't contain an array property with name: ${itemName}`, + ); + } + + return value ?? []; +} + +/** + * Checks if a request failed + */ +function checkPagingRequest(response: PathUncheckedResponse, expectedStatuses: string[]): void { + if (!expectedStatuses.includes(response.status)) { + throw createRestError( + `Pagination failed with unexpected statusCode ${response.status}`, + response, + ); + } +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pollingHelpers.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pollingHelpers.ts new file mode 100644 index 000000000000..63b3c201e5a8 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/pollingHelpers.ts @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { + PollerLike, + OperationState, + ResourceLocationConfig, + RunningOperation, + OperationResponse, +} from "@azure/core-lro"; +import { createHttpPoller } from "@azure/core-lro"; + +import type { Client, PathUncheckedResponse } from "@azure-rest/core-client"; +import { createRestError } from "@azure-rest/core-client"; +import type { AbortSignalLike } from "@azure/abort-controller"; + +export interface GetLongRunningPollerOptions { + /** Delay to wait until next poll, in milliseconds. */ + updateIntervalInMs?: number; + /** + * The signal which can be used to abort requests. + */ + abortSignal?: AbortSignalLike; + /** + * The potential location of the result of the LRO if specified by the LRO extension in the swagger. + */ + resourceLocationConfig?: ResourceLocationConfig; + /** + * The original url of the LRO + * Should not be null when restoreFrom is set + */ + initialRequestUrl?: string; + /** + * A serialized poller which can be used to resume an existing paused Long-Running-Operation. + */ + restoreFrom?: string; + /** + * The function to get the initial response + */ + getInitialResponse?: () => PromiseLike; +} +export function getLongRunningPoller( + client: Client, + processResponseBody: (result: TResponse) => Promise, + expectedStatuses: string[], + options: GetLongRunningPollerOptions, +): PollerLike, TResult> { + const { restoreFrom, getInitialResponse } = options; + if (!restoreFrom && !getInitialResponse) { + throw new Error("Either restoreFrom or getInitialResponse must be specified"); + } + let initialResponse: TResponse | undefined = undefined; + const pollAbortController = new AbortController(); + const poller: RunningOperation = { + sendInitialRequest: async () => { + if (!getInitialResponse) { + throw new Error("getInitialResponse is required when initializing a new poller"); + } + initialResponse = await getInitialResponse(); + return getLroResponse(initialResponse, expectedStatuses); + }, + sendPollRequest: async ( + path: string, + pollOptions?: { + abortSignal?: AbortSignalLike; + }, + ) => { + // The poll request would both listen to the user provided abort signal and the poller's own abort signal + function abortListener(): void { + pollAbortController.abort(); + } + const abortSignal = pollAbortController.signal; + if (options.abortSignal?.aborted) { + pollAbortController.abort(); + } else if (pollOptions?.abortSignal?.aborted) { + pollAbortController.abort(); + } else if (!abortSignal.aborted) { + options.abortSignal?.addEventListener("abort", abortListener, { + once: true, + }); + pollOptions?.abortSignal?.addEventListener("abort", abortListener, { + once: true, + }); + } + let response; + try { + response = await client.pathUnchecked(path).get({ abortSignal }); + } finally { + options.abortSignal?.removeEventListener("abort", abortListener); + pollOptions?.abortSignal?.removeEventListener("abort", abortListener); + } + + return getLroResponse(response as TResponse, expectedStatuses); + }, + }; + return createHttpPoller(poller, { + intervalInMs: options?.updateIntervalInMs, + resourceLocationConfig: options?.resourceLocationConfig, + restoreFrom: options?.restoreFrom, + processResult: (result: unknown) => { + return processResponseBody(result as TResponse); + }, + }); +} +/** + * Converts a Rest Client response to a response that the LRO implementation understands + * @param response - a rest client http response + * @param deserializeFn - deserialize function to convert Rest response to modular output + * @returns - An LRO response that the LRO implementation understands + */ +function getLroResponse( + response: TResponse, + expectedStatuses: string[], +): OperationResponse { + if (!expectedStatuses.includes(response.status)) { + throw createRestError(response); + } + + return { + flatResponse: response, + rawResponse: { + ...response, + statusCode: Number.parseInt(response.status), + body: response.body, + }, + }; +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/urlTemplate.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/urlTemplate.ts new file mode 100644 index 000000000000..c7109898692a --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/static-helpers/urlTemplate.ts @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// --------------------- +// interfaces +// --------------------- +interface ValueOptions { + isFirst: boolean; // is first value in the expression + op?: string; // operator + varValue?: any; // variable value + varName?: string; // variable name + modifier?: string; // modifier e.g * + reserved?: boolean; // if true we'll keep reserved words with not encoding +} + +export interface UrlTemplateOptions { + // if set to true, reserved characters will not be encoded + allowReserved?: boolean; +} + +// --------------------- +// helpers +// --------------------- +function encodeComponent(val: string, reserved?: boolean, op?: string): string { + return (reserved ?? op === "+") || op === "#" + ? encodeReservedComponent(val) + : encodeRFC3986URIComponent(val); +} + +function encodeReservedComponent(str: string): string { + return str + .split(/(%[0-9A-Fa-f]{2})/g) + .map((part) => (!/%[0-9A-Fa-f]/.test(part) ? encodeURI(part) : part)) + .join(""); +} + +function encodeRFC3986URIComponent(str: string): string { + return encodeURIComponent(str).replace( + /[!'()*]/g, + (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`, + ); +} + +function isDefined(val: any): boolean { + return val !== undefined && val !== null; +} + +function getNamedAndIfEmpty(op?: string): [boolean, string] { + return [!!op && [";", "?", "&"].includes(op), !!op && ["?", "&"].includes(op) ? "=" : ""]; +} + +function getFirstOrSep(op?: string, isFirst = false): string { + if (isFirst) { + return !op || op === "+" ? "" : op; + } else if (!op || op === "+" || op === "#") { + return ","; + } else if (op === "?") { + return "&"; + } else { + return op; + } +} + +function getExpandedValue(option: ValueOptions): string { + let isFirst = option.isFirst; + const { op, varName, varValue: value, reserved } = option; + const vals: string[] = []; + const [named, ifEmpty] = getNamedAndIfEmpty(op); + + if (Array.isArray(value)) { + for (const val of value.filter(isDefined)) { + // prepare the following parts: separator, varName, value + vals.push(`${getFirstOrSep(op, isFirst)}`); + if (named && varName) { + vals.push(`${encodeURIComponent(varName)}`); + if (val === "") { + vals.push(ifEmpty); + } else { + vals.push("="); + } + } + vals.push(encodeComponent(val, reserved, op)); + isFirst = false; + } + } else if (typeof value === "object") { + for (const key of Object.keys(value)) { + const val = value[key]; + if (!isDefined(val)) { + continue; + } + // prepare the following parts: separator, key, value + vals.push(`${getFirstOrSep(op, isFirst)}`); + if (key) { + vals.push(`${encodeURIComponent(key)}`); + if (named && val === "") { + vals.push(ifEmpty); + } else { + vals.push("="); + } + } + vals.push(encodeComponent(val, reserved, op)); + isFirst = false; + } + } + return vals.join(""); +} + +function getNonExpandedValue(option: ValueOptions): string | undefined { + const { op, varName, varValue: value, isFirst, reserved } = option; + const vals: string[] = []; + const first = getFirstOrSep(op, isFirst); + const [named, ifEmpty] = getNamedAndIfEmpty(op); + if (named && varName) { + vals.push(encodeComponent(varName, reserved, op)); + if (value === "") { + if (!ifEmpty) { + vals.push(ifEmpty); + } + return !vals.join("") ? undefined : `${first}${vals.join("")}`; + } + vals.push("="); + } + + const items = []; + if (Array.isArray(value)) { + for (const val of value.filter(isDefined)) { + items.push(encodeComponent(val, reserved, op)); + } + } else if (typeof value === "object") { + for (const key of Object.keys(value)) { + if (!isDefined(value[key])) { + continue; + } + items.push(encodeRFC3986URIComponent(key)); + items.push(encodeComponent(value[key], reserved, op)); + } + } + vals.push(items.join(",")); + return !vals.join(",") ? undefined : `${first}${vals.join("")}`; +} + +function getVarValue(option: ValueOptions): string | undefined { + const { op, varName, modifier, isFirst, reserved, varValue: value } = option; + + if (!isDefined(value)) { + return undefined; + } else if (["string", "number", "boolean"].includes(typeof value)) { + let val = value.toString(); + const [named, ifEmpty] = getNamedAndIfEmpty(op); + const vals: string[] = [getFirstOrSep(op, isFirst)]; + if (named && varName) { + // No need to encode varName considering it is already encoded + vals.push(varName); + if (val === "") { + vals.push(ifEmpty); + } else { + vals.push("="); + } + } + if (modifier && modifier !== "*") { + val = val.substring(0, parseInt(modifier, 10)); + } + vals.push(encodeComponent(val, reserved, op)); + return vals.join(""); + } else if (modifier === "*") { + return getExpandedValue(option); + } else { + return getNonExpandedValue(option); + } +} + +// --------------------------------------------------------------------------------------------------- +// This is an implementation of RFC 6570 URI Template: https://datatracker.ietf.org/doc/html/rfc6570. +// --------------------------------------------------------------------------------------------------- +export function expandUrlTemplate( + template: string, + context: Record, + option?: UrlTemplateOptions, +): string { + const result = template.replace(/\{([^{}]+)\}|([^{}]+)/g, (_, expr, text) => { + if (!expr) { + return encodeReservedComponent(text); + } + let op; + if (["+", "#", ".", "/", ";", "?", "&"].includes(expr[0])) { + op = expr[0]; + expr = expr.slice(1); + } + const varList = expr.split(/,/g); + const result = []; + for (const varSpec of varList) { + const varMatch = /([^:*]*)(?::(\d+)|(\*))?/.exec(varSpec); + if (!varMatch || !varMatch[1]) { + continue; + } + const varValue = getVarValue({ + isFirst: result.length === 0, + op, + varValue: context[varMatch[1]], + varName: varMatch[1], + modifier: varMatch[2] || varMatch[3], + reserved: option?.allowReserved, + }); + if (varValue) { + result.push(varValue); + } + } + return result.join(""); + }); + + return normalizeUnreserved(result); +} + +/** + * Normalize an expanded URI by decoding percent-encoded unreserved characters. + * RFC 3986 unreserved: "-" / "." / "~" + */ +function normalizeUnreserved(uri: string): string { + return uri.replace(/%([0-9A-Fa-f]{2})/g, (match, hex) => { + const char = String.fromCharCode(parseInt(hex, 16)); + // Decode only if it's unreserved + if (/[\-.~]/.test(char)) { + return char; + } + return match; // leave other encodings intact + }); +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/src/widgetManagerClient.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/src/widgetManagerClient.ts new file mode 100644 index 000000000000..0e1a46a9a676 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/src/widgetManagerClient.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { WidgetManagerContext, WidgetManagerClientOptionalParams } from "./api/index.js"; +import { createWidgetManager } from "./api/index.js"; +import type { WidgetsOperations } from "./classic/widgets/index.js"; +import { _getWidgetsOperations } from "./classic/widgets/index.js"; +import type { TokenCredential } from "@azure/core-auth"; +import type { Pipeline } from "@azure/core-rest-pipeline"; + +export { WidgetManagerClientOptionalParams } from "./api/widgetManagerContext.js"; + +export class WidgetManagerClient { + private _client: WidgetManagerContext; + /** The pipeline used by this client to make requests */ + public readonly pipeline: Pipeline; + + constructor( + endpointParam: string, + credential: TokenCredential, + options: WidgetManagerClientOptionalParams = {}, + ) { + const prefixFromOptions = options?.userAgentOptions?.userAgentPrefix; + const userAgentPrefix = prefixFromOptions + ? `${prefixFromOptions} azsdk-js-client` + : `azsdk-js-client`; + this._client = createWidgetManager(endpointParam, credential, { + ...options, + userAgentOptions: { userAgentPrefix }, + }); + this.pipeline = this._client.pipeline; + this.widgets = _getWidgetsOperations(this._client); + } + + /** The operation groups for widgets */ + public readonly widgets: WidgetsOperations; +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/sampleTest.spec.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/sampleTest.spec.ts new file mode 100644 index 000000000000..19cf10ee4956 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/sampleTest.spec.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// import { 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); + }); +}); diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/utils/recordedClient.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/utils/recordedClient.ts new file mode 100644 index 000000000000..285e4643c45e --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/test/public/utils/recordedClient.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { RecorderStartOptions, VitestTestContext } from "@azure-tools/test-recorder"; +import { Recorder } from "@azure-tools/test-recorder"; + +const replaceableVariables: Record = { + SUBSCRIPTION_ID: "azure_subscription_id", +}; + +const recorderEnvSetup: RecorderStartOptions = { + envSetupForPlayback: replaceableVariables, +}; + +/** + * creates the recorder and reads the environment variables from the `.env` file. + * Should be called first in the test suite to make sure environment variables are + * read before they are being used. + */ +export async function createRecorder(context: VitestTestContext): Promise { + const recorder = new Recorder(context); + await recorder.start(recorderEnvSetup); + return recorder; +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/test/snippets.spec.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/test/snippets.spec.ts new file mode 100644 index 000000000000..779321f34a31 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/test/snippets.spec.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { WidgetManagerClient } from "../src/index.js"; +import { DefaultAzureCredential, InteractiveBrowserCredential } from "@azure/identity"; +import { setLogLevel } from "@azure/logger"; +import { describe, it } from "vitest"; + +describe("snippets", () => { + it("ReadmeSampleCreateClient_Node", async () => { + const client = new WidgetManagerClient("", new DefaultAzureCredential()); + }); + + it("ReadmeSampleCreateClient_Browser", async () => { + const credential = new InteractiveBrowserCredential({ + tenantId: "", + clientId: "", + }); + const client = new WidgetManagerClient("", credential); + }); + + it("SetLogLevel", async () => { + setLogLevel("info"); + }); +}); diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.browser.config.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.browser.config.json new file mode 100644 index 000000000000..3da19148c30d --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.browser.config.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.browser.base.json", + "compilerOptions": { + "paths": { + "@azure/contoso-widgetmanager": ["./dist/browser/index.d.ts"], + "@azure/contoso-widgetmanager/*": ["./dist/browser/*"], + "$internal/*": ["./dist/browser/*"] + } + } +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.json new file mode 100644 index 000000000000..76a906bf45bf --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.json @@ -0,0 +1,13 @@ +{ + "references": [ + { + "path": "./tsconfig.src.json" + }, + { + "path": "./tsconfig.test.json" + }, + { + "path": "./tsconfig.snippets.json" + } + ] +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.snippets.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.snippets.json new file mode 100644 index 000000000000..6f3148b5ed97 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.snippets.json @@ -0,0 +1,3 @@ +{ + "extends": ["../../../tsconfig.snippets.base.json"] +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.src.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.src.json new file mode 100644 index 000000000000..bae70752dd38 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.src.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tsconfig.lib.json" +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.json new file mode 100644 index 000000000000..42798ad68913 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "references": [ + { + "path": "./tsconfig.test.node.json" + }, + { + "path": "./tsconfig.browser.config.json" + } + ], + "compilerOptions": { + "composite": true + }, + "files": [] +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.node.json b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.node.json new file mode 100644 index 000000000000..8da2b61370d6 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsconfig.test.node.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../tsconfig.test.node.base.json", + "compilerOptions": { + "paths": { + "@azure/contoso-widgetmanager": ["./src/index.ts"], + "@azure/contoso-widgetmanager/*": ["./src/*"], + "$internal/*": ["./src/*"] + } + } +} diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/tsp-location.yaml b/sdk/contosowidgetmanager/contoso-widgetmanager/tsp-location.yaml new file mode 100644 index 000000000000..f84a668b586b --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/tsp-location.yaml @@ -0,0 +1,5 @@ +directory: specification/contosowidgetmanager/Contoso.WidgetManager +commit: ea9e2ff730d93906cb1671950e2415fcec64da51 +repo: Azure/azure-rest-api-specs +additionalDirectories: +- specification/contosowidgetmanager/Contoso.WidgetManager.Shared diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.browser.config.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.browser.config.ts new file mode 100644 index 000000000000..72964f281efe --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.browser.config.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import viteConfig from "../../../vitest.browser.shared.config.ts"; + +export default viteConfig; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.config.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.config.ts new file mode 100644 index 000000000000..0dfa15cc4498 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.config.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import viteConfig from "../../../vitest.shared.config.ts"; + +export default viteConfig; diff --git a/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.esm.config.ts b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.esm.config.ts new file mode 100644 index 000000000000..5e9735e9b144 --- /dev/null +++ b/sdk/contosowidgetmanager/contoso-widgetmanager/vitest.esm.config.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { mergeConfig } from "vitest/config"; +import vitestConfig from "./vitest.config.ts"; +import vitestEsmConfig from "../../../vitest.esm.shared.config.ts"; + +export default mergeConfig(vitestConfig, vitestEsmConfig);