1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[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`
This commit is contained in:
Nick Krantz
2025-06-05 08:45:52 -05:00
committed by GitHub
parent 7f72396cb2
commit 729d5d3134
45 changed files with 404 additions and 248 deletions

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -54,4 +54,7 @@ export type FormFieldElement = FillableFormFieldElement | HTMLSpanElement;
export type FormElementWithAttribute = FormFieldElement & Record<string, string | null | undefined>;
export type AutofillCipherTypeId = CipherType.Login | CipherType.Card | CipherType.Identity;
export type AutofillCipherTypeId =
| typeof CipherType.Login
| typeof CipherType.Card
| typeof CipherType.Identity;

View File

@@ -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<typeof AtRiskCarouselDialogResult>;
@Component({
selector: "vault-at-risk-carousel-dialog",

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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<typeof GeneratorDialogAction>;
@Component({
selector: "app-vault-generator-dialog",

View File

@@ -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<typeof VaultState>;
@Component({
selector: "app-vault",

View File

@@ -319,13 +319,13 @@ export class VaultPopupItemsService {
* @private
*/
private sortCiphersForAutofill(a: CipherView, b: CipherView): number {
const typeOrder: Record<CipherType, number> = {
const typeOrder = {
[CipherType.Login]: 1,
[CipherType.Card]: 2,
[CipherType.Identity]: 3,
[CipherType.SecureNote]: 4,
[CipherType.SshKey]: 5,
};
} as Record<CipherType, number>;
// Compare types first
if (typeOrder[a.type] < typeOrder[b.type]) {

View File

@@ -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<typeof CredentialGeneratorDialogAction>;
@Component({
selector: "credential-generator-dialog",

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)),
),
);
}

View File

@@ -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>(BrowserPromptState.Loading);
const setAttribute = jest.fn();
const getAttribute = jest.fn().mockReturnValue("width=1010");

View File

@@ -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<boolean>;
}
// 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<typeof VaultItemDialogResult>;
@Component({
selector: "app-vault-item-dialog",

View File

@@ -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<typeof WebVaultGeneratorDialogAction>;
@Component({
selector: "web-vault-generator-dialog",

View File

@@ -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<typeof AddEditCipherDialogResult>;
/**
* The close result of the AddEditCipherDialogV2 component.

View File

@@ -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<typeof BulkDeleteDialogResult>;
/**
* Strongly typed helper to open a BulkDeleteDialog

View File

@@ -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<typeof BulkMoveDialogResult>;
/**
* Strongly typed helper to open a BulkMoveDialog

View File

@@ -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<typeof FolderAddEditDialogResult>;
/**
* Strongly typed helper to open a FolderAddEdit dialog

View File

@@ -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<typeof VisibleVaultBanner>;
type PremiumBannerReprompt = {
numberOfDismissals: number;
@@ -34,7 +35,7 @@ type PremiumBannerReprompt = {
};
/** Banners that will be re-shown on a new session */
type SessionBanners = Omit<VisibleVaultBanner, VisibleVaultBanner.Premium>;
type SessionBanners = Omit<VisibleVaultBanner, typeof VisibleVaultBanner.Premium>;
export const PREMIUM_BANNER_REPROMPT_KEY = new UserKeyDefinition<PremiumBannerReprompt>(
PREMIUM_BANNER_DISK_LOCAL,

View File

@@ -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) {

View File

@@ -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<typeof VaultFilterLabel>;
export type VaultFilterSection = {
data$: Observable<TreeNode<VaultFilterType>>;

View File

@@ -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;
}

View File

@@ -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<typeof ViewCipherDialogResult>;
export interface ViewCipherDialogCloseResult {
action: ViewCipherDialogResult;

View File

@@ -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<typeof BrowserPromptState>;
type PromptErrorStates = typeof BrowserPromptState.Error | typeof BrowserPromptState.ManualOpen;
@Injectable({
providedIn: "root",

View File

@@ -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 {

View File

@@ -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<typeof NudgeType>;
export const NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition<
Partial<Record<NudgeType, NudgeStatus>>

View File

@@ -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<typeof CipherRepromptType>;

View File

@@ -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();
});
});
});

View File

@@ -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<keyof _CipherType, CipherType>` 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<Record<CipherType, keyof typeof CipherType>>;
/**
* 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;
};

View File

@@ -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<keyof _FieldType, FieldType> = _FieldType;

View File

@@ -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<typeof LoginLinkedId>;
// 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<typeof CardLinkedId>;
// 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<typeof IdentityLinkedId>;

View File

@@ -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<typeof SecureNoteType>;

View File

@@ -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;

View File

@@ -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[];

View File

@@ -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<typeof SecurityTaskStatus>;

View File

@@ -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<typeof SecurityTaskType>;

View File

@@ -0,0 +1,2 @@
/** Creates a union type consisting of all values within the record. */
export type UnionOfValues<T extends Record<string, unknown>> = T[keyof T];

View File

@@ -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);

View File

@@ -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];

View File

@@ -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();

View File

@@ -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<typeof AttachmentDialogResult>;
export interface AttachmentDialogCloseResult {
action: AttachmentDialogResult;

View File

@@ -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<typeof AddEditFolderDialogResult>;
export type AddEditFolderDialogData = {
/** When provided, dialog will display edit folder variant */

View File

@@ -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<typeof CollectionAssignmentResult>;
const MY_VAULT_ID = "MyVault";