Buckets:
| import { createOpencodeClient } from "@opencode-ai/sdk/v2" | |
| import type { GlobalEvent } from "@opencode-ai/sdk/v2" | |
| import { Flag } from "@opencode-ai/core/flag/flag" | |
| import { createSimpleContext } from "./helper" | |
| import { batch, onCleanup, onMount } from "solid-js" | |
| export type EventSource = { | |
| subscribe: (handler: (event: GlobalEvent) => void) => Promise<() => void> | |
| } | |
| export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ | |
| name: "SDK", | |
| init: (props: { | |
| url: string | |
| directory?: string | |
| fetch?: typeof fetch | |
| headers?: RequestInit["headers"] | |
| events?: EventSource | |
| }) => { | |
| const abort = new AbortController() | |
| let sse: AbortController | undefined | |
| function createSDK() { | |
| return createOpencodeClient({ | |
| baseUrl: props.url, | |
| signal: abort.signal, | |
| directory: props.directory, | |
| fetch: props.fetch, | |
| headers: props.headers, | |
| }) | |
| } | |
| let sdk = createSDK() | |
| const handlers = new Set<(event: GlobalEvent) => void>() | |
| const emitter = { | |
| emit(_type: "event", event: GlobalEvent) { | |
| for (const handler of handlers) handler(event) | |
| }, | |
| on(_type: "event", handler: (event: GlobalEvent) => void) { | |
| handlers.add(handler) | |
| return () => { | |
| handlers.delete(handler) | |
| } | |
| }, | |
| } | |
| let queue: GlobalEvent[] = [] | |
| let timer: Timer | undefined | |
| let last = 0 | |
| const retryDelay = 1000 | |
| const maxRetryDelay = 30000 | |
| const flush = () => { | |
| if (queue.length === 0) return | |
| const events = queue | |
| queue = [] | |
| timer = undefined | |
| last = Date.now() | |
| // Batch all event emissions so all store updates result in a single render | |
| batch(() => { | |
| for (const event of events) { | |
| emitter.emit("event", event) | |
| } | |
| }) | |
| } | |
| const handleEvent = (event: GlobalEvent) => { | |
| queue.push(event) | |
| const elapsed = Date.now() - last | |
| if (timer) return | |
| // If we just flushed recently (within 16ms), batch this with future events | |
| // Otherwise, process immediately to avoid latency | |
| if (elapsed < 16) { | |
| timer = setTimeout(flush, 16) | |
| return | |
| } | |
| flush() | |
| } | |
| function startSSE() { | |
| sse?.abort() | |
| const ctrl = new AbortController() | |
| sse = ctrl | |
| ;(async () => { | |
| let attempt = 0 | |
| while (true) { | |
| if (abort.signal.aborted || ctrl.signal.aborted) break | |
| const events = await sdk.global.event({ | |
| signal: ctrl.signal, | |
| sseMaxRetryAttempts: 0, | |
| }) | |
| if (Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) { | |
| // Start syncing workspaces, it's important to do this after | |
| // we've started listening to events | |
| await sdk.sync.start().catch(() => {}) | |
| } | |
| for await (const event of events.stream) { | |
| if (ctrl.signal.aborted) break | |
| handleEvent(event) | |
| } | |
| if (timer) clearTimeout(timer) | |
| if (queue.length > 0) flush() | |
| attempt += 1 | |
| if (abort.signal.aborted || ctrl.signal.aborted) break | |
| // Exponential backoff | |
| const backoff = Math.min(retryDelay * 2 ** (attempt - 1), maxRetryDelay) | |
| await new Promise((resolve) => setTimeout(resolve, backoff)) | |
| } | |
| })().catch(() => {}) | |
| } | |
| onMount(async () => { | |
| if (props.events) { | |
| const unsub = await props.events.subscribe(handleEvent) | |
| onCleanup(unsub) | |
| if (Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) { | |
| // Start syncing workspaces, it's important to do this after | |
| // we've started listening to events | |
| await sdk.sync.start().catch(() => {}) | |
| } | |
| } else { | |
| startSSE() | |
| } | |
| }) | |
| onCleanup(() => { | |
| abort.abort() | |
| sse?.abort() | |
| if (timer) clearTimeout(timer) | |
| handlers.clear() | |
| }) | |
| return { | |
| get client() { | |
| return sdk | |
| }, | |
| directory: props.directory, | |
| event: emitter, | |
| fetch: props.fetch ?? fetch, | |
| url: props.url, | |
| } | |
| }, | |
| }) | |
Xet Storage Details
- Size:
- 4.12 kB
- Xet hash:
- a17a26c8414d034cd1e7dc16034ed6fafc0f17fc0964bad6ce453ab2c54ab93b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.