import type { ArchivePlatesResponse, LibraryFilePlatesResponse } from '../types/plates';
const API_BASE = '/api/v1';
export class ApiError extends Error {
status: number;
/** Stable error code from a structured backend detail (`{code, message}`).
* Frontend uses this to look up an i18n key instead of showing the raw
* English fallback. Null when the backend returned a plain-string detail. */
code: string | null;
constructor(message: string, status: number, code: string | null = null) {
super(message);
this.name = 'ApiError';
this.status = status;
this.code = code;
}
}
// Auth token storage
// By default tokens are stored in sessionStorage (tab-scoped, cleared on close).
// When the token originates from the ?token= URL param (kiosk bootstrap), it is
// additionally persisted in localStorage so the kiosk survives page reloads.
// 'persistent' also writes to localStorage so the token survives tab close
// (used by Remember Me and the ?token= kiosk bootstrap).
let authToken: string | null =
sessionStorage.getItem('auth_token') ?? localStorage.getItem('auth_token');
export type TokenPersistence = 'session' | 'persistent';
export function setAuthToken(token: string | null, persistence: TokenPersistence = 'session') {
authToken = token;
try {
if (token) {
sessionStorage.setItem('auth_token', token);
} else {
sessionStorage.removeItem('auth_token');
}
} catch (err) {
// Storage unavailable (quota exceeded, private mode): in-memory token still works for this tab.
console.warn('setAuthToken: sessionStorage unavailable, token kept in-memory only', err);
}
try {
if (!token) {
localStorage.removeItem('auth_token');
} else if (persistence === 'persistent') {
localStorage.setItem('auth_token', token);
}
} catch (err) {
console.warn('setAuthToken: localStorage operation failed', err);
}
}
export function getAuthToken(): string | null {
return authToken;
}
// Stream token for image/video URLs loaded via
/