From 729d5d313447641369c882bfb7b01c213ac2c17e Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:45:52 -0500 Subject: [PATCH] [PM-21546] Migrate from `enum` to constant object (#14975) * add generic `union-of-values` helper * migrate `GeneratorDialogAction` to a constant * migrate `VaultState` to a constant * migrate `AtRiskCarouselDialogResult` to a constant * migrate `CredentialGeneratorDialogAction` to a constant * migrate `FolderAddEditDialogResult` to a constant * migrate `ViewCipherDialogResult` to a constant * migrate `VisibleVaultBanner` to a constant * migrate `VaultFilterLabel` to a constant * migrate `WebVaultGeneratorDialogResult` to a constant * migrate `BulkDeleteDialogResult` to a constant * migrate `BulkMoveDialogResult` to a constant * migrate `AddEditCipherDialogResult` to a constant * migrate `VaultItemDialogResult` to a constant * migrate `BrowserPromptState` to a constant * migrate `NudgeType` to a constant * migrate `SecurityTaskStatus` to a constant * migrate `CipherRepromptType` to a constant * migrate `SecureNoteType` to a constant * migrate `FieldType` to a constant * migrate `LinkedIdType` to a constant * migrate `CollectionAssignmentResult` to a constant * migrate `AddEditFolderDialogResult` to a constant * migrate `AttachmentDialogResult` to a constant * fix CipherType in delete organization dialog * fix `in` statement in VaultFilter * Fix build errors across enum updates * fix two more CipherType castings * update CipherResponse `CipherType` * define type for `fieldType` parameter * refine how `cipherTypeNames` is generated and add utility function for grabbing cipher type name * use `CipherType` rather than `number` * add stricter typing for `FieldType` * add fixme for `CipherType` to be ADR-0025 compliant * remove error throw for `toCipherTypeName` and instead update typing to have `| undefined` * add helpers for CipherType conversions * prefer `undefined` --- .../autofill/background/overlay.background.ts | 2 +- .../browser/cipher-context-menu-handler.ts | 4 +- apps/browser/src/autofill/types/index.ts | 5 +- .../at-risk-carousel-dialog.component.ts | 11 +-- .../add-edit/add-edit-v2.component.ts | 4 +- .../item-more-options.component.ts | 4 +- .../vault-generator-dialog.component.ts | 13 +-- .../components/vault-v2/vault-v2.component.ts | 15 ++-- .../services/vault-popup-items.service.ts | 4 +- .../credential-generator-dialog.component.ts | 13 +-- .../src/vault/app/vault/vault-v2.component.ts | 13 ++- .../src/vault/app/vault/vault.component.ts | 8 +- .../delete-organization-dialog.component.ts | 4 +- ...browser-extension-prompt.component.spec.ts | 2 +- .../vault-item-dialog.component.ts | 17 ++-- .../web-generator-dialog.component.ts | 13 +-- .../individual-vault/add-edit-v2.component.ts | 15 ++-- .../bulk-delete-dialog.component.ts | 13 +-- .../bulk-move-dialog.component.ts | 13 +-- .../folder-add-edit.component.ts | 15 ++-- .../services/vault-banners.service.ts | 21 ++--- .../vault-banners/vault-banners.component.ts | 2 +- .../models/vault-filter-section.type.ts | 19 ++--- .../shared/models/vault-filter.model.ts | 6 +- .../vault/individual-vault/view.component.ts | 15 ++-- .../browser-extension-prompt.service.ts | 21 ++--- .../abstractions/admin-task.abstraction.ts | 2 +- .../src/vault/services/nudges.service.ts | 37 +++++---- .../src/vault/enums/cipher-reprompt-type.ts | 14 ++-- .../src/vault/enums/cipher-type.spec.ts | 66 +++++++++++++++ libs/common/src/vault/enums/cipher-type.ts | 67 ++++++++++++++-- .../common/src/vault/enums/field-type.enum.ts | 20 +++-- .../src/vault/enums/linked-id-type.enum.ts | 80 ++++++++++--------- .../src/vault/enums/secure-note-type.enum.ts | 12 +-- .../src/vault/models/data/cipher.data.ts | 2 +- .../vault/models/response/cipher.response.ts | 3 +- .../tasks/enums/security-task-status.enum.ts | 14 ++-- .../tasks/enums/security-task-type.enum.ts | 12 +-- .../common/src/vault/types/union-of-values.ts | 2 + .../bitwarden/bitwarden-csv-importer.ts | 2 +- libs/importer/src/services/import.service.ts | 4 +- .../custom-fields/custom-fields.component.ts | 2 +- .../attachments/attachments-v2.component.ts | 15 ++-- .../add-edit-folder-dialog.component.ts | 13 +-- .../assign-collections.component.ts | 13 +-- 45 files changed, 404 insertions(+), 248 deletions(-) create mode 100644 libs/common/src/vault/enums/cipher-type.spec.ts create mode 100644 libs/common/src/vault/types/union-of-values.ts diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index ce0dbe5bb2..2ff08328e3 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -459,7 +459,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { const cipherView = cipherViews[cipherIndex]; if ( !this.cardAndIdentityCiphers.has(cipherView) && - [CipherType.Card, CipherType.Identity].includes(cipherView.type) + ([CipherType.Card, CipherType.Identity] as CipherType[]).includes(cipherView.type) ) { this.cardAndIdentityCiphers.add(cipherView); } diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index e2bf75350a..b1d65fdea9 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -97,7 +97,9 @@ export class CipherContextMenuHandler { private async updateForCipher(cipher: CipherView) { if ( cipher == null || - !new Set([CipherType.Login, CipherType.Card, CipherType.Identity]).has(cipher.type) + !new Set([CipherType.Login, CipherType.Card, CipherType.Identity] as CipherType[]).has( + cipher.type, + ) ) { return; } diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index 30ebf38fef..93bf35d1b3 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -54,4 +54,7 @@ export type FormFieldElement = FillableFormFieldElement | HTMLSpanElement; export type FormElementWithAttribute = FormFieldElement & Record; -export type AutofillCipherTypeId = CipherType.Login | CipherType.Card | CipherType.Identity; +export type AutofillCipherTypeId = + | typeof CipherType.Login + | typeof CipherType.Card + | typeof CipherType.Identity; diff --git a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts index 4f6a682e58..08c466d21a 100644 --- a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts @@ -1,5 +1,6 @@ import { Component, inject, signal } from "@angular/core"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DialogRef, ButtonModule, @@ -10,11 +11,11 @@ import { import { I18nPipe } from "@bitwarden/ui-common"; import { DarkImageSourceDirective, VaultCarouselModule } from "@bitwarden/vault"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AtRiskCarouselDialogResult { - Dismissed = "dismissed", -} +export const AtRiskCarouselDialogResult = { + Dismissed: "dismissed", +} as const; + +type AtRiskCarouselDialogResult = UnionOfValues; @Component({ selector: "vault-at-risk-carousel-dialog", diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 83bced8882..5aac720738 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -17,7 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; @@ -64,7 +64,7 @@ import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-a class QueryParams { constructor(params: Params) { this.cipherId = params.cipherId; - this.type = params.type != undefined ? parseInt(params.type, null) : undefined; + this.type = toCipherType(params.type); this.clone = params.clone === "true"; this.folderId = params.folderId; this.organizationId = params.organizationId; diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index bb7b74f8c6..165dd6d6d3 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -103,7 +103,9 @@ export class ItemMoreOptionsComponent implements OnInit { * Determines if the cipher can be autofilled. */ get canAutofill() { - return [CipherType.Login, CipherType.Card, CipherType.Identity].includes(this.cipher.type); + return ([CipherType.Login, CipherType.Card, CipherType.Identity] as CipherType[]).includes( + this.cipher.type, + ); } get isLogin() { diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts index f02ce46e93..b0103aaacf 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts @@ -5,6 +5,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -30,12 +31,12 @@ export interface GeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum GeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const GeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type GeneratorDialogAction = UnionOfValues; @Component({ selector: "app-vault-generator-dialog", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 9310953dbb..792f2b34f9 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -24,6 +24,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { ButtonModule, DialogService, @@ -55,13 +56,13 @@ import { VaultHeaderV2Component } from "./vault-header/vault-header-v2.component import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from "."; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -enum VaultState { - Empty, - NoResults, - DeactivatedOrg, -} +const VaultState = { + Empty: 0, + NoResults: 1, + DeactivatedOrg: 2, +} as const; + +type VaultState = UnionOfValues; @Component({ selector: "app-vault", diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index b4cf79e742..c1dd9b30c6 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -319,13 +319,13 @@ export class VaultPopupItemsService { * @private */ private sortCiphersForAutofill(a: CipherView, b: CipherView): number { - const typeOrder: Record = { + const typeOrder = { [CipherType.Login]: 1, [CipherType.Card]: 2, [CipherType.Identity]: 3, [CipherType.SecureNote]: 4, [CipherType.SshKey]: 5, - }; + } as Record; // Compare types first if (typeOrder[a.type] < typeOrder[b.type]) { diff --git a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts index 1a375fc0f5..2634992010 100644 --- a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts +++ b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts @@ -3,6 +3,7 @@ import { Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, ButtonModule, @@ -31,12 +32,12 @@ export interface CredentialGeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CredentialGeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const CredentialGeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type CredentialGeneratorDialogAction = UnionOfValues; @Component({ selector: "credential-generator-dialog", diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 70f0f29dee..50e6bfb51c 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -34,7 +34,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -323,6 +323,7 @@ export class VaultV2Component implements OnInit, OnDestroy { async load() { const params = await firstValueFrom(this.route.queryParams).catch(); + const paramCipherAddType = toCipherType(params.addType); if (params.cipherId) { const cipherView = new CipherView(); cipherView.id = params.cipherId; @@ -333,17 +334,15 @@ export class VaultV2Component implements OnInit, OnDestroy { } else { await this.viewCipher(cipherView).catch(() => {}); } - } else if (params.action === "add") { - this.addType = Number(params.addType); + } else if (params.action === "add" && paramCipherAddType) { + this.addType = paramCipherAddType; await this.addCipher(this.addType).catch(() => {}); } + const paramCipherType = toCipherType(params.type); this.activeFilter = new VaultFilter({ status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", - cipherType: - params.action === "add" || params.type == null - ? undefined - : (parseInt(params.type) as CipherType), + cipherType: params.action === "add" || paramCipherType == null ? undefined : paramCipherType, selectedFolderId: params.folderId, selectedCollectionId: params.selectedCollectionId, selectedOrganizationId: params.selectedOrganizationId, diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index 6c9a3217bf..0d66dbc7d7 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -31,7 +31,7 @@ import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -282,16 +282,16 @@ export class VaultComponent implements OnInit, OnDestroy { await this.viewCipher(cipherView); } } else if (params.action === "add") { - this.addType = Number(params.addType); + this.addType = toCipherType(params.addType); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.addCipher(this.addType); } + const paramCipherType = toCipherType(params.type); this.activeFilter = new VaultFilter({ status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", - cipherType: - params.action === "add" || params.type == null ? null : parseInt(params.type, null), + cipherType: params.action === "add" || paramCipherType == null ? null : paramCipherType, selectedFolderId: params.folderId, selectedCollectionId: params.selectedCollectionId, selectedOrganizationId: params.selectedOrganizationId, diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts index 8c2bfe079d..1b41dc31a6 100644 --- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts @@ -18,7 +18,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DIALOG_DATA, @@ -162,7 +162,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy { organizationContentSummary.itemCountByType.push( new OrganizationContentSummaryItem( count, - this.getOrganizationItemLocalizationKeysByType(CipherType[cipherType]), + this.getOrganizationItemLocalizationKeysByType(toCipherTypeName(cipherType)), ), ); } diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts index ee81ff5237..1b5b012ab1 100644 --- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts +++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts @@ -16,7 +16,7 @@ describe("BrowserExtensionPromptComponent", () => { let component: BrowserExtensionPromptComponent; const start = jest.fn(); const openExtension = jest.fn(); - const pageState$ = new BehaviorSubject(BrowserPromptState.Loading); + const pageState$ = new BehaviorSubject(BrowserPromptState.Loading); const setAttribute = jest.fn(); const getAttribute = jest.fn().mockReturnValue("width=1010"); diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 11c326d72d..5ab06cd333 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -29,6 +29,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -95,29 +96,29 @@ export interface VaultItemDialogParams { restore?: (c: CipherView) => Promise; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VaultItemDialogResult { +export const VaultItemDialogResult = { /** * A cipher was saved (created or updated). */ - Saved = "saved", + Saved: "saved", /** * A cipher was deleted. */ - Deleted = "deleted", + Deleted: "deleted", /** * The dialog was closed to navigate the user the premium upgrade page. */ - PremiumUpgrade = "premiumUpgrade", + PremiumUpgrade: "premiumUpgrade", /** * A cipher was restored */ - Restored = "restored", -} + Restored: "restored", +} as const; + +export type VaultItemDialogResult = UnionOfValues; @Component({ selector: "app-vault-item-dialog", diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts index 8ff0709a5e..7454b4d10f 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts @@ -4,6 +4,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -26,12 +27,12 @@ export interface WebVaultGeneratorDialogResult { generatedValue?: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum WebVaultGeneratorDialogAction { - Selected = "selected", - Canceled = "canceled", -} +export const WebVaultGeneratorDialogAction = { + Selected: "selected", + Canceled: "canceled", +} as const; + +type WebVaultGeneratorDialogAction = UnionOfValues; @Component({ selector: "web-vault-generator-dialog", diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index 76b7cd3723..bfad71aca4 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -11,6 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -35,13 +36,13 @@ import { WebCipherFormGenerationService } from "../services/web-cipher-form-gene /** * The result of the AddEditCipherDialogV2 component. */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AddEditCipherDialogResult { - Edited = "edited", - Added = "added", - Canceled = "canceled", -} +export const AddEditCipherDialogResult = { + Edited: "edited", + Added: "added", + Canceled: "canceled", +} as const; + +type AddEditCipherDialogResult = UnionOfValues; /** * The close result of the AddEditCipherDialogV2 component. diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index 43a44cf506..128afdcccf 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -12,6 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherBulkDeleteRequest } from "@bitwarden/common/vault/models/request/cipher-bulk-delete.request"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -29,12 +30,12 @@ export interface BulkDeleteDialogParams { unassignedCiphers?: string[]; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BulkDeleteDialogResult { - Deleted = "deleted", - Canceled = "canceled", -} +export const BulkDeleteDialogResult = { + Deleted: "deleted", + Canceled: "canceled", +} as const; + +type BulkDeleteDialogResult = UnionOfValues; /** * Strongly typed helper to open a BulkDeleteDialog diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index dc262b0133..ef43a3ead8 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -11,6 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DialogConfig, DialogRef, @@ -23,12 +24,12 @@ export interface BulkMoveDialogParams { cipherIds?: string[]; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BulkMoveDialogResult { - Moved = "moved", - Canceled = "canceled", -} +export const BulkMoveDialogResult = { + Moved: "moved", + Canceled: "canceled", +} as const; + +type BulkMoveDialogResult = UnionOfValues; /** * Strongly typed helper to open a BulkMoveDialog diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts index 3050c00dd6..15c3e18544 100644 --- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts @@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogConfig, @@ -114,13 +115,13 @@ export interface FolderAddEditDialogParams { folderId: string; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum FolderAddEditDialogResult { - Deleted = "deleted", - Canceled = "canceled", - Saved = "saved", -} +export const FolderAddEditDialogResult = { + Deleted: "deleted", + Canceled: "canceled", + Saved: "saved", +} as const; + +export type FolderAddEditDialogResult = UnionOfValues; /** * Strongly typed helper to open a FolderAddEdit dialog diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts index ca16541f88..17aaf5271b 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/services/vault-banners.service.ts @@ -15,17 +15,18 @@ import { } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { PBKDF2KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VisibleVaultBanner { - KDFSettings = "kdf-settings", - OutdatedBrowser = "outdated-browser", - Premium = "premium", - VerifyEmail = "verify-email", - PendingAuthRequest = "pending-auth-request", -} +export const VisibleVaultBanner = { + KDFSettings: "kdf-settings", + OutdatedBrowser: "outdated-browser", + Premium: "premium", + VerifyEmail: "verify-email", + PendingAuthRequest: "pending-auth-request", +} as const; + +export type VisibleVaultBanner = UnionOfValues; type PremiumBannerReprompt = { numberOfDismissals: number; @@ -34,7 +35,7 @@ type PremiumBannerReprompt = { }; /** Banners that will be re-shown on a new session */ -type SessionBanners = Omit; +type SessionBanners = Omit; export const PREMIUM_BANNER_REPROMPT_KEY = new UserKeyDefinition( PREMIUM_BANNER_DISK_LOCAL, diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts index 22a4f5f8c9..7eafaa50c1 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts @@ -98,7 +98,7 @@ export class VaultBannersComponent implements OnInit { showVerifyEmail ? VisibleVaultBanner.VerifyEmail : null, showLowKdf ? VisibleVaultBanner.KDFSettings : null, showPendingAuthRequest ? VisibleVaultBanner.PendingAuthRequest : null, - ].filter((banner): banner is VisibleVaultBanner => banner !== null); // ensures the filtered array contains only VisibleVaultBanner values + ].filter((banner) => banner !== null); } freeTrialMessage(organization: FreeTrial) { diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts index 7566dbdc50..4210a6c812 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts @@ -1,6 +1,7 @@ import { Observable } from "rxjs"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { CipherTypeFilter, @@ -15,15 +16,15 @@ export type VaultFilterType = | FolderFilter | CollectionFilter; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum VaultFilterLabel { - OrganizationFilter = "organizationFilter", - TypeFilter = "typeFilter", - FolderFilter = "folderFilter", - CollectionFilter = "collectionFilter", - TrashFilter = "trashFilter", -} +export const VaultFilterLabel = { + OrganizationFilter: "organizationFilter", + TypeFilter: "typeFilter", + FolderFilter: "folderFilter", + CollectionFilter: "collectionFilter", + TrashFilter: "trashFilter", +} as const; + +type VaultFilterLabel = UnionOfValues; export type VaultFilterSection = { data$: Observable>; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts index c486ad800a..7f001f3aab 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, isCipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -77,8 +77,8 @@ export class VaultFilter { } get cipherType(): CipherType { - return this.selectedCipherTypeNode?.node.type in CipherType - ? (this.selectedCipherTypeNode?.node.type as CipherType) + return isCipherType(this.selectedCipherTypeNode?.node.type) + ? this.selectedCipherTypeNode?.node.type : null; } diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index 15a1a46b10..2c6f4d1fdb 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -20,6 +20,7 @@ import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -54,13 +55,13 @@ export interface ViewCipherDialogParams { disableEdit?: boolean; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum ViewCipherDialogResult { - Edited = "edited", - Deleted = "deleted", - PremiumUpgrade = "premiumUpgrade", -} +export const ViewCipherDialogResult = { + Edited: "edited", + Deleted: "deleted", + PremiumUpgrade: "premiumUpgrade", +} as const; + +type ViewCipherDialogResult = UnionOfValues; export interface ViewCipherDialogCloseResult { action: ViewCipherDialogResult; diff --git a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts index f928404a2a..0f401c04ab 100644 --- a/apps/web/src/app/vault/services/browser-extension-prompt.service.ts +++ b/apps/web/src/app/vault/services/browser-extension-prompt.service.ts @@ -6,18 +6,19 @@ import { AnonLayoutWrapperDataService } from "@bitwarden/auth/angular"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { VaultMessages } from "@bitwarden/common/vault/enums/vault-messages.enum"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum BrowserPromptState { - Loading = "loading", - Error = "error", - Success = "success", - ManualOpen = "manualOpen", - MobileBrowser = "mobileBrowser", -} +export const BrowserPromptState = { + Loading: "loading", + Error: "error", + Success: "success", + ManualOpen: "manualOpen", + MobileBrowser: "mobileBrowser", +} as const; -type PromptErrorStates = BrowserPromptState.Error | BrowserPromptState.ManualOpen; +export type BrowserPromptState = UnionOfValues; + +type PromptErrorStates = typeof BrowserPromptState.Error | typeof BrowserPromptState.ManualOpen; @Injectable({ providedIn: "root", diff --git a/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts index 6f5963c332..f6b0239272 100644 --- a/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts +++ b/bitwarden_license/bit-web/src/app/vault/services/abstractions/admin-task.abstraction.ts @@ -8,7 +8,7 @@ import { SecurityTask, SecurityTaskStatus, SecurityTaskType } from "@bitwarden/c */ export type CreateTasksRequest = Readonly<{ cipherId?: CipherId; - type: SecurityTaskType.UpdateAtRiskCredential; + type: typeof SecurityTaskType.UpdateAtRiskCredential; }>; export abstract class AdminTaskService { diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts index 25b111d888..25f0e30de7 100644 --- a/libs/angular/src/vault/services/nudges.service.ts +++ b/libs/angular/src/vault/services/nudges.service.ts @@ -5,6 +5,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { UserKeyDefinition, NUDGES_DISK } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { HasItemsNudgeService, @@ -25,25 +26,23 @@ export type NudgeStatus = { /** * Enum to list the various nudge types, to be used by components/badges to show/hide the nudge */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum NudgeType { - /** Nudge to show when user has no items in their vault - * Add future nudges here - */ - EmptyVaultNudge = "empty-vault-nudge", - VaultSettingsImportNudge = "vault-settings-import-nudge", - HasVaultItems = "has-vault-items", - AutofillNudge = "autofill-nudge", - AccountSecurity = "account-security", - DownloadBitwarden = "download-bitwarden", - NewLoginItemStatus = "new-login-item-status", - NewCardItemStatus = "new-card-item-status", - NewIdentityItemStatus = "new-identity-item-status", - NewNoteItemStatus = "new-note-item-status", - NewSshItemStatus = "new-ssh-item-status", - GeneratorNudgeStatus = "generator-nudge-status", -} +export const NudgeType = { + /** Nudge to show when user has no items in their vault */ + EmptyVaultNudge: "empty-vault-nudge", + VaultSettingsImportNudge: "vault-settings-import-nudge", + HasVaultItems: "has-vault-items", + AutofillNudge: "autofill-nudge", + AccountSecurity: "account-security", + DownloadBitwarden: "download-bitwarden", + NewLoginItemStatus: "new-login-item-status", + NewCardItemStatus: "new-card-item-status", + NewIdentityItemStatus: "new-identity-item-status", + NewNoteItemStatus: "new-note-item-status", + NewSshItemStatus: "new-ssh-item-status", + GeneratorNudgeStatus: "generator-nudge-status", +} as const; + +export type NudgeType = UnionOfValues; export const NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition< Partial> diff --git a/libs/common/src/vault/enums/cipher-reprompt-type.ts b/libs/common/src/vault/enums/cipher-reprompt-type.ts index 190a9bad04..91b05399d3 100644 --- a/libs/common/src/vault/enums/cipher-reprompt-type.ts +++ b/libs/common/src/vault/enums/cipher-reprompt-type.ts @@ -1,6 +1,8 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CipherRepromptType { - None = 0, - Password = 1, -} +import { UnionOfValues } from "../types/union-of-values"; + +export const CipherRepromptType = { + None: 0, + Password: 1, +} as const; + +export type CipherRepromptType = UnionOfValues; diff --git a/libs/common/src/vault/enums/cipher-type.spec.ts b/libs/common/src/vault/enums/cipher-type.spec.ts new file mode 100644 index 0000000000..41eee6ea0b --- /dev/null +++ b/libs/common/src/vault/enums/cipher-type.spec.ts @@ -0,0 +1,66 @@ +import { + CipherType, + cipherTypeNames, + isCipherType, + toCipherType, + toCipherTypeName, +} from "./cipher-type"; + +describe("CipherType", () => { + describe("toCipherTypeName", () => { + it("should map CipherType correctly", () => { + // identity test as the value is calculated + expect(cipherTypeNames).toEqual({ + 1: "Login", + 2: "SecureNote", + 3: "Card", + 4: "Identity", + 5: "SshKey", + }); + }); + }); + + describe("toCipherTypeName", () => { + it("returns the associated name for the cipher type", () => { + expect(toCipherTypeName(1)).toBe("Login"); + expect(toCipherTypeName(2)).toBe("SecureNote"); + expect(toCipherTypeName(3)).toBe("Card"); + expect(toCipherTypeName(4)).toBe("Identity"); + expect(toCipherTypeName(5)).toBe("SshKey"); + }); + + it("returns undefined for an invalid cipher type", () => { + expect(toCipherTypeName(999 as any)).toBeUndefined(); + expect(toCipherTypeName("" as any)).toBeUndefined(); + }); + }); + + describe("isCipherType", () => { + it("returns true for valid CipherType values", () => { + [1, 2, 3, 4, 5].forEach((value) => { + expect(isCipherType(value)).toBe(true); + }); + }); + + it("returns false for invalid CipherType values", () => { + expect(isCipherType(999 as any)).toBe(false); + expect(isCipherType("Login" as any)).toBe(false); + expect(isCipherType(null)).toBe(false); + expect(isCipherType(undefined)).toBe(false); + }); + }); + + describe("toCipherType", () => { + it("converts valid values to CipherType", () => { + expect(toCipherType("1")).toBe(CipherType.Login); + expect(toCipherType("02")).toBe(CipherType.SecureNote); + }); + + it("returns null for invalid values", () => { + expect(toCipherType(999 as any)).toBeUndefined(); + expect(toCipherType("Login" as any)).toBeUndefined(); + expect(toCipherType(null)).toBeUndefined(); + expect(toCipherType(undefined)).toBeUndefined(); + }); + }); +}); diff --git a/libs/common/src/vault/enums/cipher-type.ts b/libs/common/src/vault/enums/cipher-type.ts index 30d80cdef7..31fb72f477 100644 --- a/libs/common/src/vault/enums/cipher-type.ts +++ b/libs/common/src/vault/enums/cipher-type.ts @@ -1,9 +1,60 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CipherType { - Login = 1, - SecureNote = 2, - Card = 3, - Identity = 4, - SshKey = 5, +const _CipherType = Object.freeze({ + Login: 1, + SecureNote: 2, + Card: 3, + Identity: 4, + SshKey: 5, +} as const); + +type _CipherType = typeof _CipherType; + +export type CipherType = _CipherType[keyof _CipherType]; + +// FIXME: Update typing of `CipherType` to be `Record` which is ADR-0025 compliant when the TypeScript version is at least 5.8. +export const CipherType: typeof _CipherType = _CipherType; + +/** + * Reverse mapping of Cipher Types to their associated names. + * Prefer using {@link toCipherTypeName} rather than accessing this object directly. + * + * When represented as an enum in TypeScript, this mapping was provided + * by default. Now using a constant object it needs to be defined manually. + */ +export const cipherTypeNames = Object.freeze( + Object.fromEntries(Object.entries(CipherType).map(([key, value]) => [value, key])), +) as Readonly>; + +/** + * Returns the associated name for the cipher type, will throw when the name is not found. + */ +export function toCipherTypeName(type: CipherType): keyof typeof CipherType | undefined { + const name = cipherTypeNames[type]; + + return name; } + +/** + * @returns `true` if the value is a valid `CipherType`, `false` otherwise. + */ +export const isCipherType = (value: unknown): value is CipherType => { + return Object.values(CipherType).includes(value as CipherType); +}; + +/** + * Converts a value to a `CipherType` if it is valid, otherwise returns `null`. + */ +export const toCipherType = (value: unknown): CipherType | undefined => { + if (isCipherType(value)) { + return value; + } + + if (typeof value === "string") { + const valueAsInt = parseInt(value, 10); + + if (isCipherType(valueAsInt)) { + return valueAsInt; + } + } + + return undefined; +}; diff --git a/libs/common/src/vault/enums/field-type.enum.ts b/libs/common/src/vault/enums/field-type.enum.ts index df5016890b..0e8e2aaca3 100644 --- a/libs/common/src/vault/enums/field-type.enum.ts +++ b/libs/common/src/vault/enums/field-type.enum.ts @@ -1,8 +1,12 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum FieldType { - Text = 0, - Hidden = 1, - Boolean = 2, - Linked = 3, -} +const _FieldType = Object.freeze({ + Text: 0, + Hidden: 1, + Boolean: 2, + Linked: 3, +} as const); + +type _FieldType = typeof _FieldType; + +export type FieldType = _FieldType[keyof _FieldType]; + +export const FieldType: Record = _FieldType; diff --git a/libs/common/src/vault/enums/linked-id-type.enum.ts b/libs/common/src/vault/enums/linked-id-type.enum.ts index b329aecb3f..20ef15e620 100644 --- a/libs/common/src/vault/enums/linked-id-type.enum.ts +++ b/libs/common/src/vault/enums/linked-id-type.enum.ts @@ -1,46 +1,48 @@ +import { UnionOfValues } from "../types/union-of-values"; + export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; // LoginView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum LoginLinkedId { - Username = 100, - Password = 101, -} +export const LoginLinkedId = { + Username: 100, + Password: 101, +} as const; + +export type LoginLinkedId = UnionOfValues; // CardView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CardLinkedId { - CardholderName = 300, - ExpMonth = 301, - ExpYear = 302, - Code = 303, - Brand = 304, - Number = 305, -} +export const CardLinkedId = { + CardholderName: 300, + ExpMonth: 301, + ExpYear: 302, + Code: 303, + Brand: 304, + Number: 305, +} as const; + +export type CardLinkedId = UnionOfValues; // IdentityView -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum IdentityLinkedId { - Title = 400, - MiddleName = 401, - Address1 = 402, - Address2 = 403, - Address3 = 404, - City = 405, - State = 406, - PostalCode = 407, - Country = 408, - Company = 409, - Email = 410, - Phone = 411, - Ssn = 412, - Username = 413, - PassportNumber = 414, - LicenseNumber = 415, - FirstName = 416, - LastName = 417, - FullName = 418, -} +export const IdentityLinkedId = { + Title: 400, + MiddleName: 401, + Address1: 402, + Address2: 403, + Address3: 404, + City: 405, + State: 406, + PostalCode: 407, + Country: 408, + Company: 409, + Email: 410, + Phone: 411, + Ssn: 412, + Username: 413, + PassportNumber: 414, + LicenseNumber: 415, + FirstName: 416, + LastName: 417, + FullName: 418, +} as const; + +export type IdentityLinkedId = UnionOfValues; diff --git a/libs/common/src/vault/enums/secure-note-type.enum.ts b/libs/common/src/vault/enums/secure-note-type.enum.ts index 4fbd05e6bd..bb5838d028 100644 --- a/libs/common/src/vault/enums/secure-note-type.enum.ts +++ b/libs/common/src/vault/enums/secure-note-type.enum.ts @@ -1,5 +1,7 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecureNoteType { - Generic = 0, -} +import { UnionOfValues } from "../types/union-of-values"; + +export const SecureNoteType = { + Generic: 0, +} as const; + +export type SecureNoteType = UnionOfValues; diff --git a/libs/common/src/vault/models/data/cipher.data.ts b/libs/common/src/vault/models/data/cipher.data.ts index 1be70283fb..7554f23f6a 100644 --- a/libs/common/src/vault/models/data/cipher.data.ts +++ b/libs/common/src/vault/models/data/cipher.data.ts @@ -57,7 +57,7 @@ export class CipherData { this.organizationUseTotp = response.organizationUseTotp; this.favorite = response.favorite; this.revisionDate = response.revisionDate; - this.type = response.type; + this.type = response.type as CipherType; this.name = response.name; this.notes = response.notes; this.collectionIds = collectionIds != null ? collectionIds : response.collectionIds; diff --git a/libs/common/src/vault/models/response/cipher.response.ts b/libs/common/src/vault/models/response/cipher.response.ts index 944a19e088..d4c907ae2b 100644 --- a/libs/common/src/vault/models/response/cipher.response.ts +++ b/libs/common/src/vault/models/response/cipher.response.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { BaseResponse } from "../../../models/response/base.response"; +import { CipherType } from "../../enums"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; import { CardApi } from "../api/card.api"; import { CipherPermissionsApi } from "../api/cipher-permissions.api"; @@ -17,7 +18,7 @@ export class CipherResponse extends BaseResponse { id: string; organizationId: string; folderId: string; - type: number; + type: CipherType; name: string; notes: string; fields: FieldApi[]; diff --git a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts index c8c26266e6..44cb33bf65 100644 --- a/libs/common/src/vault/tasks/enums/security-task-status.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-status.enum.ts @@ -1,13 +1,15 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecurityTaskStatus { +import { UnionOfValues } from "../../types/union-of-values"; + +export const SecurityTaskStatus = { /** * Default status for newly created tasks that have not been completed. */ - Pending = 0, + Pending: 0, /** * Status when a task is considered complete and has no remaining actions */ - Completed = 1, -} + Completed: 1, +} as const; + +export type SecurityTaskStatus = UnionOfValues; diff --git a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts index 79a2d23c8b..36a3982c43 100644 --- a/libs/common/src/vault/tasks/enums/security-task-type.enum.ts +++ b/libs/common/src/vault/tasks/enums/security-task-type.enum.ts @@ -1,8 +1,10 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum SecurityTaskType { +import { UnionOfValues } from "../../types/union-of-values"; + +export const SecurityTaskType = { /** * Task to update a cipher's password that was found to be at-risk by an administrator */ - UpdateAtRiskCredential = 0, -} + UpdateAtRiskCredential: 0, +} as const; + +export type SecurityTaskType = UnionOfValues; diff --git a/libs/common/src/vault/types/union-of-values.ts b/libs/common/src/vault/types/union-of-values.ts new file mode 100644 index 0000000000..e7c721652e --- /dev/null +++ b/libs/common/src/vault/types/union-of-values.ts @@ -0,0 +1,2 @@ +/** Creates a union type consisting of all values within the record. */ +export type UnionOfValues> = T[keyof T]; diff --git a/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts index abda9a04a8..b900e9e8d7 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-csv-importer.ts @@ -44,7 +44,7 @@ export class BitwardenCsvImporter extends BaseImporter implements Importer { cipher.reprompt = parseInt( this.getValueOrDefault(value.reprompt, CipherRepromptType.None.toString()), 10, - ); + ) as CipherRepromptType; } catch (e) { // eslint-disable-next-line console.error("Unable to parse reprompt value", e); diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index adfa427c66..3789ee7536 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -21,7 +21,7 @@ import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.serv import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; import { CipherRequest } from "@bitwarden/common/vault/models/request/cipher.request"; import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -426,7 +426,7 @@ export class ImportService implements ImportServiceAbstraction { switch (key.match(/^\w+/)[0]) { case "Ciphers": item = importResult.ciphers[i]; - itemType = CipherType[item.type]; + itemType = toCipherTypeName(item.type); break; case "Folders": item = importResult.folders[i]; diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts index 52cb740ad0..c8edba6c9f 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts @@ -155,7 +155,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { // Populate options for linked custom fields this.linkedFieldOptions = optionsArray.map(([id, linkedFieldOption]) => ({ name: this.i18nService.t(linkedFieldOption.i18nKey), - value: id, + value: id as LinkedIdType, })); const prefillCipher = this.cipherFormContainer.getInitialCipherView(); diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts index 7eb3d37129..11c15f6350 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts @@ -4,6 +4,7 @@ import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { ButtonModule, DialogModule, @@ -24,13 +25,13 @@ export interface AttachmentsDialogParams { /** * Enum representing the possible results of the attachment dialog. */ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AttachmentDialogResult { - Uploaded = "uploaded", - Removed = "removed", - Closed = "closed", -} +export const AttachmentDialogResult = { + Uploaded: "uploaded", + Removed: "removed", + Closed: "closed", +} as const; + +export type AttachmentDialogResult = UnionOfValues; export interface AttachmentDialogCloseResult { action: AttachmentDialogResult; diff --git a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index bb79c7877a..381893d54a 100644 --- a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -19,6 +19,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { DIALOG_DATA, DialogRef, @@ -34,12 +35,12 @@ import { } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum AddEditFolderDialogResult { - Created = "created", - Deleted = "deleted", -} +export const AddEditFolderDialogResult = { + Created: "created", + Deleted: "deleted", +} as const; + +export type AddEditFolderDialogResult = UnionOfValues; export type AddEditFolderDialogData = { /** When provided, dialog will display edit folder variant */ diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 42e6d9c92c..124dc78303 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -40,6 +40,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { AsyncActionsModule, BitSubmitDirective, @@ -82,12 +83,12 @@ export interface CollectionAssignmentParams { isSingleCipherAdmin?: boolean; } -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum CollectionAssignmentResult { - Saved = "saved", - Canceled = "canceled", -} +export const CollectionAssignmentResult = { + Saved: "saved", + Canceled: "canceled", +} as const; + +export type CollectionAssignmentResult = UnionOfValues; const MY_VAULT_ID = "MyVault";