diff --git a/apps/check/package.json b/apps/check/package.json index 6d87cc0f..0af6d5e5 100644 --- a/apps/check/package.json +++ b/apps/check/package.json @@ -26,7 +26,8 @@ "@ipld/car": "^5.4.6", "idb-keyval": "^6.2.4", "oauth4webapi": "^3.8.6", - "solid-js": "^1.9.13" + "solid-js": "^1.9.13", + "@getcirrus/space-conformance": "workspace:*" }, "devDependencies": { "@tailwindcss/vite": "^4.3.0", diff --git a/apps/check/public/client-metadata.json b/apps/check/public/client-metadata.json deleted file mode 100644 index 975c3f5d..00000000 --- a/apps/check/public/client-metadata.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "client_id": "https://check.cirrus.earth/client-metadata.json", - "client_name": "check · a PDS validator", - "client_uri": "https://check.cirrus.earth", - "redirect_uris": [ - "https://check.cirrus.earth/oauth/callback", - "https://check.cirrus.earth/oauth/flow-callback" - ], - "scope": "atproto transition:generic repo:earth.cirrus.check.testrecord include:site.standard.authFull", - "grant_types": ["authorization_code", "refresh_token"], - "response_types": ["code"], - "application_type": "web", - "token_endpoint_auth_method": "none", - "dpop_bound_access_tokens": true -} diff --git a/apps/check/src/App.tsx b/apps/check/src/App.tsx index bf7d82d4..c5607f05 100644 --- a/apps/check/src/App.tsx +++ b/apps/check/src/App.tsx @@ -3,7 +3,9 @@ import { anonymousChecks, writeChecks } from "./checks"; import { OAuthFlowView } from "./components/OAuthFlowView"; import { RecentRuns } from "./components/RecentRuns"; import { RunView } from "./components/RunView"; +import { SpacesConformanceView } from "./SpacesConformanceView"; import { + SPACES_SCOPE, completeCallback, getAgent, isCallbackPath, @@ -53,7 +55,7 @@ type BootState = | { kind: "callback-error"; message: string } | { kind: "flow-callback"; state: FlowState }; -type Mode = "landing" | "confirm-writes"; +type Mode = "landing" | "confirm-writes" | "confirm-spaces" | "spaces"; export function App() { // Both callback paths show the "completing sign-in" loading view first, @@ -107,6 +109,11 @@ export function App() { `/?target=${encodeURIComponent(stashedTarget)}`, ); await beginRun(stashedTarget, writeChecks, "writes"); + } else if (intent === "spaces") { + // The spaces view runs itself against the session's own PDS + // as soon as it mounts with an agent present. + history.replaceState(null, "", "/"); + setMode("spaces"); } else { history.replaceState( null, @@ -234,6 +241,44 @@ export function App() { setMode("landing"); } + function startSpacesTests() { + const value = target().trim(); + if (!value) { + setAuthError("Enter a handle first"); + return; + } + setAuthError(null); + // Same shape as the write tests: confirm what the run will do BEFORE + // the redirect, so the user sees it before authorizing. + setMode("confirm-spaces"); + } + + async function confirmSpaces() { + const value = target().trim(); + if (!value) { + setAuthError("Enter a handle first"); + setMode("landing"); + return; + } + + // Already signed in — but almost certainly with the write-test scope, + // which grants nothing on spaces. Sign out and re-authorize with the + // space scope so the consent screen shows what the run actually needs. + if (signedInDid()) await signOut(); + + sessionStorage.setItem(INTENT_KEY, "spaces"); + sessionStorage.setItem(TARGET_KEY, value); + try { + await startLogin(value, SPACES_SCOPE); + } catch (error) { + if (error instanceof Error && error.message === "redirecting") return; + sessionStorage.removeItem(INTENT_KEY); + sessionStorage.removeItem(TARGET_KEY); + setAuthError(error instanceof Error ? error.message : String(error)); + setMode("landing"); + } + } + function startOAuthConformance() { const value = target().trim(); if (!value) { @@ -311,7 +356,26 @@ export function App() { function ModeSurface() { return ( - }> + }> + setMode("landing")} /> + + ); + } + + function ConfirmOrLanding() { + return ( + }> + + + } + > + {(message) => (
@@ -625,6 +709,99 @@ function ConfirmWritesView(props: { ); } +function ConfirmSpacesView(props: { + target: string; + onConfirm: () => void; + onCancel: () => void; +}) { + return ( +
+
+ + ☁️ + CHECK + + +
+ +
+
+
+ Confirm spaces conformance run +
+

{props.target}

+ +
+ you'll be redirected to your PDS to authorize the space scopes + before the run starts — a PDS without the spaces alpha will refuse + the sign-in +
+ +
+

+ This run makes real (but disposable) changes to your PDS. + Specifically: +

+
    +
  • + · Create probe spaces of type{" "} + app.bsky.group with + run-unique keys, deleted at the end of each check +
  • +
  • + · Write and delete records in the + neutral{" "} + test.conformance.*{" "} + collections inside those probe spaces +
  • +
  • + · Upload one 16-byte test blob and + verify it is not served publicly +
  • +
  • + · Exercise your PDS's own{" "} + getDelegationToken →{" "} + getSpaceCredential → + read flow +
  • +
+

+ Probe spaces never touch your public repo or existing spaces, and + every check deletes what it created — a failed cleanup can be + removed with{" "} + pds spaces tooling. + The session is signed out automatically when the run finishes. +

+
+ +
+ + +
+
+
+
+ ); +} + function CallbackView(props: { state: BootState }) { return (
diff --git a/apps/check/src/SpacesConformanceView.tsx b/apps/check/src/SpacesConformanceView.tsx new file mode 100644 index 00000000..04379e98 --- /dev/null +++ b/apps/check/src/SpacesConformanceView.tsx @@ -0,0 +1,297 @@ +/** + * Runner #3: the browser adapter for the spaces conformance suite. + * + * Signed in (the landing-page flow), the run carries the operator, blob + * and delegation capabilities: probe spaces, record writes, applyWrites + * atomicity, blob isolation and the operator's own delegation → credential + * → read flow all execute for real against the user's PDS, authenticated + * through the DPoP-bound OAuth session. Results stream into the table as + * each check lands. + * + * The checks a browser genuinely cannot run — harness-held foreign + * identities, the alpha crypto libs — are not shown at all: a row (or a + * count) implies a way they could have run here, and there isn't one. + * They are covered by the CLI and vitest runners. + * + * Without a session it degrades to the anonymous slice (discovery and + * unauthenticated request shapes) against any host the user names. + */ + +import { createSignal, For, onMount, Show } from "solid-js"; +import { + browserCatalog, + filterCatalog, + operatorChecks, + runChecks, + type CheckContext, + type CheckResult, + type RunReport, +} from "@getcirrus/space-conformance"; +import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; +import { getAgent, signOut, signedInDid } from "./lib/oauth"; + +function normalizeOrigin(input: string): string { + const trimmed = input.trim().replace(/\/+$/, ""); + if (/^https?:\/\//i.test(trimmed)) return trimmed; + return `https://${trimmed}`; +} + +async function resolveDid(origin: string): Promise { + const res = await fetch(`${origin}/.well-known/did.json`); + if (!res.ok) throw new Error(`did.json returned ${res.status}`); + const doc = (await res.json()) as { id?: string }; + if (!doc.id) throw new Error("did.json has no id"); + return doc.id; +} + +/** + * A fetch that routes same-origin requests through the OAuth agent (which + * signs each one with the session's DPoP key) and everything else through + * the plain window fetch. The conformance context keeps its bare `fetch` + * for the requests that must stay anonymous. + */ +function agentFetch(agent: OAuthUserAgent, origin: string): typeof fetch { + return (input, init) => { + const url = new URL( + typeof input === "string" + ? input + : input instanceof URL + ? input.toString() + : input.url, + ); + if (url.origin === origin) { + return agent.handle(url.pathname + url.search, init); + } + return fetch(input, init); + }; +} + +const STATUS_STYLE: Record = { + pass: "text-pass", + fail: "text-fail", + error: "text-fail", + skipped: "text-faint", +}; +const STATUS_ICON: Record = { + pass: "✓", + fail: "✗", + error: "!", + skipped: "–", +}; + +export function SpacesConformanceView(props: { onExit: () => void }) { + const [target, setTarget] = createSignal(""); + const [running, setRunning] = createSignal(false); + const [error, setError] = createSignal(null); + const [results, setResults] = createSignal([]); + const [report, setReport] = createSignal(null); + const [authedRun, setAuthedRun] = createSignal(false); + + async function runCatalog( + context: Omit, + capabilities: Parameters[1], + ) { + setRunning(true); + setError(null); + setResults([]); + setReport(null); + try { + const filtered = filterCatalog(browserCatalog, capabilities); + const result = await runChecks({ + catalog: filtered, + context, + suiteVersion: "web", + alphaBuild: "0.0.0-spaces-alpha-20260818163953", + // Stream rows into the table as checks land. Skipped results + // are checks this transport can never run — not listing them + // avoids implying they could have; the footer counts them. + onResult: (result) => { + if (result.status !== "skipped") { + setResults((prev) => [...prev, result]); + } + }, + }); + setReport(result); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setRunning(false); + } + } + + async function runAuthed(agent: OAuthUserAgent) { + const origin = normalizeOrigin(agent.session.info.aud); + const did = agent.session.info.sub; + setAuthedRun(true); + setTarget(origin); + await runCatalog( + { + target: { origin, did }, + fetch: window.fetch.bind(window), + operator: { + oauth: true, + fetch: agentFetch(agent, origin), + }, + }, + { + capabilities: ["operator", "pds-blobs", "pds-delegation"], + destructive: true, + }, + ); + // Sessions are ephemeral — sign out once the run has finished, the + // same convention as the write tests. + if (signedInDid()) void signOut(); + } + + async function onRunAnonymous(event: Event) { + event.preventDefault(); + const raw = target().trim(); + if (!raw) return; + setAuthedRun(false); + try { + const origin = normalizeOrigin(raw); + const did = await resolveDid(origin); + await runCatalog( + { + target: { origin, did, implementation: origin }, + fetch: window.fetch.bind(window), + }, + { capabilities: [] }, + ); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } + } + + onMount(() => { + // Arriving from the landing flow the user has just authorized; run + // immediately against their own PDS. With no session this is the + // anonymous surface and waits for a target instead. + void getAgent().then((agent) => { + if (agent) void runAuthed(agent); + }); + }); + + // In an anonymous run, the operator checks were skipped but WOULD run + // with a sign-in — worth a pointer. (The alpha-lib/identity checks are + // deliberately not mentioned at all: they never run in a browser.) + const operatorIds = new Set(operatorChecks.map((c) => c.id)); + const signInWouldRun = () => + (report()?.results ?? []).filter( + (r) => r.status === "skipped" && operatorIds.has(r.id), + ).length; + + return ( +
+
+ + spaces conformance (alpha) +
+ +
+

SPACES CONFORMANCE

+

+ + Discovery and request-shape checks against a live spaces host. + Sign in from the landing page to run the write, blob and + credential checks against your own PDS. + + } + > + Running the operator checks against{" "} + {target()}: probe spaces, record + writes, blob isolation and the delegation → credential → read flow. + +

+ + +
+ setTarget(e.currentTarget.value)} + class="flex-1 rounded border border-faint bg-transparent px-3 py-2 text-sm" + /> + +
+
+ + + {(message) => ( +

Could not run: {message()}

+ )} +
+ + 0 || running()}> +
+ + running… {results().length} checks completed +
+ } + > + {(r) => ( +
+ {r().summary.pass} pass · {r().summary.fail} fail ·{" "} + {r().summary.error} errored · alpha {r().alphaBuild} +
+ )} +
+
    + + {(result) => ( +
  • + + {STATUS_ICON[result.status]} + +
    +
    + {result.id} + + {result.tier} + +
    +
    {result.detail}
    +
    +
  • + )} +
    + +
  • + + running next check +
  • +
    +
+ 0}> +

+ {signInWouldRun()} more checks need an authenticated session — + start from SPACES CONFORMANCE on the landing page to run them + against your own PDS. +

+
+
+ + +
+ ); +} diff --git a/apps/check/src/checks/account.ts b/apps/check/src/checks/account.ts index 0db9a6d2..79c3c489 100644 --- a/apps/check/src/checks/account.ts +++ b/apps/check/src/checks/account.ts @@ -188,10 +188,9 @@ const getAccountInviteCodes: Check = { const guard = sessionMismatch(ctx); if (guard) return guard; const client = authedClient(ctx.agent!); - const res = await client.get( - "com.atproto.server.getAccountInviteCodes", - { params: {} }, - ); + const res = await client.get("com.atproto.server.getAccountInviteCodes", { + params: {}, + }); if (!res.ok) { return { status: "fail", diff --git a/apps/check/src/checks/blobs.ts b/apps/check/src/checks/blobs.ts index c18f39f3..1b4a28f4 100644 --- a/apps/check/src/checks/blobs.ts +++ b/apps/check/src/checks/blobs.ts @@ -5,7 +5,9 @@ import type { Check, CheckOutcome } from "../types"; let listBlobsResponse: ComAtprotoSyncListBlobs.$output | undefined; let firstBlobCid: string | undefined; -let getBlobResponse: { contentType: string | null; byteLength: number } | undefined; +let getBlobResponse: + | { contentType: string | null; byteLength: number } + | undefined; function reset() { listBlobsResponse = undefined; @@ -13,7 +15,11 @@ function reset() { getBlobResponse = undefined; } -function xrpcUrl(pds: string, nsid: string, params: Record): string { +function xrpcUrl( + pds: string, + nsid: string, + params: Record, +): string { const qs = new URLSearchParams(params).toString(); return `${pds}/xrpc/${nsid}${qs ? `?${qs}` : ""}`; } diff --git a/apps/check/src/checks/firehose.ts b/apps/check/src/checks/firehose.ts index d5b87c09..ff486c18 100644 --- a/apps/check/src/checks/firehose.ts +++ b/apps/check/src/checks/firehose.ts @@ -95,7 +95,9 @@ const connect: Check = { try { await new Promise((resolve, reject) => { const timer = setTimeout(() => { - reject(new Error(`Connection timed out after ${CONNECT_TIMEOUT_MS}ms`)); + reject( + new Error(`Connection timed out after ${CONNECT_TIMEOUT_MS}ms`), + ); }, CONNECT_TIMEOUT_MS); ws.addEventListener( "open", @@ -220,7 +222,9 @@ const connect: Check = { finish("diversity"); } }); - ws.addEventListener("close", () => finish("server-close"), { once: true }); + ws.addEventListener("close", () => finish("server-close"), { + once: true, + }); ws.addEventListener("error", () => finish("ws-error"), { once: true }); }); @@ -257,7 +261,11 @@ const collectFrames: Check = { message: `No frames received in ${collectionElapsedMs}ms — relay may be idle (terminated: ${collectionTerminationReason})`, evidence: { expected: ">=1 frame", - actual: { frames: 0, terminatedBy: collectionTerminationReason, elapsedMs: collectionElapsedMs }, + actual: { + frames: 0, + terminatedBy: collectionTerminationReason, + elapsedMs: collectionElapsedMs, + }, }, }; } @@ -452,8 +460,7 @@ const commitBlocksIsValidCar: Check = { } catch (error) { failures.push({ seq: f.body.seq, - error: - error instanceof Error ? error.message : String(error), + error: error instanceof Error ? error.message : String(error), }); } } @@ -464,11 +471,8 @@ const commitBlocksIsValidCar: Check = { commits.length === 1 ? "" : "s" } failed CAR validation`, evidence: { - expected: - "body.blocks parses as a CAR and contains the commit block", - error: failures - .map((f) => `seq=${f.seq}: ${f.error}`) - .join("\n"), + expected: "body.blocks parses as a CAR and contains the commit block", + error: failures.map((f) => `seq=${f.seq}: ${f.error}`).join("\n"), }, }; } @@ -534,7 +538,8 @@ const commitDeprecatedBlobs: Check = { return { status: "skip", message: "No #commit frames observed" }; } const offenders = commits.filter( - (f) => Array.isArray(f.body.blobs) && (f.body.blobs as unknown[]).length > 0, + (f) => + Array.isArray(f.body.blobs) && (f.body.blobs as unknown[]).length > 0, ); if (offenders.length === 0) { return { @@ -737,8 +742,7 @@ const commitOpsHavePrev: Check = { status: "warn", message: `No sampled update/delete ops carry prev (${missingPrev}/${updateDeleteOps} missing) — could not confirm Sync 1.1 support. Sign in to run the live write probe, or trigger a fresh write and re-run.`, evidence: { - expected: - "at least one #repoOp with action=update|delete carries prev", + expected: "at least one #repoOp with action=update|delete carries prev", actual: firstOffending, }, }; @@ -850,7 +854,9 @@ const liveListenStart: Check = { try { await new Promise((resolve, reject) => { const timer = setTimeout(() => { - reject(new Error(`Connection timed out after ${CONNECT_TIMEOUT_MS}ms`)); + reject( + new Error(`Connection timed out after ${CONNECT_TIMEOUT_MS}ms`), + ); }, CONNECT_TIMEOUT_MS); ws.addEventListener( "open", diff --git a/apps/check/src/checks/identity.ts b/apps/check/src/checks/identity.ts index 42bd540b..050bd3f3 100644 --- a/apps/check/src/checks/identity.ts +++ b/apps/check/src/checks/identity.ts @@ -1,6 +1,11 @@ import { ComAtprotoIdentityResolveHandle } from "@atcute/atproto"; import { getPdsEndpoint } from "@atcute/identity"; -import { isDid, isHandle, type Did, type Handle } from "@atcute/lexicons/syntax"; +import { + isDid, + isHandle, + type Did, + type Handle, +} from "@atcute/lexicons/syntax"; import { didDocResolver, handleResolver } from "../lib/resolvers"; import { publicClient, validateLexicon } from "../lib/xrpc"; import type { Check, CheckOutcome } from "../types"; @@ -82,7 +87,8 @@ const resolveHandle: Check = { // handle, it's most likely a PDS host, not a user account. Probe // /xrpc/_health to confirm and, if so, auto-switch to PDS-URL mode // so the rest of the run is useful. - const looksLikeHostname = /\./.test(ctx.handle) && !ctx.handle.endsWith("."); + const looksLikeHostname = + /\./.test(ctx.handle) && !ctx.handle.endsWith("."); if (looksLikeHostname) { const probeUrl = `https://${ctx.handle}/xrpc/_health`; try { @@ -97,7 +103,8 @@ const resolveHandle: Check = { status: "warn", message: `not a user handle, but ${ctx.handle} responds to /xrpc/_health — auto-switching to PDS-URL mode. Identity/repo/sync checks needing a user DID will skip.`, evidence: { - expected: "DNS TXT _atproto. or /.well-known/atproto-did to return a DID", + expected: + "DNS TXT _atproto. or /.well-known/atproto-did to return a DID", actual: `handle resolution failed; ${probeUrl} returned ${res.status}`, error: errMsg, }, diff --git a/apps/check/src/checks/oauth-discovery.ts b/apps/check/src/checks/oauth-discovery.ts index 222c0e1f..d65fab75 100644 --- a/apps/check/src/checks/oauth-discovery.ts +++ b/apps/check/src/checks/oauth-discovery.ts @@ -132,9 +132,7 @@ const protectedResourceValidates: Check = { }; } const issues: string[] = []; - const expectedResource = ctx.pds - ? trimTrailingSlash(ctx.pds) - : undefined; + const expectedResource = ctx.pds ? trimTrailingSlash(ctx.pds) : undefined; const resource = protectedResource.resource; if (typeof resource !== "string" || resource.length === 0) { issues.push("missing field: resource"); @@ -142,9 +140,7 @@ const protectedResourceValidates: Check = { expectedResource && trimTrailingSlash(resource) !== expectedResource ) { - issues.push( - `resource: expected ${expectedResource}, got ${resource}`, - ); + issues.push(`resource: expected ${expectedResource}, got ${resource}`); } const servers = protectedResource.authorization_servers; if (!Array.isArray(servers) || servers.length === 0) { @@ -335,9 +331,7 @@ const authServerValidates: Check = { ); } if (!grants.includes("refresh_token")) { - warnings.push( - "grant_types_supported: should include refresh_token", - ); + warnings.push("grant_types_supported: should include refresh_token"); } } } @@ -459,7 +453,9 @@ const jwksValidates: Check = { } // kty is required per RFC 7517 §4.1 if (typeof key.kty !== "string" || (key.kty as string).length === 0) { - hardFailures.push(`keys[${i}]: missing field kty (required by RFC 7517 §4.1)`); + hardFailures.push( + `keys[${i}]: missing field kty (required by RFC 7517 §4.1)`, + ); } // kid is recommended for key rotation; warn if absent if (typeof key.kid !== "string" || (key.kid as string).length === 0) { diff --git a/apps/check/src/checks/repo-read.ts b/apps/check/src/checks/repo-read.ts index 934fc485..0b0db793 100644 --- a/apps/check/src/checks/repo-read.ts +++ b/apps/check/src/checks/repo-read.ts @@ -8,7 +8,7 @@ import type { Did, Nsid } from "@atcute/lexicons/syntax"; import { CarReader } from "@ipld/car"; import { validateLexicon } from "../lib/xrpc"; import type { Check, CheckOutcome } from "../types"; -import { verifyCar } from "../lib/verify" +import { verifyCar } from "../lib/verify"; let cachedClient: { pds: string; client: Client } | undefined; @@ -34,7 +34,11 @@ let listRecordsBody: ComAtprotoRepoListRecords.$output | undefined; let getRecordBody: ComAtprotoRepoGetRecord.$output | undefined; let repoCarBytes: Uint8Array | undefined; -function xrpcUrl(pds: string, nsid: string, params: Record): string { +function xrpcUrl( + pds: string, + nsid: string, + params: Record, +): string { const qs = new URLSearchParams(params).toString(); return `${pds}/xrpc/${nsid}${qs ? `?${qs}` : ""}`; } @@ -68,7 +72,8 @@ const describeRepo: Check = { if (!res.ok) { return { status: "fail", - message: `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: + `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, @@ -154,7 +159,9 @@ const listRecords: Check = { const pds = ctx.pds!; const did = ctx.did!; const collection = - collections && collections.length > 0 ? collections[0]! : "app.bsky.feed.post"; + collections && collections.length > 0 + ? collections[0]! + : "app.bsky.feed.post"; const url = xrpcUrl(pds, "com.atproto.repo.listRecords", { repo: did, collection, @@ -167,7 +174,8 @@ const listRecords: Check = { if (!res.ok) { return { status: "fail", - message: `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: + `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, @@ -255,7 +263,8 @@ const getRecord: Check = { if (!res.ok) { return { status: "fail", - message: `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: + `${res.status} ${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, @@ -280,7 +289,10 @@ const getRecord: Check = { message: `CID matches (${expectedCid.slice(0, 16)}…)`, evidence: { request: { method: "GET", url }, - response: { status: res.status, body: { uri: res.data.uri, cid: res.data.cid } }, + response: { + status: res.status, + body: { uri: res.data.uri, cid: res.data.cid }, + }, }, }; } catch (error) { @@ -360,7 +372,12 @@ const listRecordsCursor: Check = { cursor, }); const second = await client.get("com.atproto.repo.listRecords", { - params: { repo: did as Did, collection: collection as Nsid, limit: 1, cursor }, + params: { + repo: did as Did, + collection: collection as Nsid, + limit: 1, + cursor, + }, }); if (!second.ok) { return { @@ -697,7 +714,10 @@ const getRepoCarValidates: Check = { return { status: "fail", message: `${missingRoots.length} root CID(s) not present in block set`, - evidence: { expected: missingRoots, actual: [...cidStrings].slice(0, 5) }, + evidence: { + expected: missingRoots, + actual: [...cidStrings].slice(0, 5), + }, }; } return { @@ -728,11 +748,11 @@ const getRepoCarVerifyCommitSignature: Check = { label: "CAR file commit signature validates", requires: ["pds", "did"], run: async (ctx): Promise => { - const didDoc = ctx.didDoc! + const didDoc = ctx.didDoc!; if (!repoCarBytes) { return { status: "skip", message: "no CAR bytes to parse" }; } - const result = await verifyCar(repoCarBytes,didDoc) + const result = await verifyCar(repoCarBytes, didDoc); if (result.ok) { return { status: "pass", @@ -744,7 +764,7 @@ const getRepoCarVerifyCommitSignature: Check = { message: result.message, }; } - } + }, }; export const repoReadChecks: Check[] = [ diff --git a/apps/check/src/checks/repo-write.ts b/apps/check/src/checks/repo-write.ts index 485c7624..3da5e88f 100644 --- a/apps/check/src/checks/repo-write.ts +++ b/apps/check/src/checks/repo-write.ts @@ -54,7 +54,9 @@ function sessionMismatch(ctx: { return null; } -function buildClient(agent: { handle: (path: string, init?: RequestInit) => Promise }) { +function buildClient(agent: { + handle: (path: string, init?: RequestInit) => Promise; +}) { return new Client({ handler: agent }); } diff --git a/apps/check/src/checks/server.ts b/apps/check/src/checks/server.ts index f1164d3e..2e5c8381 100644 --- a/apps/check/src/checks/server.ts +++ b/apps/check/src/checks/server.ts @@ -198,8 +198,7 @@ const healthCheck: Check = { evidence: { request: { method: "GET", url }, response: { status: res.status, body }, - error: - "_health convention is { status, version } — both missing", + error: "_health convention is { status, version } — both missing", }, }; } diff --git a/apps/check/src/checks/sync.ts b/apps/check/src/checks/sync.ts index e600ea36..7b7f48eb 100644 --- a/apps/check/src/checks/sync.ts +++ b/apps/check/src/checks/sync.ts @@ -44,8 +44,7 @@ const getLatestCommit: Check = { if (!res.ok) { return { status: "fail", - message: - `${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: `${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, @@ -107,8 +106,7 @@ const getRepoStatus: Check = { if (!res.ok) { return { status: "fail", - message: - `${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: `${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, @@ -179,8 +177,7 @@ const getBlocks: Check = { if (!res.ok) { return { status: "fail", - message: - `${res.data.error}: ${res.data.message ?? ""}`.trim(), + message: `${res.data.error}: ${res.data.message ?? ""}`.trim(), evidence: { request: { method: "GET", url }, response: { status: res.status, body: res.data }, diff --git a/apps/check/src/components/CheckRow.tsx b/apps/check/src/components/CheckRow.tsx index af967dc1..8e8fdde1 100644 --- a/apps/check/src/components/CheckRow.tsx +++ b/apps/check/src/components/CheckRow.tsx @@ -113,9 +113,7 @@ export function CheckRow(props: { result: CheckResult }) {
- - check - + check {props.result.check.id}
@@ -124,9 +122,7 @@ export function CheckRow(props: { result: CheckResult }) { message - - {message()} - + {message()}
)}
diff --git a/apps/check/src/components/OAuthFlowView.tsx b/apps/check/src/components/OAuthFlowView.tsx index b808286a..cb17b594 100644 --- a/apps/check/src/components/OAuthFlowView.tsx +++ b/apps/check/src/components/OAuthFlowView.tsx @@ -135,8 +135,7 @@ function downloadFlowJson(state: FlowState) { status: s.status, message: s.message, evidence: s.evidence, - durationMs: - s.startedAt && s.endedAt ? s.endedAt - s.startedAt : null, + durationMs: s.startedAt && s.endedAt ? s.endedAt - s.startedAt : null, })), }; downloadString( @@ -288,10 +287,7 @@ export function OAuthFlowView(props: { pass++; applicable++; } - if ( - step.status === "fail" || - step.status === "warn" - ) { + if (step.status === "fail" || step.status === "warn") { applicable++; } if (step.status === "fail") fail++; @@ -330,9 +326,7 @@ export function OAuthFlowView(props: { OAuth conformance flow
-

- {props.state.target} -

+

{props.state.target}