/*** UTILITY ------------------------------------------ ***/ import type { Tab } from "./session.svelte.ts"; const MAX_STRING_BYTES = 1024 * 1024; const MAX_TABS = 50; function isObject(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function stringTooLong(value: string): boolean { return value.length > MAX_STRING_BYTES; } function validateTab(raw: unknown, index: number): TabExport | string { if (!isObject(raw)) return `tabs[${index}]: not an object`; if (typeof raw.headers !== "string") return `tabs[${index}].headers: not a string`; if (typeof raw.query !== "string") return `tabs[${index}].query: not a string`; if (typeof raw.title !== "string") return `tabs[${index}].title: not a string`; if (typeof raw.variables !== "string") return `tabs[${index}].variables: not a string`; if (raw.operationName !== null && typeof raw.operationName !== "string") return `tabs[${index}].operationName: not a string or null`; if (stringTooLong(raw.headers)) return `tabs[${index}].headers: exceeds 1 MB`; if (stringTooLong(raw.query)) return `tabs[${index}].query: exceeds 1 MB`; if (stringTooLong(raw.title)) return `tabs[${index}].title: exceeds 1 MB`; if (stringTooLong(raw.variables)) return `tabs[${index}].variables: exceeds 1 MB`; if (typeof raw.operationName === "string" && stringTooLong(raw.operationName)) return `tabs[${index}].operationName: exceeds 1 MB`; return { headers: raw.headers, operationName: raw.operationName, query: raw.query, title: raw.title, variables: raw.variables }; } /*** EXPORT ------------------------------------------- ***/ export type TabExport = { headers: string; operationName: string | null; query: string; title: string; variables: string; }; export type SessionExport = { exportedAt: string; tabs: TabExport[]; version: 1; }; export type ImportResult = { added: number; errors: string[]; skipped: number; }; export function tabToExport(tab: Tab): TabExport { return { headers: tab.headers, operationName: tab.operationName, query: tab.query, title: tab.title, variables: tab.variables }; } export function validateSessionExport(data: unknown): SessionExport | { error: string } { if (!isObject(data)) return { error: "not an object" }; if (data.version !== 1) return { error: `unsupported version: ${String(data.version)}` }; if (typeof data.exportedAt !== "string") return { error: "exportedAt: not a string" }; if (!Array.isArray(data.tabs)) return { error: "tabs: not an array" }; if (data.tabs.length > MAX_TABS) return { error: `tabs: exceeds ${MAX_TABS}` }; const tabs: TabExport[] = []; for (let i = 0; i < data.tabs.length; i++) { const result = validateTab(data.tabs[i], i); if (typeof result === "string") return { error: result }; tabs.push(result); } return { exportedAt: data.exportedAt, tabs, version: 1 }; }