diff options
| author | netop://ウィビ <paul@webb.page> | 2026-04-24 16:37:33 -0700 |
|---|---|---|
| committer | netop://ウィビ <paul@webb.page> | 2026-04-24 16:37:33 -0700 |
| commit | 510fd8cbe53abb39cba2c7cbaaefcf2783dc0066 (patch) | |
| tree | 8f753a33c475b285f2a297785d34cda3b0a8faed /source/library/state/session-io.ts | |
| parent | 261f3bdb77799009344aab4a60686b7186ebd3b0 (diff) | |
| download | graphiql-510fd8cbe53abb39cba2c7cbaaefcf2783dc0066.tar.gz graphiql-510fd8cbe53abb39cba2c7cbaaefcf2783dc0066.zip | |
Implement v0.6-1.0: shortcuts, format, export/import, splitter, timing, APQ
- v0.6: matchShortcut + format(); Cmd+Shift+Enter/W/F + Cmd+Alt+arrows
- v0.7: SessionStore.exportAll/importTabs with version-1 validator
- v0.8: Splitter component + four resize handles persisted under layout.*
- v0.10: createApqFetcher (HTTP-only) wrapping shared http-body helpers
- Drop .svelte re-exports from index.ts for multi-entry JSR/npm publishing
Diffstat (limited to 'source/library/state/session-io.ts')
| -rw-r--r-- | source/library/state/session-io.ts | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/source/library/state/session-io.ts b/source/library/state/session-io.ts new file mode 100644 index 0000000..a5e2ea9 --- /dev/null +++ b/source/library/state/session-io.ts @@ -0,0 +1,127 @@ + + + + +/*** 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<string, unknown> { + 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 + }; +} |