diff options
Diffstat (limited to 'source/library/state')
| -rw-r--r-- | source/library/state/history.svelte.ts | 2 | ||||
| -rw-r--r-- | source/library/state/session-io.ts | 116 | ||||
| -rw-r--r-- | source/library/state/session.svelte.ts | 34 |
3 files changed, 82 insertions, 70 deletions
diff --git a/source/library/state/history.svelte.ts b/source/library/state/history.svelte.ts index 2726283..80de3c0 100644 --- a/source/library/state/history.svelte.ts +++ b/source/library/state/history.svelte.ts @@ -32,12 +32,10 @@ export type HistoryInput = { export class HistoryStore { entries = $state<HistoryEntry[]>([]); - #storage: Storage; constructor(storage: Storage) { this.#storage = storage; - const restored = storage.get<HistoryEntry[]>(STORAGE_KEY); if (Array.isArray(restored)) diff --git a/source/library/state/session-io.ts b/source/library/state/session-io.ts index a5e2ea9..f2491ba 100644 --- a/source/library/state/session-io.ts +++ b/source/library/state/session-io.ts @@ -9,59 +9,14 @@ 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 ImportResult = { + added: number; + errors: string[]; + skipped: number; +}; + export type TabExport = { headers: string; operationName: string | null; @@ -76,12 +31,6 @@ export type SessionExport = { version: 1; }; -export type ImportResult = { - added: number; - errors: string[]; - skipped: number; -}; - export function tabToExport(tab: Tab): TabExport { return { headers: tab.headers, @@ -125,3 +74,56 @@ export function validateSessionExport(data: unknown): SessionExport | { error: s version: 1 }; } + +/*** HELPER ------------------------------------------- ***/ + +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 + }; +} diff --git a/source/library/state/session.svelte.ts b/source/library/state/session.svelte.ts index 76777e5..f84bb17 100644 --- a/source/library/state/session.svelte.ts +++ b/source/library/state/session.svelte.ts @@ -3,29 +3,26 @@ /*** UTILITY ------------------------------------------ ***/ -import type { Fetcher, FetcherResult } from "../fetcher/types.ts"; import { format } from "../graphql/format.ts"; + import { deriveTitle, parseOperations, type OperationInfo } from "../graphql/operations.ts"; + import { tabToExport, type ImportResult, type SessionExport, type TabExport } from "./session-io.ts"; + +import type { Fetcher, FetcherResult } from "../fetcher/types.ts"; import type { Storage } from "./storage.ts"; const STORAGE_KEY = "session"; -function isAsyncIterable<T>(value: unknown): value is AsyncIterable<T> { - return typeof value === "object" && - value !== null && - typeof (value as AsyncIterable<T>)[Symbol.asyncIterator] === "function"; -} - type Snapshot = { activeId: string; tabs: Tab[]; @@ -72,7 +69,6 @@ export class SessionStore { activeId = $state<string>(""); tabs = $state<Tab[]>([]); active = $derived(this.tabs.find((t) => t.id === this.activeId)); - #storage: Storage; constructor(storage: Storage) { @@ -106,6 +102,7 @@ export class SessionStore { if (this.tabs.length === 1) { const fresh = this.#blank(); + this.tabs = [fresh]; this.activeId = fresh.id; @@ -154,8 +151,8 @@ export class SessionStore { } importTabs(data: SessionExport, opts: { mode: "append" | "replace" }): ImportResult { - const errors: string[] = []; const capped = data.tabs.slice(0, 50); + const errors: string[] = []; const skipped = data.tabs.length - capped.length; if (opts.mode === "replace") { @@ -181,7 +178,11 @@ export class SessionStore { if (capped.length > 0) this.activeId = this.tabs[this.tabs.length - 1].id; - return { added: capped.length, errors, skipped }; + return { + added: capped.length, + errors, + skipped + }; } nextTab() { @@ -249,8 +250,8 @@ export class SessionStore { tab.timing = { endMs: startMs, firstByteMs: startMs, startMs }; try { - const variables = tab.variables.trim() ? JSON.parse(tab.variables) : {}; const headers = tab.headers.trim() ? JSON.parse(tab.headers) : {}; + const variables = tab.variables.trim() ? JSON.parse(tab.variables) : {}; const result = await fetcher({ headers, @@ -312,13 +313,16 @@ export class SessionStore { } const firstByteMs = performance.now(); + tab.timing = { ...tab.timing, firstByteMs }; tab.result = JSON.stringify(result, null, 2); tab.timing = { ...tab.timing, endMs: performance.now() }; + return true; } catch(err) { tab.result = JSON.stringify({ error: String(err) }, null, 2); tab.timing = { ...tab.timing, endMs: performance.now() }; + return false; } } @@ -431,3 +435,11 @@ export class SessionStore { }; } } + +/*** HELPER ------------------------------------------- ***/ + +function isAsyncIterable<T>(value: unknown): value is AsyncIterable<T> { + return typeof value === "object" && + value !== null && + typeof (value as AsyncIterable<T>)[Symbol.asyncIterator] === "function"; +} |