-
Notifications
You must be signed in to change notification settings - Fork 611
feat: add a way to bypass feature flags #2771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import type { Workspace } from "@unkey/db/src/types"; | ||
| import { type ClassValue, clsx } from "clsx"; | ||
| import { twMerge } from "tailwind-merge"; | ||
|
|
||
|
|
@@ -128,3 +129,64 @@ export function throttle<T extends (...args: any[]) => any>( | |
|
|
||
| return throttled; | ||
| } | ||
|
|
||
| type WorkspaceFeatures = Pick<Workspace, "features" | "betaFeatures">; | ||
|
|
||
| type ConfigObject = WorkspaceFeatures["betaFeatures"] & WorkspaceFeatures["features"]; | ||
|
|
||
| type FlagValue<T extends keyof ConfigObject> = NonNullable<ConfigObject[T]>; | ||
|
|
||
| /** | ||
| * Checks if a workspace has access to a specific feature or beta feature. | ||
| * In development environment, returns devModeDefault value or true if not specified. | ||
| * In production, returns the feature value if set, otherwise returns undefined. | ||
| * | ||
| * @param workspace - The workspace to check access for | ||
| * @param flagName - The name of the feature to check | ||
| * @param options - Configuration options | ||
| * @param options.devModeDefault - Value to return in development environment. Defaults to true if not specified. | ||
| * For boolean flags, you don't need to specify true as it's the default. | ||
| * @returns The feature value (boolean | number | string) or undefined if not set in production | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Check if workspace has access to logs page (boolean flag) | ||
| * const hasLogsAccess = getFlag(workspace, "logsPage"); | ||
| * if (!hasLogsAccess) { | ||
| * return notFound(); | ||
| * } | ||
| * | ||
| * // Check feature with numeric value | ||
| * const userLimit = getFlag(workspace, "userLimit", { | ||
| * devModeDefault: 1000 | ||
| * }); | ||
| * | ||
| * // Check feature with string value | ||
| * const tier = getFlag(workspace, "serviceTier", { | ||
| * devModeDefault: "premium" | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export function getFlag<TFlagName extends keyof ConfigObject>( | ||
| workspace: Partial<WorkspaceFeatures>, | ||
| flagName: TFlagName, | ||
| options: { devModeDefault?: FlagValue<TFlagName> } = {}, | ||
| ): FlagValue<TFlagName> | undefined { | ||
| if (process.env.NODE_ENV === "development") { | ||
| return options.devModeDefault ?? (true as FlagValue<TFlagName>); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still not really happy with this, cause some poor engineer in the future will run into an issue where they expect a number, typescript tells them it's a number, but actually the value is a boolean :/
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should only be evaluated as boolean if nothing passed here |
||
| } | ||
|
|
||
| if (!workspace) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be before the development check, cause if the workspace doesn't exist, we have bigger problems and we should not mask that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I agree let's check this first. |
||
| throw new Error( | ||
| "Cannot get feature flag: No workspace found in database. Please verify workspace exists in the database or create a new workspace record.", | ||
| ); | ||
| } | ||
|
|
||
| const betaFeature = workspace.betaFeatures?.[flagName as keyof Workspace["betaFeatures"]]; | ||
| if (betaFeature != null) { | ||
| return betaFeature as FlagValue<TFlagName> | undefined; | ||
| } | ||
|
|
||
| const feature = workspace.features?.[flagName as keyof Workspace["features"]]; | ||
| return feature as FlagValue<TFlagName> | undefined; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.