diff options
| author | netop://ウィビ <paul@webb.page> | 2026-04-24 14:17:38 -0700 |
|---|---|---|
| committer | netop://ウィビ <paul@webb.page> | 2026-04-24 14:17:38 -0700 |
| commit | 261f3bdb77799009344aab4a60686b7186ebd3b0 (patch) | |
| tree | 8e87c6610a307f15f0c4b32f68b19424273fc6ad /source/library/state/session.svelte.ts | |
| parent | 8a59f92d031963e23ecc84b75feecf43eb4dd146 (diff) | |
| download | graphiql-261f3bdb77799009344aab4a60686b7186ebd3b0.tar.gz graphiql-261f3bdb77799009344aab4a60686b7186ebd3b0.zip | |
Implement v0.4 subscriptions + v0.5 theming and plugin slots
- SSE and WebSocket fetchers via graphql-sse and graphql-ws,
returning AsyncIterable results
- SessionStore.run consumes AsyncIterable streams with
subscriptionMode "append" (timestamped) or "replace" and
honors an AbortSignal for cancellation
- Chrome CSS variables documented in styles/theme.scss with
prefers-color-scheme light/dark gating and .graphiql-light override
- Svelte 5 snippet slots on GraphiQL: toolbarExtras, tabExtras,
resultFooter
Diffstat (limited to 'source/library/state/session.svelte.ts')
| -rw-r--r-- | source/library/state/session.svelte.ts | 53 |
1 files changed, 51 insertions, 2 deletions
diff --git a/source/library/state/session.svelte.ts b/source/library/state/session.svelte.ts index d9f52ff..9345d29 100644 --- a/source/library/state/session.svelte.ts +++ b/source/library/state/session.svelte.ts @@ -3,7 +3,7 @@ /*** UTILITY ------------------------------------------ ***/ -import type { Fetcher } from "../fetcher/types.ts"; +import type { Fetcher, FetcherResult } from "../fetcher/types.ts"; import { deriveTitle, parseOperations, @@ -13,6 +13,12 @@ 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[]; @@ -40,6 +46,13 @@ export type TabSeed = { variables?: string; }; +export type SubscriptionMode = "append" | "replace"; + +export type RunOptions = { + signal?: AbortSignal; + subscriptionMode?: SubscriptionMode; +}; + export class SessionStore { activeId = $state<string>(""); tabs = $state<Tab[]>([]); @@ -115,12 +128,15 @@ export class SessionStore { tab.titleDirty = true; } - async run(fetcher: Fetcher): Promise<boolean> { + async run(fetcher: Fetcher, options: RunOptions = {}): Promise<boolean> { const tab = this.active; if (!tab) return false; + const mode = options.subscriptionMode ?? "append"; + const signal = options.signal; + try { const variables = tab.variables.trim() ? JSON.parse(tab.variables) : {}; const headers = tab.headers.trim() ? JSON.parse(tab.headers) : {}; @@ -132,6 +148,39 @@ export class SessionStore { variables }); + if (isAsyncIterable<FetcherResult>(result)) { + tab.result = ""; + const iterator = result[Symbol.asyncIterator](); + + try { + while (true) { + if (signal?.aborted) { + await iterator.return?.(); + break; + } + + const step = await iterator.next(); + + if (step.done) + break; + + const payload = JSON.stringify(step.value, null, 2); + + if (mode === "append") { + const stamp = new Date().toISOString(); + const chunk = `// ${stamp}\n${payload}\n`; + tab.result = tab.result ? `${tab.result}\n${chunk}` : chunk; + } else { + tab.result = payload; + } + } + } finally { + await iterator.return?.(); + } + + return true; + } + tab.result = JSON.stringify(result, null, 2); return true; } catch(err) { |