Skip to main content
Glama
feature_flags.store.ts4.66 kB
import { defineStore } from "pinia"; import * as _ from "lodash-es"; import { addStoreHooks } from "@si/vue-lib/pinia"; import { posthog } from "@/utils/posthog"; type FeatureFlag = UserFlag | WorkspaceFlag; type UserFlag = keyof typeof USER_FLAG_MAPPING; type WorkspaceFlag = keyof typeof WORKSPACE_FLAG_MAPPING; // translation from store key to posthog feature flag name const USER_FLAG_MAPPING = { // STORE_FLAG_NAME: "posthogFlagName", MODULES_TAB: "modules_tab", ADMIN_PANEL_ACCESS: "si_admin_panel_access", PRIVATE_SCOPED_MODULES: "private-scoped-modules", SIMPLE_SOCKET_UI: "simple-socket-ui", SQLITE_TOOLS: "sqlite-tools", COMPONENT_HISTORY_FUNCS: "component-history-funcs", GOOGLE_CLOUD_UI: "google-cloud-ui", SHOW_WS_DISCONNECT: "show-ws-disconnect", } as const; const WORKSPACE_FLAG_MAPPING: Record<string, string> = { // STORE_FLAG_NAME: "posthogFlagName", }; // List of all feature flags const FEATURE_FLAGS = Object.keys({ ...USER_FLAG_MAPPING, ...WORKSPACE_FLAG_MAPPING, }) as FeatureFlag[]; export function useFeatureFlagsStore() { // const route = useRoute(); // const workspacePk = route?.params?.workspacePk as WorkspacePk | undefined; // once the old UI is deleted, replace this with the commented out code above const workspacePk = window.location.pathname.split("/")[2]; return addStoreHooks( undefined, undefined, defineStore("feature-flags", { // all flags default to undefined, but we put entries in the feature flags anyway state: () => Object.fromEntries( FEATURE_FLAGS.map((flag) => [flag, undefined]), ) as Record<FeatureFlag, boolean | undefined>, getters: { allFeatureFlags: (state) => FEATURE_FLAGS.map((name) => ({ name, value: state[name] })), }, actions: { /** * Sets flags * * NOTE: This is deliberately not async, so that all flags are set at the same time and * there is no UI "flicker" if (for example) posthog has a flag as false but then we * set an override. * * DO NOT set feature flags anywhere else but here. * * @param featureFlags - Set of general feature flags from posthog * @param workspaceFlags - Set of workspace-specific feature flags from posthog */ setFlags(featureFlags: Set<string>, workspaceFlags: Set<string>) { // Set the flags! for (const [flag, phFlag] of Object.entries(USER_FLAG_MAPPING)) { this[flag] = featureFlags.has(phFlag); } for (const [flag, phFlag] of Object.entries(WORKSPACE_FLAG_MAPPING)) { this[flag] = workspaceFlags.has(phFlag); } // You can override feature flags while working on a feature by setting them to true/false here // for example: // this.FEATURE_FLAG_NAME ??= false; // } }, /** * Fetches workspace-specific feature flags * * If the response is not ok, it return an empty array. * @returns */ async fetchWorkspaceFlags(): Promise<string[]> { if (!workspacePk) return []; try { const resp = await fetch( `${import.meta.env.VITE_POSTHOG_API_HOST}/decide/?v=3`, { method: "POST", body: JSON.stringify({ api_key: import.meta.env.VITE_POSTHOG_PUBLIC_KEY, distinct_id: workspacePk, }), }, ); if (!resp.ok) { // TODO probably should just throw here // eslint-disable-next-line no-console console.error( `Error retrieving workspace-specific flags: ${resp}`, ); return []; } const json = await resp.json(); return Object.keys(json.featureFlags); } catch (err) { reportError(err); return []; } }, }, async onActivated() { // Grab workspace-specific flags once, and listen for feature flag changes from posthog const workspaceFlags = this.fetchWorkspaceFlags(); // TODO remove feature flag listener on deactivate posthog.onFeatureFlags(async (flags) => { try { this.setFlags(new Set(flags), new Set(await workspaceFlags)); } catch (e) { // eslint-disable-next-line no-console console.error("Error setting feature flags", e); throw e; } }); }, }), )(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server