diff --git a/change/monosize-storage-azure-a9e6fc6b-938e-4645-95c4-32659838f654.json b/change/monosize-storage-azure-a9e6fc6b-938e-4645-95c4-32659838f654.json new file mode 100644 index 0000000..805242b --- /dev/null +++ b/change/monosize-storage-azure-a9e6fc6b-938e-4645-95c4-32659838f654.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: add support for workload identity authentication with azure pipelines.", + "packageName": "monosize-storage-azure", + "email": "tristan.watanabe@gmail.com", + "dependentChangeType": "patch" +} diff --git a/package.json b/package.json index 9c6bd91..b5a2baf 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ }, "dependencies": { "@azure/data-tables": "^13.0.0", + "@azure/identity": "^4.4.0", "@upstash/redis": "^1.18.0", "acorn": "^8.11.3", "ci-info": "^3.7.0", diff --git a/packages/monosize-storage-azure/package.json b/packages/monosize-storage-azure/package.json index 2a7d007..105277e 100644 --- a/packages/monosize-storage-azure/package.json +++ b/packages/monosize-storage-azure/package.json @@ -5,6 +5,7 @@ "types": "./src/index.d.mts", "dependencies": { "@azure/data-tables": "^13.0.0", + "@azure/identity": "^4.4.0", "monosize": "^0.6.2", "node-fetch": "^3.3.0", "picocolors": "^1.0.0", diff --git a/packages/monosize-storage-azure/src/createTableClient.mts b/packages/monosize-storage-azure/src/createTableClient.mts new file mode 100644 index 0000000..c53aaed --- /dev/null +++ b/packages/monosize-storage-azure/src/createTableClient.mts @@ -0,0 +1,67 @@ +import { AzureNamedKeyCredential, TableClient } from '@azure/data-tables'; +import { AzurePipelinesCredential } from '@azure/identity'; +import type { AzureStorageConfig } from './types.mjs'; + +export function createTableClient(options: Required>): TableClient { + const { authType, tableName } = options; + + const AZURE_STORAGE_TABLE_NAME = tableName; + + if (authType === 'AzureNamedKeyCredential') { + const requiredEnvVars = ['BUNDLESIZE_ACCOUNT_NAME', 'BUNDLESIZE_ACCOUNT_KEY']; + validateRequiredEnvVariables({ + requiredEnvVars, + authType, + }); + + const AZURE_STORAGE_ACCOUNT = process.env['BUNDLESIZE_ACCOUNT_NAME'] as string; + const AZURE_ACCOUNT_KEY = process.env['BUNDLESIZE_ACCOUNT_KEY'] as string; + + return new TableClient( + `https://${AZURE_STORAGE_ACCOUNT}.table.core.windows.net`, + AZURE_STORAGE_TABLE_NAME, + new AzureNamedKeyCredential(AZURE_STORAGE_ACCOUNT, AZURE_ACCOUNT_KEY), + ); + } + + if (authType === 'AzurePipelinesCredential') { + const requiredEnvVars = [ + 'BUNDLESIZE_ACCOUNT_NAME', + 'AZURE_TENANT_ID', + 'AZURE_CLIENT_ID', + 'AZURE_SERVICE_CONNECTION_ID', + 'SYSTEM_ACCESSTOKEN', + ]; + validateRequiredEnvVariables({ + requiredEnvVars, + authType, + }); + + const AZURE_STORAGE_ACCOUNT = process.env['BUNDLESIZE_ACCOUNT_NAME'] as string; + const TENANT_ID = process.env['AZURE_TENANT_ID'] as string; + const CLIENT_ID = process.env['AZURE_CLIENT_ID'] as string; + const SERVICE_CONNECTION_ID = process.env['AZURE_SERVICE_CONNECTION_ID'] as string; + const SYSTEM_ACCESSTOKEN = process.env['SYSTEM_ACCESSTOKEN'] as string; + + return new TableClient( + `https://${AZURE_STORAGE_ACCOUNT}.table.core.windows.net`, + AZURE_STORAGE_TABLE_NAME, + new AzurePipelinesCredential(TENANT_ID, CLIENT_ID, SERVICE_CONNECTION_ID, SYSTEM_ACCESSTOKEN), + ); + } + + throw new Error(`monosize-storage-azure: "authType: ${authType}" is not supported.`); +} + +function validateRequiredEnvVariables(options: { requiredEnvVars: string[]; authType: string }): void { + const { requiredEnvVars, authType } = options; + const missingEnvVars = requiredEnvVars.filter(envParamName => typeof process.env[envParamName] !== 'string'); + + if (missingEnvVars.length > 0) { + throw new Error( + `monosize-storage-azure: Missing required environment variable(s) for authType ${authType}: ${missingEnvVars.join( + ', ', + )} not in your process.env.`, + ); + } +} diff --git a/packages/monosize-storage-azure/src/createTableClient.test.mts b/packages/monosize-storage-azure/src/createTableClient.test.mts new file mode 100644 index 0000000..3623ad9 --- /dev/null +++ b/packages/monosize-storage-azure/src/createTableClient.test.mts @@ -0,0 +1,85 @@ +import { beforeEach, describe, expect, it, vitest } from 'vitest'; +import { AzureNamedKeyCredential, TableClient } from '@azure/data-tables'; +import { AzurePipelinesCredential } from '@azure/identity'; +import { createTableClient } from './createTableClient.mjs'; +import type { AzureAuthenticationType } from './types.mjs'; + +vitest.mock('@azure/data-tables', () => { + return { + AzureNamedKeyCredential: vitest.fn(), + TableClient: vitest.fn().mockImplementation(() => { + return { + createTable: vitest.fn(), + deleteTable: vitest.fn(), + }; + }), + }; +}); + +vitest.mock('@azure/identity', () => { + return { + AzurePipelinesCredential: vitest.fn(), + }; +}); + +describe('createTableClient', () => { + beforeEach(() => { + vitest.resetAllMocks(); + vitest.unstubAllEnvs(); + }); + + it('should create TableClient with AzureNamedKeyCredential', () => { + vitest.stubEnv('BUNDLESIZE_ACCOUNT_NAME', 'test-account-name'); + vitest.stubEnv('BUNDLESIZE_ACCOUNT_KEY', 'test-account-key'); + + const authType = 'AzureNamedKeyCredential'; + const tableName = 'test-table'; + createTableClient({ authType, tableName }); + + expect(AzureNamedKeyCredential).toHaveBeenCalledWith( + process.env['BUNDLESIZE_ACCOUNT_NAME'] as string, + process.env['BUNDLESIZE_ACCOUNT_KEY'] as string, + ); + + expect(TableClient).toHaveBeenCalledWith( + 'https://test-account-name.table.core.windows.net', + tableName, + expect.any(AzureNamedKeyCredential), + ); + }); + + it('should create TableClient with AzurePipelinesCredential', () => { + vitest.stubEnv('BUNDLESIZE_ACCOUNT_NAME', 'test-account-name'); + vitest.stubEnv('AZURE_TENANT_ID', 'test-tenant-id'); + vitest.stubEnv('AZURE_CLIENT_ID', 'test-client-id'); + vitest.stubEnv('AZURE_SERVICE_CONNECTION_ID', 'test-service-connection-id'); + vitest.stubEnv('SYSTEM_ACCESSTOKEN', 'test-system-access-token'); + vitest.stubEnv('SYSTEM_OIDCREQUESTURI', 'test-system-oidc-request-uri'); + + const authType = 'AzurePipelinesCredential'; + const tableName = 'test-table'; + createTableClient({ authType, tableName }); + + expect(AzurePipelinesCredential).toHaveBeenCalledWith( + process.env['AZURE_TENANT_ID'] as string, + process.env['AZURE_CLIENT_ID'] as string, + process.env['AZURE_SERVICE_CONNECTION_ID'] as string, + process.env['SYSTEM_ACCESSTOKEN'] as string, + ); + + expect(TableClient).toHaveBeenCalledWith( + 'https://test-account-name.table.core.windows.net', + tableName, + expect.any(AzurePipelinesCredential), + ); + }); + + it('should throw an error for unsupported authType', () => { + const authType = 'AzureNamedKeyCredentail' as AzureAuthenticationType; + const tableName = 'test-table'; + + expect(() => createTableClient({ authType, tableName })).toThrow( + `monosize-storage-azure: "authType: ${authType}" is not supported.`, + ); + }); +}); diff --git a/packages/monosize-storage-azure/src/index.mts b/packages/monosize-storage-azure/src/index.mts index a4ae2a1..5b3b20a 100644 --- a/packages/monosize-storage-azure/src/index.mts +++ b/packages/monosize-storage-azure/src/index.mts @@ -1,13 +1,13 @@ import type { StorageAdapter } from 'monosize'; import { createGetRemoteReport } from './getRemoteReport.mjs'; -import { uploadReportToRemote } from './uploadReportToRemote.mjs'; +import { createUploadReportToRemote } from './uploadReportToRemote.mjs'; import type { AzureStorageConfig } from './types.mjs'; function createAzureStorage(config: AzureStorageConfig): StorageAdapter { return { getRemoteReport: createGetRemoteReport(config), - uploadReportToRemote, + uploadReportToRemote: createUploadReportToRemote(config), }; } diff --git a/packages/monosize-storage-azure/src/types.mts b/packages/monosize-storage-azure/src/types.mts index 3de86b2..96b7252 100644 --- a/packages/monosize-storage-azure/src/types.mts +++ b/packages/monosize-storage-azure/src/types.mts @@ -1,3 +1,16 @@ +/** + * @see https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/samples/AzureIdentityExamples.md#authenticating-azure-hosted-applications + */ +export type AzureAuthenticationType = 'AzureNamedKeyCredential' | 'AzurePipelinesCredential'; + export type AzureStorageConfig = { endpoint: string; + /** + * @default 'AzureNamedKeyCredential' auth type + */ + authType?: AzureAuthenticationType; + /** + * @default 'latest' table name + */ + tableName?: string; }; diff --git a/packages/monosize-storage-azure/src/uploadReportToRemote.mts b/packages/monosize-storage-azure/src/uploadReportToRemote.mts index 3a1021b..dd0c7b6 100644 --- a/packages/monosize-storage-azure/src/uploadReportToRemote.mts +++ b/packages/monosize-storage-azure/src/uploadReportToRemote.mts @@ -1,6 +1,8 @@ -import { AzureNamedKeyCredential, odata, TableClient, TableTransaction } from '@azure/data-tables'; -import { BundleSizeReportEntry, StorageAdapter } from 'monosize'; +import { odata, TableTransaction } from '@azure/data-tables'; +import { BundleSizeReportEntry, BundleSizeReport, StorageAdapter } from 'monosize'; import pc from 'picocolors'; +import { createTableClient } from './createTableClient.mjs'; +import type { AzureStorageConfig } from './types.mjs'; export const ENTRIES_PER_CHUNK = 90; @@ -14,71 +16,64 @@ export function splitArrayToChunks(arr: T[], size: number): T[][] { return [...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(i * size, (i + 1) * size)); } -export const uploadReportToRemote: StorageAdapter['uploadReportToRemote'] = async (branch, commitSHA, localReport) => { - if (typeof process.env['BUNDLESIZE_ACCOUNT_KEY'] !== 'string') { - throw new Error('monosize-storage-azure: "BUNDLESIZE_ACCOUNT_KEY" is not defined in your process.env'); - } +export function createUploadReportToRemote(config: AzureStorageConfig) { + const { authType = 'AzureNamedKeyCredential', tableName = 'latest' } = config; - if (typeof process.env['BUNDLESIZE_ACCOUNT_NAME'] !== 'string') { - throw new Error('monosize-storage-azure: "BUNDLESIZE_ACCOUNT_NAME" is not defined in your process.env'); - } + async function uploadReportToRemote( + branch: string, + commitSHA: string, + localReport: BundleSizeReport, + ): ReturnType { + const client = createTableClient({ authType, tableName }); - if (localReport.length === 0) { - console.log([pc.yellow('[w]'), 'No entries to upload'].join(' ')); - return; - } + if (localReport.length === 0) { + console.log([pc.yellow('[w]'), 'No entries to upload'].join(' ')); + return; + } + + const transaction = new TableTransaction(); + const entitiesIterator = client.listEntities({ + queryOptions: { + filter: odata`PartitionKey eq ${branch}`, + }, + }); - const AZURE_STORAGE_ACCOUNT = process.env['BUNDLESIZE_ACCOUNT_NAME']; - const AZURE_STORAGE_TABLE_NAME = 'latest'; - const AZURE_ACCOUNT_KEY = process.env['BUNDLESIZE_ACCOUNT_KEY']; - - const credentials = new AzureNamedKeyCredential(AZURE_STORAGE_ACCOUNT, AZURE_ACCOUNT_KEY); - const client = new TableClient( - `https://${AZURE_STORAGE_ACCOUNT}.table.core.windows.net`, - AZURE_STORAGE_TABLE_NAME, - credentials, - ); - - const transaction = new TableTransaction(); - const entitiesIterator = await client.listEntities({ - queryOptions: { - filter: odata`PartitionKey eq ${branch}`, - }, - }); - - for await (const entity of entitiesIterator) { - // We can't delete and create entries with the same "rowKey" in the same transaction - // => we delete only entries not present in existing report - const isEntryPresentInExistingReport = Boolean(localReport.find(entry => createRowKey(entry) === entity.rowKey)); - const shouldEntryBeDeleted = !isEntryPresentInExistingReport; - - if (shouldEntryBeDeleted) { - transaction.deleteEntity(entity.partitionKey as string, entity.rowKey as string); + for await (const entity of entitiesIterator) { + // We can't delete and create entries with the same "rowKey" in the same transaction + // => we delete only entries not present in existing report + const isEntryPresentInExistingReport = Boolean(localReport.find(entry => createRowKey(entry) === entity.rowKey)); + const shouldEntryBeDeleted = !isEntryPresentInExistingReport; + + if (shouldEntryBeDeleted) { + transaction.deleteEntity(entity.partitionKey as string, entity.rowKey as string); + } } - } - localReport.forEach(entry => { - transaction.upsertEntity( - { - partitionKey: branch, - rowKey: createRowKey(entry), + localReport.forEach(entry => { + transaction.upsertEntity( + { + partitionKey: branch, + rowKey: createRowKey(entry), - name: entry.name, - packageName: entry.packageName, - path: entry.path, + name: entry.name, + packageName: entry.packageName, + path: entry.path, - minifiedSize: entry.minifiedSize, - gzippedSize: entry.gzippedSize, + minifiedSize: entry.minifiedSize, + gzippedSize: entry.gzippedSize, - commitSHA, - }, - 'Replace', - ); - }); + commitSHA, + }, + 'Replace', + ); + }); - const chunks = splitArrayToChunks(transaction.actions, ENTRIES_PER_CHUNK); + const chunks = splitArrayToChunks(transaction.actions, ENTRIES_PER_CHUNK); - for (const chunk of chunks) { - await client.submitTransaction(chunk); + for (const chunk of chunks) { + await client.submitTransaction(chunk); + } } -}; + + return uploadReportToRemote; +} diff --git a/packages/monosize-storage-azure/src/uploadReportToRemote.test.mts b/packages/monosize-storage-azure/src/uploadReportToRemote.test.mts index f38ac99..436b1af 100644 --- a/packages/monosize-storage-azure/src/uploadReportToRemote.test.mts +++ b/packages/monosize-storage-azure/src/uploadReportToRemote.test.mts @@ -1,14 +1,25 @@ import { beforeEach, beforeAll, describe, expect, it, vitest, type Mock } from 'vitest'; -import { createRowKey, ENTRIES_PER_CHUNK, splitArrayToChunks, uploadReportToRemote } from './uploadReportToRemote.mjs'; +import { + createRowKey, + ENTRIES_PER_CHUNK, + splitArrayToChunks, + createUploadReportToRemote, +} from './uploadReportToRemote.mjs'; import { sampleReport, bigReport } from './__fixture__/sampleReports.mjs'; import { BundleSizeReportEntry } from 'monosize'; +import type { AzureStorageConfig } from './types.mjs'; const getRemoteReport = vitest.hoisted( () => vitest.fn() as Mock>, ); const submitTransaction = vitest.hoisted(() => vitest.fn()); +const testConfig: AzureStorageConfig = { + endpoint: 'https://localhost', + authType: 'AzureNamedKeyCredential', +}; + vitest.mock('@azure/data-tables', async () => { const listEntities = () => { const data = getRemoteReport(); @@ -82,6 +93,7 @@ describe('uploadReportToRemote', () => { const localReport = sampleReport.slice(0, 1); getRemoteReport.mockReturnValueOnce(remoteReport); + const uploadReportToRemote = createUploadReportToRemote(testConfig); await uploadReportToRemote(branchName, commitSHA, localReport); expect(submitTransaction).toHaveBeenCalledTimes(1); @@ -105,6 +117,7 @@ describe('uploadReportToRemote', () => { const localReport = bigReport; getRemoteReport.mockReturnValueOnce(remoteReport); + const uploadReportToRemote = createUploadReportToRemote(testConfig); await uploadReportToRemote(branchName, commitSHA, localReport); expect(submitTransaction).toHaveBeenCalledTimes(Math.ceil(localReport.length / ENTRIES_PER_CHUNK)); @@ -122,6 +135,7 @@ describe('uploadReportToRemote', () => { it('performs no actions if local report is empty', async () => { // eslint-disable-next-line @typescript-eslint/no-empty-function const log = vitest.spyOn(console, 'log').mockImplementation(() => {}); + const uploadReportToRemote = createUploadReportToRemote(testConfig); await uploadReportToRemote(branchName, commitSHA, []); expect(log).toHaveBeenCalledTimes(1); diff --git a/yarn.lock b/yarn.lock index 41d610d..37b44c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31,28 +31,38 @@ __metadata: languageName: node linkType: hard -"@azure/core-auth@npm:^1.3.0, @azure/core-auth@npm:^1.4.0": - version: 1.4.0 - resolution: "@azure/core-auth@npm:1.4.0" +"@azure/abort-controller@npm:^2.0.0": + version: 2.1.2 + resolution: "@azure/abort-controller@npm:2.1.2" dependencies: - "@azure/abort-controller": ^1.0.0 - tslib: ^2.2.0 - checksum: 1c76c296fe911ad39fc780b033c25a92c41c5a15f011b816d42c13584f627a4dd153dfb4334900ec93eb5b006e14bdda37e8412a7697c5a9636a0abaccffad39 + tslib: ^2.6.2 + checksum: 22176c04ea01498311c6bbd336669f6e3faffad1cbb0c9ebc6ee9c1ff2cf958fd17ce73c7354b99d8bda9fcd311325ece7bee248875279174e3fc460e8b1a63d languageName: node linkType: hard -"@azure/core-client@npm:^1.0.0": - version: 1.7.1 - resolution: "@azure/core-client@npm:1.7.1" +"@azure/core-auth@npm:^1.3.0, @azure/core-auth@npm:^1.4.0, @azure/core-auth@npm:^1.5.0": + version: 1.7.2 + resolution: "@azure/core-auth@npm:1.7.2" dependencies: - "@azure/abort-controller": ^1.0.0 + "@azure/abort-controller": ^2.0.0 + "@azure/core-util": ^1.1.0 + tslib: ^2.6.2 + checksum: dcbba47f32e4cc929e078fd5d714af43185f3781ff5c19f01cba9e0d9078690e716be8172dd77a13aa3a81380d338a678b974bc5cbaa2e0d8629fb262ee3f0df + languageName: node + linkType: hard + +"@azure/core-client@npm:^1.0.0, @azure/core-client@npm:^1.9.2": + version: 1.9.2 + resolution: "@azure/core-client@npm:1.9.2" + dependencies: + "@azure/abort-controller": ^2.0.0 "@azure/core-auth": ^1.4.0 "@azure/core-rest-pipeline": ^1.9.1 "@azure/core-tracing": ^1.0.0 - "@azure/core-util": ^1.0.0 + "@azure/core-util": ^1.6.1 "@azure/logger": ^1.0.0 - tslib: ^2.2.0 - checksum: de1aab073180867720503f5d564dc0eb586fda58dbf18e322389e0f1460b3aee60d981bbbb3160de1ecf8e38a4d3e7f2a5867c6d0c198b7da5377c5c80787369 + tslib: ^2.6.2 + checksum: 961b829dfda4f734a763e9480a2ea622a7031ba2da4126d0add6e351a9f73ddc5782bf2b766735d976b61da3857014e0a90223d1f85d1c68468747a7a56851c3 languageName: node linkType: hard @@ -92,13 +102,13 @@ __metadata: languageName: node linkType: hard -"@azure/core-util@npm:^1.0.0": - version: 1.1.1 - resolution: "@azure/core-util@npm:1.1.1" +"@azure/core-util@npm:^1.0.0, @azure/core-util@npm:^1.1.0, @azure/core-util@npm:^1.3.0, @azure/core-util@npm:^1.6.1": + version: 1.9.2 + resolution: "@azure/core-util@npm:1.9.2" dependencies: - "@azure/abort-controller": ^1.0.0 - tslib: ^2.2.0 - checksum: 0c4a9e086a65e2411bf858fd36ecb8ff08b9941be081b37c6268ca0c3cfeebe18e863cced3d744201487519bb3698507096a890f63987e9179acf590c01d9561 + "@azure/abort-controller": ^2.0.0 + tslib: ^2.6.2 + checksum: 63c7ab2bdd6e75e38af33e37c9844515c546ed3e8f88fb98926ec88287dfabb249b9fd156658d42bfccbaeb46369254e7cf53eb6ef789b9d88880585eaabb298 languageName: node linkType: hard @@ -129,6 +139,28 @@ __metadata: languageName: node linkType: hard +"@azure/identity@npm:^4.4.0": + version: 4.4.1 + resolution: "@azure/identity@npm:4.4.1" + dependencies: + "@azure/abort-controller": ^1.0.0 + "@azure/core-auth": ^1.5.0 + "@azure/core-client": ^1.9.2 + "@azure/core-rest-pipeline": ^1.1.0 + "@azure/core-tracing": ^1.0.0 + "@azure/core-util": ^1.3.0 + "@azure/logger": ^1.0.0 + "@azure/msal-browser": ^3.14.0 + "@azure/msal-node": ^2.9.2 + events: ^3.0.0 + jws: ^4.0.0 + open: ^8.0.0 + stoppable: ^1.1.0 + tslib: ^2.2.0 + checksum: 8dba5a1e347b349eb999e46cce190dedef02c0a81a179d4c4bf67ea4407c9f44ace5eec08f1ac8c963f8723e1074d32a9878a0ae5d4d4a880b19389b5fb6e7a1 + languageName: node + linkType: hard + "@azure/logger@npm:^1.0.0": version: 1.0.3 resolution: "@azure/logger@npm:1.0.3" @@ -138,6 +170,33 @@ __metadata: languageName: node linkType: hard +"@azure/msal-browser@npm:^3.14.0": + version: 3.20.0 + resolution: "@azure/msal-browser@npm:3.20.0" + dependencies: + "@azure/msal-common": 14.14.0 + checksum: 237a041bbe898f46676ddc0e0a0cfb26c821efecc71de19168bd70e216da14f3e83bfad064e4c5e5491828ae3e096a2d46d58b5ba227b820fe716639f9d80f5c + languageName: node + linkType: hard + +"@azure/msal-common@npm:14.14.0": + version: 14.14.0 + resolution: "@azure/msal-common@npm:14.14.0" + checksum: c77f51bdddb34da008786d7517713232dc69b7de9deec438ef463098e535ebdb8241ac04e9ddaee859d788dee71d683bf7ef0acab47781457d5c4ea561a8addf + languageName: node + linkType: hard + +"@azure/msal-node@npm:^2.9.2": + version: 2.12.0 + resolution: "@azure/msal-node@npm:2.12.0" + dependencies: + "@azure/msal-common": 14.14.0 + jsonwebtoken: ^9.0.0 + uuid: ^8.3.0 + checksum: ad02d84ff0510594165672f0a39ba78f962631046051daf4de16ad4f783e0ee4c8e372aa99d17ac461c9d52bcceb1215c0d527443d97d5028c5d738029c4e71c + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.22.13": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" @@ -3975,6 +4034,13 @@ __metadata: languageName: node linkType: hard +"buffer-equal-constant-time@npm:1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -4636,6 +4702,15 @@ __metadata: languageName: node linkType: hard +"ecdsa-sig-formatter@npm:1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: ^5.0.1 + checksum: 207f9ab1c2669b8e65540bce29506134613dd5f122cccf1e6a560f4d63f2732d427d938f8481df175505aad94583bcb32c688737bb39a6df0625f903d6d93c03 + languageName: node + linkType: hard + "ejs@npm:^3.1.7": version: 3.1.10 resolution: "ejs@npm:3.1.10" @@ -5304,7 +5379,7 @@ __metadata: languageName: node linkType: hard -"events@npm:^3.2.0": +"events@npm:^3.0.0, events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 @@ -6668,6 +6743,66 @@ __metadata: languageName: node linkType: hard +"jsonwebtoken@npm:^9.0.0": + version: 9.0.2 + resolution: "jsonwebtoken@npm:9.0.2" + dependencies: + jws: ^3.2.2 + lodash.includes: ^4.3.0 + lodash.isboolean: ^3.0.3 + lodash.isinteger: ^4.0.4 + lodash.isnumber: ^3.0.3 + lodash.isplainobject: ^4.0.6 + lodash.isstring: ^4.0.1 + lodash.once: ^4.0.0 + ms: ^2.1.1 + semver: ^7.5.4 + checksum: fc739a6a8b33f1974f9772dca7f8493ca8df4cc31c5a09dcfdb7cff77447dcf22f4236fb2774ef3fe50df0abeb8e1c6f4c41eba82f500a804ab101e2fbc9d61a + languageName: node + linkType: hard + +"jwa@npm:^1.4.1": + version: 1.4.1 + resolution: "jwa@npm:1.4.1" + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: ^5.0.1 + checksum: ff30ea7c2dcc61f3ed2098d868bf89d43701605090c5b21b5544b512843ec6fd9e028381a4dda466cbcdb885c2d1150f7c62e7168394ee07941b4098e1035e2f + languageName: node + linkType: hard + +"jwa@npm:^2.0.0": + version: 2.0.0 + resolution: "jwa@npm:2.0.0" + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: ^5.0.1 + checksum: 8f00b71ad5fe94cb55006d0d19202f8f56889109caada2f7eeb63ca81755769ce87f4f48101967f398462e3b8ae4faebfbd5a0269cb755dead5d63c77ba4d2f1 + languageName: node + linkType: hard + +"jws@npm:^3.2.2": + version: 3.2.2 + resolution: "jws@npm:3.2.2" + dependencies: + jwa: ^1.4.1 + safe-buffer: ^5.0.1 + checksum: f0213fe5b79344c56cd443428d8f65c16bf842dc8cb8f5aed693e1e91d79c20741663ad6eff07a6d2c433d1831acc9814e8d7bada6a0471fbb91d09ceb2bf5c2 + languageName: node + linkType: hard + +"jws@npm:^4.0.0": + version: 4.0.0 + resolution: "jws@npm:4.0.0" + dependencies: + jwa: ^2.0.0 + safe-buffer: ^5.0.1 + checksum: d68d07aa6d1b8cb35c363a9bd2b48f15064d342a5d9dc18a250dbbce8dc06bd7e4792516c50baa16b8d14f61167c19e851fd7f66b59ecc68b7f6a013759765f7 + languageName: node + linkType: hard + "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -6747,6 +6882,48 @@ __metadata: languageName: node linkType: hard +"lodash.includes@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.includes@npm:4.3.0" + checksum: 71092c130515a67ab3bd928f57f6018434797c94def7f46aafa417771e455ce3a4834889f4267b17887d7f75297dfabd96231bf704fd2b8c5096dc4a913568b6 + languageName: node + linkType: hard + +"lodash.isboolean@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isboolean@npm:3.0.3" + checksum: b70068b4a8b8837912b54052557b21fc4774174e3512ed3c5b94621e5aff5eb6c68089d0a386b7e801d679cd105d2e35417978a5e99071750aa2ed90bffd0250 + languageName: node + linkType: hard + +"lodash.isinteger@npm:^4.0.4": + version: 4.0.4 + resolution: "lodash.isinteger@npm:4.0.4" + checksum: 6034821b3fc61a2ffc34e7d5644bb50c5fd8f1c0121c554c21ac271911ee0c0502274852845005f8651d51e199ee2e0cfebfe40aaa49c7fe617f603a8a0b1691 + languageName: node + linkType: hard + +"lodash.isnumber@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isnumber@npm:3.0.3" + checksum: 913784275b565346255e6ae6a6e30b760a0da70abc29f3e1f409081585875105138cda4a429ff02577e1bc0a7ae2a90e0a3079a37f3a04c3d6c5aaa532f4cab2 + languageName: node + linkType: hard + +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 + languageName: node + linkType: hard + +"lodash.isstring@npm:^4.0.1": + version: 4.0.1 + resolution: "lodash.isstring@npm:4.0.1" + checksum: eaac87ae9636848af08021083d796e2eea3d02e80082ab8a9955309569cb3a463ce97fd281d7dc119e402b2e7d8c54a23914b15d2fc7fff56461511dc8937ba0 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -6754,6 +6931,13 @@ __metadata: languageName: node linkType: hard +"lodash.once@npm:^4.0.0": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 + languageName: node + linkType: hard + "lodash@npm:^4.17.15, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -7269,6 +7453,7 @@ __metadata: resolution: "monosize-monorepo@workspace:." dependencies: "@azure/data-tables": ^13.0.0 + "@azure/identity": ^4.4.0 "@nx/eslint": 17.3.2 "@nx/eslint-plugin": 17.3.2 "@nx/js": 17.3.2 @@ -7686,14 +7871,14 @@ __metadata: languageName: node linkType: hard -"open@npm:^8.4.0": - version: 8.4.0 - resolution: "open@npm:8.4.0" +"open@npm:^8.0.0, open@npm:^8.4.0": + version: 8.4.2 + resolution: "open@npm:8.4.2" dependencies: define-lazy-prop: ^2.0.0 is-docker: ^2.1.1 is-wsl: ^2.2.0 - checksum: e9545bec64cdbf30a0c35c1bdc310344adf8428a117f7d8df3c0af0a0a24c513b304916a6d9b11db0190ff7225c2d578885080b761ed46a3d5f6f1eebb98b63c + checksum: 6388bfff21b40cb9bd8f913f9130d107f2ed4724ea81a8fd29798ee322b361ca31fa2cdfb491a5c31e43a3996cfe9566741238c7a741ada8d7af1cb78d85cf26 languageName: node linkType: hard @@ -8427,7 +8612,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -8750,6 +8935,13 @@ __metadata: languageName: node linkType: hard +"stoppable@npm:^1.1.0": + version: 1.1.0 + resolution: "stoppable@npm:1.1.0" + checksum: 63104fcbdece130bc4906fd982061e763d2ef48065ed1ab29895e5ad00552c625f8a4c50c9cd2e3bfa805c8a2c3bfdda0f07c5ae39694bd2d5cb0bee1618d1e9 + languageName: node + linkType: hard + "string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -9215,10 +9407,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.4.1, tslib@npm:^2.5.0": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad +"tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.4.1, tslib@npm:^2.5.0, tslib@npm:^2.6.2": + version: 2.6.3 + resolution: "tslib@npm:2.6.3" + checksum: 74fce0e100f1ebd95b8995fbbd0e6c91bdd8f4c35c00d4da62e285a3363aaa534de40a80db30ecfd388ed7c313c42d930ee0eaf108e8114214b180eec3dbe6f5 languageName: node linkType: hard