@microsoft/vscode-azext-azureauth 5.1.1 → 6.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +9 -79
  3. package/dist/cjs/src/contracts/AzureAccount.js +7 -0
  4. package/dist/cjs/src/contracts/AzureSubscriptionProviderRequestOptions.js +48 -0
  5. package/dist/cjs/src/index.js +13 -10
  6. package/dist/cjs/src/providers/AzureDevOpsSubscriptionProvider.js +178 -0
  7. package/dist/cjs/src/providers/AzureSubscriptionProviderBase.js +393 -0
  8. package/dist/cjs/src/providers/VSCodeAzureSubscriptionProvider.js +269 -0
  9. package/dist/cjs/src/utils/Limiter.js +41 -0
  10. package/dist/cjs/src/{NotSignedInError.js → utils/NotSignedInError.js} +3 -2
  11. package/dist/cjs/src/utils/configuredAzureEnv.js +14 -16
  12. package/dist/cjs/src/utils/dedupeSubscriptions.js +27 -0
  13. package/dist/cjs/src/utils/getMetricsForTelemetry.js +47 -0
  14. package/dist/cjs/src/{getSessionFromVSCode.js → utils/getSessionFromVSCode.js} +5 -2
  15. package/dist/cjs/src/utils/getSignalForToken.js +29 -0
  16. package/dist/cjs/src/utils/map/CaselessMap.js +71 -0
  17. package/dist/cjs/src/utils/map/TwoKeyCaselessMap.js +194 -0
  18. package/dist/cjs/src/utils/screen.js +62 -0
  19. package/dist/cjs/src/{signInToTenant.js → utils/signInToTenant.js} +15 -13
  20. package/dist/cjs/src/utils/tryGetTokenExpiration.js +25 -0
  21. package/dist/esm/src/contracts/AzureAccount.d.ts +5 -0
  22. package/dist/esm/src/contracts/AzureAccount.js +6 -0
  23. package/dist/esm/src/{AzureAuthentication.d.ts → contracts/AzureAuthentication.d.ts} +1 -1
  24. package/dist/esm/src/{AzureSubscription.d.ts → contracts/AzureSubscription.d.ts} +4 -4
  25. package/dist/esm/src/contracts/AzureSubscriptionProvider.d.ts +112 -0
  26. package/dist/esm/src/contracts/AzureSubscriptionProviderRequestOptions.d.ts +103 -0
  27. package/dist/esm/src/contracts/AzureSubscriptionProviderRequestOptions.js +44 -0
  28. package/dist/esm/src/contracts/AzureTenant.d.ts +15 -0
  29. package/dist/esm/src/index.d.ts +13 -10
  30. package/dist/esm/src/index.js +13 -10
  31. package/dist/esm/src/providers/AzureDevOpsSubscriptionProvider.d.ts +68 -0
  32. package/dist/esm/src/providers/AzureDevOpsSubscriptionProvider.js +140 -0
  33. package/dist/esm/src/providers/AzureSubscriptionProviderBase.d.ts +74 -0
  34. package/dist/esm/src/providers/AzureSubscriptionProviderBase.js +356 -0
  35. package/dist/esm/src/providers/VSCodeAzureSubscriptionProvider.d.ts +70 -0
  36. package/dist/esm/src/providers/VSCodeAzureSubscriptionProvider.js +232 -0
  37. package/dist/esm/src/utils/Limiter.d.ts +9 -0
  38. package/dist/esm/src/utils/Limiter.js +37 -0
  39. package/dist/esm/src/{NotSignedInError.d.ts → utils/NotSignedInError.d.ts} +2 -2
  40. package/dist/esm/src/{NotSignedInError.js → utils/NotSignedInError.js} +3 -2
  41. package/dist/esm/src/utils/configuredAzureEnv.d.ts +7 -4
  42. package/dist/esm/src/utils/configuredAzureEnv.js +14 -16
  43. package/dist/esm/src/utils/dedupeSubscriptions.d.ts +14 -0
  44. package/dist/esm/src/utils/dedupeSubscriptions.js +24 -0
  45. package/dist/esm/src/utils/getMetricsForTelemetry.d.ts +32 -0
  46. package/dist/esm/src/utils/getMetricsForTelemetry.js +44 -0
  47. package/dist/esm/src/{getSessionFromVSCode.js → utils/getSessionFromVSCode.js} +5 -2
  48. package/dist/esm/src/utils/getSignalForToken.d.ts +7 -0
  49. package/dist/esm/src/utils/getSignalForToken.js +26 -0
  50. package/dist/esm/src/utils/map/CaselessMap.d.ts +28 -0
  51. package/dist/esm/src/utils/map/CaselessMap.js +67 -0
  52. package/dist/esm/src/utils/map/TwoKeyCaselessMap.d.ts +49 -0
  53. package/dist/esm/src/utils/map/TwoKeyCaselessMap.js +190 -0
  54. package/dist/esm/src/utils/screen.d.ts +9 -0
  55. package/dist/esm/src/utils/screen.js +59 -0
  56. package/dist/esm/src/utils/signInToTenant.d.ts +7 -0
  57. package/dist/esm/src/{signInToTenant.js → utils/signInToTenant.js} +16 -14
  58. package/dist/esm/src/utils/tryGetTokenExpiration.d.ts +2 -0
  59. package/dist/esm/src/utils/tryGetTokenExpiration.js +22 -0
  60. package/package.json +33 -23
  61. package/AzureFederatedCredentialsGuide.md +0 -174
  62. package/dist/cjs/src/AzureDevOpsSubscriptionProvider.js +0 -215
  63. package/dist/cjs/src/VSCodeAzureSubscriptionProvider.js +0 -395
  64. package/dist/cjs/src/utils/getUnauthenticatedTenants.js +0 -23
  65. package/dist/cjs/src/utils/isGetSubscriptionsFilter.js +0 -27
  66. package/dist/esm/src/AzureDevOpsSubscriptionProvider.d.ts +0 -68
  67. package/dist/esm/src/AzureDevOpsSubscriptionProvider.js +0 -210
  68. package/dist/esm/src/AzureSubscriptionProvider.d.ts +0 -82
  69. package/dist/esm/src/AzureTenant.d.ts +0 -5
  70. package/dist/esm/src/VSCodeAzureSubscriptionProvider.d.ts +0 -116
  71. package/dist/esm/src/VSCodeAzureSubscriptionProvider.js +0 -358
  72. package/dist/esm/src/signInToTenant.d.ts +0 -6
  73. package/dist/esm/src/utils/getUnauthenticatedTenants.d.ts +0 -9
  74. package/dist/esm/src/utils/getUnauthenticatedTenants.js +0 -20
  75. package/dist/esm/src/utils/isGetSubscriptionsFilter.d.ts +0 -14
  76. package/dist/esm/src/utils/isGetSubscriptionsFilter.js +0 -23
  77. /package/dist/cjs/src/{AzureAuthentication.js → contracts/AzureAuthentication.js} +0 -0
  78. /package/dist/cjs/src/{AzureSubscription.js → contracts/AzureSubscription.js} +0 -0
  79. /package/dist/cjs/src/{AzureSubscriptionProvider.js → contracts/AzureSubscriptionProvider.js} +0 -0
  80. /package/dist/cjs/src/{AzureTenant.js → contracts/AzureTenant.js} +0 -0
  81. /package/dist/esm/src/{AzureAuthentication.js → contracts/AzureAuthentication.js} +0 -0
  82. /package/dist/esm/src/{AzureSubscription.js → contracts/AzureSubscription.js} +0 -0
  83. /package/dist/esm/src/{AzureSubscriptionProvider.js → contracts/AzureSubscriptionProvider.js} +0 -0
  84. /package/dist/esm/src/{AzureTenant.js → contracts/AzureTenant.js} +0 -0
  85. /package/dist/esm/src/{getSessionFromVSCode.d.ts → utils/getSessionFromVSCode.d.ts} +0 -0
@@ -0,0 +1,37 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ export class Limiter {
6
+ runningPromises;
7
+ maxDegreeOfParalellism;
8
+ outstandingPromises;
9
+ constructor(maxDegreeOfParalellism) {
10
+ this.maxDegreeOfParalellism = maxDegreeOfParalellism;
11
+ this.outstandingPromises = [];
12
+ this.runningPromises = 0;
13
+ }
14
+ queue(factory) {
15
+ return new Promise((c, e) => {
16
+ this.outstandingPromises.push({ factory, c, e });
17
+ this.consume();
18
+ });
19
+ }
20
+ consume() {
21
+ while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
22
+ const iLimitedTask = this.outstandingPromises.shift();
23
+ this.runningPromises++;
24
+ const promise = iLimitedTask.factory();
25
+ promise.then(iLimitedTask.c, iLimitedTask.e);
26
+ promise.then(() => this.consumed(), () => this.consumed());
27
+ }
28
+ }
29
+ consumed() {
30
+ this.runningPromises--;
31
+ if (this.outstandingPromises.length > 0) {
32
+ this.consume();
33
+ }
34
+ }
35
+ }
36
+ /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */
37
+ //# sourceMappingURL=Limiter.js.map
@@ -6,10 +6,10 @@ export declare class NotSignedInError extends Error {
6
6
  constructor();
7
7
  }
8
8
  /**
9
- * Tests if an object is a `NotSignedInError`. This should be used instead of `instanceof`.
9
+ * Tests if an object is a {@link NotSignedInError}. This should be used instead of `instanceof`.
10
10
  *
11
11
  * @param error The object to test
12
12
  *
13
- * @returns True if the object is a NotSignedInError, false otherwise
13
+ * @returns True if the object is a {@link NotSignedInError}, false otherwise
14
14
  */
15
15
  export declare function isNotSignedInError(error: unknown): error is NotSignedInError;
@@ -13,13 +13,14 @@ export class NotSignedInError extends Error {
13
13
  }
14
14
  }
15
15
  /**
16
- * Tests if an object is a `NotSignedInError`. This should be used instead of `instanceof`.
16
+ * Tests if an object is a {@link NotSignedInError}. This should be used instead of `instanceof`.
17
17
  *
18
18
  * @param error The object to test
19
19
  *
20
- * @returns True if the object is a NotSignedInError, false otherwise
20
+ * @returns True if the object is a {@link NotSignedInError}, false otherwise
21
21
  */
22
22
  export function isNotSignedInError(error) {
23
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare, @typescript-eslint/no-unnecessary-condition
23
24
  return !!error && typeof error === 'object' && error.isNotSignedInError === true;
24
25
  }
25
26
  //# sourceMappingURL=NotSignedInError.js.map
@@ -1,13 +1,15 @@
1
1
  import * as azureEnv from '@azure/ms-rest-azure-env';
2
2
  import * as vscode from 'vscode';
3
+ declare class ExtendedEnvironment extends azureEnv.Environment {
4
+ readonly isCustomCloud: boolean;
5
+ constructor(parameters: azureEnv.EnvironmentParameters, isCustomCloud: boolean);
6
+ }
3
7
  /**
4
8
  * Gets the configured Azure environment.
5
9
  *
6
10
  * @returns The configured Azure environment from the settings in the built-in authentication provider extension
7
11
  */
8
- export declare function getConfiguredAzureEnv(): azureEnv.Environment & {
9
- isCustomCloud: boolean;
10
- };
12
+ export declare function getConfiguredAzureEnv(): ExtendedEnvironment;
11
13
  /**
12
14
  * Sets the configured Azure cloud.
13
15
  *
@@ -16,9 +18,10 @@ export declare function getConfiguredAzureEnv(): azureEnv.Environment & {
16
18
  *
17
19
  * @param target (Optional) The configuration target to use, by default {@link vscode.ConfigurationTarget.Global}.
18
20
  */
19
- export declare function setConfiguredAzureEnv(cloud: 'AzureCloud' | 'ChinaCloud' | 'USGovernment' | undefined | azureEnv.EnvironmentParameters, target?: vscode.ConfigurationTarget): Promise<void>;
21
+ export declare function setConfiguredAzureEnv(cloud: 'AzureCloud' | 'ChinaCloud' | 'USGovernment' | undefined | null | azureEnv.EnvironmentParameters, target?: vscode.ConfigurationTarget): Promise<void>;
20
22
  /**
21
23
  * Gets the ID of the authentication provider configured to be used
22
24
  * @returns The provider ID to use, either `'microsoft'` or `'microsoft-sovereign-cloud'`
23
25
  */
24
26
  export declare function getConfiguredAuthProviderId(): string;
27
+ export {};
@@ -14,6 +14,16 @@ var CloudEnvironmentSettingValue;
14
14
  CloudEnvironmentSettingValue["USGovernment"] = "USGovernment";
15
15
  CloudEnvironmentSettingValue["Custom"] = "custom";
16
16
  })(CloudEnvironmentSettingValue || (CloudEnvironmentSettingValue = {}));
17
+ class ExtendedEnvironment extends azureEnv.Environment {
18
+ isCustomCloud;
19
+ constructor(parameters, isCustomCloud) {
20
+ super(parameters);
21
+ this.isCustomCloud = isCustomCloud;
22
+ // The Environment constructor only copies required properties. Copy all remaining
23
+ // optional properties (e.g. storageEndpointSuffix, keyVaultDnsSuffix) from the source.
24
+ Object.assign(this, parameters);
25
+ }
26
+ }
17
27
  /**
18
28
  * Gets the configured Azure environment.
19
29
  *
@@ -23,31 +33,19 @@ export function getConfiguredAzureEnv() {
23
33
  const authProviderConfig = vscode.workspace.getConfiguration(CustomCloudConfigurationSection);
24
34
  const environmentSettingValue = authProviderConfig.get(CloudEnvironmentSettingName);
25
35
  if (environmentSettingValue === CloudEnvironmentSettingValue.ChinaCloud) {
26
- return {
27
- ...azureEnv.Environment.ChinaCloud,
28
- isCustomCloud: false,
29
- };
36
+ return new ExtendedEnvironment(azureEnv.Environment.ChinaCloud, false);
30
37
  }
31
38
  else if (environmentSettingValue === CloudEnvironmentSettingValue.USGovernment) {
32
- return {
33
- ...azureEnv.Environment.USGovernment,
34
- isCustomCloud: false,
35
- };
39
+ return new ExtendedEnvironment(azureEnv.Environment.USGovernment, false);
36
40
  }
37
41
  else if (environmentSettingValue === CloudEnvironmentSettingValue.Custom) {
38
42
  const customCloud = authProviderConfig.get(CustomEnvironmentSettingName);
39
43
  if (customCloud) {
40
- return {
41
- ...new azureEnv.Environment(customCloud),
42
- isCustomCloud: true,
43
- };
44
+ return new ExtendedEnvironment(customCloud, true);
44
45
  }
45
46
  throw new Error(vscode.l10n.t('The custom cloud choice is not configured. Please configure the setting `{0}.{1}`.', CustomCloudConfigurationSection, CustomEnvironmentSettingName));
46
47
  }
47
- return {
48
- ...azureEnv.Environment.get(azureEnv.Environment.AzureCloud.name),
49
- isCustomCloud: false,
50
- };
48
+ return new ExtendedEnvironment(azureEnv.Environment.AzureCloud, false);
51
49
  }
52
50
  /**
53
51
  * Sets the configured Azure cloud.
@@ -0,0 +1,14 @@
1
+ import type { AzureSubscription } from '../contracts/AzureSubscription';
2
+ /**
3
+ * Deduplicates (and sorts) a list of Azure subscriptions.
4
+ * In short, the behavior is that the combination of account + tenant + subscriptionId will be unique.
5
+ * The last appearance of any duplicates will be kept. The resulting list is sorted by subscription name.
6
+ *
7
+ * Please note:
8
+ * - The same tenant may appear under different accounts.
9
+ * - The same subscriptionId may appear under different accounts that share the same tenant.
10
+ * - Rarely, the same subscriptionId may also appear under different accounts + different tenants.
11
+ *
12
+ * @param subscriptions The list of subscriptions to deduplicate
13
+ */
14
+ export declare function dedupeSubscriptions(subscriptions: AzureSubscription[]): AzureSubscription[];
@@ -0,0 +1,24 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /**
6
+ * Deduplicates (and sorts) a list of Azure subscriptions.
7
+ * In short, the behavior is that the combination of account + tenant + subscriptionId will be unique.
8
+ * The last appearance of any duplicates will be kept. The resulting list is sorted by subscription name.
9
+ *
10
+ * Please note:
11
+ * - The same tenant may appear under different accounts.
12
+ * - The same subscriptionId may appear under different accounts that share the same tenant.
13
+ * - Rarely, the same subscriptionId may also appear under different accounts + different tenants.
14
+ *
15
+ * @param subscriptions The list of subscriptions to deduplicate
16
+ */
17
+ export function dedupeSubscriptions(subscriptions) {
18
+ const deduped = new Map();
19
+ for (const sub of subscriptions) {
20
+ deduped.set(`${sub.account.id}/${sub.tenantId}/${sub.subscriptionId}`, sub);
21
+ }
22
+ return Array.from(deduped.values()).sort((a, b) => a.name.localeCompare(b.name));
23
+ }
24
+ //# sourceMappingURL=dedupeSubscriptions.js.map
@@ -0,0 +1,32 @@
1
+ import type { AzureSubscriptionProvider } from '../contracts/AzureSubscriptionProvider';
2
+ /**
3
+ * Telemetry metrics about Azure authentication.
4
+ */
5
+ export interface AzureAuthTelemetryMetrics {
6
+ /**
7
+ * The total number of accounts. This number is always accurate.
8
+ */
9
+ totalAccounts: number;
10
+ /**
11
+ * The total number of tenants. This number only includes tenants for signed-in accounts.
12
+ */
13
+ visibleTenants: number;
14
+ /**
15
+ * The total number of subscriptions, but limited to querying the first 10 account+tenants.
16
+ */
17
+ visibleSubscriptions: number;
18
+ /**
19
+ * A JSON-stringified list of subscription IDs (up to 25).
20
+ */
21
+ subscriptionIdList: string;
22
+ /**
23
+ * Whether the subscription ID list is incomplete (i.e. there were more than 25 subscriptions).
24
+ */
25
+ subscriptionIdListIsIncomplete: boolean;
26
+ }
27
+ /**
28
+ * Gets telemetry metrics about accounts, tenants, and subscriptions.
29
+ * @param subscriptionProvider The subscription provider to use to get the metrics.
30
+ * @throws A NotSignedInError if no accounts are signed in.
31
+ */
32
+ export declare function getMetricsForTelemetry(subscriptionProvider: AzureSubscriptionProvider): Promise<AzureAuthTelemetryMetrics>;
@@ -0,0 +1,44 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /**
6
+ * Gets telemetry metrics about accounts, tenants, and subscriptions.
7
+ * @param subscriptionProvider The subscription provider to use to get the metrics.
8
+ * @throws A NotSignedInError if no accounts are signed in.
9
+ */
10
+ export async function getMetricsForTelemetry(subscriptionProvider) {
11
+ // Get the total number of accounts (requires no web requests)
12
+ // Errors are deliberately not caught here, so if no accounts are signed in, this will throw
13
+ const accounts = await subscriptionProvider.getAccounts();
14
+ const numAccounts = accounts.length;
15
+ // Get the total number of tenants across all accounts (requires one web request per account, which is reasonable)
16
+ let numTenants = 0;
17
+ for (const account of accounts) {
18
+ try {
19
+ const tenants = await subscriptionProvider.getTenantsForAccount(account);
20
+ numTenants += tenants.length;
21
+ }
22
+ catch {
23
+ continue; // If we can't get tenants for an account, skip it instead of throwing
24
+ }
25
+ }
26
+ // Get as many subscriptions as we can (requires one web request per account+tenant)
27
+ // This is limited to the first 10 account+tenants, thus a max of 10 web requests
28
+ // Errors are deliberately not caught here; none are expected since at least one account is signed in
29
+ const subscriptions = await subscriptionProvider.getAvailableSubscriptions();
30
+ const numSubscriptions = subscriptions.length;
31
+ // Get subscription IDs (up to 25)
32
+ const subscriptionSet = new Set();
33
+ subscriptions.slice(0, 25).forEach(sub => {
34
+ subscriptionSet.add(sub.subscriptionId);
35
+ });
36
+ return {
37
+ totalAccounts: numAccounts,
38
+ visibleTenants: numTenants,
39
+ visibleSubscriptions: numSubscriptions,
40
+ subscriptionIdList: JSON.stringify(Array.from(subscriptionSet)),
41
+ subscriptionIdListIsIncomplete: subscriptions.length > 25,
42
+ };
43
+ }
44
+ //# sourceMappingURL=getMetricsForTelemetry.js.map
@@ -3,13 +3,16 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import * as vscode from "vscode";
6
- import { getConfiguredAuthProviderId, getConfiguredAzureEnv } from "./utils/configuredAzureEnv";
7
- import { isAuthenticationWwwAuthenticateRequest } from "./utils/isAuthenticationWwwAuthenticateRequest";
6
+ import { getConfiguredAuthProviderId, getConfiguredAzureEnv } from "./configuredAzureEnv";
7
+ import { isAuthenticationWwwAuthenticateRequest } from "./isAuthenticationWwwAuthenticateRequest";
8
8
  function ensureEndingSlash(value) {
9
9
  return value.endsWith('/') ? value : `${value}/`;
10
10
  }
11
11
  function getResourceScopes(scopes) {
12
12
  if (scopes === undefined || scopes === "" || scopes.length === 0) {
13
+ // For https://github.com/microsoft/vscode-azurefunctions/issues/3913, the default scope was changed from
14
+ // ~'https://management.azure.com/.default' (`AzureEnv.resourceManagerEndpointUrl`) to
15
+ // ~'https://management.core.windows.net/.default' (`AzureEnv.managementEndpointUrl`)
13
16
  scopes = ensureEndingSlash(getConfiguredAzureEnv().managementEndpointUrl);
14
17
  }
15
18
  const arrScopes = (Array.isArray(scopes) ? scopes : [scopes])
@@ -0,0 +1,7 @@
1
+ import type * as vscode from 'vscode';
2
+ /**
3
+ * Gets an AbortSignal that is tied to the given {@link vscode.CancellationToken}.
4
+ * @param token The token to convert
5
+ * @returns The {@link AbortSignal} or undefined if no token is provided
6
+ */
7
+ export declare function getSignalForToken(token: vscode.CancellationToken | undefined): AbortSignal | undefined;
@@ -0,0 +1,26 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /**
6
+ * Gets an AbortSignal that is tied to the given {@link vscode.CancellationToken}.
7
+ * @param token The token to convert
8
+ * @returns The {@link AbortSignal} or undefined if no token is provided
9
+ */
10
+ export function getSignalForToken(token) {
11
+ if (!token) {
12
+ return undefined;
13
+ }
14
+ const controller = new AbortController();
15
+ if (token.isCancellationRequested) {
16
+ controller.abort();
17
+ }
18
+ else {
19
+ const disposable = token.onCancellationRequested(() => {
20
+ disposable.dispose();
21
+ controller.abort();
22
+ });
23
+ }
24
+ return controller.signal;
25
+ }
26
+ //# sourceMappingURL=getSignalForToken.js.map
@@ -0,0 +1,28 @@
1
+ export declare class CaselessMap<V> implements Map<string, V> {
2
+ #private;
3
+ clear(): void;
4
+ delete(key: string): boolean;
5
+ /**
6
+ * @important Keys returned **match original case**!
7
+ */
8
+ forEach(callbackfn: (value: V, key: string, map: Map<string, V>) => void, thisArg?: any): void;
9
+ get(key: string): V | undefined;
10
+ has(key: string): boolean;
11
+ set(key: string, value: V): this;
12
+ get size(): number;
13
+ /**
14
+ * @important Keys returned **match original case**!
15
+ */
16
+ entries(): MapIterator<[string, V]>;
17
+ /**
18
+ * @important Keys returned **match original case**!
19
+ */
20
+ keys(): MapIterator<string>;
21
+ values(): MapIterator<V>;
22
+ /**
23
+ * @important Keys returned **match original case**!
24
+ */
25
+ [Symbol.iterator](): MapIterator<[string, V]>;
26
+ readonly [Symbol.toStringTag]: string;
27
+ private normalizeKey;
28
+ }
@@ -0,0 +1,67 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ export class CaselessMap {
6
+ #map = new Map();
7
+ clear() {
8
+ this.#map.clear();
9
+ }
10
+ delete(key) {
11
+ return this.#map.delete(this.normalizeKey(key));
12
+ }
13
+ /**
14
+ * @important Keys returned **match original case**!
15
+ */
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Must exactly match interface
17
+ forEach(callbackfn, thisArg) {
18
+ this.#map.forEach(entry => {
19
+ callbackfn.call(thisArg, entry.value, entry.originalKey, this);
20
+ });
21
+ }
22
+ get(key) {
23
+ return this.#map.get(this.normalizeKey(key))?.value;
24
+ }
25
+ has(key) {
26
+ return this.#map.has(this.normalizeKey(key));
27
+ }
28
+ set(key, value) {
29
+ this.#map.set(this.normalizeKey(key), { originalKey: key, value });
30
+ return this;
31
+ }
32
+ get size() {
33
+ return this.#map.size;
34
+ }
35
+ /**
36
+ * @important Keys returned **match original case**!
37
+ */
38
+ *entries() {
39
+ for (const [, entry] of this.#map) {
40
+ yield [entry.originalKey, entry.value];
41
+ }
42
+ }
43
+ /**
44
+ * @important Keys returned **match original case**!
45
+ */
46
+ *keys() {
47
+ for (const [, entry] of this.#map) {
48
+ yield entry.originalKey;
49
+ }
50
+ }
51
+ *values() {
52
+ for (const [, entry] of this.#map) {
53
+ yield entry.value;
54
+ }
55
+ }
56
+ /**
57
+ * @important Keys returned **match original case**!
58
+ */
59
+ [Symbol.iterator]() {
60
+ return this.entries();
61
+ }
62
+ [Symbol.toStringTag] = 'CaselessMap';
63
+ normalizeKey(key) {
64
+ return key.toLowerCase();
65
+ }
66
+ }
67
+ //# sourceMappingURL=CaselessMap.js.map
@@ -0,0 +1,49 @@
1
+ export declare class TwoKeyCaselessMap<V> implements Iterable<[string, string, V]> {
2
+ #private;
3
+ private readonly testMode;
4
+ constructor(testMode?: boolean);
5
+ clear(): void;
6
+ clearByFirstKey(key1: string): void;
7
+ clearBySecondKey(key2: string): void;
8
+ delete(key1: string, key2: string): boolean;
9
+ /**
10
+ * @important Keys returned **match original case**!
11
+ */
12
+ forEach(callbackfn: (value: V, key1: string, key2: string) => void, thisArg?: any): void;
13
+ get(key1: string, key2: string): V | undefined;
14
+ has(key1: string, key2: string): boolean;
15
+ set(key1: string, key2: string, value: V): this;
16
+ get size(): number;
17
+ /**
18
+ * @important Keys returned **match original case**!
19
+ */
20
+ entriesByFirstKey(key1: string): MapIterator<[string, V]>;
21
+ /**
22
+ * @important Keys returned **match original case**!
23
+ */
24
+ entriesBySecondKey(key2: string): MapIterator<[string, V]>;
25
+ /**
26
+ * @important Keys returned **match original case**!
27
+ */
28
+ entries(): MapIterator<[string, string, V]>;
29
+ /**
30
+ * @important Keys returned **match original case**!
31
+ */
32
+ keysByFirstKey(key1: string): MapIterator<string>;
33
+ /**
34
+ * @important Keys returned **match original case**!
35
+ */
36
+ keysBySecondKey(key2: string): MapIterator<string>;
37
+ /**
38
+ * @important Keys returned **match original case**!
39
+ */
40
+ keys(): MapIterator<[string, string]>;
41
+ valuesByFirstKey(key1: string): MapIterator<V>;
42
+ valuesBySecondKey(key2: string): MapIterator<V>;
43
+ values(): MapIterator<V>;
44
+ /**
45
+ * @important Keys returned **match original case**!
46
+ */
47
+ [Symbol.iterator](): MapIterator<[string, string, V]>;
48
+ readonly [Symbol.toStringTag]: string;
49
+ }
@@ -0,0 +1,190 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import { CaselessMap } from './CaselessMap';
6
+ export class TwoKeyCaselessMap {
7
+ testMode;
8
+ #map1 = new CaselessMap(); // First map has key1 as the outer key and key2 as the inner key
9
+ #map2 = new CaselessMap(); // Second key map has key2 as the outer key and key1 as the inner key
10
+ #size = 0;
11
+ constructor(testMode = false) {
12
+ this.testMode = testMode;
13
+ }
14
+ clear() {
15
+ this.#map1.clear();
16
+ this.#map2.clear();
17
+ this.#size = 0;
18
+ }
19
+ clearByFirstKey(key1) {
20
+ const innerMap1 = this.#map1.get(key1);
21
+ if (!innerMap1) {
22
+ return;
23
+ }
24
+ // Decrement size before any deletions
25
+ this.#size -= innerMap1.size;
26
+ // Remove all entries from #map2 that reference this key1
27
+ innerMap1.forEach((_, key2) => {
28
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The existence of innerMap1 ensures this is non-null
29
+ const innerMap2 = this.#map2.get(key2);
30
+ innerMap2.delete(key1);
31
+ if (innerMap2.size === 0) {
32
+ this.#map2.delete(key2);
33
+ }
34
+ });
35
+ // Remove the entire inner map from #map1
36
+ this.#map1.delete(key1);
37
+ }
38
+ clearBySecondKey(key2) {
39
+ const innerMap2 = this.#map2.get(key2);
40
+ if (!innerMap2) {
41
+ return;
42
+ }
43
+ // Decrement size before any deletions
44
+ this.#size -= innerMap2.size;
45
+ // Remove all entries from #map1 that reference this key2
46
+ innerMap2.forEach((_, key1) => {
47
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The existence of innerMap2 ensures this is non-null
48
+ const innerMap1 = this.#map1.get(key1);
49
+ innerMap1.delete(key2);
50
+ if (innerMap1.size === 0) {
51
+ this.#map1.delete(key1);
52
+ }
53
+ });
54
+ // Remove the entire inner map from #map2
55
+ this.#map2.delete(key2);
56
+ }
57
+ delete(key1, key2) {
58
+ if (!this.has(key1, key2)) {
59
+ return false;
60
+ }
61
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The has() check above ensures non-null
62
+ const innerMap1 = this.#map1.get(key1);
63
+ innerMap1.delete(key2);
64
+ if (innerMap1.size === 0) {
65
+ this.#map1.delete(key1);
66
+ }
67
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The has() check above ensures non-null
68
+ const innerMap2 = this.#map2.get(key2);
69
+ innerMap2.delete(key1);
70
+ if (innerMap2.size === 0) {
71
+ this.#map2.delete(key2);
72
+ }
73
+ this.#size--;
74
+ return true;
75
+ }
76
+ /**
77
+ * @important Keys returned **match original case**!
78
+ */
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Must exactly match interface
80
+ forEach(callbackfn, thisArg) {
81
+ this.#map1.forEach((innerMap1, key1) => {
82
+ innerMap1.forEach((value, key2) => {
83
+ callbackfn.call(thisArg, value, key1, key2);
84
+ });
85
+ });
86
+ }
87
+ get(key1, key2) {
88
+ return this.#map1.get(key1)?.get(key2);
89
+ }
90
+ has(key1, key2) {
91
+ const result1 = this.#map1.get(key1)?.has(key2);
92
+ if (this.testMode) {
93
+ const result2 = this.#map2.get(key2)?.has(key1);
94
+ if (!!result1 !== !!result2) {
95
+ throw new Error('Inconsistent state in TwoKeyCaselessMap');
96
+ }
97
+ }
98
+ return result1 ?? false;
99
+ }
100
+ set(key1, key2, value) {
101
+ const alreadyHadEntry = this.has(key1, key2);
102
+ let innerMap1 = this.#map1.get(key1);
103
+ if (!innerMap1) {
104
+ innerMap1 = new CaselessMap();
105
+ this.#map1.set(key1, innerMap1);
106
+ }
107
+ innerMap1.set(key2, value);
108
+ let innerMap2 = this.#map2.get(key2);
109
+ if (!innerMap2) {
110
+ innerMap2 = new CaselessMap();
111
+ this.#map2.set(key2, innerMap2);
112
+ }
113
+ innerMap2.set(key1, value);
114
+ if (!alreadyHadEntry) {
115
+ this.#size++;
116
+ }
117
+ return this;
118
+ }
119
+ get size() {
120
+ return this.#size;
121
+ }
122
+ /**
123
+ * @important Keys returned **match original case**!
124
+ */
125
+ entriesByFirstKey(key1) {
126
+ return this.#map1.get(key1)?.entries() ?? emptyIterator();
127
+ }
128
+ /**
129
+ * @important Keys returned **match original case**!
130
+ */
131
+ entriesBySecondKey(key2) {
132
+ return this.#map2.get(key2)?.entries() ?? emptyIterator();
133
+ }
134
+ /**
135
+ * @important Keys returned **match original case**!
136
+ */
137
+ *entries() {
138
+ for (const [key1, innerMap1] of this.#map1) {
139
+ for (const [key2, value] of innerMap1) {
140
+ yield [key1, key2, value];
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * @important Keys returned **match original case**!
146
+ */
147
+ keysByFirstKey(key1) {
148
+ return this.#map1.get(key1)?.keys() ?? emptyIterator();
149
+ }
150
+ /**
151
+ * @important Keys returned **match original case**!
152
+ */
153
+ keysBySecondKey(key2) {
154
+ return this.#map2.get(key2)?.keys() ?? emptyIterator();
155
+ }
156
+ /**
157
+ * @important Keys returned **match original case**!
158
+ */
159
+ *keys() {
160
+ for (const [key1, innerMap1] of this.#map1) {
161
+ for (const key2 of innerMap1.keys()) {
162
+ yield [key1, key2];
163
+ }
164
+ }
165
+ }
166
+ valuesByFirstKey(key1) {
167
+ return this.#map1.get(key1)?.values() ?? emptyIterator();
168
+ }
169
+ valuesBySecondKey(key2) {
170
+ return this.#map2.get(key2)?.values() ?? emptyIterator();
171
+ }
172
+ *values() {
173
+ for (const innerMap1 of this.#map1.values()) {
174
+ for (const value of innerMap1.values()) {
175
+ yield value;
176
+ }
177
+ }
178
+ }
179
+ /**
180
+ * @important Keys returned **match original case**!
181
+ */
182
+ [Symbol.iterator]() {
183
+ return this.entries();
184
+ }
185
+ [Symbol.toStringTag] = 'TwoKeyCaselessMap';
186
+ }
187
+ function emptyIterator() {
188
+ return [][Symbol.iterator]();
189
+ }
190
+ //# sourceMappingURL=TwoKeyCaselessMap.js.map