UNPKG

wcz-layout

Version:

1 lines 21.8 kB
{"version":3,"file":"utils-KaM5VMTJ.mjs","names":["OFFLINE_USER_KEY","OFFLINE_PHOTO_KEY","GRACE_PERIOD","Cached","value","T","savedAt","recall","key","localStorage","raw","getItem","JSON","parse","Date","now","removeItem","remember","undefined","setItem","stringify","isOfflineFailure","error","navigator","onLine","TypeError","withOfflineFallback","fetchFresh","Promise","fresh","clearSessionSnapshot","String","window","addEventListener","event","location","href","createEnv","z","clientEnv","clientPrefix","client","runtimeEnv","import","meta","env","emptyStringAsUndefined","serverEnv","server","ENTRA_CLIENT_ID","string","min","ENTRA_TENANT_ID","ENTRA_CLIENT_SECRET","AWS_ISSUER","url","optional","AWS_DOMAIN","AWS_CLIENT_ID","AWS_CLIENT_SECRET","SESSION_SECRET","FILE_BASE_URL","APPROVAL_BASE_URL","PEOPLESOFT_BASE_URL","EMAIL_BASE_URL","AI_BASE_URL","AI_SUBSCRIPTION_KEY","process","useSession","serverEnv","ProviderId","User","AppSessionData","provider","refreshToken","user","pkceVerifier","state","returnTo","AppSession","Awaited","ReturnType","getAppSession","Promise","getSession","name","password","SESSION_SECRET","queryOptions","createServerFn","QueryClientParam","ProviderId","User","OFFLINE_USER_KEY","withOfflineFallback","getAppSession","getSessionUser","method","handler","Promise","session","data","user","getLoginProviders","Array","id","label","providers","map","loginProvidersQueryOptions","queryKey","queryFn","staleTime","userQueryOptions","Infinity","networkMode","refetchOnReconnect","getUser","queryClient","import","meta","env","SSR","ensureQueryData","AnyFieldApi","redirect","ParsedLocation","manifest","permissions","QueryClientParam","TokenPayload","ProviderId","User","getUser","WISTRON_PRIMARY_COLOR","WISTRON_SECONDARY_COLOR","Platform","isAndroid","test","userAgent","isIOS","isWindows","isMacOS","navigator","RootRouteHeadOptions","rootRouteHead","options","meta","charSet","name","content","title","links","rel","sizes","href","type","GuardArgs","location","context","requireAuth","permissionKey","user","queryClient","encodeURIComponent","reloadDocument","hasPermission","Error","NON_PRODUCTION","isTestEnv","origin","split","slice","join","FormOmittedProps","getFieldStatus","field","state","isTouched","hasError","errors","length","helperText","message","sanitizeReturnTo","value","toKebabCase","str","replaceAll","toLowerCase","foldForSearch","String","normalize","permissionGroups","Set","Object","values","flat","buildUser","payload","provider","email","preferred_username","department","toUpperCase","employeeId","companyName","groups","filter","group","has","key","some","includes"],"sources":["../src/lib/auth/offline.ts","../src/env.ts","../src/lib/auth/session.ts","../src/lib/auth/user.ts","../src/lib/utils.ts"],"sourcesContent":["/**\n * Offline fallback for identity. The refresh token never leaves the sealed\n * httpOnly cookie — what is cached here is only the display snapshot (user\n * claims and avatar) so the UI and offline route permissions can still work\n * while the server is unreachable. Server requests remain independently\n * authorized. At-rest protection is the device's OS login and disk encryption.\n */\n\nexport const OFFLINE_USER_KEY = \"auth-user\";\nexport const OFFLINE_PHOTO_KEY = \"auth-photo\";\n\n/** One shift — matches Intune's default offline grace period. */\nconst GRACE_PERIOD = 12 * 60 * 60 * 1000;\n\ntype Cached<T> = { value: T; savedAt: number };\n\nconst recall = <T>(key: string): T | null => {\n if (typeof localStorage === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n const { value, savedAt } = JSON.parse(raw) as Cached<T>;\n if (Date.now() - savedAt > GRACE_PERIOD) {\n localStorage.removeItem(key);\n return null;\n }\n return value;\n } catch {\n localStorage.removeItem(key);\n return null;\n }\n};\n\nconst remember = (key: string, value: unknown): void => {\n if (typeof localStorage === \"undefined\") return;\n try {\n if (value === null || value === undefined) localStorage.removeItem(key);\n else localStorage.setItem(key, JSON.stringify({ value, savedAt: Date.now() }));\n } catch {\n // Storage full or blocked — the offline fallback is best-effort.\n }\n};\n\nconst isOfflineFailure = (error: unknown): boolean =>\n (typeof navigator !== \"undefined\" && !navigator.onLine) || error instanceof TypeError;\n\n/**\n * Answers from the server when reachable, remembering the result; when the call\n * has a transport failure, replays the last result instead, for up to\n * {@link GRACE_PERIOD} since it was last confirmed.\n *\n * Cached values are untrusted client state. Server authorization must never\n * depend on them.\n */\nexport const withOfflineFallback = async <T>(\n key: string,\n fetchFresh: () => Promise<T>,\n): Promise<T | null> => {\n try {\n const fresh = await fetchFresh();\n remember(key, fresh);\n return fresh;\n } catch (error) {\n if (!isOfflineFailure(error)) throw error;\n return recall<T>(key);\n }\n};\n\n/** Drops the cached identity. Call on logout, before leaving the page. */\nexport const clearSessionSnapshot = (): void => {\n if (typeof localStorage === \"undefined\") return;\n localStorage.removeItem(OFFLINE_USER_KEY);\n localStorage.removeItem(OFFLINE_PHOTO_KEY);\n localStorage.setItem(\"auth-logout\", String(Date.now()));\n};\n\nif (typeof window !== \"undefined\")\n window.addEventListener(\"storage\", (event) => {\n if (event.key === \"auth-logout\") location.href = \"/auth/logout\";\n });\n","import { createEnv } from \"@t3-oss/env-core\";\nimport { z } from \"zod\";\n\nexport const clientEnv = createEnv({\n clientPrefix: \"VITE_\",\n client: {},\n runtimeEnv: import.meta.env,\n emptyStringAsUndefined: true,\n});\n\nexport const serverEnv = createEnv({\n server: {\n ENTRA_CLIENT_ID: z.string().min(1),\n ENTRA_TENANT_ID: z.string().min(1),\n ENTRA_CLIENT_SECRET: z.string().min(1),\n AWS_ISSUER: z.url().optional(),\n AWS_DOMAIN: z.url().optional(),\n AWS_CLIENT_ID: z.string().min(1).optional(),\n AWS_CLIENT_SECRET: z.string().min(1).optional(),\n SESSION_SECRET: z.string().min(32),\n FILE_BASE_URL: z.string().min(1).optional(),\n APPROVAL_BASE_URL: z.string().min(1).optional(),\n PEOPLESOFT_BASE_URL: z.string().min(1).optional(),\n EMAIL_BASE_URL: z.string().min(1).optional(),\n AI_BASE_URL: z.string().min(1).optional(),\n AI_SUBSCRIPTION_KEY: z.string().min(1).optional(),\n },\n runtimeEnv: process.env,\n emptyStringAsUndefined: true,\n});\n","import type { useSession } from \"@tanstack/react-start/server\";\r\nimport { serverEnv } from \"~/env\";\r\nimport type { ProviderId, User } from \"~/models/User\";\r\n\r\nexport interface AppSessionData {\r\n /** Which identity provider signed this user in. */\r\n provider?: ProviderId;\r\n /** The only token persisted; delegated access tokens are minted from it. */\r\n refreshToken?: string;\r\n user?: User;\r\n /** Transient, only between /auth/login and /auth/callback. */\r\n pkceVerifier?: string;\r\n state?: string;\r\n returnTo?: string;\r\n}\r\n\r\n/** `SessionManager` is not re-exported upstream, so name it through `useSession`. */\r\nexport type AppSession = Awaited<ReturnType<typeof useSession<AppSessionData>>>;\r\n\r\nexport const getAppSession = async (): Promise<AppSession> => {\r\n // Dynamic import: `@tanstack/react-start/server` is the server entry and drags\r\n // in the SSR renderer (`react-dom/server`). Keep it out of the client graph —\r\n // this helper is only ever called server-side, and all callers already await it.\r\n // Aliased off the `use*` name so it isn't mistaken for a React hook (it's a\r\n // server session helper, not a hook).\r\n const { useSession: getSession } = await import(\"@tanstack/react-start/server\");\r\n\r\n return getSession<AppSessionData>({\r\n name: \"session\",\r\n password: serverEnv.SESSION_SECRET,\r\n });\r\n};\r\n","import { queryOptions } from \"@tanstack/react-query\";\nimport { createServerFn } from \"@tanstack/react-start\";\nimport type { QueryClientParam } from \"~/models/QueryClientParam\";\nimport type { ProviderId, User } from \"~/models/User\";\nimport { OFFLINE_USER_KEY, withOfflineFallback } from \"./offline\";\nimport { getAppSession } from \"./session\";\n\n/**\n * Reads the signed-in user from the session cookie, or null. As a server function\n * it runs in-process when called on the server (SSR, middleware) and as an RPC\n * when called from the client — so it doubles as the client `queryFn`.\n */\nexport const getSessionUser = createServerFn({ method: \"GET\" }).handler(\n async (): Promise<User | null> => {\n const session = await getAppSession();\n return session.data.user ?? null;\n },\n);\n\n/** The identity providers the server offers, for the login page. */\nexport const getLoginProviders = createServerFn({ method: \"GET\" }).handler(\n async (): Promise<Array<{ id: ProviderId; label: string }>> => {\n // Dynamic import: `providers` reads `serverEnv` and must not reach the client.\n const { providers } = await import(\"./providers\");\n return providers.map(({ id, label }) => ({ id, label }));\n },\n);\n\n/**\n * Feeds `LoginForm`. Server config, not per-user, so it is safe to prime on the\n * server — the `/login` route's loader does that to avoid a flash of empty card.\n */\nexport const loginProvidersQueryOptions = queryOptions({\n queryKey: [\"auth\", \"providers\"],\n queryFn: () => getLoginProviders(),\n staleTime: \"static\",\n});\n\n/**\n * Client-side query for the signed-in user. Shared between `getUser` (route\n * `beforeLoad`) and the always-mounted observer in `LayoutProvider` — the\n * observer is what seeds the offline snapshot on the very first page load\n * (`beforeLoad` does not re-run after SSR hydration) and what makes\n * `refetchOnReconnect` fire once connectivity returns.\n */\nexport const userQueryOptions = queryOptions({\n queryKey: [\"auth\", \"user\"],\n queryFn: () => withOfflineFallback(OFFLINE_USER_KEY, () => getSessionUser()),\n staleTime: Infinity,\n // Default \"online\" pauses the query (never calling queryFn) once the browser\n // reports no connection — which is exactly when the fallback is needed.\n networkMode: \"offlineFirst\",\n // Back online: re-confirm the session and re-stamp the snapshot's grace period.\n refetchOnReconnect: \"always\",\n});\n\nexport const getUser = ({ queryClient }: QueryClientParam): Promise<User | null> => {\n if (import.meta.env.SSR) return getSessionUser();\n\n return queryClient.ensureQueryData(userQueryOptions);\n};\n","import type { AnyFieldApi } from \"@tanstack/react-form\";\nimport { redirect } from \"@tanstack/react-router\";\nimport type { ParsedLocation } from \"@tanstack/react-router\";\nimport { manifest, permissions } from \"virtual:wcz-layout\";\nimport type { QueryClientParam } from \"~/models/QueryClientParam\";\nimport type { TokenPayload } from \"~/models/TokenPayload\";\nimport type { ProviderId, User } from \"~/models/User\";\nimport { getUser } from \"./auth/user\";\n\nexport const WISTRON_PRIMARY_COLOR = \"#00506E\";\nexport const WISTRON_SECONDARY_COLOR = \"#64DC00\";\n\nexport class Platform {\n static get isAndroid() {\n return /android/i.test(this.userAgent);\n }\n static get isIOS() {\n return /iPad|iPhone|iPod/.test(this.userAgent);\n }\n static get isWindows() {\n return /windows/i.test(this.userAgent);\n }\n static get isMacOS() {\n return /Macintosh|MacIntel|MacPPC|Mac68K/.test(this.userAgent);\n }\n\n private static get userAgent() {\n return typeof navigator === \"undefined\" ? \"\" : navigator.userAgent;\n }\n}\n\ninterface RootRouteHeadOptions {\n manifest?: string;\n}\n\nexport const rootRouteHead = (options?: RootRouteHeadOptions) => ({\n meta: [\n { charSet: \"utf-8\" },\n { name: \"viewport\", content: \"width=device-width, initial-scale=1\" },\n { title: manifest.name },\n { name: \"og:type\", content: \"website\" },\n { name: \"og:title\", content: manifest.name },\n { name: \"og:image\", content: \"/favicon-32x32.png\" },\n ],\n links: [\n { rel: \"apple-touch-icon\", sizes: \"180x180\", href: \"/apple-touch-icon.png\" },\n { rel: \"icon\", type: \"image/png\", sizes: \"32x32\", href: \"/favicon-32x32.png\" },\n { rel: \"icon\", type: \"image/png\", sizes: \"16x16\", href: \"/favicon-16x16.png\" },\n { rel: \"manifest\", href: options?.manifest || \"/manifest.json\" },\n { rel: \"icon\", href: \"/favicon.ico\" },\n ],\n});\n\ntype GuardArgs = { location: ParsedLocation; context: QueryClientParam };\n\n/**\n * Route guard for `beforeLoad`. Routes are public by default; add this to close one.\n *\n * - `requireAuth()` — signed in with any provider. The only gate a population\n * without permission groups (e.g. AWS users) can pass.\n * - `requireAuth(\"admin\")` — signed in *and* in a group listed under that key.\n *\n * `reloadDocument` is required: `/auth/login` is a component-less server route and\n * must be reached with a full-document navigation. It starts the login directly when\n * only one provider is registered, otherwise it forwards to the `/login` chooser.\n */\nexport const requireAuth = (permissionKey?: keyof typeof permissions) => {\n return async ({ location, context }: GuardArgs) => {\n const user = await getUser({ queryClient: context.queryClient });\n\n if (!user)\n throw redirect({\n href: `/auth/login?returnTo=${encodeURIComponent(location.href)}`,\n reloadDocument: true,\n });\n\n if (permissionKey && !hasPermission(user, permissionKey))\n throw new Error(\"You do not have permission to access this page.\");\n\n return { user };\n };\n};\n\n/* Internal Utils */\nconst NON_PRODUCTION = /(?:^|[.-])(?:dev|qas|uat|test)(?:[.-]|$)/i;\n\n/** True only for a dev/qas/uat/test domain — localhost and production are not. */\nexport const isTestEnv = (origin: string | undefined) =>\n NON_PRODUCTION.test((origin ?? \"\").split(\".\").slice(1).join(\".\"));\n\nexport type FormOmittedProps =\n | \"name\"\n | \"value\"\n | \"onChange\"\n | \"onBlur\"\n | \"error\"\n | \"helperText\"\n | \"renderInput\"\n | \"type\"\n | \"aria-label\";\n\nexport const getFieldStatus = (field: AnyFieldApi) => {\n const { meta } = field.state;\n\n const isTouched = meta.isTouched;\n const hasError = !!meta.errors.length;\n const helperText = meta.errors[0]?.message;\n\n return { isTouched, hasError, helperText };\n};\n\n/**\n * Only same-origin paths are allowed, so `returnTo` cannot become an open\n * redirect. Backslash counts as a separator too: browsers normalize `/\\evil`\n * to the protocol-relative `//evil`.\n */\nexport const sanitizeReturnTo = (value: string | null | undefined): string =>\n value && /^\\/(?![/\\\\])/.test(value) ? value : \"/\";\n\nexport const toKebabCase = (str: string): string => {\n return str\n .replaceAll(/([a-z])([A-Z])/g, \"$1-$2\")\n .replaceAll(/[\\s_]+/g, \"-\")\n .replaceAll(/[^a-zA-Z0-9-]/g, \"\")\n .toLowerCase()\n .replaceAll(/-+/g, \"-\")\n .replaceAll(/(^-|-$)/g, \"\");\n};\n\nexport const foldForSearch = (value: unknown): string =>\n String(value ?? \"\")\n .toLowerCase()\n .normalize(\"NFD\")\n .replaceAll(/[\\u{300}-\\u{36F}]/gu, \"\")\n .normalize(\"NFC\");\n\nconst permissionGroups = new Set<string>(Object.values(permissions).flat());\n\n/**\n * Maps id/access-token claims to the serializable `User`. Groups are trimmed to\n * those named in `permissions.ts` — an unfiltered group list would blow the\n * cookie. Cognito's claim names differ, so both are read.\n */\nexport const buildUser = (payload: TokenPayload, provider: ProviderId): User => ({\n provider,\n name: (payload.name ?? payload[\"cognito:username\"] ?? \"\").split(\"/\")[0],\n email: (payload.preferred_username ?? payload.email ?? \"\").toLowerCase(),\n department: payload.department?.toUpperCase() || \"\",\n employeeId: (payload.employeeId ?? payload[\"cognito:username\"] ?? \"\").toUpperCase(),\n companyName: payload.companyName || \"\",\n groups:\n (payload.groups ?? payload[\"cognito:groups\"])?.filter((group) => permissionGroups.has(group)) ??\n [],\n});\n\nexport const hasPermission = (user: User | null, key: keyof typeof permissions): boolean =>\n user ? permissions[key].some((group) => user.groups.includes(group)) : false;\n"],"mappings":"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"}