diff --git a/index.html b/index.html --- a/index.html +++ b/index.html @@ -12,12 +12,20 @@ } body { margin: 0; - padding: 2rem; + padding: 2rem 2rem 0.5rem; line-height: 1.5; + min-height: 100vh; + box-sizing: border-box; + display: flex; + flex-direction: column; } .container { max-width: 48em; margin: 0 auto; + width: 100%; + flex: 1; + display: flex; + flex-direction: column; } h1 { font-size: 1rem; @@ -25,45 +33,186 @@ } p { color: #444; + margin: 0; } #dropzone { - border: 3px dashed currentColor; - border-radius: 1em; + border: 3px dashed #888; + border-radius: 0.5em; padding: 3rem; text-align: center; cursor: pointer; - margin: 3rem 0 1.5rem; + margin: 3rem 0; } #dropzone.dragover { border-style: dotted; font-style: italic; } - #results { - white-space: pre-wrap; + /* top-level result blocks get spacing; per-element rules can override */ + #results .head, + #results .keyline, + #results .err, + #results details { + margin-top: 1.25rem; + } + #results .head:first-child, + #results .err:first-child { + margin-top: 0; + } + .head { + display: flex; + gap: 0.5em; + align-items: baseline; + } + .filename { + font-size: 0.9em; word-break: break-all; - overflow-wrap: anywhere; } - .ok { - color: green; + .badge { + display: inline-block; + padding: 0.1em 0.6em; + border-radius: 1em; + font-weight: 700; + font-size: 0.9em; } - .bad { + .badge.ok { + background: green; + color: #fff; + } + .badge.bad { + background: red; + color: #fff; + } + .err { color: red; + word-break: break-all; } - .warn { + .keyline { + word-break: break-all; + } + .commit-wrap { + margin: 3rem 0 2rem; + } + .commit-title { + font-weight: 600; + margin: 0.5rem 0; + } + .commit-wrap > table { + border-collapse: collapse; + width: 100%; + } + .commit th, + .commit td { + text-align: left; + padding: 0.4em 0.6em; + border-bottom: 1px solid #8888; + vertical-align: top; + word-break: break-all; + } + .commit td { + font-size: 0.9em; + } + .commit th { + font-weight: 600; + font-size: 0.85em; + opacity: 0.7; + } + .commit td:first-child { + white-space: nowrap; + } + .commit tr.warn td { color: darkorange; + } + .commit tr.unexpected td { + opacity: 0.6; + font-style: italic; + } + .commit .note { + font-size: 0.8em; + font-style: italic; + opacity: 0.8; + } + .copyrow { + word-break: break-all; + } + .copyrow code { + display: block; + font-size: 0.85em; + max-height: 8em; + overflow: auto; + margin: 0.5em 0; + } + .copyrow button { + font-family: inherit; + font-size: 0.85em; + margin-right: 0.5em; + cursor: pointer; + } + details { + margin-bottom: 3rem; + } + details > summary { + opacity: 0.6; + font-size: 0.85em; + cursor: pointer; + } + .copyrow .copybtn { + margin-right: 0.75em; + } + /* segmented hex/base64 toggle */ + .fmt-toggle { + display: inline-flex; + border: 1px solid #8888; + border-radius: 0.3em; + overflow: hidden; + font-size: 0.85em; + vertical-align: middle; + } + .fmt-toggle button { + font-family: inherit; + margin: 0; + padding: 0.15em 0.6em; + cursor: pointer; + background: none; + border: none; + color: inherit; + opacity: 0.5; + } + .fmt-toggle button[aria-pressed='true'] { + opacity: 1; + background: #8883; + } + footer { + margin-top: auto; + padding-top: 0.5rem; + border-top: 1px solid #8888; + font-size: 0.85em; + color: #888; + } + footer a { + color: inherit; + } + .muted { + opacity: 0.6; } @media (prefers-color-scheme: dark) { p { color: #ccc; } - .ok { - color: #7ee78b; + .badge.ok { + background: #2ea043; } - .bad { + .badge.bad { + background: #da3633; + } + .err { color: #ff7b72; } - .warn { + .commit tr.warn td { color: #ffa657; + } + .commit th, + .commit td { + border-bottom-color: #fff2; } } @@ -72,7 +221,7 @@

CAR commit object signature verifier

- extract the commit object, resolve the identity's public key, check the archive's signature against it. does not verify repository contents. + Extracts the commit object, resolves the identity's public key, and checks the archive's signature. Does not verify other archive contents.

@@ -81,6 +230,12 @@
+ +
diff --git a/dist/index.html b/dist/index.html new file mode 100644 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,242 @@ + + + + + atproto CAR signature verifier + + + + + + +
+

CAR commit object signature verifier

+

+ Extracts the commit object, resolves the identity's public key, and checks the archive's signature. Does not verify other archive contents. +

+ +
+ drop a .car here (or click) +
+ + +
+ + +
+ + diff --git a/src/debug.ts b/src/debug.ts --- a/src/debug.ts +++ b/src/debug.ts @@ -6,87 +6,142 @@ const EXPECTED_FIELDS = new Set(['version', 'did', 'data', 'rev', 'sig', 'prev']); +/** one row of the commit table */ +export interface CommitField { + /** field name as decoded */ + name: string; + /** the atcute type the value decoded to (no back-inferred CBOR tag) */ + type: string; + /** byte length where meaningful (Bytes, CidLink), else undefined */ + length?: number; + /** human-readable value */ + value: string; + /** note flagging something unexpected (e.g. prev should be null) */ + note?: string; + /** true for fields beyond the six expected commit fields */ + unexpected?: boolean; +} + +/** structured description of a decoded commit, for the UI to render as a table */ +export interface CommitDescription { + /** all six expected fields present and correctly typed? */ + wellFormed: boolean; + /** the six known fields, in canonical order, then any unexpected fields */ + fields: CommitField[]; +} + /** bytes → lowercase hex */ const toHex = (bytes: Uint8Array): string => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); -/** truncate a long string for inline display, keeping head + tail */ -const truncate = (s: string, max = 52): string => - s.length <= max ? s : `${s.slice(0, max - 1)}…`; - /** - * render a deserialized commit as a readable, typed text tree. the point is to - * make an unexpected value in any field — especially `prev`, which should be - * `null` — impossible to miss. unknown fields are shown with a warning. + * describe a deserialized commit as structured rows. the point is to make an + * unexpected value in any field — especially `prev`, which should be `null` — + * impossible to miss. we surface the atcute type as given by the decoder; we do + * *not* back-infer CBOR tag numbers, since a wrong inference here would be + * extremely misleading for anyone debugging the bytes. */ -export const renderCommit = (commit: Record): string => { - const wellFormed = isWellFormedCommit(commit); - const lines: string[] = []; - - lines.push(`commit ${wellFormed ? '(well-formed)' : '(malformed)'}`); - lines.push('─'.repeat(40)); - +export const describeCommit = (commit: Record): CommitDescription => { const c = commit as Partial; - const annotate = (label: string, value: string, type: string, note?: string) => { - const noteStr = note ? ` ← ${note}` : ''; - lines.push(` ${label.padEnd(8)}: ${value} (${type})${noteStr}`); - }; + const fields: CommitField[] = []; // version - if (typeof c.version === 'number') { - annotate('version', String(c.version), 'number'); - } else { - annotate('version', JSON.stringify(c.version), typeof c.version, 'expected 3'); - } + fields.push(describeValue('version', c.version, (v) => typeof v === 'number', String(c.version))); // did - if (typeof c.did === 'string') { - annotate('did', c.did, 'string'); - } else { - annotate('did', JSON.stringify(c.did), String(c.did), 'expected string'); - } + fields.push(describeValue('did', c.did, (v) => typeof v === 'string', typeof c.did === 'string' ? c.did : JSON.stringify(c.did))); // data (CID of the MST root) - if (isCidLink(c.data)) { - annotate('data', truncate(CID.toString(fromCidLink(c.data))), 'CidLink'); - } else { - annotate('data', JSON.stringify(c.data), String(c.data), 'expected CidLink'); - } + fields.push(describeCidLink('data', c.data)); // rev - if (typeof c.rev === 'string') { - annotate('rev', c.rev, 'string'); - } else { - annotate('rev', JSON.stringify(c.rev), String(c.rev), 'expected string'); - } + fields.push(describeValue('rev', c.rev, (v) => typeof v === 'string', typeof c.rev === 'string' ? c.rev : JSON.stringify(c.rev))); // prev — the field this tool exists to inspect if (c.prev === null) { - annotate('prev', 'null', 'null'); + fields.push({ name: 'prev', type: 'null', value: 'null' }); } else if (isCidLink(c.prev)) { - annotate('prev', truncate(CID.toString(fromCidLink(c.prev))), 'CidLink', 'expected null! commit points at a previous commit'); + fields.push({ + name: 'prev', + type: 'CidLink', + length: fromCidLink(c.prev).bytes.length, + value: CID.toString(fromCidLink(c.prev)), + note: 'expected null! commit points at a previous commit', + }); } else { - annotate('prev', JSON.stringify(c.prev), String(c.prev), 'expected null'); + fields.push({ + name: 'prev', + type: typeof c.prev, + value: JSON.stringify(c.prev), + note: 'expected null', + }); } // sig if (isBytes(c.sig)) { const sigBytes = fromBytes(c.sig); - annotate('sig', `${sigBytes.length} bytes`, 'Bytes', `hex: ${truncate(toHex(sigBytes), 80)}`); + fields.push({ + name: 'sig', + type: 'Bytes', + length: sigBytes.length, + value: toHex(sigBytes), + }); } else { - annotate('sig', JSON.stringify(c.sig), String(c.sig), 'expected Bytes'); + fields.push({ + name: 'sig', + type: typeof c.sig, + value: JSON.stringify(c.sig), + note: 'expected Bytes', + }); } // surface any fields beyond the six expected ones const unknown = Object.keys(commit).filter((k) => !EXPECTED_FIELDS.has(k)); - if (unknown.length > 0) { - lines.push('─'.repeat(40)); - lines.push(`(unexpected fields: ${unknown.join(', ')})`); - for (const key of unknown) { - const v = commit[key]; - lines.push(` ${key.padEnd(8)}: ${JSON.stringify(v)} (${typeof v})`); - } + for (const key of unknown) { + const v = commit[key]; + fields.push({ + name: key, + type: typeof v, + value: JSON.stringify(v), + unexpected: true, + }); } - return lines.join('\n'); + return { wellFormed: isWellFormedCommit(commit), fields }; +}; + +/** helper: describe a value with an ok/expected flag */ +const describeValue = ( + name: string, + value: unknown, + expected: (v: unknown) => boolean, + display: string, +): CommitField => { + const field: CommitField = { + name, + type: typeof value, + value: display, + }; + if (!expected(value)) { + field.note = 'unexpected type'; + } + return field; +}; + +/** helper: describe a CidLink field (data, prev) */ +const describeCidLink = (name: string, value: unknown): CommitField => { + if (isCidLink(value)) { + return { + name, + type: 'CidLink', + length: fromCidLink(value).bytes.length, + value: CID.toString(fromCidLink(value)), + }; + } + return { + name, + type: typeof value, + value: JSON.stringify(value), + note: 'expected CidLink', + }; }; diff --git a/src/main.ts b/src/main.ts --- a/src/main.ts +++ b/src/main.ts @@ -1,45 +1,185 @@ -import { renderCommit } from './debug.ts'; +import { describeCommit } from './debug.ts'; import { verifyCar } from './verify.ts'; const dropzone = document.getElementById('dropzone') as HTMLDivElement; const fileinput = document.getElementById('fileinput') as HTMLInputElement; const results = document.getElementById('results') as HTMLDivElement; -/** append a line with an optional class */ -const line = (text: string, cls?: string) => { - const el = document.createElement('div'); - el.textContent = text; - if (cls) el.className = cls; - results.appendChild(el); +/** bytes → lowercase hex */ +const toHex = (bytes: Uint8Array): string => + Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + +/** bytes → unpadded base64 */ +const toBase64 = (bytes: Uint8Array): string => { + let s = btoa(String.fromCharCode(...bytes)); + return s.replace(/=+$/, ''); +}; + +/** human-readable byte size, e.g. 933 KiB, 1.4 MiB, 4021 B */ +const prettySize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B`; + const units = ['KiB', 'MiB', 'GiB']; + let n = bytes / 1024; + let i = 0; + while (n >= 1024 && i < units.length - 1) { + n /= 1024; + i++; + } + // 2-3 significant digits + return `${n >= 100 ? n.toFixed(0) : n >= 10 ? n.toFixed(1) : n.toFixed(2)} ${units[i]}`; +}; + +/** create an element with optional text, class, and attrs */ +const el = ( + tag: K, + opts: { text?: string; cls?: string; href?: string; title?: string } = {}, +): HTMLElementTagNameMap[K] => { + const e = document.createElement(tag); + if (opts.text !== undefined) e.textContent = opts.text; + if (opts.cls) e.className = opts.cls; + if (opts.href !== undefined && e instanceof HTMLAnchorElement) e.href = opts.href; + if (opts.title !== undefined) e.title = opts.title; + return e; +}; + +/** copy text to the clipboard, returning whether it succeeded */ +const copyText = async (text: string): Promise => { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + return false; + } }; /** render the outcome of a verification attempt */ const handleFile = async (file: File) => { results.innerHTML = ''; - line(`file: ${file.name} (${file.size} bytes)`); + + // badge + filename + pretty size, one line (badge prepended after verify) + const head = el('div', { cls: 'head' }); + head.append(el('span', { text: file.name, cls: 'filename' }), el('span', { text: prettySize(file.size), cls: 'muted' })); + results.append(head); const buf = new Uint8Array(await file.arrayBuffer()); - const res = await verifyCar(buf); - // always show the debug view of the commit when we have one — even on failure. - if (res.commit) { - line(''); - line(renderCommit(res.commit)); - line(''); + // validity badge — prepended onto the filename line + const badgeCls = res.ok ? (res.signatureValid ? 'ok' : 'bad') : 'bad'; + const badgeLabel = res.ok ? (res.signatureValid ? 'VALID' : 'BAD SIGNATURE') : 'ERROR'; + head.prepend(badge(badgeLabel, badgeCls)); + + if (!res.ok) { + results.append(el('div', { text: `${res.stage}: ${res.message}`, cls: 'err' })); } + // public key, one line of text if (res.ok) { - line(`did resolved to ${res.publicKey.type} key (jwtAlg ${res.publicKey.jwtAlg})`, 'muted'); - line(`publicKeyMultibase: ${res.publicKey.publicKeyMultibase}`, 'muted'); - if (res.signatureValid) { - line('signature: VALID', 'ok'); - } else { - line('signature: INVALID', 'bad'); - } - } else { - line(`${res.stage} error: ${res.message}`, 'bad'); + results.append(keyLine(res.publicKey.type, res.publicKey.publicKeyMultibase, res.didDocUrl)); } + + // commit table — always when we have a commit, even on failure + if (res.commit) { + results.append(commitTable(res.commit)); + // raw commit bytes, hidden by default in a
+ if (res.ok) { + results.append(copyBytesRow(res.commitBytes)); + } + } +}; + +/** a validity badge */ +const badge = (label: string, cls: string): HTMLElement => el('span', { text: label, cls: `badge ${cls}` }); + +/** one-line public-key summary as plain text: + * `resolved key: [zQ3sh...](did-doc-link) (secp256k1)` */ +const keyLine = (type: string, multibase: string, didDocUrl: string): HTMLElement => { + const row = el('div', { cls: 'keyline' }); + row.append(el('span', { text: 'Resolved key: ', cls: 'muted' })); + // the key links to its DID doc + if (didDocUrl) { + const a = el('a', { text: multibase, href: didDocUrl, title: didDocUrl }); + a.target = '_blank'; + a.rel = 'noopener noreferrer'; + row.append(a); + } else { + row.append(el('span', { text: multibase })); + } + row.append(el('span', { text: ` (${type})` })); + return row; +}; + +/** the commit fields as a table */ +const commitTable = (commit: Record): HTMLElement => { + const desc = describeCommit(commit); + const table = el('table', { cls: 'commit' }); + const thead = el('thead'); + const headRow = el('tr'); + headRow.append(el('th', { text: 'field' }), el('th', { text: 'type' }), el('th', { text: 'value' })); + thead.append(headRow); + table.append(thead); + + const tbody = el('tbody'); + for (const f of desc.fields) { + const tr = el('tr', { cls: f.note ? 'warn' : f.unexpected ? 'unexpected' : undefined }); + const typeCell = el('td', { text: f.type + (f.length !== undefined ? ` (${f.length})` : '') }); + const valCell = el('td', { text: f.value, title: f.value }); + tr.append(el('td', { text: f.name }), typeCell, valCell); + if (f.note) { + valCell.append(el('div', { text: f.note, cls: 'note' })); + } + tbody.append(tr); + } + table.append(tbody); + + const wrap = el('div', { cls: 'commit-wrap' }); + wrap.append(el('div', { text: `Commit object ${desc.wellFormed ? '(well-formed):' : '(malformed):'}`, cls: 'commit-title' }), table); + return wrap; +}; + +/** raw commit bytes in a
, with a hex/base64 toggle and copy button */ +const copyBytesRow = (bytes: Uint8Array): HTMLElement => { + const details = el('details'); + const summary = el('summary', { text: 'Raw commit object bytes' }); + details.append(summary); + + const body = el('div', { cls: 'copyrow' }); + let asHex = true; + + const value = el('code', { text: toHex(bytes) }); + const copyBtn = el('button', { text: 'copy', cls: 'copybtn' }); + + // segmented hex/base64 toggle + const toggle = el('span', { cls: 'fmt-toggle' }); + const hexBtn = el('button', { text: 'hex' }); + const b64Btn = el('button', { text: 'base64' }); + hexBtn.setAttribute('aria-pressed', 'true'); + b64Btn.setAttribute('aria-pressed', 'false'); + toggle.append(hexBtn, b64Btn); + + const render = () => { + value.textContent = asHex ? toHex(bytes) : toBase64(bytes); + hexBtn.setAttribute('aria-pressed', String(asHex)); + b64Btn.setAttribute('aria-pressed', String(!asHex)); + }; + + copyBtn.addEventListener('click', async () => { + const ok = await copyText(value.textContent ?? ''); + copyBtn.textContent = ok ? 'copied!' : 'failed'; + setTimeout(() => (copyBtn.textContent = 'copy'), 1200); + }); + hexBtn.addEventListener('click', () => { + asHex = true; + render(); + }); + b64Btn.addEventListener('click', () => { + asHex = false; + render(); + }); + + body.append(value, copyBtn, toggle); + details.append(body); + return details; }; // --- drag & drop --- diff --git a/src/verify.ts b/src/verify.ts --- a/src/verify.ts +++ b/src/verify.ts @@ -1,7 +1,7 @@ import * as CAR from '@atcute/car'; import * as CBOR from '@atcute/cbor'; import type { Bytes } from '@atcute/cbor'; -import { fromBytes as unwrapBytes, isBytes } from '@atcute/cbor'; +import { fromBytes as unwrapBytes } from '@atcute/cbor'; import * as CID from '@atcute/cid'; import { getPublicKeyFromDidController, verifySig } from '@atcute/crypto'; import { @@ -9,7 +9,7 @@ PlcDidDocumentResolver, WebDidDocumentResolver, } from '@atcute/identity-resolver'; -import { getAtprotoVerificationMaterial, isAtprotoDid } from '@atcute/identity'; +import { getAtprotoVerificationMaterial, isAtprotoDid, isPlcDid, isWebDid, webDidToDocumentUrl } from '@atcute/identity'; import { isWellFormedCommit } from './types.ts'; @@ -21,10 +21,12 @@ publicKey: { type: string; jwtAlg: string; publicKeyMultibase: string }; /** did the signature verify against the resolved key? */ signatureValid: boolean; - /** sha256 of the re-encoded unsigned-commit bytes (what was signed) */ - unsignedSha256: string; - /** hex of the signature bytes */ - sigHex: string; + /** the commit's DID, for the link-out */ + did: string; + /** a human-viewable URL for the DID document the key was resolved from */ + didDocUrl: string; + /** raw bytes of the commit block (as stored in the CAR), for copying to a CBOR debugger */ + commitBytes: Uint8Array; } /** shape we surface to the UI: material from the DID doc + jwtAlg from the parsed key */ @@ -67,8 +69,11 @@ */ export const verifyCar = async (carBytes: Uint8Array): Promise => { let commit: Record; + let commitBytes: Uint8Array; try { - commit = extractCommit(carBytes); + const extracted = extractCommit(carBytes); + commit = extracted.commit; + commitBytes = extracted.bytes; } catch (err) { return { ok: false, @@ -92,14 +97,15 @@ } try { - const { valid, unsignedBytes, sigBytes } = await verifyCommitSignature(commit, publicKey.found); + const valid = await verifyCommitSignature(commit, publicKey.found); return { ok: true, commit, publicKey: publicKey.resolved, signatureValid: valid, - unsignedSha256: toHex(await sha256(unsignedBytes)), - sigHex: toHex(sigBytes), + did: publicKey.did, + didDocUrl: publicKey.didDocUrl, + commitBytes, }; } catch (err) { return { @@ -112,7 +118,7 @@ }; /** read the CAR root block and decode it as a commit */ -const extractCommit = (carBytes: Uint8Array): Record => { +const extractCommit = (carBytes: Uint8Array): { commit: Record; bytes: Uint8Array } => { const reader = CAR.fromUint8Array(carBytes); const roots = reader.roots; if (roots.length < 1) { @@ -136,13 +142,26 @@ throw new Error(`root block did not decode to a map`); } - return decoded; + return { commit: decoded, bytes: commitBytes }; +}; + +/** a human-viewable URL for a DID document (plc.directory for did:plc, well-known for did:web) */ +const didDocUrlFor = (did: string): string => { + if (isPlcDid(did)) { + return `https://plc.directory/${did}`; + } + if (isWebDid(did)) { + return webDidToDocumentUrl(did).href; + } + return ''; }; /** resolve the commit's DID to a public key plus the material we display */ const resolveKey = async (commit: Record): Promise<{ found: ReturnType; resolved: ResolvedKey; + did: string; + didDocUrl: string; }> => { const did = commit['did']; if (typeof did !== 'string') { @@ -162,6 +181,8 @@ return { found, resolved: { type: found.type, jwtAlg: found.jwtAlg, publicKeyMultibase: material.publicKeyMultibase }, + did, + didDocUrl: didDocUrlFor(did), }; }; @@ -169,12 +190,11 @@ * verify the commit signature: strip `sig`, re-encode, verify against the bytes. * the unsigned commit is re-encoded with the same dag-cbor codec the signer used, * so the round-trip is faithful (CidLink values round-trip through their tags). - * returns the unsigned + signature bytes for debug display. */ const verifyCommitSignature = async ( commit: Record, found: ReturnType, -): Promise<{ valid: boolean; unsignedBytes: Uint8Array; sigBytes: Uint8Array }> => { +): Promise => { if (!isWellFormedCommit(commit)) { throw new Error(`commit is not well-formed (missing/invalid fields)`); } @@ -183,15 +203,5 @@ const sigBytes = unwrapBytes(sig as Bytes) as Uint8Array; const data = CBOR.encode(unsigned) as Uint8Array; - const valid = await verifySig(found, sigBytes, data); - return { valid, unsignedBytes: data, sigBytes }; + return await verifySig(found, sigBytes, data); }; - -const toHex = (bytes: Uint8Array): string => - Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); - -const sha256 = async (data: Uint8Array): Promise => - new Uint8Array(await crypto.subtle.digest('SHA-256', data as BufferSource)); - -/** is this a `Bytes`-wrapped value? re-exported for the debug renderer */ -export { isBytes }; diff --git a/dist/assets/index-CywrNjA2.js b/dist/assets/index-CywrNjA2.js new file mode 100644 --- /dev/null +++ b/dist/assets/index-CywrNjA2.js @@ -0,0 +1,1 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(o){if(o.ep)return;o.ep=!0;const s=n(o);fetch(o.href,s)}})();const xn=e=>e.toHex(),Rt=new TextEncoder,rt=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}),mn=crypto.subtle,Ve=e=>new Uint8Array(e),q=Ve,En=(e,t)=>{const n=e.length,r=t.length,o=nc)return 1}return nr?1:0},Bt=(e,t)=>{let n=0;const r=e.length;let o;if(t===void 0)for(o=t=0;oc){s.set(i.subarray(0,c),n);break}s.set(i,n),n+=i.length}return s},ot=e=>Rt.encode(e),vn=(e,t,n,r)=>{let o;return n===void 0?o=e:o=e.subarray(n),Rt.encodeInto(t,o).written},A=String.fromCharCode,kn=(e,t,n)=>{if(n<4){if(n<2){if(n===0)return"";const R=e[t];return R&128?null:A(R)}const y=e[t],E=e[t+1];if((y|E)&128)return null;if(n===2)return A(y,E);const k=e[t+2];return k&128?null:A(y,E,k)}const r=e[t],o=e[t+1],s=e[t+2],i=e[t+3];if((r|o|s|i)&128)return null;if(n<8){if(n===4)return A(r,o,s,i);const y=e[t+4];if(y&128)return null;if(n===5)return A(r,o,s,i,y);const E=e[t+5];if(E&128)return null;if(n===6)return A(r,o,s,i,y,E);const k=e[t+6];return k&128?null:A(r,o,s,i,y,E,k)}const c=e[t+4],a=e[t+5],f=e[t+6],l=e[t+7];if((c|a|f|l)&128)return null;if(n<12){if(n===8)return A(r,o,s,i,c,a,f,l);const y=e[t+8];if(y&128)return null;if(n===9)return A(r,o,s,i,c,a,f,l,y);const E=e[t+9];if(E&128)return null;if(n===10)return A(r,o,s,i,c,a,f,l,y,E);const k=e[t+10];return k&128?null:A(r,o,s,i,c,a,f,l,y,E,k)}const u=e[t+8],d=e[t+9],g=e[t+10],h=e[t+11];if((u|d|g|h)&128)return null;if(n===12)return A(r,o,s,i,c,a,f,l,u,d,g,h);const w=e[t+12];if(w&128)return null;if(n===13)return A(r,o,s,i,c,a,f,l,u,d,g,h,w);const b=e[t+13];if(b&128)return null;if(n===14)return A(r,o,s,i,c,a,f,l,u,d,g,h,w,b);const m=e[t+14];return m&128?null:A(r,o,s,i,c,a,f,l,u,d,g,h,w,b,m)},An=(e,t=0,n=e.length-t)=>{if(n<=15){const r=kn(e,t,n);if(r!==null)return r}return t===0&&n===e.length?rt.decode(e):rt.decode(e.subarray(t,t+n))},Sn=e=>e>=56320&&e<=57343,Rn=e=>{const t=e.length;let n=0,r=0;for(;n+3=128)break;n+=4,r+=4}for(;n56319?(n+=1,r+=3):Sn(e.charCodeAt(n+1))?(n+=2,r+=4):(n+=1,r+=3)}return r},Bn=async e=>new Uint8Array(await mn.digest("SHA-256",e)),Ze=(e,t,n)=>r=>{const o=(1<t;)i-=t,s+=e[o&c>>i];if(i!==0&&(s+=e[o&c<{const r=new Uint8Array(256).fill(255);for(let c=0;c{let a=c.length;if(n&&a!==0){if(a%i!==0)throw new SyntaxError("unexpected end of data");let g=0;for(;c.charCodeAt(a-1)===61;)if(--a,++g>=i)throw new SyntaxError("invalid base string")}const f=q(a*t/8|0);let l=0,u=0,d=0;for(let g=0;g=8&&(l-=8,f[d++]=255&u>>l)}if(l>=t||(255&u<<8-l)!==0)throw new SyntaxError("unexpected end of data");return f}},Cn=e=>{if(e.length>=255)throw new RangeError("alphabet too long");const t=e.length,n=e.charAt(0),r=Math.log(256)/Math.log(t);return o=>{if(o.length===0)return"";let s=0,i=0,c=0;const a=o.length;for(;c!==a&&o[c]===0;)c++,s++;const f=a-c,l=f*r+1>>>0,u=Ve(l);{const h=f%3,w=a-h;for(;c{if(e.length>=255)throw new RangeError("alphabet too long");const t=new Uint8Array(128).fill(255);for(let i=0;i=128)throw new RangeError("non-ASCII character in alphabet");if(t[c]!==255)throw new RangeError(`${e[i]} is ambiguous`);t[c]=i}const n=e.length,r=n*n,o=e.charAt(0),s=Math.log(n)/Math.log(256);return i=>{if(i.length===0)return q(0);let c=0,a=0,f=0;for(;i[c]===o;)a++,c++;const l=i.length-c,u=l*s+1>>>0,d=Ve(u);{const w=l&1,b=i.length-w;for(;c>>=8;if(R!==0)throw new Error("non-zero carry");f=Te,c+=2}}if(c>>=8;if(b!==0)throw new Error("non-zero carry");f=m}let g=u-f;for(;g!==u&&d[g]===0;)g++;if(g===a)return d;const h=q(a+(u-g));return h.fill(0,0,a),h.set(d.subarray(g),a),h}},$n="0123456789abcdef",In=Ze($n,4,!1),Tn="fromHex"in Uint8Array,_t=Tn?xn:In,Dn=(()=>{const e=new Uint8Array(128).fill(255),t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let n=0;n{const n=t%3;if(e.length!==(t/3|0)*4+Un[n])throw new SyntaxError("invalid base64 string");if(n!==0&&(Dn[e.charCodeAt(e.length-1)]&(n===1?15:3))!==0)throw new SyntaxError("invalid base64 string")},On=(e,t)=>{if(e.length!==((t+2)/3|0)*4)throw new SyntaxError("invalid base64 string")},Nn=e=>{const t=Uint8Array.fromBase64(e,{alphabet:"base64",lastChunkHandling:"loose"});return Ln(e,t.length),t},jn=e=>e.toBase64({alphabet:"base64",omitPadding:!0}),Mn=e=>{const t=Uint8Array.fromBase64(e,{alphabet:"base64",lastChunkHandling:"strict"});return On(e,t.length),t},Kn=e=>e.toBase64({alphabet:"base64url",omitPadding:!0}),Ge="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",Pn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",Fn=Ct(Ge,6,!1),zn=Ze(Ge,6,!1),Hn=Ct(Ge,6,!0),Vn=Ze(Pn,6,!1),Re="fromBase64"in Uint8Array,Zn=Re?Nn:Fn,Gn=Re?jn:zn,Xn=Re?Mn:Hn,st=Re?Kn:Vn,Yn="abcdefghijklmnopqrstuvwxyz234567",qn=(()=>{const e=new Uint8Array(32);for(let t=0;t<32;t++)e[t]=Yn.charCodeAt(t);return e})(),ae=String.fromCharCode,$t=e=>{const t=e.length,n=t/5|0,r=t-n*5,o=qn;let s="",i=0;const c=n/2|0;for(let a=0;a>>3],o[(f<<2|l>>>6)&31],o[l>>>1&31],o[(l<<4|u>>>4)&31],o[(u<<1|d>>>7)&31],o[d>>>2&31],o[(d<<3|g>>>5)&31],o[g&31],o[h>>>3],o[(h<<2|w>>>6)&31],o[w>>>1&31],o[(w<<4|b>>>4)&31],o[(b<<1|m>>>7)&31],o[m>>>2&31],o[(m<<3|y>>>5)&31],o[y&31]),i+=10}if(n&1){const a=e[i],f=e[i+1],l=e[i+2],u=e[i+3],d=e[i+4];s+=ae(o[a>>>3],o[(a<<2|f>>>6)&31],o[f>>>1&31],o[(f<<4|l>>>4)&31],o[(l<<1|u>>>7)&31],o[u>>>2&31],o[(u<<3|d>>>5)&31],o[d&31]),i+=5}if(r>0){let a=0,f=0;for(let l=i;l=5;)f-=5,s+=ae(o[a>>>f&31]);f>0&&(s+=ae(o[a<<5-f&31]))}return s},Wn="abcdefghijklmnopqrstuvwxyz234567",T=(()=>{const e=new Uint8Array(128).fill(255);for(let t=0;t<32;t++)e[Wn.charCodeAt(t)]=t;return e})(),Jn=e=>{const t=e.length,n=q(t*5/8|0);let r=0,o=0;const s=t-t%8;for(;o>>2,n[r+1]=(w<<6|b<<1|m>>>4)&255,n[r+2]=(m<<4|y>>>1)&255,n[r+3]=(y<<7|E<<2|k>>>3)&255,n[r+4]=(k<<5|R)&255,r+=5}if(o=8&&(i-=8,n[r++]=255&c>>i)}if(i>=5||(255&c<<8-i)!==0)throw new SyntaxError("unexpected end of data")}return n},It="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",Qn=_n(It),er=Cn(It),Ne=1,Tt=18,Dt=85,Ut=113,tr=e=>{if(e.length<36)throw new RangeError("cid too short");const t=e[0],n=e[1],r=e[2],o=e[3];if(t!==Ne)throw new RangeError(`incorrect cid version (got v${t})`);if(n!==Ut&&n!==Dt)throw new RangeError(`incorrect cid codec (got 0x${n.toString(16)})`);if(r!==Tt)throw new RangeError(`incorrect cid digest codec (got 0x${r.toString(16)})`);if(o!==32)throw new RangeError(`incorrect cid digest size (got ${o})`);return[{version:Ne,codec:n,digest:{codec:r,contents:e.subarray(4,36)},bytes:e.subarray(0,36)},e.subarray(36)]},Xe=e=>{const[t,n]=tr(e);if(n.length!==0)throw new RangeError("cid bytes includes remainder");return t},Lt=e=>{if(e.length!==59||e[0]!=="b")throw new SyntaxError("not a valid cid string");const t=Jn(e.slice(1));return Xe(t)},Ye=e=>`b${$t(e.bytes)}`,nr=e=>{if(e.length!==37||e[0]!==0)throw new SyntaxError("invalid binary cid");return Xe(e.subarray(1))},rr=Symbol.for("@atcute/cid-link-wrapper");class ee{[rr]=!0;bytes;constructor(t){this.bytes=t}get $link(){const t=`b${$t(this.bytes)}`;return Object.defineProperty(this,"$link",{value:t,enumerable:!0}),t}toJSON(){return{$link:this.$link}}}const pe=e=>{const t=e;return t instanceof ee||t!==null&&typeof t=="object"&&typeof t.$link=="string"},ye=e=>e instanceof ee?Xe(e.bytes):Lt(e.$link),or=Symbol.for("@atcute/bytes-wrapper");class Be{[or]=!0;buf;constructor(t){this.buf=t}get $bytes(){return Gn(this.buf)}toJSON(){return{$bytes:this.$bytes}}}const Ot=e=>{const t=e;return t instanceof Be||t!==null&&typeof t=="object"&&typeof t.$bytes=="string"},sr=e=>new Be(e),qe=e=>{if(e instanceof Be)return e.buf;const t=e.$bytes;return t.charCodeAt(t.length-1)===61?Xn(t):Zn(t)},B=(e,t)=>{if(t>e.b.length-e.p)throw new RangeError("unexpected end of input")},je=(e,t)=>{if(t<24)return t;let n;switch(t){case 24:{if(B(e,1),n=he(e),n<24)throw new TypeError("non-canonical argument encoding");break}case 25:{if(B(e,2),n=cr(e),n<256)throw new TypeError("non-canonical argument encoding");break}case 26:{if(B(e,4),n=Me(e),n<65536)throw new TypeError("non-canonical argument encoding");break}case 27:{if(B(e,8),n=ar(e),n<4294967296)throw new TypeError("non-canonical argument encoding");break}default:throw new Error(`invalid argument encoding; got ${t}`)}return n},ir=e=>{B(e,8);const n=(e.v??=new DataView(e.b.buffer,e.b.byteOffset,e.b.byteLength)).getFloat64(e.p);if(!Number.isFinite(n))throw new RangeError("NaN and Infinity values not supported");return e.p+=8,n},he=e=>e.b[e.p++],cr=e=>{let t=e.p;const n=e.b,r=n[t++]<<8|n[t++];return e.p=t,r},Me=e=>{let t=e.p;const n=e.b,r=(n[t++]<<24|n[t++]<<16|n[t++]<<8|n[t++])>>>0;return e.p=t,r},ar=e=>{const t=Me(e),n=Me(e);if(t>2097151)throw new RangeError("can't decode integers beyond safe integer range");return t*2**32+n},Nt=(e,t)=>{B(e,t);const n=An(e.b,e.p,t);return e.p+=t,n},fr=(e,t)=>{B(e,t);const n=e.b.subarray(e.p,e.p+=t);return sr(n)},lr=(e,t)=>{B(e,t);const n=nr(e.b.subarray(e.p,e.p+=t));return new ee(n.bytes)},it=e=>{B(e,1);const t=he(e),n=t>>5;if(n!==3)throw new TypeError(`expected map to only have string keys; got type ${n}`);const r=t&31,o=r<24?r:je(e,r);return e.ks=e.p,e.kl=o,Nt(e,o)},ur=e=>{const t=e.length,n={b:e,v:null,p:0,ks:0,kl:0};let r=null,o;e:for(;n.p>5,c=s&31,a=i===7?0:c<24?c:je(n,c);switch(i){case 0:{o=a;break}case 1:{if(o=-1-a,o0){B(n,a),r={t:1,c:o=new Array(a),k:null,r:a,n:r};continue e}o=[];break}case 5:{if(o={},a>0){const f=it(n);r={t:0,c:o,k:f,ks:n.ks,kl:n.kl,r:a,n:r};continue e}break}case 6:{switch(a){case 42:{B(n,1);const f=he(n),l=f>>5,u=f&31;if(l!==2)throw new TypeError(`expected cid-link to be type 2 (bytes); got type ${l}`);const d=je(n,u);o=lr(n,d);break}default:throw new TypeError(`unsupported tag; got ${a}`)}break}case 7:{switch(c){case 20:case 21:{o=c===21;break}case 22:{o=null;break}case 27:{o=ir(n);break}default:throw new Error(`invalid simple value; got ${c}`)}break}default:throw new TypeError(`invalid type; got ${i}`)}for(;r!==null;){switch(r.t){case 0:{const f=r.c,l=r.k;l==="__proto__"&&Object.defineProperty(f,l,{enumerable:!0,configurable:!0,writable:!0}),f[l]=o;break}case 1:{const f=r.c,l=f.length-r.r;f[l]=o;break}}if(--r.r){if(!r.t){const f=r.ks,l=r.kl;r.k=it(n);const u=n.ks,d=n.kl;let g=d-l;if(g===0){const h=n.b;for(let w=0;w{const[t,n]=ur(e);if(n.length!==0)throw new Error("decoded value contains remainder");return t},W=9,Mt=1024,dr=Math.max,pr=Number.isInteger,yr=Number.isFinite,hr=Number.MAX_SAFE_INTEGER,gr=Number.MIN_SAFE_INTEGER,_=(e,t)=>{const n=e.b,r=e.p;n.byteLengthe<24?1:e<256?2:e<65536?3:e<4294967296?5:9,wr=(e,t)=>{const n=e.b;(e.v??=new DataView(n.buffer,n.byteOffset,n.byteLength)).setFloat64(e.p,t),e.p+=8},C=(e,t)=>{e.b[e.p++]=t},br=(e,t)=>{let n=e.p;const r=e.b;r[n++]=t>>>8,r[n++]=t&255,e.p=n},xr=(e,t)=>{let n=e.p;const r=e.b;r[n++]=t>>>24,r[n++]=t>>>16&255,r[n++]=t>>>8&255,r[n++]=t&255,e.p=n},mr=(e,t)=>{let n=e.p;const r=e.b,o=t/2**32|0,s=t>>>0;r[n++]=o>>>24,r[n++]=o>>>16&255,r[n++]=o>>>8&255,r[n++]=o&255,r[n++]=s>>>24,r[n++]=s>>>16&255,r[n++]=s>>>8&255,r[n++]=s&255,e.p=n},N=(e,t,n)=>{n<24?C(e,t<<5|n):n<256?(C(e,t<<5|24),C(e,n)):n<65536?(C(e,t<<5|25),br(e,n)):n<4294967296?(C(e,t<<5|26),xr(e,n)):(C(e,t<<5|27),mr(e,n))},Er=(e,t)=>{_(e,W),t<0?N(e,1,-t-1):N(e,0,t)},vr=(e,t)=>{_(e,9),C(e,251),wr(e,t)},kr=(e,t)=>{if(!yr(t))throw new RangeError("NaN and Infinity values not supported");if(t>hr||t{const n=t.length;if(n===0){_(e,1),C(e,96);return}_(e,n*3+W);e:{const c=e.p+De(n),a=t.charCodeAt(0);if(a>127)break e;e.b[c]=a;let f=1;for(;f+3127)break e;e.b[c+f]=l}N(e,3,n),e.p+=n;return}const r=De(n*2),o=e.p+r,s=vn(e.b,t,o),i=De(s);r!==i&&e.b.copyWithin(e.p+i,o,o+s),N(e,3,s),e.p+=s},at=(e,t)=>{const n=qe(t),r=n.byteLength;_(e,r+W),N(e,2,r),e.b.set(n,e.p),e.p+=r},ft=(e,t)=>{const n=t instanceof ee?t.bytes:Lt(t.$link).bytes,r=n.byteLength+1;_(e,r+2*W),N(e,6,42),N(e,2,r),e.b[e.p]=0,e.b.set(n,e.p+1),e.p+=r},Ke=(e,t)=>{switch(typeof t){case"boolean":return _(e,1),C(e,244+ +t);case"number":return kr(e,t);case"string":return ct(e,t);case"object":{if(t===null)return _(e,1),C(e,246);if(Array.isArray(t)){const n=t.length;_(e,W),N(e,4,n);for(let r=0;r({c:[],b:q(Mt),v:null,p:0,l:0}),Sr=e=>{const t=Ar();Ke(t,e);const n=t.b.subarray(0,t.p);return t.c.length?(t.c.push(n),Bt(t.c,t.l+t.p)):n},Rr=e=>{const t=e.length,n=new Array(t),r=new Array(t);for(let o=0;o=0;a--){let f=i-n[a];if(f===0&&(f=En(c??=ot(s),r[a]??=ot(e[a]))),f>0)break;e[a+1]=e[a],n[a+1]=n[a],r[a+1]=r[a]}e[a+1]=s,n[a+1]=i,r[a+1]=c}},Br=e=>{const t=Object.keys(e);let n=0,r=!0;for(let o=0;o127){r=!1;break}}const i=s.length;let c=n-1;for(;c>=0;c--){const a=t[c];if(i>a.length||i===a.length&&s>a)break;t[c+1]=a}t[c+1]=s,n++}return t.length=n,r||Rr(t),t},Kt=e=>{if(e===null||typeof e!="object")return!1;const t=e;return t.version===3&&typeof t.did=="string"&&pe(t.data)&&typeof t.rev=="string"&&(t.prev===null||pe(t.prev))&&Ot(t.sig)},Cr=new Set(["version","did","data","rev","sig","prev"]),_r=e=>Array.from(e,t=>t.toString(16).padStart(2,"0")).join(""),$r=e=>{const t=e,n=[];if(n.push(Ue("version",t.version,o=>typeof o=="number",String(t.version))),n.push(Ue("did",t.did,o=>typeof o=="string",typeof t.did=="string"?t.did:JSON.stringify(t.did))),n.push(Ir("data",t.data)),n.push(Ue("rev",t.rev,o=>typeof o=="string",typeof t.rev=="string"?t.rev:JSON.stringify(t.rev))),t.prev===null?n.push({name:"prev",type:"null",value:"null"}):pe(t.prev)?n.push({name:"prev",type:"CidLink",length:ye(t.prev).bytes.length,value:Ye(ye(t.prev)),note:"expected null! commit points at a previous commit"}):n.push({name:"prev",type:typeof t.prev,value:JSON.stringify(t.prev),note:"expected null"}),Ot(t.sig)){const o=qe(t.sig);n.push({name:"sig",type:"Bytes",length:o.length,value:_r(o)})}else n.push({name:"sig",type:typeof t.sig,value:JSON.stringify(t.sig),note:"expected Bytes"});const r=Object.keys(e).filter(o=>!Cr.has(o));for(const o of r){const s=e[o];n.push({name:o,type:typeof s,value:JSON.stringify(s),unexpected:!0})}return{wellFormed:Kt(e),fields:n}},Ue=(e,t,n,r)=>{const o={name:e,type:typeof t,value:r};return n(t)||(o.note="unexpected type"),o},Ir=(e,t)=>pe(t)?{name:e,type:"CidLink",length:ye(t).bytes.length,value:Ye(ye(t))}:{name:e,type:typeof t,value:JSON.stringify(t),note:"expected CidLink"},V=128,Z=127,Tr=2**28,Dr=Number.MAX_SAFE_INTEGER,Ur=Math.min,Pt=(e,t=0,n=e.length)=>{const r=Ur(t+n,e.length);let o=t;if(o>=r)throw new RangeError("could not decode varint");let s=e[o++],i=s&Z;if(s=r)throw new RangeError("could not decode varint");if(s=e[o++],i|=(s&Z)<<7,s=r)throw new RangeError("could not decode varint");if(s=e[o++],i|=(s&Z)<<14,s=r)throw new RangeError("could not decode varint");if(s=e[o++],i|=(s&Z)<<21,s=r)throw new RangeError("could not decode varint");if(s=e[o++],i+=(s&Z)*Tr,s=r)throw new RangeError("could not decode varint");if(s=e[o++],i+=(s&Z)*2**c,i>Dr)throw new RangeError("could not decode varint");c+=7}while(s>=V);return{value:i,nextOffset:o}},Lr=e=>{if(e===null||typeof e!="object")return!1;const{version:t,roots:n}=e;return t===1&&Array.isArray(n)&&n.every(r=>r instanceof ee)},Or=e=>{const{header:t,nextOffset:n}=Nr(e,0);return{header:t,roots:t.data.roots,[Symbol.iterator](){let r=n;return{next(){if(r>=e.length)return{done:!0,value:void 0};const o=r,{value:s,nextOffset:i}=Pt(e,r,8);if(r=i,s<36)throw new RangeError(`invalid car block; length=${s}`);const c=r,{cid:a,nextOffset:f}=jr(e,r);r=f;const l=r,u=s-36;if(l+u>e.length)throw new RangeError("unexpected end of data");const d=l+u,g=e.subarray(l,d);return r=d,{done:!1,value:{cid:a,bytes:g,entryStart:o,entryEnd:d,cidStart:c,cidEnd:l,bytesStart:l,bytesEnd:d}}},[Symbol.iterator](){return this}}}}},Nr=(e,t)=>{const n=t,{value:r,nextOffset:o}=Pt(e,t,8);if(r===0)throw new RangeError("invalid car header; length=0");const s=o,i=s+r;if(i>e.length)throw new RangeError("unexpected end of data");const c=jt(e.subarray(s,i));if(!Lr(c))throw new TypeError("expected a car v1 archive");return{header:{data:c,headerStart:n,headerEnd:i,dataStart:s,dataEnd:i},nextOffset:i}},jr=(e,t)=>{const n=t+36;if(n>e.length)throw new RangeError("unexpected end of data");const r=e.subarray(t,n),o=r[0],s=r[1],i=r[2],c=r[3];if(o!==Ne)throw new RangeError(`incorrect cid version (got v${o})`);if(s!==Ut&&s!==Dt)throw new RangeError(`incorrect cid codec (got 0x${s.toString(16)})`);if(i!==Tt)throw new RangeError(`incorrect cid digest type (got 0x${i.toString(16)})`);if(c!==32)throw new RangeError(`incorrect cid digest size (got ${c})`);return{cid:{version:o,codec:s,digest:{codec:i,contents:r.subarray(4,36)},bytes:r},nextOffset:n}};const Ft=Object.freeze({p:0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,n:0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,h:1n,a:0n,b:7n,Gx:0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,Gy:0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n}),{p:P,n:Ce,Gx:Mr,Gy:Kr,b:zt}=Ft,J=32,oe=64,Ht={publicKey:J+1,publicKeyUncompressed:oe+1,signature:oe},v=(e="",t=Error)=>{const n=new t(e),{captureStackTrace:r}=Error;throw typeof r=="function"&&r(n,v),n},Pr=e=>e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"&&e.BYTES_PER_ELEMENT===1,H=(e,t,n="")=>{const r=Pr(e),o=e?.length,s=t!==void 0;if(!r||s&&o!==t){const i=n&&`"${n}" `,c=s?` of length ${t}`:"",a=r?`length=${o}`:`type=${typeof e}`,f=i+"expected Uint8Array"+c+", got "+a;return r?v(f,RangeError):v(f,TypeError)}return e},ge=e=>new Uint8Array(e),Vt=(e,t)=>e.toString(16).padStart(t,"0"),Zt=e=>{let t="";for(const n of H(e))t+=Vt(n,2);return t},D={_0:48,_9:57,A:65,F:70,a:97,f:102},lt=e=>e>=D._0&&e<=D._9?e-D._0:e>=D.A&&e<=D.F?e-(D.A-10):e>=D.a&&e<=D.f?e-(D.a-10):void 0,Gt=e=>{const t="hex invalid";if(typeof e!="string")return v(t);const n=e.length,r=n/2;if(n%2)return v(t);const o=ge(r);for(let s=0,i=0;sglobalThis?.crypto?.subtle??v("crypto.subtle must be defined, consider polyfill"),we=(...e)=>{let t=0;for(const o of e)t+=H(o).length;const n=ge(t);let r=0;for(const o of e)n.set(o,r),r+=o.length;return n},be=BigInt,_e=(e,t,n,r="bad number: out of range")=>typeof e!="bigint"?v(r,TypeError):t<=e&&e{const n=e%t;return n>=0n?n:t+n},le=e=>p(e,Ce),Xt=(e,t)=>{(e===0n||t<=0n)&&v("no inverse n="+e+" mod="+t);let n=p(e,t),r=t,o=0n,s=1n;for(;n!==0n;){const i=r/n,c=r%n,a=o-s*i;r=n,n=c,o=s,s=a}return r===1n?p(o,t):v("no inverse")},Fr=e=>{const t=Wr[e];return typeof t!="function"&&v("hashes."+e+" not set"),t},zr=(e,t,n)=>H(Fr(e)(t,n),J,"digest"),Le=e=>e instanceof S?e:v("Point expected"),Yt=e=>p(p(e*e)*e+zt),dt=e=>_e(e,0n,P),ue=e=>_e(e,1n,P),Pe=e=>_e(e,1n,Ce),qt=e=>!(e&1n),We=e=>Uint8Array.of(e),Hr=e=>We(qt(e)?2:3),Vr=e=>{const t=Yt(ue(e));let n=1n;for(let r=t,o=(P+1n)/4n;o>0n;o>>=1n)o&1n&&(n=n*r%P),r=r*r%P;return p(n*n)!==t&&v("sqrt invalid"),qt(n)?n:p(-n)};class S{static BASE;static ZERO;X;Y;Z;constructor(t,n,r){this.X=dt(t),this.Y=ue(n),this.Z=dt(r),Object.freeze(this)}static CURVE(){return Ft}static fromAffine(t){const{x:n,y:r}=t;return n===0n&&r===0n?K:new S(n,r,1n)}static fromBytes(t){H(t);const{publicKey:n,publicKeyUncompressed:r}=Ht;let o;const s=t.length,i=t[0],c=t.subarray(1),a=xe(c,0,J);if(s===n&&(i===2||i===3)){let f=Vr(a);i===3&&(f=p(-f)),o=new S(a,f,1n)}return s===r&&i===4&&(o=new S(a,xe(c,J,oe),1n)),o?o.assertValidity():v("bad point: not on curve")}static fromHex(t){return S.fromBytes(Gt(t))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}equals(t){const{X:n,Y:r,Z:o}=this,{X:s,Y:i,Z:c}=Le(t),a=p(n*c),f=p(s*o),l=p(r*c),u=p(i*o);return a===f&&l===u}is0(){return this.equals(K)}negate(){return new S(this.X,p(-this.Y),this.Z)}double(){return this.add(this)}add(t){const{X:n,Y:r,Z:o}=this,{X:s,Y:i,Z:c}=Le(t),a=0n,f=zt;let l=0n,u=0n,d=0n;const g=p(f*3n);let h=p(n*s),w=p(r*i),b=p(o*c),m=p(n+r),y=p(s+i);m=p(m*y),y=p(h+w),m=p(m-y),y=p(n+o);let E=p(s+c);return y=p(y*E),E=p(h+b),y=p(y-E),E=p(r+o),l=p(i+c),E=p(E*l),l=p(w+b),E=p(E-l),d=p(a*y),l=p(g*b),d=p(l+d),l=p(w-d),d=p(w+d),u=p(l*d),w=p(h+h),w=p(w+h),b=p(a*b),y=p(g*y),w=p(w+b),b=p(h-b),b=p(a*b),y=p(y+b),h=p(w*y),u=p(u+h),h=p(E*y),l=p(m*l),l=p(l-h),h=p(m*w),d=p(E*d),d=p(d+h),new S(l,u,d)}subtract(t){return this.add(Le(t).negate())}multiply(t,n=!0){if(!n&&t===0n)return K;if(Pe(t),t===1n)return this;if(this.equals(Q))return oo(t).p;let r=K,o=Q;for(let s=this;t>0n;s=s.double(),t>>=1n)t&1n?r=r.add(s):n&&(o=o.add(s));return r}multiplyUnsafe(t){return this.multiply(t,!1)}toAffine(){const{X:t,Y:n,Z:r}=this;if(this.equals(K))return{x:0n,y:0n};if(r===1n)return{x:t,y:n};const o=Xt(r,P);return p(r*o)!==1n&&v("inverse invalid"),{x:p(t*o),y:p(n*o)}}assertValidity(){const{x:t,y:n}=this.toAffine();return ue(t),ue(n),p(n*n)===Yt(t)?this:v("bad point: not on curve")}toBytes(t=!0){const{x:n,y:r}=this.assertValidity().toAffine(),o=me(n);return t?we(Hr(r),o):we(We(4),o,me(r))}toHex(t){return Zt(this.toBytes(t))}}const Q=new S(Mr,Kr,1n),K=new S(0n,1n,0n);S.BASE=Q;S.ZERO=K;const Zr=(e,t,n)=>Q.multiply(t,!1).add(e.multiply(n,!1)).assertValidity(),Wt=e=>be("0x"+(Zt(e)||"0")),xe=(e,t,n)=>Wt(e.subarray(t,n)),Gr=2n**256n,me=e=>Gt(Vt(_e(e,0n,Gr),oe)),Jt=e=>e>Ce>>1n,pt=e=>[0,1,2,3].includes(e)?e:v("invalid recovery id"),Qt=e=>{e===qr&&v('Signature format "der" is not supported: switch to noble-curves'),e!=null&&e!==ie&&e!==Ee&&v("Signature format must be one of: compact, recovered, der")},en=(e,t=ie)=>{Qt(t);const n=Ht.signature+ +(t===Ee);e.length!==n&&v(`Signature format "${t}" expects Uint8Array with length ${n}`)};class se{r;s;recovery;constructor(t,n,r){this.r=Pe(t),this.s=Pe(n),r!=null&&(this.recovery=pt(r)),Object.freeze(this)}static fromBytes(t,n=ie){en(t,n);let r;n===Ee&&(r=t[0],t=t.subarray(1));const o=xe(t,0,J),s=xe(t,J,oe);return new se(o,s,r)}addRecoveryBit(t){return new se(this.r,this.s,t)}hasHighS(){return Jt(this.s)}toBytes(t=ie){Qt(t);const{r:n,s:r,recovery:o}=this,s=we(me(n),me(r));return t===Ee?we(We(pt(o)),s):s}}const Xr=e=>{e.length>8192&&v("input is too large");const t=e.length*8-256,n=Wt(e);return t>0?n>>be(t):n},Yr=e=>le(Xr(H(e))),ie="compact",Ee="recovered",qr="der",yt="SHA-256",Wr={hmacSha256Async:async(e,t)=>{const n=ut(),r="HMAC",o=await n.importKey("raw",e,{name:r,hash:{name:yt}},!1,["sign"]);return ge(await n.sign(r,o,t))},hmacSha256:void 0,sha256Async:async e=>ge(await ut().digest(yt,e)),sha256:void 0},Jr=(e,t,n)=>{const r=H(e,void 0,"message");return t.prehash?zr("sha256",r):r},Qr=(e,t,n,r={})=>{const{lowS:o,format:s}=r;e instanceof se&&v("Signature must be in Uint8Array, use .toBytes()"),en(e,s),H(n,void 0,"publicKey");try{const{r:i,s:c}=se.fromBytes(e,s),a=Yr(t),f=S.fromBytes(n);if(o&&Jt(c))return!1;const l=Xt(c,Ce),u=le(a*l),d=le(i*l),g=Zr(f,u,d).toAffine();return le(g.x)===i}catch{return!1}},eo=e=>({lowS:e.lowS??!0,prehash:e.prehash??!0,format:e.format??ie,extraEntropy:e.extraEntropy??!1}),to=(e,t,n,r={})=>{r=eo(r);const o=Jr(t,r);return Qr(e,o,n,r)},ve=8,no=256,tn=Math.ceil(no/ve)+1,Fe=2**(ve-1),ro=()=>{const e=[];let t=Q,n=t;for(let r=0;r{const n=t.negate();return e?n:t},oo=e=>{const t=ht||(ht=ro());let n=K,r=Q;const o=2**ve,s=o,i=be(o-1),c=be(ve);for(let a=0;a>=c,f>Fe&&(f-=s,e+=1n);const l=a*Fe,u=l,d=l+Math.abs(f)-1,g=a%2!==0,h=f<0;f===0?r=r.add(gt(g,t[u])):n=n.add(gt(h,t[d]))}return e!==0n&&v("invalid wnaf"),{p:n,f:r}},so=e=>{let t,n=0n;for(let r=1;r<=(t=e.length)>>1;r++)n|=BigInt(e[t-r])<so(e)<=t>>1n,co=e=>e[0]===2||e[0]===3,nn=e=>e[0]===4,ao=e=>{O(nn(e),"not an uncompressed point");const t=e.length-1,n=t>>1,r=e.slice(0,n+1);return r[0]=2+(e[t]&1),r},ke=(e,t)=>`z${er(Bt([e,t]))}`,O=(e,t)=>{if(!e)throw new TypeError(t)},rn=(e,t)=>{if(!e)throw new SyntaxError(t)},on=(e,t)=>{throw new Error(t)},wt=Uint8Array.from([231,1]);Uint8Array.from([129,38]);const fo=(e,t)=>{const n=S.fromBytes(e).toBytes(!1);return{kty:"EC",crv:"secp256k1",alg:"ES256K",x:st(n.subarray(1,33)),y:st(n.subarray(33,65)),key_ops:["verify","sign"]}};class Je{type="secp256k1";jwtAlg="ES256K";_publicKey;constructor(t){this._publicKey=t}static async importRaw(t){return new Je(t)}async verify(t,n,r){if(t.length!==64)return!1;const o=r?.allowMalleableSig??!1,s=await Bn(n);return to(t,s,this._publicKey,{lowS:!o,prehash:!1})}async exportPublicKey(t){const n=this._publicKey;if(t==="jwk")return fo(n);switch(t){case"did":return`did:key:${ke(wt,n)}`;case"multikey":return ke(wt,n);case"raw":return n;case"rawHex":return _t(n)}on(t,`unknown "${t}" export format`)}}const te=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,lo=0xffffffff00000001000000000000000000000000fffffffffffffffffffffffcn,uo=0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bn,po=0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n,yo=(te+1n)/4n,ho=e=>e.reduce((t,n,r)=>t+(BigInt(n)<Uint8Array.from(Array.from({length:t},(n,r)=>Number(BigInt.asUintN(8,e>>BigInt((t-r-1)*8))))),go=(e,t)=>(e=e*e%t,e=e*e%t,e=e*e%t,e=e*e%t,e),wo=(e,t,n)=>{const r=[1n];for(let o=0;o<15;o++)r.push(r[o]*e%n);return Array.from(t.toString(16)).reduce((o,s)=>go(o,n)*r[parseInt(s,16)]%n,1n)},xt=e=>{O(co(e),"not a compressed point"),O(e.length===33,"invalid compressed point length");const t=ho(e.subarray(1)),n=(t**3n+lo*t+uo)%te;let r=wo(n,yo,te);O(r*r%te===n,"invalid curve point"),(e[0]^Number(BigInt.asUintN(1,r)))&1&&(r=te-r);const o=new Uint8Array(65);return o[0]=4,o.set(bt(t,32),1),o.set(bt(r,32),33),o},mt=Uint8Array.from([128,36]);Uint8Array.from([134,38]);const ne={name:"ECDSA",namedCurve:"P-256",hash:"SHA-256"};let fe;const bo=async(e,t,n)=>{if(fe===!0||nn(e))return crypto.subtle.importKey("raw",e,ne,t,n);if(fe===!1)return crypto.subtle.importKey("raw",xt(e),ne,t,n);try{const r=await crypto.subtle.importKey("raw",e,ne,t,n);return fe=!0,r}catch{const r=await crypto.subtle.importKey("raw",xt(e),ne,t,n);return fe=!1,r}},xo=Uint8Array.from([48,19,6,7,42,134,72,206,61,2,1,6,8,42,134,72,206,61,3,1,7]);Uint8Array.from([48,65,2,1,0,...xo,4,39,48,37,2,1,1,4,32]);class Ae{type="p256";jwtAlg="ES256";_publicKey;constructor(t){this._publicKey=t}static async importRaw(t){const n=await bo(t,!0,["verify"]);return new Ae(n)}static async importCryptoKey(t){return O(t.algorithm.namedCurve==="P-256","not an ECDSA P-256 key"),O(t.type==="public","not a public key"),O(t.extractable,"key must be extractable"),new Ae(t)}async verify(t,n,r){return t.length!==64||!r?.allowMalleableSig&&!io(t,po)?!1:await crypto.subtle.verify(ne,this._publicKey,t,n)}async exportPublicKey(t){if(t==="jwk")return await crypto.subtle.exportKey("jwk",this._publicKey);const n=await crypto.subtle.exportKey("raw",this._publicKey),r=ao(new Uint8Array(n));switch(t){case"did":return`did:key:${ke(mt,r)}`;case"multikey":return ke(mt,r);case"raw":return r;case"rawHex":return _t(r)}on(t,`unknown "${t}" export format`)}}const ze=e=>(rn(e.length>=2&&e[0]==="z","not a multibase base58btc string"),Qn(e.slice(1))),mo=e=>{const t=ze(e);rn(t.length>=3,"multikey too short");const n=t[0]<<8|t[1],r=t.subarray(2);switch(n){case 32804:return{type:"p256",jwtAlg:"ES256",publicKeyBytes:r};case 59137:return{type:"secp256k1",jwtAlg:"ES256K",publicKeyBytes:r}}O(!1,`unsupported key type (0x${n.toString(16).padStart(4,"0")})`)},Eo=e=>{const t=e.publicKeyMultibase;switch(e.type){case"Multikey":return mo(t);case"EcdsaSecp256r1VerificationKey2019":return{type:"p256",jwtAlg:"ES256",publicKeyBytes:ze(t)};case"EcdsaSecp256k1VerificationKey2019":return{type:"secp256k1",jwtAlg:"ES256K",publicKeyBytes:ze(t)}}O(!1,`unsupported controller type (${e.type})`)},vo=async(e,t,n,r)=>{switch(e.type){case"p256":return await(await Ae.importRaw(e.publicKeyBytes)).verify(t,n,r);case"secp256k1":return await(await Je.importRaw(e.publicKeyBytes)).verify(t,n,r)}},ko=/^did:([a-z]+):([a-zA-Z0-9._:%-]*[a-zA-Z0-9._-])$/,Ao=e=>typeof e=="string"&&e.length>=7&&e.length<=2048&&ko.test(e),So={lang:void 0,message:void 0,abortEarly:void 0,abortPipeEarly:void 0};function sn(e){return So}let Ro;function Bo(e){return Ro?.get(e)}let Co;function _o(e){return Co?.get(e)}let $o;function Io(e,t){return $o?.get(e)?.get(t)}function To(e){const t=typeof e;return t==="string"?`"${e}"`:t==="number"||t==="bigint"||t==="boolean"?`${e}`:t==="object"||t==="function"?(e&&Object.getPrototypeOf(e)?.constructor?.name)??"null":t}function I(e,t,n,r,o){const s=o&&"input"in o?o.input:n.value,i=o?.expected??e.expects??null,c=o?.received??To(s),a={kind:e.kind,type:e.type,input:s,expected:i,received:c,message:`Invalid ${t}: ${i?`Expected ${i} but r`:"R"}eceived ${c}`,requirement:e.requirement,path:o?.path,issues:o?.issues,lang:r.lang,abortEarly:r.abortEarly,abortPipeEarly:r.abortPipeEarly},f=e.kind==="schema",l=o?.message??e.message??Io(e.reference,a.lang)??(f?_o(a.lang):null)??r.message??Bo(a.lang);l!==void 0&&(a.message=typeof l=="function"?l(a):l),f&&(n.typed=!1),n.issues?n.issues.push(a):n.issues=[a]}const Et=new WeakMap;function j(e){let t=Et.get(e);return t||(t={version:1,vendor:"valibot",validate(n){return e["~run"]({value:n},sn())}},Et.set(e,t)),t}function cn(e,t){return Object.prototype.hasOwnProperty.call(e,t)&&t!=="__proto__"&&t!=="prototype"&&t!=="constructor"}function Do(e,t){const n=[...new Set(e)];return n.length>1?`(${n.join(` ${t} `)})`:n[0]??"never"}var Uo=class extends Error{constructor(e){super(e[0].message),this.name="ValiError",this.issues=e}};function F(e,t){return{kind:"validation",type:"check",reference:F,async:!1,expects:null,requirement:e,message:t,"~run"(n,r){return n.typed&&!this.requirement(n.value)&&I(this,"input",n,r),n}}}function an(e,t){return{kind:"validation",type:"regex",reference:an,async:!1,expects:`${e}`,requirement:e,message:t,"~run"(n,r){return n.typed&&!this.requirement.test(n.value)&&I(this,"format",n,r),n}}}function Lo(e,t,n){return typeof e.fallback=="function"?e.fallback(t,n):e.fallback}function Oo(e,t){return{...e,"~run"(n,r){const o=n.issues&&[...n.issues];if(n=e["~run"](n,r),n.issues){for(const s of n.issues)if(!o?.includes(s)){let i=n.value;for(const c of t){const a=i[c],f={type:"unknown",origin:"value",input:i,key:c,value:a};if(s.path?s.path.push(f):s.path=[f],!a)break;i=a}}}return n}}}function fn(e,t,n){return typeof e.default=="function"?e.default(t,n):e.default}function U(e,t){return{kind:"schema",type:"array",reference:U,expects:"Array",async:!1,item:e,message:t,get"~standard"(){return j(this)},"~run"(n,r){const o=n.value;if(Array.isArray(o)){n.typed=!0,n.value=[];for(let s=0;sn.expects),"|"),async:!1,options:e,message:t,get"~standard"(){return j(this)},"~run"(n,r){let o,s,i;for(const c of this.options){const a=c["~run"]({value:n.value},r);if(a.typed)if(a.issues)s?s.push(a):s=[a];else{o=a;break}else i?i.push(a):i=[a]}if(o)return o;if(s){if(s.length===1)return s[0];I(this,"type",n,r,{issues:vt(s)}),n.typed=!0}else{if(i?.length===1)return i[0];I(this,"type",n,r,{issues:vt(i)})}return n}}}function un(){return{kind:"schema",type:"unknown",reference:un,expects:"unknown",async:!1,get"~standard"(){return j(this)},"~run"(e){return e.typed=!0,e}}}function No(e,t,n){const r=e["~run"]({value:t},sn());if(r.issues)throw new Uo(r.issues);return r.value}function z(...e){return{...e[0],pipe:e,get"~standard"(){return j(this)},"~run"(t,n){for(const r of e)if(r.kind!=="metadata"){if(t.issues&&(r.kind==="schema"||r.kind==="transformation")){t.typed=!1;break}(!t.issues||!n.abortEarly&&!n.abortPipeEarly)&&(t=r["~run"](t,n))}return t}}}const jo=/^#[^#]+$/,Mo=/^z[a-km-zA-HJ-NP-Z1-9]+$/,X=z($(),F(e=>URL.canParse(e),"must be a url")),Qe=z($(),F(e=>jo.test(e)||URL.canParse(e),"must be a did relative uri")),Ko=z($(),an(Mo,"must be a base58 multibase")),de=ln(Ao,"must be a did"),kt=z($e({id:Qe,type:$(),controller:de,publicKeyMultibase:L(Ko),publicKeyJwk:L(Se($(),un()))}),Oo(F(e=>{switch(e.type){case"Multikey":case"EcdsaSecp256k1VerificationKey2019":case"EcdsaSecp256r1VerificationKey2019":return e.publicKeyMultibase!==void 0}return!0},"missing public key multibase"),["publicKeyMultibase"])),Po=$e({id:Qe,type:Y([$(),U($())]),serviceEndpoint:Y([X,Se($(),X),U(Y([X,Se($(),X)]))])}),Oe=(e,t=n=>n)=>{const n=new Set;for(const r of e){const o=t(r);if(n.has(o))return!0;n.add(o)}return!1},Fo=z($e({"@context":L(U(X)),id:de,alsoKnownAs:L(z(U(X),F(e=>!Oe(e),"duplicate aka entries"))),verificationMethod:L(z(U(kt),F(e=>!Oe(e,t=>t.id),"duplicate verification method ids"))),service:L(U(Po)),controller:L(Y([de,U(de)])),authentication:L(U(Y([Qe,kt])))}),F(e=>{const t=e.service;if(!t?.length)return!0;const n=e.id,r=t.map(o=>o.id[0]==="#"?n+o.id:o.id);return!Oe(r)},"duplicate service ids")),zo=(e,t)=>{const n=e.verificationMethod;if(!n)return;const r=`${e.id}${t}`;for(let o=0,s=n.length;ozo(e,"#atproto"),Vo=/^did:plc:([a-z2-7]{24})$/,dn=e=>typeof e=="string"&&e.length===32&&Vo.test(e),Zo=/^did:web:([a-zA-Z0-9%-]+(?:(?:\.[a-zA-Z0-9%-]+)*(?:\.[a-zA-Z]{2,}))?)?((?::[a-zA-Z0-9\-%.]+)+)?$/,Go=/^did:web:([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,})|localhost(?:%3[aA]\d+)?)$/,Xo=e=>typeof e=="string"&&e.length>=9&&Zo.test(e),Yo=e=>typeof e=="string"&&e.length>=12&&Go.test(e),pn=e=>{const[t,...n]=e.slice(8).split(":").map(decodeURIComponent);let r="/"+n.join("/");r==="/"?r="/.well-known/did.json":r+="/did.json";const o=new URL(`https://${t}${r}`);return o.hostname==="localhost"&&(o.protocol="http:"),o},qo=e=>dn(e)||Yo(e),Wo=e=>{const t=e.indexOf(":",4);return e.slice(4,t)};class et extends Error{name="DidResolutionError"}class tt extends et{name="UnsupportedDidMethodError";did;constructor(t){super(`unsupported did method; did=${t}`),this.did=t}}class yn extends et{name="DocumentNotFoundError";did;constructor(t){super(`did document not found; did=${t}`),this.did=t}}class hn extends et{name="FailedDocumentResolutionError";did;constructor(t,n){super(`failed to resolve did document; did=${t}`,n),this.did=t}}class Jo{#e;constructor({methods:t}){this.#e=new Map(Object.entries(t))}async resolve(t,n){const r=Wo(t),o=this.#e.get(r);if(o===void 0)throw new tt(t);return await o.resolve(t,n)}}function Qo(...e){return e.reduce(es)}const es=(e,t)=>n=>e(n).then(t);class Ie extends Error{name="FetchResponseError"}class nt extends Ie{name="FailedResponseError";response;constructor(t){super(`got http ${t.status}`),this.response=t}get status(){return this.response.status}}class At extends Ie{name="ImproperContentTypeError";contentType;constructor(t,n){super(n),this.contentType=t}}class He extends Ie{name="ImproperContentLengthError";expectedSize;actualSize;constructor(t,n,r){super(r),this.expectedSize=t,this.actualSize=n}}class ts extends Ie{name="ImproperResponseError"}class ns extends TransformStream{constructor(t){let n=0;super({transform(r,o){if(n+=r.length,n>t){o.error(new He(t,n,"response content-length too large"));return}o.enqueue(r)}})}}const rs=async e=>{if(e.ok)return e;throw new nt(e)},os=(e,t)=>async n=>{await is(n,e);const r=await cs(n,t);try{const o=JSON.parse(r);return{response:n,json:o}}catch(o){throw new ts("unexpected json data",{cause:o})}},ss=e=>async t=>{const n=No(e,t.json);return{response:t.response,json:n}},is=async(e,t)=>{const n=e.headers.get("content-type")?.split(";",1)[0].trim().toLowerCase();if(n===void 0)throw e.body&&await e.body.cancel(),new At(null,"missing response content-type");if(!t.test(n))throw e.body&&await e.body.cancel(),new At(n,"unexpected response content-type")},cs=async(e,t)=>{const n=e.headers.get("content-length");if(n!==null){const s=Number(n);if(!/^\d+$/.test(n)||!Number.isSafeInteger(s))throw e.body?.cancel(),new He(t,null,"invalid response content-length");if(s>t)throw e.body?.cancel(),new He(t,s,"response content-length too large")}if(e.body===null)return"";const r=e.body.pipeThrough(new ns(t)).pipeThrough(new TextDecoderStream);let o="";for await(const s of as(r))o+=s;return o},as=Symbol.asyncIterator in ReadableStream.prototype?e=>e[Symbol.asyncIterator]():e=>{const t=e.getReader();return{[Symbol.asyncIterator](){return this},next(){return t.read()},async return(){return await t.cancel(),{done:!0,value:void 0}},async throw(n){return await t.cancel(n),{done:!0,value:void 0}}}},gn=Qo(rs,os(/^application\/(did\+ld\+)?json$/,20*1024),ss(Fo));class fs{apiUrl;#e;constructor({apiUrl:t="https://plc.directory",fetch:n=fetch}={}){this.apiUrl=t,this.#e=n}async resolve(t,n){if(!t.startsWith("did:plc:"))throw new tt(t);let r;try{const o=new URL(`/${encodeURIComponent(t)}`,this.apiUrl),s=await(0,this.#e)(o,{signal:n?.signal,cache:n?.noCache?"no-cache":void 0,redirect:"manual",headers:{accept:"application/did+ld+json,application/json"}});if(s.status>=300&&s.status<400)throw new TypeError("unexpected redirect");r=(await gn(s)).json}catch(o){throw o instanceof nt&&o.status===404?new yn(t):new hn(t,{cause:o})}return r}}class ls{#e;constructor({fetch:t=fetch}={}){this.#e=t}async resolve(t,n){if(!t.startsWith("did:web:"))throw new tt(t);let r;try{const o=pn(t),s=await(0,this.#e)(o,{signal:n?.signal,cache:n?.noCache?"no-cache":void 0,redirect:"manual",headers:{accept:"application/did+ld+json,application/json"}});if(s.status>=300&&s.status<400)throw new TypeError("unexpected redirect");r=(await gn(s)).json}catch(o){throw o instanceof nt&&o.status===404?new yn(t):new hn(t,{cause:o})}return r}}const us=new Jo({methods:{plc:new fs,web:new ls}}),ds=async e=>{let t,n;try{const o=ps(e);t=o.commit,n=o.bytes}catch(o){return{ok:!1,stage:"parse",message:o instanceof Error?o.message:String(o)}}let r;try{r=await hs(t)}catch(o){return{ok:!1,stage:"resolve",message:o instanceof Error?o.message:String(o),commit:t}}try{const o=await gs(t,r.found);return{ok:!0,commit:t,publicKey:r.resolved,signatureValid:o,did:r.did,didDocUrl:r.didDocUrl,commitBytes:n}}catch(o){return{ok:!1,stage:"verify",message:o instanceof Error?o.message:String(o),commit:t}}},ps=e=>{const t=Or(e),n=t.roots;if(n.length<1)throw new Error(`CAR has no roots; got=${n.length}`);const r=new Map;for(const c of t)r.set(Ye(c.cid),c.bytes);const o=n[0].$link,s=r.get(o);if(s===void 0)throw new Error(`root CID not present in CAR blocks; cid=${o}`);const i=jt(s);if(i===null||typeof i!="object")throw new Error("root block did not decode to a map");return{commit:i,bytes:s}},ys=e=>dn(e)?`https://plc.directory/${e}`:Xo(e)?pn(e).href:"",hs=async e=>{const t=e.did;if(typeof t!="string")throw new Error("commit has no string 'did' field");if(!qo(t))throw new Error(`commit 'did' is not a supported atproto DID: ${t}`);const n=await us.resolve(t),r=Ho(n);if(r===void 0)throw new Error("DID document has no #atproto verification method");const o=Eo(r);return{found:o,resolved:{type:o.type,jwtAlg:o.jwtAlg,publicKeyMultibase:r.publicKeyMultibase},did:t,didDocUrl:ys(t)}},gs=async(e,t)=>{if(!Kt(e))throw new Error("commit is not well-formed (missing/invalid fields)");const{sig:n,...r}=e,o=qe(n),s=Sr(r);return await vo(t,o,s)},M=document.getElementById("dropzone"),re=document.getElementById("fileinput"),G=document.getElementById("results"),St=e=>Array.from(e,t=>t.toString(16).padStart(2,"0")).join(""),ws=e=>btoa(String.fromCharCode(...e)).replace(/=+$/,""),bs=e=>{if(e<1024)return`${e} B`;const t=["KiB","MiB","GiB"];let n=e/1024,r=0;for(;n>=1024&&r=100?n.toFixed(0):n>=10?n.toFixed(1):n.toFixed(2)} ${t[r]}`},x=(e,t={})=>{const n=document.createElement(e);return t.text!==void 0&&(n.textContent=t.text),t.cls&&(n.className=t.cls),t.href!==void 0&&n instanceof HTMLAnchorElement&&(n.href=t.href),t.title!==void 0&&(n.title=t.title),n},xs=async e=>{try{return await navigator.clipboard.writeText(e),!0}catch{return!1}},wn=async e=>{G.innerHTML="";const t=x("div",{cls:"head"});t.append(x("span",{text:e.name,cls:"filename"}),x("span",{text:bs(e.size),cls:"muted"})),G.append(t);const n=new Uint8Array(await e.arrayBuffer()),r=await ds(n),o=r.ok&&r.signatureValid?"ok":"bad",s=r.ok?r.signatureValid?"VALID":"BAD SIGNATURE":"ERROR";t.prepend(ms(s,o)),r.ok||G.append(x("div",{text:`${r.stage}: ${r.message}`,cls:"err"})),r.ok&&G.append(Es(r.publicKey.type,r.publicKey.publicKeyMultibase,r.didDocUrl)),r.commit&&(G.append(vs(r.commit)),r.ok&&G.append(ks(r.commitBytes)))},ms=(e,t)=>x("span",{text:e,cls:`badge ${t}`}),Es=(e,t,n)=>{const r=x("div",{cls:"keyline"});if(r.append(x("span",{text:"Resolved key: ",cls:"muted"})),n){const o=x("a",{text:t,href:n,title:n});o.target="_blank",o.rel="noopener noreferrer",r.append(o)}else r.append(x("span",{text:t}));return r.append(x("span",{text:` (${e})`})),r},vs=e=>{const t=$r(e),n=x("table",{cls:"commit"}),r=x("thead"),o=x("tr");o.append(x("th",{text:"field"}),x("th",{text:"type"}),x("th",{text:"value"})),r.append(o),n.append(r);const s=x("tbody");for(const c of t.fields){const a=x("tr",{cls:c.note?"warn":c.unexpected?"unexpected":void 0}),f=x("td",{text:c.type+(c.length!==void 0?` (${c.length})`:"")}),l=x("td",{text:c.value,title:c.value});a.append(x("td",{text:c.name}),f,l),c.note&&l.append(x("div",{text:c.note,cls:"note"})),s.append(a)}n.append(s);const i=x("div",{cls:"commit-wrap"});return i.append(x("div",{text:`Commit object ${t.wellFormed?"(well-formed):":"(malformed):"}`,cls:"commit-title"}),n),i},ks=e=>{const t=x("details"),n=x("summary",{text:"Raw commit object bytes"});t.append(n);const r=x("div",{cls:"copyrow"});let o=!0;const s=x("code",{text:St(e)}),i=x("button",{text:"copy",cls:"copybtn"}),c=x("span",{cls:"fmt-toggle"}),a=x("button",{text:"hex"}),f=x("button",{text:"base64"});a.setAttribute("aria-pressed","true"),f.setAttribute("aria-pressed","false"),c.append(a,f);const l=()=>{s.textContent=o?St(e):ws(e),a.setAttribute("aria-pressed",String(o)),f.setAttribute("aria-pressed",String(!o))};return i.addEventListener("click",async()=>{const u=await xs(s.textContent??"");i.textContent=u?"copied!":"failed",setTimeout(()=>i.textContent="copy",1200)}),a.addEventListener("click",()=>{o=!0,l()}),f.addEventListener("click",()=>{o=!1,l()}),r.append(s,i,c),t.append(r),t},bn=e=>{e.preventDefault(),e.stopPropagation()};document.addEventListener("dragover",bn);document.addEventListener("drop",bn);const As=e=>{e.preventDefault(),M.classList.add("dragover")},Ss=()=>{M.classList.remove("dragover")},Rs=async e=>{e.preventDefault(),M.classList.remove("dragover");const t=e.dataTransfer?.files?.[0];t&&await wn(t)};M.addEventListener("dragover",As);M.addEventListener("dragleave",Ss);M.addEventListener("drop",Rs);M.addEventListener("click",()=>re.click());M.addEventListener("keydown",e=>{(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),re.click())});re.addEventListener("change",()=>{const e=re.files?.[0];e&&wn(e),re.value=""});