1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +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

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