aboutsummaryrefslogtreecommitdiff
path: root/source/library/state
diff options
context:
space:
mode:
Diffstat (limited to 'source/library/state')
-rw-r--r--source/library/state/history.svelte.ts2
-rw-r--r--source/library/state/session-io.ts116
-rw-r--r--source/library/state/session.svelte.ts34
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";
+}