summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-01-26 12:21:24 -0600
committeradamelmore <[email protected]>2026-01-26 12:21:27 -0600
commitb0f865eae5a79742560ddea472fa8ba25871aa3d (patch)
tree94ceed7496c0efdbf12423f82704c9facf4cb78d
parentac53a372b0b97fca93f887036f5957eddb88b606 (diff)
downloadopencode-b0f865eae5a79742560ddea472fa8ba25871aa3d.tar.gz
opencode-b0f865eae5a79742560ddea472fa8ba25871aa3d.zip
chore: debug changelog
-rw-r--r--packages/console/app/src/routes/changelog.json.ts33
-rw-r--r--packages/console/app/src/routes/changelog/index.tsx115
2 files changed, 136 insertions, 12 deletions
diff --git a/packages/console/app/src/routes/changelog.json.ts b/packages/console/app/src/routes/changelog.json.ts
index 48dd985d7..f82c8421d 100644
--- a/packages/console/app/src/routes/changelog.json.ts
+++ b/packages/console/app/src/routes/changelog.json.ts
@@ -104,7 +104,12 @@ export async function GET() {
cacheTtl: 60 * 5,
cacheEverything: true,
},
- } as any).catch(() => undefined)
+ } as any).catch((err) => {
+ console.error("[changelog.json] fetch failed", {
+ error: err instanceof Error ? err.message : String(err),
+ })
+ return undefined
+ })
const fail = () =>
new Response(JSON.stringify({ releases: [] }), {
@@ -116,10 +121,30 @@ export async function GET() {
},
})
- if (!response?.ok) return fail()
+ if (!response) return fail()
+ if (!response.ok) {
+ const body = await response.text().catch(() => undefined)
+ console.error("[changelog.json] github non-ok", {
+ status: response.status,
+ remaining: response.headers.get("x-ratelimit-remaining"),
+ reset: response.headers.get("x-ratelimit-reset"),
+ body: body?.slice(0, 300),
+ })
+ return fail()
+ }
- const data = await response.json().catch(() => undefined)
- if (!Array.isArray(data)) return fail()
+ const data = await response.json().catch((err) => {
+ console.error("[changelog.json] json parse failed", {
+ error: err instanceof Error ? err.message : String(err),
+ })
+ return undefined
+ })
+ if (!Array.isArray(data)) {
+ console.error("[changelog.json] invalid json", {
+ type: typeof data,
+ })
+ return fail()
+ }
const releases = data as Release[]
diff --git a/packages/console/app/src/routes/changelog/index.tsx b/packages/console/app/src/routes/changelog/index.tsx
index c7dca97a7..ec5cd4e9d 100644
--- a/packages/console/app/src/routes/changelog/index.tsx
+++ b/packages/console/app/src/routes/changelog/index.tsx
@@ -1,11 +1,11 @@
import "./index.css"
import { Title, Meta, Link } from "@solidjs/meta"
-import { createAsync } from "@solidjs/router"
+import { createAsync, useSearchParams } from "@solidjs/router"
import { Header } from "~/component/header"
import { Footer } from "~/component/footer"
import { Legal } from "~/component/legal"
import { config } from "~/config"
-import { For, Show, createSignal } from "solid-js"
+import { For, Show, createSignal, onMount } from "solid-js"
import { getRequestEvent } from "solid-js/web"
type HighlightMedia = { type: "video"; src: string } | { type: "image"; src: string; width: string; height: string }
@@ -31,6 +31,21 @@ type ChangelogRelease = {
sections: { title: string; items: string[] }[]
}
+type LoadMeta = {
+ endpoint: string
+ ssr: boolean
+ hasEvent: boolean
+ ok: boolean
+ status?: number
+ contentType?: string
+ error?: string
+}
+
+type Load = {
+ releases: ChangelogRelease[]
+ meta: LoadMeta
+}
+
function endpoint() {
const event = getRequestEvent()
if (event) return new URL("/changelog.json", event.request.url).toString()
@@ -38,12 +53,64 @@ function endpoint() {
return `${config.baseUrl}/changelog.json`
}
-async function getReleases() {
- const response = await fetch(endpoint()).catch(() => undefined)
- if (!response?.ok) return []
+async function getReleases(debug = false): Promise<Load> {
+ const url = endpoint()
+ const meta = {
+ endpoint: url,
+ ssr: import.meta.env.SSR,
+ hasEvent: Boolean(getRequestEvent()),
+ ok: false,
+ } satisfies LoadMeta
+
+ const response = await fetch(url).catch((err) => {
+ console.error("[changelog] fetch failed", {
+ ...meta,
+ error: err instanceof Error ? err.message : String(err),
+ })
+ return undefined
+ })
+
+ if (!response) return { releases: [], meta: { ...meta, error: "fetch_failed" } }
+ if (!response.ok) {
+ const contentType = response.headers.get("content-type") ?? undefined
+ const body = debug ? await response.text().catch(() => undefined) : undefined
+ console.error("[changelog] fetch non-ok", {
+ ...meta,
+ status: response.status,
+ contentType,
+ body: body?.slice(0, 300),
+ })
+ return { releases: [], meta: { ...meta, status: response.status, contentType, error: "bad_status" } }
+ }
+
+ const contentType = response.headers.get("content-type") ?? undefined
+ const copy = debug ? response.clone() : undefined
+ const json = await response.json().catch(async (err) => {
+ const body = copy ? await copy.text().catch(() => undefined) : undefined
+ console.error("[changelog] json parse failed", {
+ ...meta,
+ status: response.status,
+ contentType,
+ error: err instanceof Error ? err.message : String(err),
+ body: body?.slice(0, 300),
+ })
+ return undefined
+ })
+
+ const releases = Array.isArray(json?.releases) ? (json.releases as ChangelogRelease[]) : []
+ if (!releases.length) {
+ console.error("[changelog] empty releases", {
+ ...meta,
+ status: response.status,
+ contentType,
+ keys: json && typeof json === "object" ? Object.keys(json) : undefined,
+ })
+ }
- const json = await response.json().catch(() => undefined)
- return Array.isArray(json?.releases) ? (json.releases as ChangelogRelease[]) : []
+ return {
+ releases,
+ meta: { ...meta, ok: true, status: response.status, contentType },
+ }
}
function formatDate(dateString: string) {
@@ -134,7 +201,22 @@ function CollapsibleSections(props: { sections: { title: string; items: string[]
}
export default function Changelog() {
- const releases = createAsync(() => getReleases())
+ const [params] = useSearchParams()
+ const debug = () => params.debug === "1"
+ const data = createAsync(() => getReleases(debug()))
+ const [client, setClient] = createSignal<Load | undefined>(undefined)
+ const releases = () => client()?.releases ?? data()?.releases ?? []
+
+ onMount(() => {
+ queueMicrotask(async () => {
+ const server = data()?.releases
+ if (!server) return
+ if (server.length) return
+
+ const next = await getReleases(debug())
+ setClient(next)
+ })
+ })
return (
<main data-page="changelog">
@@ -152,6 +234,23 @@ export default function Changelog() {
</section>
<section data-component="releases">
+ <Show when={releases().length === 0}>
+ <p>
+ No changelog entries found. <a href="/changelog.json">View JSON</a>
+ </p>
+ </Show>
+ <Show when={debug()}>
+ <pre style={{ "font-size": "12px", "line-height": "1.4", padding: "12px" }}>
+ {JSON.stringify(
+ {
+ server: data()?.meta,
+ client: client()?.meta,
+ },
+ null,
+ 2,
+ )}
+ </pre>
+ </Show>
<For each={releases()}>
{(release) => {
return (