DocDoeAI / src /lib /api /mode.ts
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
2.84 kB
import { isApiError } from "@/lib/api/client";
import { getAuthToken, isDemoPreviewSession } from "@/lib/auth/auth-client";
import { getPublicDemoMode } from "@/lib/env";
export function isLocalResourceId(id: string | null | undefined) {
if (!id) return false;
if (id.startsWith("doc-local-") || id.startsWith("paper-local-") || id.startsWith("job-local-") || id.startsWith("render-local-")) {
return true;
}
return !/^(doc|paper|vjob|usr)_[a-z0-9]+$/i.test(id);
}
export function hasLiveSession() {
return Boolean(getAuthToken()) && !isDemoPreviewSession();
}
/**
* Returns true when the frontend should use local mock data instead of calling the API.
*
* In "demo" mode: always use demo data.
* In "live" mode: never use demo data.
* In "auto" mode: use demo data only for local resource IDs (doc-local-xxx).
*/
export function shouldUseDemoData(resourceId?: string | null) {
if (isDemoPreviewSession()) return true;
const mode = getPublicDemoMode();
if (mode === "demo") return true;
if (mode === "live") return false;
// "auto" β€” use demo data only for local IDs (offline-created resources)
if (resourceId && isLocalResourceId(resourceId)) return true;
return false;
}
/**
* Whether to silently fall back to demo data on API errors.
*
* Only returns true in explicit "demo" mode, or in "auto" mode when
* no auth token exists (i.e., no backend connection has been established).
*
* When a real session exists (token present), errors must surface to the
* user so they know something failed β€” not silently replaced with fakes.
*/
function shouldFallbackOnError(): boolean {
if (isDemoPreviewSession()) return true;
const mode = getPublicDemoMode();
if (mode === "demo") return true;
if (mode === "live") return false;
// "auto" β€” only fall back if there is no live session (no token = no backend)
return !hasLiveSession();
}
/**
* Wraps an API call with a fallback. In production (live/auto with token),
* errors are propagated to the UI so students see real error states.
* Only in demo mode (or auto without a backend) are errors silently caught.
*/
export async function withDemoFallback<T>(
request: () => Promise<T>,
fallback: () => Promise<T> | T,
): Promise<T> {
try {
return await request();
} catch (error) {
// Auth errors ALWAYS re-throw regardless of mode
if (isApiError(error) && error.isAuthError) {
throw error;
}
// In demo-only mode (no real backend), silently fall back
if (shouldFallbackOnError()) {
const isRecoverable =
(isApiError(error) && (error.status === null || error.status >= 500)) ||
error instanceof TypeError;
if (isRecoverable) {
return fallback();
}
}
// In production: propagate the error so the UI shows a real error state
throw error;
}
}