Stores and Types
Store Exports
useAppStore— authentication state, current user info, menu maps, and permission tokens; persisted (isAuthenticated/custom/authTokenssurvive reload).useTabStore— the open-tabs list backing the multi-tab layout; persisted.useThemeStore— the user's picked color scheme and semantic colors, menu layout mode, and layout toggles; persisted. Unset color and color-scheme values follow the application'sdefaultTheme.
All three are created with createPersistedStore (see Store and Atom) — their state types do not need a name field; the storage key comes from the persistence options instead.
Related types:
AppStateTabTabStateColorScheme—"system" | "light" | "dark"ThemeColors—Record<SemanticColor, string>DefaultTheme—{ colorScheme?: ColorScheme; colors?: Partial<ThemeColors> }MenuLayoutMode—"vertical" | "horizontal" | "mixed"ThemeState
API and Domain Types
Entity identity:
Entity<TId = string>— base interface that only carriesid
Standalone audit field interfaces (use these for composite primary keys or non-id keyed records):
CreationTracked<TId, TDate>—createdAt/createdBy/createdByNameFullTracked<TId, TDate>— extendsCreationTrackedwithupdatedAt/updatedBy/updatedByName
Composed entity interfaces:
CreationAuditedEntity<TId, TDate>—Entity+CreationTrackedFullAuditedEntity<TId, TDate>—Entity+FullTracked
Batch parameter helper:
Many<T>— wraps alist: T[]for batch create / update payloads
User and Menu Types
Gender—"male" | "female" | "unknown"UserMenuType—"directory" | "menu" | "view" | "report", open to project-specific extension viaLiteralUnionUserMenu—type/path/name/icon?/meta?/children?UserMenuMeta—params?: Record<string, string>andsearch?: Record<string, string>bound to a menu's route, plus any keys augmented ontoRegister['menuMeta']UserInfo—detailsis typed via theRegisteraugmentation point (see below)AppCustomState— the shape ofAppState.custom; defaults toAnyObject, narrowed viaRegister['appCustomState']ChallengeSpec—{ data?: unknown; response: unknown }, the contract for one login challenge typeResolvedChallenges— the challenge-type registry resolved fromRegister['challenges'], or an openRecord<string, ChallengeSpec>when not augmentedRegister— empty interface that projects augment viadeclare moduleto refineUserInfo['details'],AppState.custom,UserMenuMeta,loginParams, and the login challenge registry. For the built-inpassword_changechallenge, registerPasswordChangeChallengeSpecunderPASSWORD_CHANGE_CHALLENGE_TYPE— see Login challengesLoginParams—PasswordLoginParams | TrustCodeLoginParamsplus project-registered login mechanismsPasswordLoginParams—{ type: "password"; principal: string; credentials: string }TrustCodeLoginParams—{ type: "trust_code"; principal: string; credentials: string };principalis the initiating app idUseLoginFlowOptions— the options accepted byuseLoginFlowSsoLoginFlow— the state returned byuseSsoLoginLoginChallengeAutoResolver/LoginChallengeAutoResolvers— optional per-type callbacks that answer a challenge from data the page already holdsUserDetails— fallback shape (Record<string, unknown>) whenRegister['userDetails']is not augmentedOrderSpec—{ column: string; direction: "asc" | "desc" }
department_selection Challenge
The built-in department_selection login challenge carries a free-form meta field on both the challenge payload and each department entry. The framework itself only transports this data; the project declares the keys the login screen actually reads. There is no exported DepartmentSelectionChallengeMeta type — the shape is declared through Register['challenges'] augmentation, and ResolvedChallenges carries it.
Each department entry has an optional meta?: { orgId?: string; parentId?: string } — orgId identifies which organization owns the department, and parentId enables rendering a tree. The challenge-level meta carries organizations — the organization hierarchy the departments hang off — as meta?: { organizations?: Array<{ id: string; name: string; parentId?: string }> }.
Organizations live in the challenge-level meta rather than as extra departments entries because every entry in the departments list is selectable — a grouping node listed among them would become a choosable one.
declare module "@vef-framework-react/starter" {
interface Register {
challenges: {
department_selection: {
data: {
departments: Array<{
id: string;
name: string;
meta?: { orgId?: string; parentId?: string };
}>;
meta?: {
organizations?: Array<{ id: string; name: string; parentId?: string }>;
};
};
response: string;
};
};
}
}
Extending UserInfo.details
UserInfo.details resolves through the Register interface, mirroring the pattern used by @tanstack/react-query for mutationMeta. Augment it once in your project to get strongly typed user attributes everywhere UserInfo flows through the app:
// src/types/vef-augment.d.ts
declare module "@vef-framework-react/starter" {
interface Register {
userDetails: {
department: string;
organization: string;
};
}
}
After this declaration, userInfo.details.department is typed as string across the application without forking the framework. The same Register interface also accepts appCustomState, menuMeta, and challenges members for the other extension points above.
Router Types
RouterContext
Query Helpers
extractQueryParamsnoopMutationFn