@ghostlytools/cli 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/assets/{api-CrxVxtY1.js → api-Dq2QXQDk.js} +141 -40
- package/dist/assets/api-Dq2QXQDk.js.map +1 -0
- package/dist/assets/{api-BiaX6l6f.js → api-TiB5QSG9.js} +2 -2
- package/dist/assets/{apiKit-CajiDxdK.js → apiKit-CA2x5GJF.js} +7 -3
- package/dist/assets/apiKit-CA2x5GJF.js.map +1 -0
- package/dist/assets/{apps-CbfiCSIw.js → apps-BxV9i_Y4.js} +3 -4
- package/dist/assets/{apps-CbfiCSIw.js.map → apps-BxV9i_Y4.js.map} +1 -1
- package/dist/assets/{apps-t23gkc28.js → apps-C-T8jeji.js} +1 -1
- package/dist/assets/{arkade-D4BVJFEQ.js → arkade-C_qDmHnm.js} +2 -2
- package/dist/assets/{arkade-D4BVJFEQ.js.map → arkade-C_qDmHnm.js.map} +1 -1
- package/dist/assets/{backup-Cg52WeQi.js → backup-Bugnn3Ue.js} +1 -1
- package/dist/assets/{backup-BAMi2lAq.js → backup-CnMN2oUK.js} +8 -8
- package/dist/assets/backup-CnMN2oUK.js.map +1 -0
- package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
- package/dist/assets/bitcoind-CZchoh43.js +349 -0
- package/dist/assets/bitcoind-CZchoh43.js.map +1 -0
- package/dist/assets/bitcoindProvider-DGw3xBSa.js +97 -0
- package/dist/assets/bitcoindProvider-DGw3xBSa.js.map +1 -0
- package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
- package/dist/assets/buttons-Bj7Vf7Pn.js +2 -0
- package/dist/assets/{buttons-CX1pKB0j.js → buttons-CvNBzh2S.js} +4 -5
- package/dist/assets/{buttons-CX1pKB0j.js.map → buttons-CvNBzh2S.js.map} +1 -1
- package/dist/assets/{endpoint-Dn7H8qE1.js → endpoint-BOTFMpfS.js} +44 -12
- package/dist/assets/endpoint-BOTFMpfS.js.map +1 -0
- package/dist/assets/{envelope-BvU0fLEt.js → envelope-WjingYSc.js} +2 -2
- package/dist/assets/{envelope-BvU0fLEt.js.map → envelope-WjingYSc.js.map} +1 -1
- package/dist/assets/{errors-my6VJR_0.js → errors-DO5z6h_l.js} +29 -1
- package/dist/assets/errors-DO5z6h_l.js.map +1 -0
- package/dist/assets/groupFiles-IEUW3DQz.js +3202 -0
- package/dist/assets/groupFiles-IEUW3DQz.js.map +1 -0
- package/dist/assets/{host-Cmd205Dc.js → host-CWt-jtqz.js} +41 -16
- package/dist/assets/host-CWt-jtqz.js.map +1 -0
- package/dist/assets/{hyperdhtRelay-CEmBpT0Q.js → hyperdhtRelay-CPYwSLLS.js} +2 -3
- package/dist/assets/{hyperdhtRelay-CEmBpT0Q.js.map → hyperdhtRelay-CPYwSLLS.js.map} +1 -1
- package/dist/assets/{irohWeb--YyEA-hX.js → irohWeb-4oChIOIp.js} +2 -3
- package/dist/assets/{irohWeb--YyEA-hX.js.map → irohWeb-4oChIOIp.js.map} +1 -1
- package/dist/assets/{main-BzNN6bkx.js → main-DbqcdShN.js} +142 -35
- package/dist/assets/main-DbqcdShN.js.map +1 -0
- package/dist/assets/{mainline-D5D5C-jT.js → mainline-ZkiOnevs.js} +2 -2
- package/dist/assets/{mainline-D5D5C-jT.js.map → mainline-ZkiOnevs.js.map} +1 -1
- package/dist/assets/{networkReason-DxZWHw6v.js → networkReason-CetKkTPo.js} +2 -2
- package/dist/assets/{networkReason-DxZWHw6v.js.map → networkReason-CetKkTPo.js.map} +1 -1
- package/dist/assets/nwc-CgkqyvsE.js +612 -0
- package/dist/assets/nwc-CgkqyvsE.js.map +1 -0
- package/dist/assets/nwcProvider-4eVz_8HP.js +77 -0
- package/dist/assets/nwcProvider-4eVz_8HP.js.map +1 -0
- package/dist/assets/{profiles-B6F4oa9K.js → profiles-BHc9pEYg.js} +2 -2
- package/dist/assets/{profiles-B6F4oa9K.js.map → profiles-BHc9pEYg.js.map} +1 -1
- package/dist/assets/registry-DlAKO0Fy.js +3793 -0
- package/dist/assets/registry-DlAKO0Fy.js.map +1 -0
- package/dist/assets/registry-OBJBnHVQ.js +2 -0
- package/dist/assets/resolve-CouHhCpJ.js +591 -0
- package/dist/assets/resolve-CouHhCpJ.js.map +1 -0
- package/dist/assets/{server-CBCEYKMN.js → server-Ss0gJ-nw.js} +184 -1181
- package/dist/assets/server-Ss0gJ-nw.js.map +1 -0
- package/dist/assets/{src-DhfGgAUu.js → src-DDzSERyc.js} +2249 -72
- package/dist/assets/src-DDzSERyc.js.map +1 -0
- package/dist/assets/{turnRefs-ChEfgbSX.js → turnRefs-B87dtrHM.js} +2 -2
- package/dist/assets/{turnRefs-ChEfgbSX.js.map → turnRefs-B87dtrHM.js.map} +1 -1
- package/dist/assets/types-C4_KafL_.js +122 -0
- package/dist/assets/types-C4_KafL_.js.map +1 -0
- package/dist/assets/{usdt-C1Xao2xH.js → usdt-Fj8CQO2z.js} +3 -3
- package/dist/assets/{usdt-C1Xao2xH.js.map → usdt-Fj8CQO2z.js.map} +1 -1
- package/dist/ghostly.mjs +1 -1
- package/package.json +1 -1
- package/dist/assets/api-CrxVxtY1.js.map +0 -1
- package/dist/assets/apiKit-CajiDxdK.js.map +0 -1
- package/dist/assets/backup-BAMi2lAq.js.map +0 -1
- package/dist/assets/buttons-CWl2yNJu.js +0 -2
- package/dist/assets/endpoint-Dn7H8qE1.js.map +0 -1
- package/dist/assets/errors-my6VJR_0.js.map +0 -1
- package/dist/assets/groupFiles-BNTSDaoq.js +0 -7685
- package/dist/assets/groupFiles-BNTSDaoq.js.map +0 -1
- package/dist/assets/groupFiles-DBbjOLuD.js +0 -2088
- package/dist/assets/groupFiles-DBbjOLuD.js.map +0 -1
- package/dist/assets/host-Cmd205Dc.js.map +0 -1
- package/dist/assets/main-BzNN6bkx.js.map +0 -1
- package/dist/assets/server-CBCEYKMN.js.map +0 -1
- package/dist/assets/src-DhfGgAUu.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"groupFiles-DBbjOLuD.js","names":[],"sources":["../../../core/src/bytes.ts","../../../core/src/text.ts","../../../core/src/frames.ts","../../../core/src/canonicalJson.ts","../../../core/src/appStatements.ts","../../../core/src/messageTime.ts","../../../core/src/statusCards.ts","../../../core/src/video.ts","../../../core/src/voice.ts","../../../core/src/files.ts","../../../core/src/image.ts","../../../core/src/groupFiles.ts"],"sourcesContent":["const textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder(\"utf-8\", { fatal: true });\n\nexport function utf8Encode(str: string): Uint8Array {\n return textEncoder.encode(str);\n}\n\nexport function utf8Decode(bytes: Uint8Array): string {\n return textDecoder.decode(bytes);\n}\n\nexport function concatBytes(...parts: Uint8Array[]): Uint8Array {\n let length = 0;\n for (const p of parts) length += p.length;\n const out = new Uint8Array(length);\n let offset = 0;\n for (const p of parts) {\n out.set(p, offset);\n offset += p.length;\n }\n return out;\n}\n\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];\n return diff === 0;\n}\n\ntype NativeToBase64 = (this: Uint8Array, options?: { alphabet?: \"base64\" | \"base64url\"; omitPadding?: boolean }) => string;\n/** `Uint8Array.prototype.toBase64` (Node 25+, Chrome 140+, Firefox 133+, Safari 18.2+): ~200x the JS below. */\nconst nativeToBase64 = (Uint8Array.prototype as { toBase64?: NativeToBase64 }).toBase64;\n\nconst tableOf = (chars: string) => Uint8Array.from(chars, (c) => c.charCodeAt(0));\nconst BASE64_TABLE = tableOf(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\");\nconst BASE64URL_TABLE = tableOf(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\");\n\n/**\n * Base64 where the runtime has no native encoder (Node 22 and 24, older WebViews): the characters go into bytes and\n * become one string at the end. Every file chunk is encoded (files/3 `pf-data`), so this is on a transfer's path.\n */\nfunction encodeBase64(bytes: Uint8Array, table: Uint8Array, pad: boolean): string {\n const out = new Uint8Array(Math.ceil(bytes.length / 3) * 4);\n let o = 0, i = 0;\n for (; i + 2 < bytes.length; i += 3) {\n const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];\n out[o++] = table[n >>> 18];\n out[o++] = table[(n >>> 12) & 63];\n out[o++] = table[(n >>> 6) & 63];\n out[o++] = table[n & 63];\n }\n const rest = bytes.length - i;\n if (rest) {\n const n = (bytes[i] << 16) | (rest === 2 ? bytes[i + 1] << 8 : 0);\n out[o++] = table[n >>> 18];\n out[o++] = table[(n >>> 12) & 63];\n if (rest === 2) out[o++] = table[(n >>> 6) & 63];\n else if (pad) out[o++] = 61; // \"=\"\n if (pad) out[o++] = 61;\n }\n return textDecoder.decode(out.subarray(0, o));\n}\n\nexport function toBase64(bytes: Uint8Array): string {\n return nativeToBase64 ? nativeToBase64.call(bytes) : encodeBase64(bytes, BASE64_TABLE, true);\n}\n\nexport function fromBase64(str: string): Uint8Array {\n const binary = atob(str);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nexport function toBase64Url(bytes: Uint8Array): string {\n return nativeToBase64 ? nativeToBase64.call(bytes, { alphabet: \"base64url\", omitPadding: true }) : encodeBase64(bytes, BASE64URL_TABLE, false);\n}\n\nexport function fromBase64Url(str: string): Uint8Array {\n let b = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (b.length % 4) b += \"=\";\n return fromBase64(b);\n}\n\nconst Z32_ALPHABET = \"ybndrfg8ejkmcpqxot1uwisza345h769\";\n\nexport function toZ32(bytes: Uint8Array): string {\n let out = \"\";\n let bits = 0;\n let value = 0;\n for (const byte of bytes) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += Z32_ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n value &= (1 << bits) - 1;\n }\n if (bits > 0) out += Z32_ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\nexport function fromZ32(str: string): Uint8Array {\n const out = new Uint8Array(Math.floor((str.length * 5) / 8));\n let bits = 0;\n let value = 0;\n let index = 0;\n for (const char of str) {\n const v = Z32_ALPHABET.indexOf(char);\n if (v < 0) throw new Error(`Invalid z-base-32 character: ${char}`);\n value = (value << 5) | v;\n bits += 5;\n if (bits >= 8) {\n out[index++] = (value >>> (bits - 8)) & 0xff;\n bits -= 8;\n }\n value &= (1 << bits) - 1;\n }\n return out;\n}\n\nexport function randomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n return bytes;\n}\n","/**\n * Display text a peer chooses: nicknames and service names. It is shown next\n * to the peer's messages and in the chat header, so it must not be able to\n * read as something it is not, and it must not be able to grow without bound.\n */\n\n/** Longest nickname kept from a peer. The `hello` frame has always cut here. */\nexport const MAX_NICK_LENGTH = 64;\n\n/**\n * Invisible and direction-changing characters. Unlike a file name, display\n * text may legitimately hold emoji and Indic or Arabic joiners, so the two\n * joiners stay and the rest of Cf goes: bidi overrides and isolates reorder\n * what follows them, and the others hide text or let two different names\n * render identically.\n */\nconst HIDDEN = /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n/** Zero-width non-joiner and zero-width joiner. */\nconst KEEP = /[\\u200c\\u200d]/;\n\n/** `text` without its invisible and direction-changing characters, at any length: for display text with a cut of its own. */\nexport function stripHidden(text: string): string {\n return text.replace(HIDDEN, (char) => (KEEP.test(char) ? char : \"\"));\n}\n\n/**\n * What draws nothing when it stands alone and `HIDDEN` leaves in, since text needs it beside something else: the\n * joiners, the fillers, the variation selectors and the other default-ignorable code points, combining marks, white\n * space and the blank Braille pattern.\n */\nconst UNSEEN = /^[\\p{Default_Ignorable_Code_Point}\\p{M}\\p{White_Space}\\u2800]*$/u;\n\n/** Whether `text` holds a character that shows by itself: a name of joiners, fillers or marks alone has none. */\nexport function hasVisible(text: string): boolean {\n return !UNSEEN.test(text);\n}\n\n/** Undefined when nothing visible is left, so callers can fall back. */\nexport function sanitizeDisplayText(text: string, max: number): string | undefined {\n const clean = [...stripHidden(text)].slice(0, max).join(\"\").trim();\n return hasVisible(clean) ? clean : undefined;\n}\n\n/** A nickname as it arrived from a peer, ready to show. */\nexport function sanitizeNick(nick: unknown): string | undefined {\n return typeof nick === \"string\" ? sanitizeDisplayText(nick, MAX_NICK_LENGTH) : undefined;\n}\n\n/**\n * `text` without the characters of `chars` it ends with, in one pass from the end. A `/[…]+$/` replace would retry\n * from every character of a run that does not reach the end.\n */\nexport function trimEndOf(text: string, chars: string): string {\n let end = text.length;\n while (end > 0 && chars.includes(text[end - 1])) end--;\n return end === text.length ? text : text.slice(0, end);\n}\n","import { utf8Encode } from \"./bytes\";\nimport { sanitizeNick } from \"./text\";\n\n/**\n * Framing on the `ghostly/1` DataChannel (ordered, reliable).\n *\n * - Text messages are control frames: compact JSON with a `t` discriminator,\n * the same shape Ghostly already uses for `_msgs` and `_call`.\n * - Binary messages are body chunks that belong to a stream opened by a\n * control frame: `<kind u8><stream id u32 BE><flags u8><payload>`.\n *\n * Everything here handles data from the remote peer and must never trust it.\n */\nexport const LIMITS = {\n /** Largest control frame accepted, headers included. */\n maxControlFrameBytes: 60 * 1024,\n /** Longest payment endpoint value (a Cashu token, an invoice) a payment frame may carry; a longer one is dropped. */\n maxPaymentEndpointChars: 32 * 1024,\n /** Largest DataChannel message sent. 16 KiB is safe across WebRTC stacks. */\n maxChunkMessageBytes: 16 * 1024,\n maxRequestBodyBytes: 8 * 1024 * 1024,\n maxResponseBodyBytes: 64 * 1024 * 1024,\n maxHeaderCount: 128,\n maxHeaderValueBytes: 16 * 1024,\n maxPathBytes: 8 * 1024,\n /** Requests a host serves at once for one peer. */\n maxConcurrentRequestsPerPeer: 32,\n /** Requests a client keeps in flight; the rest wait in a queue. */\n maxClientInFlight: 16,\n maxChatMessageBytes: 16 * 1024,\n maxFileBytes: 100 * 1024 * 1024,\n /** Files a peer may be sending us at once. */\n maxIncomingFilesPerPeer: 3,\n /** Received bytes kept for one peer, in flight included. Past it new files are refused. */\n maxStoredIncomingBytesPerPeer: 500 * 1024 * 1024,\n requestTimeoutMs: 60_000,\n bodyIdleTimeoutMs: 30_000,\n /** Stop writing to the channel above this many buffered bytes. */\n sendHighWaterMark: 1024 * 1024,\n sendLowWaterMark: 256 * 1024,\n} as const;\n\nexport const PROTOCOL_VERSION = 1;\n\nexport const CHUNK_KIND = { requestBody: 1, responseBody: 2, fileBody: 3 } as const;\nexport type ChunkKind = (typeof CHUNK_KIND)[keyof typeof CHUNK_KIND];\n\nexport const CHUNK_FLAG_END = 1;\nconst CHUNK_HEADER_BYTES = 6;\nexport const MAX_CHUNK_PAYLOAD_BYTES = LIMITS.maxChunkMessageBytes - CHUNK_HEADER_BYTES;\n\nexport type HeaderList = [string, string][];\n\n/** First frame in each direction once the channel opens. */\nexport interface HelloFrame {\n t: \"hello\";\n v: number;\n /** Authoritative service list, same wire shape as the `_svc` record. */\n svc: unknown;\n nick?: string;\n}\n\n/** Chat message. Used instead of `_msgs` while the data link is up. */\nexport interface ChatFrame {\n t: \"m\";\n ts: number;\n m: string;\n}\n\n/** A `_call` signal, sent here as well so a connected peer rings without waiting for its next poll. */\nexport interface CallFrame {\n t: \"call\";\n s: string;\n}\n\n/** Updated service list while connected. */\nexport interface ServicesFrame {\n t: \"svc\";\n svc: unknown;\n}\n\nexport interface HttpRequestFrame {\n t: \"req\";\n id: number;\n /** Service id. The host maps it to a target; the peer never names a URL. */\n s: string;\n m: string;\n /** Origin-form path with query, e.g. `/api/items?page=2` */\n p: string;\n h: HeaderList;\n /** True when request body chunks follow. */\n b: boolean;\n}\n\nexport interface HttpResponseFrame {\n t: \"res\";\n id: number;\n st: number;\n h: HeaderList;\n /** True when response body chunks follow. */\n b: boolean;\n}\n\n/** Announces a file; its bytes follow as `fileBody` chunks on stream `id`. */\nexport interface FileFrame {\n t: \"file\";\n id: number;\n /** File id chosen by the sender, unique within the link. */\n f: string;\n ts: number;\n n: string;\n /** Size in bytes. The transfer fails unless exactly this many arrive. */\n s: number;\n m: string;\n}\n\n/**\n * A way to get paid, in Paykit's terms: a Payment Endpoint Identifier\n * (`btc-lightning-bolt11`, `cashu`, …) and its payload (an invoice, the mints\n * accepted, …). See `payments.ts`.\n */\nexport type WireEndpoint = [identifier: string, payload: string];\n\n/** Payment Request: the sender asks to be paid. */\nexport interface PayRequestFrame {\n t: \"pay-req\";\n /** Request id, chosen by the payee. */\n id: string;\n ts: number;\n /** Decimal amount as text, and its asset (`sat`). */\n v: string;\n u: string;\n memo?: string;\n e: WireEndpoint[];\n /** The `pay-ask` this answers, if any. */\n a?: string;\n /**\n * `mainnet` (real money) or `testnet` (test coins): what pays it. Absent from apps before wallets had their own\n * network; the endpoints tell then (a test mint, a test chain).\n */\n n?: \"mainnet\" | \"testnet\";\n}\n\n/** A payer asking to pay: the payee answers with a `pay-req` whose `a` is this id. */\nexport interface PayAskFrame {\n t: \"pay-ask\";\n id: string;\n ts: number;\n v: string;\n u: string;\n m: \"arkade\" | \"usdt\" | \"bark\" | \"bitcoin\" | \"fedimint\" | \"spark\";\n memo?: string;\n /** The network the payer will pay from: the payee answers from its wallet of that network. */\n n?: \"mainnet\" | \"testnet\";\n}\n\n/** A payment that travels in band, such as an ecash token. */\nexport interface PayFrame {\n t: \"pay\";\n id: string;\n ts: number;\n /** The request this settles, if any. */\n rid?: string;\n v: string;\n u: string;\n memo?: string;\n /** The endpoint used and the payment itself. */\n e: WireEndpoint;\n}\n\n/** What became of a payment or a request: the payee's receipt, or a refusal. */\nexport interface PayResultFrame {\n t: \"pay-res\";\n /** Id of the `pay` or `pay-req` frame this answers. */\n id: string;\n ok: boolean;\n /** Amount credited, when it differs from what was sent (fees). */\n v?: string;\n err?: string;\n /** On the payee's own `pay-req`: closed for good, never to be paid (its wallet was removed). */\n c?: true;\n}\n\n/** Aborts a stream in either direction. */\nexport interface ResetFrame {\n t: \"rst\";\n id: number;\n /** Which stream: `q` a request I sent, `s` a response I am sending, `f` a file (either side). */\n d: \"q\" | \"s\" | \"f\";\n e: string;\n}\n\nexport interface PingFrame {\n t: \"ping\" | \"pong\";\n ts: number;\n}\n\nexport type ControlFrame =\n | PayAskFrame\n | HelloFrame\n | ChatFrame\n | CallFrame\n | ServicesFrame\n | HttpRequestFrame\n | HttpResponseFrame\n | FileFrame\n | PayRequestFrame\n | PayFrame\n | PayResultFrame\n | ResetFrame\n | PingFrame;\n\nexport interface ChunkFrame {\n kind: ChunkKind;\n id: number;\n end: boolean;\n payload: Uint8Array;\n}\n\nexport function encodeControl(frame: ControlFrame): string {\n const json = JSON.stringify(frame);\n if (utf8Encode(json).length > LIMITS.maxControlFrameBytes) throw new Error(\"Control frame too large\");\n return json;\n}\n\nfunction isHeaderList(value: unknown): value is HeaderList {\n if (!Array.isArray(value) || value.length > LIMITS.maxHeaderCount) return false;\n return value.every(\n (h) =>\n Array.isArray(h) &&\n h.length === 2 &&\n typeof h[0] === \"string\" &&\n typeof h[1] === \"string\" &&\n h[0].length <= 256 &&\n h[1].length <= LIMITS.maxHeaderValueBytes,\n );\n}\n\nfunction isStreamId(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0 && value <= 0xffffffff;\n}\n\nfunction isPayId(value: unknown): value is string {\n return typeof value === \"string\" && /^[A-Za-z0-9_-]{8,64}$/.test(value);\n}\n\n/** Decimal text, as in Paykit's Payment Amount: no floats on the wire. */\nfunction isAmount(value: unknown): value is string {\n return typeof value === \"string\" && /^(0|[1-9]\\d{0,15})(\\.\\d{1,8})?$/.test(value);\n}\n\nfunction isUnit(value: unknown): value is string {\n return typeof value === \"string\" && /^[a-z0-9]{1,12}$/.test(value);\n}\n\nfunction isEndpoint(value: unknown): value is WireEndpoint {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"string\" &&\n value[0].length <= 64 &&\n /^[a-z0-9][a-z0-9-]*(?:\\/[1-9][0-9]*)?$/.test(value[0]) &&\n typeof value[1] === \"string\" &&\n value[1].length <= LIMITS.maxPaymentEndpointChars\n );\n}\n\n/** Returns null for anything malformed; callers drop the frame. */\nexport function decodeControl(text: string): ControlFrame | null {\n // A character is at least one byte: the cheap check first, then the real one.\n if (text.length > LIMITS.maxControlFrameBytes || utf8Encode(text).length > LIMITS.maxControlFrameBytes) return null;\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch {\n return null;\n }\n if (!raw || typeof raw !== \"object\") return null;\n const f = raw as Record<string, unknown>;\n\n switch (f.t) {\n case \"hello\":\n if (typeof f.v !== \"number\") return null;\n return { t: \"hello\", v: f.v, svc: f.svc, nick: sanitizeNick(f.nick) };\n case \"m\":\n if (typeof f.ts !== \"number\" || typeof f.m !== \"string\") return null;\n if (utf8Encode(f.m).length > LIMITS.maxChatMessageBytes) return null;\n return { t: \"m\", ts: f.ts, m: f.m };\n case \"call\":\n if (typeof f.s !== \"string\" || utf8Encode(f.s).length > LIMITS.maxChatMessageBytes) return null;\n return { t: \"call\", s: f.s };\n case \"svc\":\n return { t: \"svc\", svc: f.svc };\n case \"req\":\n if (!isStreamId(f.id) || typeof f.s !== \"string\" || typeof f.m !== \"string\") return null;\n if (typeof f.p !== \"string\" || f.p.length > LIMITS.maxPathBytes || !isHeaderList(f.h)) return null;\n return { t: \"req\", id: f.id, s: f.s, m: f.m, p: f.p, h: f.h, b: f.b === true };\n case \"res\":\n if (!isStreamId(f.id) || typeof f.st !== \"number\" || !Number.isInteger(f.st)) return null;\n if (f.st < 100 || f.st > 599 || !isHeaderList(f.h)) return null;\n return { t: \"res\", id: f.id, st: f.st, h: f.h, b: f.b === true };\n case \"file\":\n if (!isStreamId(f.id) || typeof f.f !== \"string\" || typeof f.ts !== \"number\") return null;\n if (typeof f.n !== \"string\" || typeof f.m !== \"string\") return null;\n if (typeof f.s !== \"number\" || !Number.isSafeInteger(f.s) || f.s < 0) return null;\n return { t: \"file\", id: f.id, f: f.f, ts: f.ts, n: f.n, s: f.s, m: f.m };\n case \"pay-req\":\n case \"pay\": {\n if (!isPayId(f.id) || typeof f.ts !== \"number\" || !isAmount(f.v) || !isUnit(f.u)) return null;\n const memo = typeof f.memo === \"string\" ? f.memo.slice(0, 140) : undefined;\n if (f.t === \"pay-req\") {\n if (!Array.isArray(f.e) || f.e.length === 0 || f.e.length > 8 || !f.e.every(isEndpoint)) return null;\n return { t: \"pay-req\", id: f.id, ts: f.ts, v: f.v, u: f.u, memo, e: f.e, a: isPayId(f.a) ? f.a : undefined, n: f.n === \"mainnet\" || f.n === \"testnet\" ? f.n : undefined };\n }\n if (!isEndpoint(f.e) || (f.rid !== undefined && !isPayId(f.rid))) return null;\n return { t: \"pay\", id: f.id, ts: f.ts, rid: f.rid, v: f.v, u: f.u, memo, e: f.e };\n }\n case \"pay-ask\": {\n if (!isPayId(f.id) || typeof f.ts !== \"number\" || !isAmount(f.v) || !isUnit(f.u)) return null;\n if (f.m !== \"arkade\" && f.m !== \"usdt\" && f.m !== \"bark\" && f.m !== \"bitcoin\" && f.m !== \"fedimint\" && f.m !== \"spark\") return null;\n return { t: \"pay-ask\", id: f.id, ts: f.ts, v: f.v, u: f.u, m: f.m, memo: typeof f.memo === \"string\" ? f.memo.slice(0, 140) : undefined, n: f.n === \"mainnet\" || f.n === \"testnet\" ? f.n : undefined };\n }\n case \"pay-res\":\n if (!isPayId(f.id) || typeof f.ok !== \"boolean\") return null;\n if (f.v !== undefined && !isAmount(f.v)) return null;\n return { t: \"pay-res\", id: f.id, ok: f.ok, v: f.v, err: typeof f.err === \"string\" ? f.err.slice(0, 200) : undefined, ...(f.c === true && !f.ok ? { c: true as const } : {}) };\n case \"rst\":\n if (!isStreamId(f.id) || (f.d !== \"q\" && f.d !== \"s\" && f.d !== \"f\")) return null;\n return { t: \"rst\", id: f.id, d: f.d, e: typeof f.e === \"string\" ? f.e.slice(0, 256) : \"\" };\n case \"ping\":\n case \"pong\":\n if (typeof f.ts !== \"number\") return null;\n return { t: f.t, ts: f.ts };\n default:\n return null;\n }\n}\n\nexport function encodeChunk(chunk: ChunkFrame): Uint8Array {\n if (chunk.payload.length > MAX_CHUNK_PAYLOAD_BYTES) throw new Error(\"Chunk payload too large\");\n const out = new Uint8Array(CHUNK_HEADER_BYTES + chunk.payload.length);\n const view = new DataView(out.buffer);\n view.setUint8(0, chunk.kind);\n view.setUint32(1, chunk.id);\n view.setUint8(5, chunk.end ? CHUNK_FLAG_END : 0);\n out.set(chunk.payload, CHUNK_HEADER_BYTES);\n return out;\n}\n\nexport function decodeChunk(data: Uint8Array): ChunkFrame | null {\n if (data.length < CHUNK_HEADER_BYTES || data.length > LIMITS.maxChunkMessageBytes) return null;\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const kind = view.getUint8(0);\n if (kind !== CHUNK_KIND.requestBody && kind !== CHUNK_KIND.responseBody && kind !== CHUNK_KIND.fileBody) return null;\n return {\n kind,\n id: view.getUint32(1),\n end: (view.getUint8(5) & CHUNK_FLAG_END) !== 0,\n payload: data.subarray(CHUNK_HEADER_BYTES),\n };\n}\n\n/**\n * The slice of RTCDataChannel the protocol needs. Tests use an in-memory pair,\n * platforms wrap a real channel with `wrapDataChannel`.\n */\nexport interface FrameChannel {\n send(data: string | Uint8Array): void;\n readonly bufferedAmount: number;\n /** Resolves once the send buffer has drained below the low water mark. */\n drained(): Promise<void>;\n /**\n * Bytes the channel queues before `send` throws (the native and iroh channels). Absent: it buffers freely, as a\n * WebRTC data channel does, and a sender only keeps under `LIMITS.sendHighWaterMark`.\n */\n readonly sendBudget?: number;\n /**\n * It carries binary messages as well as text (WISP 100 § Text and binary messages): a WebRTC data channel does.\n * Absent: text only, as the Iroh channels are, and nothing binary is offered or sent on it.\n */\n readonly binary?: boolean;\n close(): void;\n onMessage: ((data: string | Uint8Array) => void) | null;\n onClose: (() => void) | null;\n}\n\nexport function wrapDataChannel(dc: RTCDataChannel): FrameChannel {\n dc.binaryType = \"arraybuffer\";\n dc.bufferedAmountLowThreshold = LIMITS.sendLowWaterMark;\n\n const channel: FrameChannel = {\n send(data) {\n if (dc.readyState !== \"open\") throw new Error(\"Data link is not open\");\n if (typeof data === \"string\") dc.send(data);\n else dc.send(data as unknown as ArrayBuffer);\n },\n get bufferedAmount() {\n return dc.bufferedAmount;\n },\n drained() {\n if (dc.bufferedAmount <= LIMITS.sendLowWaterMark || dc.readyState !== \"open\") return Promise.resolve();\n return new Promise<void>((resolve) => {\n const done = () => {\n dc.removeEventListener(\"bufferedamountlow\", done);\n dc.removeEventListener(\"close\", done);\n resolve();\n };\n dc.addEventListener(\"bufferedamountlow\", done);\n dc.addEventListener(\"close\", done);\n });\n },\n close() {\n dc.close();\n },\n binary: true,\n onMessage: null,\n onClose: null,\n };\n\n dc.addEventListener(\"message\", (event) => {\n const data = event.data;\n if (typeof data === \"string\") channel.onMessage?.(data);\n else if (data instanceof ArrayBuffer) channel.onMessage?.(new Uint8Array(data));\n });\n dc.addEventListener(\"close\", () => channel.onClose?.());\n return channel;\n}\n\n/** Writes a body as chunks, honouring backpressure. Always ends with an END chunk. */\nexport async function sendBody(\n channel: FrameChannel,\n kind: ChunkKind,\n id: number,\n body: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,\n isCancelled: () => boolean = () => false,\n): Promise<void> {\n for await (const part of body) {\n for (let offset = 0; offset < part.length; offset += MAX_CHUNK_PAYLOAD_BYTES) {\n if (isCancelled()) return;\n if (channel.bufferedAmount > LIMITS.sendHighWaterMark) await channel.drained();\n const payload = part.subarray(offset, offset + MAX_CHUNK_PAYLOAD_BYTES);\n channel.send(encodeChunk({ kind, id, end: false, payload }));\n }\n }\n if (isCancelled()) return;\n channel.send(encodeChunk({ kind, id, end: true, payload: new Uint8Array(0) }));\n}\n","import { bytesEqual, utf8Decode, utf8Encode } from \"./bytes\";\n\n/*\n * JSON in its canonical form (RFC 8785, JCS), as WISP 1200 signs and reads it: object keys sorted by their UTF-16 code\n * units, no whitespace, strings and numbers written as ECMAScript's JSON.stringify writes them. A reader never trusts\n * that bytes are canonical: it parses them and writes the value again, and refuses the bytes when the two differ. That\n * one comparison refuses a duplicated key, a key out of order, whitespace, a byte order mark, `1.0` or `1e2`, an\n * escape written another way and an integer past 2^53, so two parsers can never read different values from one text.\n */\n\n/** A value JSON can hold. */\nexport type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };\n\n/** True when `s` holds a lone UTF-16 surrogate, which I-JSON (and so JCS) does not allow. */\nfunction hasLoneSurrogate(s: string): boolean {\n for (let i = 0; i < s.length; i++) {\n const c = s.charCodeAt(i);\n if (c >= 0xd800 && c <= 0xdbff) {\n const next = s.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) { i++; continue; }\n return true;\n }\n if (c >= 0xdc00 && c <= 0xdfff) return true;\n }\n return false;\n}\n\nfunction write(value: unknown, depth: number): string {\n if (depth > 64) throw new Error(\"JSON nested deeper than 64 levels\");\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Error(\"JSON numbers must be finite\");\n return JSON.stringify(value);\n }\n if (typeof value === \"string\") {\n if (hasLoneSurrogate(value)) throw new Error(\"A JSON string holds a lone surrogate\");\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) return `[${value.map((v) => write(v, depth + 1)).join(\",\")}]`;\n if (typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n // A member whose value is undefined is left out, as JSON.stringify leaves it out.\n const keys = Object.keys(value).filter((k) => record[k] !== undefined).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n return `{${keys.map((k) => {\n if (hasLoneSurrogate(k)) throw new Error(\"A JSON key holds a lone surrogate\");\n return `${JSON.stringify(k)}:${write(record[k], depth + 1)}`;\n }).join(\",\")}}`;\n }\n throw new Error(`Not a JSON value: ${typeof value}`);\n}\n\n/** The canonical text of a JSON value. Throws on a value JSON cannot hold (a non-finite number, a lone surrogate). */\nexport function canonicalJson(value: unknown): string {\n return write(value, 0);\n}\n\n/** The canonical UTF-8 bytes of a JSON value. */\nexport function canonicalJsonBytes(value: unknown): Uint8Array {\n return utf8Encode(canonicalJson(value));\n}\n\nexport type CanonicalJsonRefusal = \"not-json\" | \"not-canonical\";\nexport type CanonicalJsonReading = { ok: true; value: JsonValue } | { ok: false; reason: CanonicalJsonRefusal; detail?: string };\n\n/**\n * Reads bytes that must be the canonical form of a JSON value: `not-json` when they are not UTF-8 JSON, `not-canonical`\n * when writing the value again gives other bytes.\n */\nexport function readCanonicalJson(bytes: Uint8Array): CanonicalJsonReading {\n let value: JsonValue;\n try {\n value = JSON.parse(utf8Decode(bytes)) as JsonValue;\n } catch (error) {\n return { ok: false, reason: \"not-json\", detail: error instanceof Error ? error.message : String(error) };\n }\n let again: Uint8Array;\n try {\n again = canonicalJsonBytes(value);\n } catch (error) {\n return { ok: false, reason: \"not-canonical\", detail: error instanceof Error ? error.message : String(error) };\n }\n if (!bytesEqual(again, bytes)) return { ok: false, reason: \"not-canonical\" };\n return { ok: true, value };\n}\n","import { ed25519 } from \"@noble/curves/ed25519.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { fromBase64Url, fromZ32, toBase64Url, toZ32, utf8Encode } from \"./bytes\";\nimport { canonicalJsonBytes, readCanonicalJson, type CanonicalJsonRefusal, type JsonValue } from \"./canonicalJson\";\nimport type { Signer } from \"./signer\";\n\n/*\n * Signed statements of WISP 1200 (Apps · Signatures): every signed object is signed the same way, Ed25519 over the\n * bytes `<prefix>`, a zero byte, then the SHA-256 of the object's canonical JSON (RFC 8785). The prefix says what the\n * object is, so no signature can be replayed as another kind. The signature travels as a statement of its own,\n * `{\"alg\":\"ed25519\",\"key\":\"<z-base32>\",\"sig\":\"<base64url>\"}`, itself canonical.\n *\n * A revocation (`ghostly-revoke/1`) is the publisher's: it names digests of its app, or every version up to a\n * `sequence`, and travels as `{\"statement\": {...}, \"signature\": {...}}` in `ghostly-revoke.json` and, verbatim, in a\n * store index's `revoked`.\n */\n\n/** The prefixes of phase 1. The others of the WISP (reports, reviews, licences, rotation, publisher proofs) come later. */\nexport const APP_PREFIXES = {\n app: \"ghostly-app/1\",\n store: \"ghostly-store/1\",\n revoke: \"ghostly-revoke/1\",\n} as const;\nexport type AppPrefix = typeof APP_PREFIXES[keyof typeof APP_PREFIXES];\n\nexport const APP_STATEMENT_LIMITS = {\n /** A signature statement's canonical bytes: 52 + 86 characters of values and the keys around them. */\n signatureBytes: 1024,\n /** Digests named by one revocation. */\n revokeDigests: 64,\n /** A revocation's reason, in characters. */\n reason: 200,\n /** Revocations in one `ghostly-revoke.json`. */\n revocations: 4096,\n} as const;\n\n/** A z-base32 Ed25519 public key: 52 characters that decode to 32 bytes and encode back to the same text. */\nexport function isAppKey(value: unknown): value is string {\n if (typeof value !== \"string\" || !/^[ybndrfg8ejkmcpqxot1uwisza345h769]{52}$/.test(value)) return false;\n const bytes = fromZ32(value);\n return bytes.length === 32 && toZ32(bytes) === value;\n}\n\n/** A SHA-256 in base64url without padding: 43 characters that decode to 32 bytes and encode back to the same text. */\nexport function isAppHash(value: unknown): value is string {\n if (typeof value !== \"string\" || !/^[A-Za-z0-9_-]{43}$/.test(value)) return false;\n return toBase64Url(fromBase64Url(value)) === value;\n}\n\n/** An app reference, `<publisher key>/<name>`. */\nexport const APP_NAME = /^[a-z][a-z0-9-]{0,31}$/;\nexport function isAppRef(value: unknown): value is string {\n if (typeof value !== \"string\") return false;\n const slash = value.indexOf(\"/\");\n return slash === 52 && isAppKey(value.slice(0, 52)) && APP_NAME.test(value.slice(53));\n}\nexport function appRef(publisher: string, name: string): string {\n return `${publisher}/${name}`;\n}\n/** The publisher key of a reference. */\nexport function appRefPublisher(ref: string): string {\n return ref.slice(0, 52);\n}\n\n/**\n * The publisher fingerprint people see: the first 16 z-base32 characters of the key (80 bits), in four groups.\n */\nexport function appFingerprint(key: string): string {\n return (key.slice(0, 16).match(/.{1,4}/g) ?? []).join(\" \");\n}\n\n/** The bytes a signature covers: the prefix, a zero byte, then the SHA-256 of the object's canonical bytes. */\nexport function appSignedBytes(prefix: AppPrefix, canonical: Uint8Array): Uint8Array {\n const p = utf8Encode(prefix);\n const out = new Uint8Array(p.length + 1 + 32);\n out.set(p, 0);\n out.set(sha256(canonical), p.length + 1);\n return out;\n}\n\nexport interface AppSignature { alg: \"ed25519\"; key: string; sig: string }\n\n/** Signs an object under a prefix: its canonical bytes and the signature statement. */\nexport async function signAppObject(prefix: AppPrefix, value: unknown, signer: Signer): Promise<{ bytes: Uint8Array; signature: AppSignature }> {\n const bytes = canonicalJsonBytes(value);\n const sig = await signer.sign(appSignedBytes(prefix, bytes));\n return { bytes, signature: { alg: \"ed25519\", key: toZ32(signer.publicKey), sig: toBase64Url(sig) } };\n}\n\n/**\n * Ed25519 verification as RFC 8032 writes it (`zip215: false`): canonical encodings of the point and the scalar only,\n * so every parser (this one, a Rust one with `verify_strict`) accepts exactly the same signatures.\n */\nfunction strictVerify(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean {\n try {\n return ed25519.verify(signature, message, publicKey, { zip215: false });\n } catch {\n return false;\n }\n}\n\nexport type AppSignatureRefusal = \"bad-signature-statement\" | \"signature-key\" | \"bad-signature\";\n\nconst exactKeys = (value: unknown, keys: readonly string[], optional: readonly string[] = []): value is Record<string, JsonValue> => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const have = Object.keys(value);\n return keys.every((k) => have.includes(k)) && have.every((k) => keys.includes(k) || optional.includes(k));\n};\n\n/** Checks the shape of a signature statement: exactly `alg`, `key` and `sig`, a 64-byte signature. */\nexport function isAppSignature(value: unknown): value is AppSignature {\n if (!exactKeys(value, [\"alg\", \"key\", \"sig\"])) return false;\n if (value.alg !== \"ed25519\" || !isAppKey(value.key)) return false;\n return typeof value.sig === \"string\" && /^[A-Za-z0-9_-]{86}$/.test(value.sig) && toBase64Url(fromBase64Url(value.sig)) === value.sig;\n}\n\n/** Reads a signature statement from its bytes, which must be canonical. */\nexport function readAppSignature(bytes: Uint8Array): { ok: true; signature: AppSignature } | { ok: false; reason: \"bad-signature-statement\"; detail?: string } {\n if (bytes.length > APP_STATEMENT_LIMITS.signatureBytes) return { ok: false, reason: \"bad-signature-statement\", detail: \"Too large\" };\n const read = readCanonicalJson(bytes);\n if (!read.ok) return { ok: false, reason: \"bad-signature-statement\", detail: read.reason };\n if (!isAppSignature(read.value)) return { ok: false, reason: \"bad-signature-statement\", detail: \"Not {alg, key, sig}\" };\n return { ok: true, signature: read.value };\n}\n\n/**\n * Verifies a signature over an object's canonical bytes under a prefix, made by `key`: `signature-key` when the\n * statement names another key, `bad-signature` when it does not verify.\n */\nexport function verifyAppSignature(prefix: AppPrefix, canonical: Uint8Array, signature: AppSignature, key: string): { ok: true } | { ok: false; reason: AppSignatureRefusal } {\n if (!isAppSignature(signature)) return { ok: false, reason: \"bad-signature-statement\" };\n if (signature.key !== key) return { ok: false, reason: \"signature-key\" };\n return strictVerify(fromBase64Url(signature.sig), appSignedBytes(prefix, canonical), fromZ32(key)) ? { ok: true } : { ok: false, reason: \"bad-signature\" };\n}\n\n/** Verifies a signed object given as bytes: they must be canonical, then the signature as above. */\nexport function verifyAppStatementBytes(prefix: AppPrefix, bytes: Uint8Array, signature: AppSignature, key: string): { ok: true; value: JsonValue } | { ok: false; reason: CanonicalJsonRefusal | AppSignatureRefusal } {\n const read = readCanonicalJson(bytes);\n if (!read.ok) return read;\n const checked = verifyAppSignature(prefix, bytes, signature, key);\n return checked.ok ? { ok: true, value: read.value } : checked;\n}\n\n// ---------- revocations ----------\n\n/** A publisher's revocation: the listed digests of its app, or every version up to `upTo` (inclusive). */\nexport type AppRevokeStatement =\n | { ghostlyRevoke: 1; app: string; digests: string[]; reason?: string }\n | { ghostlyRevoke: 1; app: string; upTo: number; reason?: string };\nexport interface SignedAppRevocation { statement: AppRevokeStatement; signature: AppSignature }\n\nexport type AppRevocationRefusal = \"bad-revocation\" | AppSignatureRefusal;\n\nconst isSequence = (v: unknown): v is number => typeof v === \"number\" && Number.isSafeInteger(v) && v >= 1;\nconst isPlainLine = (v: unknown, max: number): v is string => typeof v === \"string\" && [...v].length <= max && !/\\p{Cc}/u.test(v);\n\n/** Checks a revocation statement's shape; the reason it is refused, or null. */\nexport function checkAppRevokeStatement(value: unknown): string | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return \"Not an object\";\n const v = value as Record<string, unknown>;\n const byDigests = \"digests\" in v;\n if (!exactKeys(v, [\"ghostlyRevoke\", \"app\", byDigests ? \"digests\" : \"upTo\"], [\"reason\"])) return \"Keys are not ghostlyRevoke, app, and exactly one of digests and upTo (reason optional)\";\n if (v.ghostlyRevoke !== 1) return \"ghostlyRevoke is not 1\";\n if (!isAppRef(v.app)) return \"app is not a reference\";\n if (byDigests) {\n const d = v.digests;\n if (!Array.isArray(d) || d.length < 1 || d.length > APP_STATEMENT_LIMITS.revokeDigests) return \"digests is not 1 to 64 digests\";\n if (!d.every(isAppHash) || new Set(d).size !== d.length) return \"digests holds a bad or repeated digest\";\n } else if (!isSequence(v.upTo)) return \"upTo is not a sequence\";\n if (v.reason !== undefined && !isPlainLine(v.reason, APP_STATEMENT_LIMITS.reason)) return \"reason is not one line of 200 characters\";\n return null;\n}\n\n/** Signs a revocation with the publisher's key. */\nexport async function signAppRevocation(statement: AppRevokeStatement, signer: Signer): Promise<SignedAppRevocation> {\n const problem = checkAppRevokeStatement(statement);\n if (problem) throw new Error(`Not a revocation: ${problem}`);\n if (toZ32(signer.publicKey) !== appRefPublisher(statement.app)) throw new Error(\"A revocation is signed by its app's publisher\");\n const { signature } = await signAppObject(APP_PREFIXES.revoke, statement, signer);\n return { statement, signature };\n}\n\n/**\n * The revocations a reader has already checked, by `appRevocationId`: a store index copies its `revoked` again in every\n * version, so a reader that holds this checks each revocation's signature once. The reader bounds what it holds.\n */\nexport interface AppRevocationsChecked { has(id: string): boolean; add(id: string): void }\n\n/** What a checked revocation is kept by: the SHA-256 of its canonical bytes (statement and signature), base64url. */\nexport function appRevocationId(revocation: SignedAppRevocation): string {\n return toBase64Url(sha256(canonicalJsonBytes(revocation as unknown as JsonValue)));\n}\n\ntype AppRevocationRead = { ok: true; revocation: SignedAppRevocation } | { ok: false; reason: AppRevocationRefusal; detail?: string };\n\n/**\n * Reads one signed revocation (a parsed value, from `ghostly-revoke.json` or a store index's `revoked`): its shape, then\n * the signature under `ghostly-revoke/1` by the publisher its `app` names. One that `checked` holds verified before, so\n * its signature is not checked again; one that verifies now is added to it.\n */\nexport function readAppRevocation(value: unknown, checked?: AppRevocationsChecked): AppRevocationRead {\n const read = readAppRevocationShape(value);\n if (!read.ok) return read;\n const verified = verifyAppRevocation(read.revocation, checked);\n return verified.ok ? read : verified;\n}\n\n/** The first half of `readAppRevocation`: the shape of a signed revocation, with no signature checked. */\nexport function readAppRevocationShape(value: unknown): AppRevocationRead {\n if (!exactKeys(value, [\"statement\", \"signature\"])) return { ok: false, reason: \"bad-revocation\", detail: \"Keys are not statement and signature\" };\n const problem = checkAppRevokeStatement(value.statement);\n if (problem) return { ok: false, reason: \"bad-revocation\", detail: problem };\n if (!isAppSignature(value.signature)) return { ok: false, reason: \"bad-signature-statement\" };\n return { ok: true, revocation: { statement: value.statement as unknown as AppRevokeStatement, signature: value.signature } };\n}\n\n/** The second half: the signature of a revocation of the right shape, unless `checked` holds it. */\nexport function verifyAppRevocation(revocation: SignedAppRevocation, checked?: AppRevocationsChecked): { ok: true } | { ok: false; reason: AppSignatureRefusal } {\n const id = checked && appRevocationId(revocation);\n if (id !== undefined && checked!.has(id)) return { ok: true };\n const verified = verifyAppSignature(APP_PREFIXES.revoke, canonicalJsonBytes(revocation.statement as unknown as JsonValue), revocation.signature, appRefPublisher(revocation.statement.app));\n if (verified.ok && id !== undefined) checked!.add(id);\n return verified;\n}\n\n/**\n * Reads `ghostly-revoke.json`: canonical bytes holding a list of signed revocations. One that does not verify refuses the\n * file, as a store index's `revoked` does.\n */\nexport function readAppRevocations(bytes: Uint8Array): { ok: true; revocations: SignedAppRevocation[] } | { ok: false; reason: CanonicalJsonRefusal | AppRevocationRefusal; detail?: string } {\n const read = readCanonicalJson(bytes);\n if (!read.ok) return read;\n if (!Array.isArray(read.value) || read.value.length > APP_STATEMENT_LIMITS.revocations) return { ok: false, reason: \"bad-revocation\", detail: \"Not a list of at most 4096 revocations\" };\n const revocations: SignedAppRevocation[] = [];\n for (const entry of read.value) {\n const one = readAppRevocation(entry);\n if (!one.ok) return one;\n revocations.push(one.revocation);\n }\n return { ok: true, revocations };\n}\n","/**\n * How far ahead of this device's clock a number a peer made from its clock may be (a pin's number, a status card's\n * times): clocks drift, but a time past this is the peer's choice, not its clock.\n */\nexport const MESSAGE_CLOCK_SKEW_MS = 5 * 60_000;\n\n/**\n * The time a line of a group's history is kept under (a membership change, a rename): what its commit says, but at\n * most `MESSAGE_CLOCK_SKEW_MS` past this clock, and now when it is not a positive number. Messages do not use it:\n * they are placed where they arrive (below).\n */\nexport function receivedTimestamp(ts: number, now = Date.now()): number {\n if (!Number.isFinite(ts) || ts <= 0) return now;\n return Math.min(ts, now + MESSAGE_CLOCK_SKEW_MS);\n}\n\n/** The latest moment a date can hold. */\nconst MAX_DATE_MS = 8.64e15;\n\n/*\n * A received message has two times (WISP 400, requirement 10).\n *\n * Its place: when it was first stored on this device, kept as the row's `timestamp`. A history is sorted by it, so a\n * conversation reads in the order things happened here: what came is where it came, and what I send next goes\n * below it, whatever the sender's clock says. A peer picks the time it sends, so that time never orders anything.\n *\n * The sender's claim: kept beside it (`sentAt`) and shown on the bubble, never later than the place: a message does\n * not show a time that has not come yet.\n */\n\n/** The time a sender says, as it is kept: a positive time a date can hold, or nothing. */\nexport function claimedTime(ts: unknown): number | undefined {\n return typeof ts === \"number\" && Number.isFinite(ts) && ts > 0 && ts <= MAX_DATE_MS ? ts : undefined;\n}\n\n/**\n * The place of a row stored now: this clock, and past the last place given in its chat, so rows that come in the\n * same millisecond (a hold pickup, a group's catch-up) keep the order they came in.\n */\nexport function arrivalKey(now: number, last = 0): number {\n return Math.max(now, last + 1);\n}\n\n/** The time a message shows: what its sender said, never later than when it came. A row of mine shows its own time. */\nexport function shownTime(message: { timestamp: number; sentAt?: number }): number {\n return message.sentAt === undefined ? message.timestamp : Math.min(message.sentAt, message.timestamp);\n}\n\n/**\n * When something a peer dated happened (an edit), for this device: what the peer says, now at the latest, and now\n * when it says nothing a date can hold.\n */\nexport function heardTime(ts: unknown, now = Date.now()): number {\n return Math.min(claimedTime(ts) ?? now, now);\n}\n","import { utf8Encode } from \"./bytes\";\nimport { isAppHash, isAppRef } from \"./appStatements\";\nimport { MESSAGE_CLOCK_SKEW_MS } from \"./messageTime\";\nimport { sanitizeDisplayText } from \"./text\";\n\n/*\n * Status cards (WISP 405 · Status Cards): a bot's task or routine, shown as a small card instead of a text. A card rides\n * as `sc` beside the text of a message on every path that carries text with fields of its own (the live session's\n * `paired-message` and `paired-edit`, a private group's sealed box, a community's payload and its edit frame), and the\n * text is its fallback: what an app without cards, the DHT floor and a held item show. The bot keeps a card current by\n * editing its message; the highest edit number wins, as for any edit, and a card belongs to the version it came with.\n *\n * {\"kind\":\"task\",\"id\":\"relay-rotation\",\"title\":\"Fix relay rotation\",\"status\":\"running\",\"progress\":40,\n * \"step\":\"Running the e2e\",\"pr\":{\"url\":\"https://github.com/o/r/pull/612\",\"number\":612,\"additions\":123,\"deletions\":45}}\n *\n * Cards are display only: nothing on one runs anything, and its links are https, shown with their host. Every string\n * is one line of plain text without control, invisible or direction characters; every list and number is bounded.\n * `readStatusCard` is the reader's rule (what does not hold is left out, a card that cannot stand is dropped, never the\n * message); `checkStatusCard` is the sender's (anything out of bounds is refused, so nothing is lost silently).\n */\n\nexport const STATUS_CARD_LIMITS = {\n /** A card's JSON, as sent (UTF-8 bytes): with the fallback text it stays well inside every message bound. */\n bytes: 8 * 1024,\n /** The sender's id for the card: stable across its updates. */\n id: 64,\n title: 120,\n /** The current step, a list item, a run's summary. */\n line: 200,\n branch: 120,\n schedule: 80,\n cron: 64,\n linkLabel: 40,\n url: 512,\n items: 20,\n links: 4,\n runs: 10,\n /** Any count on a card (a PR's number, lines, files; steps). */\n count: 1_000_000_000,\n /** How far ahead a routine's next run may be. */\n nextRunMs: 366 * 24 * 60 * 60_000,\n /** Edits a message with a card takes (a text takes `MAX_EDITS_PER_MESSAGE`): a bot updates a long task often. */\n edits: 5_000,\n /** Labels on a task, and a label's length in characters. */\n tags: 3,\n tag: 24,\n /** Buttons on a message (WISP 406 · Message Buttons). */\n buttons: 6,\n /** A button's id: what a press names. */\n buttonId: 32,\n /** A button's label, in characters. */\n buttonLabel: 40,\n /** An app card's title and version, in characters (WISP 405 § An app): the manifest's bounds. */\n appTitle: 40,\n appVersion: 32,\n /** A usage card's label (\"Claude\") and account, in characters (WISP 405 § Usage). */\n usageLabel: 24,\n /** A usage window's name (\"5 h\", \"week\"), in characters. */\n usageWindow: 16,\n /** Windows beside a usage card's own, shown in its details. */\n usageWindows: 3,\n} as const;\n\n/** How a button looks: `primary` is the one highlighted, `danger` warns; `neutral` when none is said. */\nexport const BUTTON_STYLES = [\"primary\", \"neutral\", \"danger\"] as const;\nexport type ButtonStyle = typeof BUTTON_STYLES[number];\n\nexport const TASK_STATUSES = [\"queued\", \"running\", \"blocked\", \"done\", \"failed\", \"cancelled\"] as const;\nexport type TaskStatus = typeof TASK_STATUSES[number];\n/** A task that is still going: shown first, and counted on the chat's Tasks button. */\nexport const ACTIVE_TASK_STATUSES: readonly TaskStatus[] = [\"queued\", \"running\", \"blocked\"];\nexport const ITEM_STATES = [\"pending\", \"running\", \"done\", \"failed\", \"skipped\"] as const;\nexport type ItemState = typeof ITEM_STATES[number];\nexport const ROUTINE_STATES = [\"active\", \"paused\"] as const;\nexport type RoutineState = typeof ROUTINE_STATES[number];\nexport const RUN_RESULTS = [\"ok\", \"failed\", \"skipped\"] as const;\nexport type RunResult = typeof RUN_RESULTS[number];\n\n/**\n * Where a task's pull request stands: `draft` (not ready for review), `open` (ready for review), `merged`, `closed`\n * (closed without a merge). A reader that does not know the field shows the pull request as before.\n */\nexport const PR_STATES = [\"draft\", \"open\", \"merged\", \"closed\"] as const;\nexport type PrState = typeof PR_STATES[number];\n/** How a pull request's checks stand. */\nexport const PR_CHECKS = [\"passing\", \"failing\", \"pending\"] as const;\nexport type PrChecks = typeof PR_CHECKS[number];\n\nexport interface CardLink { url: string; label?: string }\nexport interface TaskItem { text: string; state: ItemState }\nexport interface TaskPr { url: string; number?: number; additions?: number; deletions?: number; files?: number; state?: PrState; checks?: PrChecks }\n\nexport interface TaskCard {\n kind: \"task\";\n id: string;\n title: string;\n status: TaskStatus;\n /** 0 to 100. */\n progress?: number;\n /** Steps done of `total`. */\n done?: number;\n total?: number;\n /** What it is doing now. */\n step?: string;\n pr?: TaskPr;\n branch?: string;\n startedAt?: number;\n updatedAt?: number;\n items?: TaskItem[];\n links?: CardLink[];\n /** Up to `STATUS_CARD_LIMITS.tags` short labels (an area, a repository), shown as chips. */\n tags?: string[];\n /** The id of another task of the same sender in the same chat that this one is a part of. */\n parent?: string;\n}\n\nexport interface RoutineRun { at: number; result: RunResult; summary?: string }\n\nexport interface RoutineCard {\n kind: \"routine\";\n id: string;\n name: string;\n /** When it runs, as people say it (\"every day 01:00\"). */\n schedule: string;\n cron?: string;\n state: RoutineState;\n lastRun?: RoutineRun;\n nextRunAt?: number;\n /** Recent runs, newest first. */\n runs?: RoutineRun[];\n links?: CardLink[];\n}\n\n/**\n * A button under a bot's message (WISP 406 · Message Buttons). `once`: a press on it answers the question for that\n * person, and their app offers none of the buttons again; a button without it may be pressed again.\n */\nexport interface CardButton { id: string; label: string; style?: ButtonStyle; once?: true }\n\n/**\n * Buttons under a bot's message (WISP 406 · Message Buttons): unlike a task or a routine, the message's text shows\n * with them, and is also what an app without buttons shows (so it should say how to answer in words). `chosen` is the\n * answer the bot took, marked for everyone; `closed`: no button takes a press any more.\n */\nexport interface ButtonsCard {\n kind: \"buttons\";\n id: string;\n buttons: CardButton[];\n chosen?: string;\n closed?: true;\n}\n\n/**\n * A mini-app the sender shared in the chat, or opened in it (WISP 405 § An app, WISP 1200 § Apps sent in a chat): what a\n * contact needs to install the same app and check it. Every field is the sender's claim, unsigned; nothing is fetched to\n * show it. `id` is made from `ref` (`appCardId`); `opened` only for \"Ana opened Chess\".\n */\nexport interface AppCard {\n kind: \"app\";\n id: string;\n ref: string;\n digest: string;\n sequence: number;\n title: string;\n version?: string;\n url?: string;\n opened?: true;\n}\n\n/** Another window of a usage card (\"week\"): how much of it is left, and when it starts again. */\nexport interface UsageWindow { window: string; left: number; resetsAt?: number }\n\n/**\n * How much of a quota a bot has left (WISP 405 § Usage): \"Claude, 62% left of the 5 h window, resets 18:00\". `left` is\n * the percent left, 0 to 100; a bot that counts can say `used` of `limit` instead, and the percent is worked out\n * (`usageLeft`). `windows`: other windows of the same quota (a week), shown in the details. One per chat stands: the\n * latest a sender changed. Display only, like every card.\n */\nexport interface UsageCard {\n kind: \"usage\";\n id: string;\n /** 0 to 100: how much is left, in percent. */\n left?: number;\n used?: number;\n limit?: number;\n /** What the quota is of (\"Claude\"). */\n label?: string;\n /** Which of the bot's accounts it is, short. */\n account?: string;\n /** The quota's window, as people say it (\"5 h\", \"week\"). */\n window?: string;\n /** When the window starts again: milliseconds. */\n resetsAt?: number;\n /** When the bot read the numbers: milliseconds. */\n updatedAt?: number;\n windows?: UsageWindow[];\n}\n\nexport type StatusCard = TaskCard | RoutineCard | ButtonsCard | AppCard | UsageCard;\nexport type StatusCardKind = StatusCard[\"kind\"];\n\nconst ID = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,63}$/;\n/** A button's id: 1 to 32 of `A-Z a-z 0-9 _ . : -`, not starting with `-`. */\nexport const BUTTON_ID = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,31}$/;\nconst CRON = /^[0-9A-Za-z*,/?#\\- ]{1,64}$/;\n\n/**\n * Whether a typed answer is a button's label or id (WISP 406 · Message Buttons): ignoring case and spaces at the ends.\n * Not the device's locale's case: the presser's and the author's apps must agree (\"I\" is not \"ı\" anywhere).\n */\nexport const sameButtonText = (a: string, b: string): boolean => a.trim().toLowerCase() === b.trim().toLowerCase();\n\n/**\n * Two buttons a typed answer could not tell apart, or none: a label that reads as an earlier label (`label`), or a\n * label that reads as another button's id (`id`), both as `sameButtonText` compares them. The labels as a reader keeps\n * them (one line, cleaned). `at` is the button that clashes, `with` the one it clashes with.\n */\nexport function buttonLabelClash(buttons: readonly unknown[]): { at: number; with: number; kind: \"label\" | \"id\" } | undefined {\n const labels = buttons.map(button => (isObject(button) ? cardLine(button.label, Infinity) : undefined));\n const ids = buttons.map(button => (isObject(button) && typeof button.id === \"string\" ? button.id : undefined));\n for (const [at, label] of labels.entries()) {\n if (!label) continue;\n const twin = labels.findIndex((other, i) => i < at && !!other && sameButtonText(other, label));\n if (twin !== -1) return { at, with: twin, kind: \"label\" };\n const named = ids.findIndex((id, i) => i !== at && id !== undefined && sameButtonText(id, label));\n if (named !== -1) return { at, with: named, kind: \"id\" };\n }\n return undefined;\n}\n\n/** An app card's id, made from its reference: the app's name, a dot, the first 16 characters of the publisher key. */\nexport function appCardId(ref: string): string {\n return `${ref.slice(53)}.${ref.slice(0, 16)}`;\n}\n\n/** An app card's `sequence`: a whole number from 0 to 2^53 - 1. */\nconst appSequence = (value: unknown): value is number => typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n\nconst isObject = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** One line of display text: runs of space (line breaks included) become one space, then the display rules for names. */\nexport function cardLine(value: unknown, max: number): string | undefined {\n if (typeof value !== \"string\") return undefined;\n return sanitizeDisplayText(value.replace(/\\s+/gu, \" \"), max);\n}\n\n/**\n * An https link a card may show: no credentials, a host, no spaces or control characters, at most\n * `STATUS_CARD_LIMITS.url` characters. Returned as the parser writes it (a non-ASCII host in punycode).\n */\nexport function cardUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\" || !value || value.length > STATUS_CARD_LIMITS.url || /[\\s\\p{Cc}\\p{Cf}]/u.test(value)) return undefined;\n let url: URL;\n try { url = new URL(value); } catch { return undefined; }\n if (url.protocol !== \"https:\" || !url.hostname || url.username || url.password) return undefined;\n return url.href.length <= STATUS_CARD_LIMITS.url ? url.href : undefined;\n}\n\n/** The host a card shows beside a link, so the reader sees where it goes before following it. */\nexport function cardLinkHost(url: string): string {\n try { return new URL(url).host; } catch { return \"\"; }\n}\n\nconst count = (value: unknown, min = 0): number | undefined =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value >= min && value <= STATUS_CARD_LIMITS.count ? value : undefined;\n\n/** A time on a card as a reader keeps it: a positive whole number, never later than `latest`. */\nconst time = (value: unknown, latest: number): number | undefined =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 ? Math.min(value, latest) : undefined;\n\nconst oneOf = <T extends string>(values: readonly T[], value: unknown): T | undefined =>\n typeof value === \"string\" && (values as readonly string[]).includes(value) ? value as T : undefined;\n\nfunction readLinks(raw: unknown): CardLink[] {\n if (!Array.isArray(raw)) return [];\n const links: CardLink[] = [];\n for (const entry of raw.slice(0, STATUS_CARD_LIMITS.links)) {\n if (!isObject(entry)) continue;\n const url = cardUrl(entry.url);\n if (!url) continue;\n const label = cardLine(entry.label, STATUS_CARD_LIMITS.linkLabel);\n links.push({ url, ...(label && { label }) });\n }\n return links;\n}\n\n/** A task's labels as a reader keeps them: the first `STATUS_CARD_LIMITS.tags` that hold, each one clean line, none twice. */\nfunction readTags(raw: unknown): string[] {\n if (!Array.isArray(raw)) return [];\n const tags: string[] = [];\n for (const entry of raw) {\n const tag = cardLine(entry, STATUS_CARD_LIMITS.tag);\n if (tag && !tags.includes(tag)) tags.push(tag);\n if (tags.length === STATUS_CARD_LIMITS.tags) break;\n }\n return tags;\n}\n\nfunction readRun(raw: unknown, now: number): RoutineRun | undefined {\n if (!isObject(raw)) return undefined;\n const at = time(raw.at, now + MESSAGE_CLOCK_SKEW_MS), result = oneOf(RUN_RESULTS, raw.result);\n if (!at || !result) return undefined;\n const summary = cardLine(raw.summary, STATUS_CARD_LIMITS.line);\n return { at, result, ...(summary && { summary }) };\n}\n\n/** A percent as a reader keeps it: a finite number, rounded and clamped into 0 to 100. */\nconst percent = (value: unknown): number | undefined =>\n typeof value === \"number\" && Number.isFinite(value) ? Math.round(Math.min(100, Math.max(0, value))) : undefined;\n\n/** A time ahead of the reader (a reset, a next run) as it keeps it: whole, positive, at most a year ahead. */\nconst ahead = (value: unknown, now: number): number | undefined =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 && value <= now + STATUS_CARD_LIMITS.nextRunMs ? value : undefined;\n\n/** A usage card as a reader keeps it (WISP 405 § Usage): dropped without a percent left or a used of limit. */\nfunction readUsage(raw: Record<string, unknown>, id: string, now: number): UsageCard | undefined {\n const left = percent(raw.left);\n let used = count(raw.used), limit = count(raw.limit, 1);\n if (used === undefined || limit === undefined || used > limit) used = limit = undefined;\n if (left === undefined && limit === undefined) return undefined;\n const label = cardLine(raw.label, STATUS_CARD_LIMITS.usageLabel), account = cardLine(raw.account, STATUS_CARD_LIMITS.usageLabel);\n const window = cardLine(raw.window, STATUS_CARD_LIMITS.usageWindow);\n const resetsAt = ahead(raw.resetsAt, now), updatedAt = time(raw.updatedAt, now + MESSAGE_CLOCK_SKEW_MS);\n const windows: UsageWindow[] = [];\n if (Array.isArray(raw.windows)) for (const entry of raw.windows.slice(0, STATUS_CARD_LIMITS.usageWindows)) {\n if (!isObject(entry)) continue;\n const name = cardLine(entry.window, STATUS_CARD_LIMITS.usageWindow), share = percent(entry.left);\n if (!name || share === undefined) continue;\n const at = ahead(entry.resetsAt, now);\n windows.push({ window: name, left: share, ...(at && { resetsAt: at }) });\n }\n return {\n kind: \"usage\", id,\n ...(left !== undefined && { left }), ...(used !== undefined && limit !== undefined && { used, limit }),\n ...(label && { label }), ...(account && { account }), ...(window && { window }),\n ...(resetsAt && { resetsAt }), ...(updatedAt && { updatedAt }), ...(windows.length && { windows }),\n };\n}\n\n/** How much of a usage card's quota is left, 0 to 100: its own percent, else worked out from used of limit. */\nexport function usageLeft(card: Pick<UsageCard, \"left\" | \"used\" | \"limit\">): number {\n if (card.left !== undefined) return card.left;\n return card.used !== undefined && card.limit ? Math.round((card.limit - card.used) / card.limit * 100) : 0;\n}\n\n/** The bytes a card takes on the wire: what `STATUS_CARD_LIMITS.bytes` bounds. */\nexport function statusCardBytes(card: unknown): number {\n try { return utf8Encode(JSON.stringify(card) ?? \"\").length; } catch { return Infinity; }\n}\n\n/**\n * A card as a reader takes it from the wire (`sc`), or undefined when it cannot stand: not an object, past\n * `STATUS_CARD_LIMITS.bytes`, a kind this app does not know, no valid id, title or name, status or state. Fields that do\n * not hold are left out and lists are cut to their bound; times are kept no later than the reader's clock allows (a\n * routine's next run up to a year ahead). The message is always kept: without a card it shows its text.\n */\nexport function readStatusCard(raw: unknown, now = Date.now()): StatusCard | undefined {\n if (!isObject(raw) || statusCardBytes(raw) > STATUS_CARD_LIMITS.bytes) return undefined;\n const id = typeof raw.id === \"string\" && ID.test(raw.id) ? raw.id : undefined;\n if (!id) return undefined;\n if (raw.kind === \"app\") {\n // Dropped unless the app it names holds: the reference, its id, the digest, the sequence and a title.\n if (!isAppRef(raw.ref) || id !== appCardId(raw.ref) || !isAppHash(raw.digest) || !appSequence(raw.sequence)) return undefined;\n const title = cardLine(raw.title, STATUS_CARD_LIMITS.appTitle);\n if (!title) return undefined;\n const version = cardLine(raw.version, STATUS_CARD_LIMITS.appVersion), url = cardUrl(raw.url);\n return { kind: \"app\", id, ref: raw.ref, digest: raw.digest, sequence: raw.sequence, title,\n ...(version && { version }), ...(url && { url }), ...(raw.opened === true && { opened: true as const }) };\n }\n const latest = now + MESSAGE_CLOCK_SKEW_MS;\n if (raw.kind === \"usage\") return readUsage(raw, id, now);\n const links = readLinks(raw.links);\n if (raw.kind === \"task\") {\n const title = cardLine(raw.title, STATUS_CARD_LIMITS.title), status = oneOf(TASK_STATUSES, raw.status);\n if (!title || !status) return undefined;\n const progress = typeof raw.progress === \"number\" && Number.isFinite(raw.progress) ? Math.round(Math.min(100, Math.max(0, raw.progress))) : undefined;\n let done = count(raw.done), total = count(raw.total, 1);\n if (done === undefined || total === undefined || done > total) done = total = undefined;\n const step = cardLine(raw.step, STATUS_CARD_LIMITS.line), branch = cardLine(raw.branch, STATUS_CARD_LIMITS.branch);\n let pr: TaskPr | undefined;\n if (isObject(raw.pr)) {\n const url = cardUrl(raw.pr.url);\n if (url) {\n const number = count(raw.pr.number, 1), additions = count(raw.pr.additions), deletions = count(raw.pr.deletions), files = count(raw.pr.files);\n const state = oneOf(PR_STATES, raw.pr.state), checks = oneOf(PR_CHECKS, raw.pr.checks);\n pr = { url, ...(number !== undefined && { number }), ...(additions !== undefined && { additions }), ...(deletions !== undefined && { deletions }), ...(files !== undefined && { files }),\n ...(state && { state }), ...(checks && { checks }) };\n }\n }\n const items: TaskItem[] = [];\n if (Array.isArray(raw.items)) for (const entry of raw.items.slice(0, STATUS_CARD_LIMITS.items)) {\n if (!isObject(entry)) continue;\n const text = cardLine(entry.text, STATUS_CARD_LIMITS.line);\n if (text) items.push({ text, state: oneOf(ITEM_STATES, entry.state) ?? \"pending\" });\n }\n const startedAt = time(raw.startedAt, latest), updatedAt = time(raw.updatedAt, latest);\n const tags = readTags(raw.tags);\n // A task is never a part of itself.\n const parent = typeof raw.parent === \"string\" && ID.test(raw.parent) && raw.parent !== id ? raw.parent : undefined;\n return {\n kind: \"task\", id, title, status,\n ...(progress !== undefined && { progress }), ...(done !== undefined && total !== undefined && { done, total }),\n ...(step && { step }), ...(pr && { pr }), ...(branch && { branch }),\n ...(startedAt && { startedAt }), ...(updatedAt && { updatedAt }),\n ...(items.length && { items }), ...(links.length && { links }),\n ...(tags.length && { tags }), ...(parent && { parent }),\n };\n }\n if (raw.kind === \"routine\") {\n const name = cardLine(raw.name, STATUS_CARD_LIMITS.title), schedule = cardLine(raw.schedule, STATUS_CARD_LIMITS.schedule), state = oneOf(ROUTINE_STATES, raw.state);\n if (!name || !schedule || !state) return undefined;\n const cron = typeof raw.cron === \"string\" && CRON.test(raw.cron.trim()) ? raw.cron.trim() : undefined;\n const lastRun = readRun(raw.lastRun, now);\n const nextRunAt = typeof raw.nextRunAt === \"number\" && Number.isSafeInteger(raw.nextRunAt) && raw.nextRunAt > 0 && raw.nextRunAt <= now + STATUS_CARD_LIMITS.nextRunMs ? raw.nextRunAt : undefined;\n const runs: RoutineRun[] = [];\n if (Array.isArray(raw.runs)) for (const entry of raw.runs.slice(0, STATUS_CARD_LIMITS.runs)) {\n const run = readRun(entry, now);\n if (run) runs.push(run);\n }\n return {\n kind: \"routine\", id, name, schedule, state,\n ...(cron && { cron }), ...(lastRun && { lastRun }), ...(nextRunAt && { nextRunAt }),\n ...(runs.length && { runs }), ...(links.length && { links }),\n };\n }\n if (raw.kind === \"buttons\") {\n if (!Array.isArray(raw.buttons)) return undefined;\n const buttons: CardButton[] = [], seen = new Set<string>();\n for (const entry of raw.buttons.slice(0, STATUS_CARD_LIMITS.buttons)) {\n if (!isObject(entry) || typeof entry.id !== \"string\" || !BUTTON_ID.test(entry.id) || seen.has(entry.id)) continue;\n const label = cardLine(entry.label, STATUS_CARD_LIMITS.buttonLabel);\n if (!label) continue;\n seen.add(entry.id);\n const style = oneOf(BUTTON_STYLES, entry.style);\n buttons.push({ id: entry.id, label, ...(style && style !== \"neutral\" && { style }), ...(entry.once === true && { once: true as const }) });\n }\n if (!buttons.length) return undefined;\n const chosen = typeof raw.chosen === \"string\" && seen.has(raw.chosen) ? raw.chosen : undefined;\n return { kind: \"buttons\", id, buttons, ...(chosen && { chosen }), ...(raw.closed === true && { closed: true as const }) };\n }\n return undefined;\n}\n\n/**\n * A card as a sender may send it, or why not: the reader's rule, but anything it would leave out or cut is refused\n * here instead (an unknown status, a link that is not https, a list past its bound, a line too long), so a bot learns\n * at once and nothing it said is lost on the way. Line breaks and invisible characters are cleaned, as a reader would.\n */\nexport function checkStatusCard(raw: unknown, now = Date.now()): { card: StatusCard } | { error: string } {\n if (!isObject(raw)) return { error: \"A card is an object\" };\n const bytes = statusCardBytes(raw);\n if (bytes > STATUS_CARD_LIMITS.bytes) return { error: `The card takes ${bytes} bytes; at most ${STATUS_CARD_LIMITS.bytes}` };\n if (raw.kind !== \"task\" && raw.kind !== \"routine\" && raw.kind !== \"buttons\" && raw.kind !== \"app\" && raw.kind !== \"usage\") return { error: \"kind is task, routine, buttons, app or usage\" };\n if (typeof raw.id !== \"string\" || !ID.test(raw.id)) return { error: `id is 1 to ${STATUS_CARD_LIMITS.id} of A-Z a-z 0-9 _ . : - and does not start with -` };\n const line = (field: string, value: unknown, max: number, required = false): string | null => {\n if (value === undefined && !required) return null;\n if (typeof value !== \"string\" || !cardLine(value, Infinity)) return `${field} is text`;\n if ([...cardLine(value, Infinity)!].length > max) return `${field} is at most ${max} characters`;\n return null;\n };\n const url = (field: string, value: unknown) => (cardUrl(value) ? null : `${field} must be an https link of at most ${STATUS_CARD_LIMITS.url} characters`);\n const whole = (field: string, value: unknown, min = 0) => (value === undefined || count(value, min) !== undefined ? null : `${field} is a whole number from ${min}`);\n const errors: (string | null)[] = [];\n if (raw.links !== undefined) {\n if (!Array.isArray(raw.links) || raw.links.length > STATUS_CARD_LIMITS.links) errors.push(`links is a list of at most ${STATUS_CARD_LIMITS.links}`);\n else for (const [i, link] of raw.links.entries()) {\n if (!isObject(link)) { errors.push(`links[${i}] is an object`); continue; }\n errors.push(url(`links[${i}].url`, link.url), line(`links[${i}].label`, link.label, STATUS_CARD_LIMITS.linkLabel));\n }\n }\n const share = (field: string, value: unknown) => (value === undefined || (typeof value === \"number\" && Number.isFinite(value) && value >= 0 && value <= 100) ? null : `${field} is a number from 0 to 100`);\n const reset = (field: string, value: unknown) => (value === undefined || ahead(value, now) !== undefined ? null : `${field} is a time in milliseconds, within a year`);\n if (raw.kind === \"usage\") {\n if (raw.links !== undefined) errors.push(\"a usage card takes no links\");\n if (raw.left === undefined && raw.used === undefined) errors.push(\"left (a percent) or used and limit\");\n errors.push(share(\"left\", raw.left));\n if ((raw.used === undefined) !== (raw.limit === undefined)) errors.push(\"used and limit go together\");\n else if (raw.used !== undefined) {\n errors.push(whole(\"used\", raw.used), whole(\"limit\", raw.limit, 1));\n if (typeof raw.used === \"number\" && typeof raw.limit === \"number\" && raw.used > raw.limit) errors.push(\"used is at most limit\");\n }\n errors.push(line(\"label\", raw.label, STATUS_CARD_LIMITS.usageLabel), line(\"account\", raw.account, STATUS_CARD_LIMITS.usageLabel), line(\"window\", raw.window, STATUS_CARD_LIMITS.usageWindow), reset(\"resetsAt\", raw.resetsAt));\n if (raw.updatedAt !== undefined && !(typeof raw.updatedAt === \"number\" && Number.isSafeInteger(raw.updatedAt) && raw.updatedAt > 0 && raw.updatedAt <= now + MESSAGE_CLOCK_SKEW_MS)) errors.push(\"updatedAt is a time in milliseconds, not in the future\");\n if (raw.windows !== undefined) {\n if (!Array.isArray(raw.windows) || raw.windows.length > STATUS_CARD_LIMITS.usageWindows) errors.push(`windows is a list of at most ${STATUS_CARD_LIMITS.usageWindows}`);\n else for (const [i, entry] of raw.windows.entries()) {\n if (!isObject(entry)) { errors.push(`windows[${i}] is an object`); continue; }\n errors.push(line(`windows[${i}].window`, entry.window, STATUS_CARD_LIMITS.usageWindow, true));\n errors.push(entry.left === undefined ? `windows[${i}].left is a number from 0 to 100` : share(`windows[${i}].left`, entry.left), reset(`windows[${i}].resetsAt`, entry.resetsAt));\n }\n }\n } else if (raw.kind === \"app\") {\n if (raw.links !== undefined) errors.push(\"an app card takes no links\");\n if (!isAppRef(raw.ref)) errors.push(\"ref is <publisher key in z-base32>/<name>\");\n else if (raw.id !== appCardId(raw.ref)) errors.push(`id is ${appCardId(raw.ref)}, made from ref`);\n if (!isAppHash(raw.digest)) errors.push(\"digest is a SHA-256 in base64url without padding\");\n if (!appSequence(raw.sequence)) errors.push(\"sequence is a whole number from 0 to 2^53 - 1\");\n errors.push(line(\"title\", raw.title, STATUS_CARD_LIMITS.appTitle, true), line(\"version\", raw.version, STATUS_CARD_LIMITS.appVersion));\n if (raw.url !== undefined) errors.push(url(\"url\", raw.url));\n if (raw.opened !== undefined && raw.opened !== true) errors.push(\"opened is true or left out\");\n } else if (raw.kind === \"buttons\") {\n if (raw.links !== undefined) errors.push(\"buttons take no links\");\n if (!Array.isArray(raw.buttons) || !raw.buttons.length || raw.buttons.length > STATUS_CARD_LIMITS.buttons) errors.push(`buttons is a list of 1 to ${STATUS_CARD_LIMITS.buttons}`);\n else {\n const ids = new Set<string>();\n for (const [i, button] of raw.buttons.entries()) {\n if (!isObject(button)) { errors.push(`buttons[${i}] is an object`); continue; }\n if (typeof button.id !== \"string\" || !BUTTON_ID.test(button.id)) errors.push(`buttons[${i}].id is 1 to ${STATUS_CARD_LIMITS.buttonId} of A-Z a-z 0-9 _ . : - and does not start with -`);\n else if (ids.has(button.id)) errors.push(`buttons[${i}].id \"${button.id}\" is used twice`);\n else ids.add(button.id);\n errors.push(line(`buttons[${i}].label`, button.label, STATUS_CARD_LIMITS.buttonLabel, true));\n if (button.style !== undefined && !oneOf(BUTTON_STYLES, button.style)) errors.push(`buttons[${i}].style is one of ${BUTTON_STYLES.join(\", \")}`);\n if (button.once !== undefined && button.once !== true) errors.push(`buttons[${i}].once is true or left out`);\n }\n // A typed answer is matched by label or id: two buttons it could not tell apart are refused (a reader takes the first).\n const clash = buttonLabelClash(raw.buttons);\n if (clash) errors.push(`buttons[${clash.at}].label ${JSON.stringify(cardLine((raw.buttons[clash.at] as Record<string, unknown>).label, Infinity))} ${clash.kind === \"label\" ? `repeats buttons[${clash.with}].label` : `is buttons[${clash.with}].id`} (ignoring case and spaces at the ends)`);\n if (raw.chosen !== undefined && !(typeof raw.chosen === \"string\" && ids.has(raw.chosen))) errors.push(\"chosen names one of the buttons\");\n }\n if (raw.closed !== undefined && raw.closed !== true) errors.push(\"closed is true or left out\");\n } else if (raw.kind === \"task\") {\n errors.push(line(\"title\", raw.title, STATUS_CARD_LIMITS.title, true));\n if (!oneOf(TASK_STATUSES, raw.status)) errors.push(`status is one of ${TASK_STATUSES.join(\", \")}`);\n if (raw.progress !== undefined && !(typeof raw.progress === \"number\" && Number.isFinite(raw.progress) && raw.progress >= 0 && raw.progress <= 100)) errors.push(\"progress is a number from 0 to 100\");\n if ((raw.done === undefined) !== (raw.total === undefined)) errors.push(\"done and total go together\");\n else if (raw.done !== undefined) {\n errors.push(whole(\"done\", raw.done), whole(\"total\", raw.total, 1));\n if (typeof raw.done === \"number\" && typeof raw.total === \"number\" && raw.done > raw.total) errors.push(\"done is at most total\");\n }\n errors.push(line(\"step\", raw.step, STATUS_CARD_LIMITS.line), line(\"branch\", raw.branch, STATUS_CARD_LIMITS.branch));\n if (raw.pr !== undefined) {\n if (!isObject(raw.pr)) errors.push(\"pr is an object\");\n else {\n errors.push(url(\"pr.url\", raw.pr.url), whole(\"pr.number\", raw.pr.number, 1), whole(\"pr.additions\", raw.pr.additions), whole(\"pr.deletions\", raw.pr.deletions), whole(\"pr.files\", raw.pr.files));\n if (raw.pr.state !== undefined && !oneOf(PR_STATES, raw.pr.state)) errors.push(`pr.state is one of ${PR_STATES.join(\", \")}`);\n if (raw.pr.checks !== undefined && !oneOf(PR_CHECKS, raw.pr.checks)) errors.push(`pr.checks is one of ${PR_CHECKS.join(\", \")}`);\n }\n }\n if (raw.tags !== undefined) {\n if (!Array.isArray(raw.tags) || raw.tags.length > STATUS_CARD_LIMITS.tags) errors.push(`tags is a list of at most ${STATUS_CARD_LIMITS.tags}`);\n else {\n const seen = new Set<string>();\n for (const [i, tag] of raw.tags.entries()) {\n const problem = line(`tags[${i}]`, tag, STATUS_CARD_LIMITS.tag, true);\n errors.push(problem);\n if (problem) continue;\n const clean = cardLine(tag, Infinity)!;\n if (seen.has(clean)) errors.push(`tags[${i}] ${JSON.stringify(clean)} is there twice`);\n seen.add(clean);\n }\n }\n }\n if (raw.parent !== undefined) {\n if (typeof raw.parent !== \"string\" || !ID.test(raw.parent)) errors.push(`parent is another task's id: 1 to ${STATUS_CARD_LIMITS.id} of A-Z a-z 0-9 _ . : - and does not start with -`);\n else if (raw.parent === raw.id) errors.push(\"parent is another task's id, not its own\");\n }\n for (const field of [\"startedAt\", \"updatedAt\"] as const) {\n const value = raw[field];\n if (value !== undefined && !(typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 && value <= now + MESSAGE_CLOCK_SKEW_MS)) errors.push(`${field} is a time in milliseconds, not in the future`);\n }\n if (raw.items !== undefined) {\n if (!Array.isArray(raw.items) || raw.items.length > STATUS_CARD_LIMITS.items) errors.push(`items is a list of at most ${STATUS_CARD_LIMITS.items}`);\n else for (const [i, item] of raw.items.entries()) {\n if (!isObject(item)) { errors.push(`items[${i}] is an object`); continue; }\n errors.push(line(`items[${i}].text`, item.text, STATUS_CARD_LIMITS.line, true));\n if (item.state !== undefined && !oneOf(ITEM_STATES, item.state)) errors.push(`items[${i}].state is one of ${ITEM_STATES.join(\", \")}`);\n }\n }\n } else {\n errors.push(line(\"name\", raw.name, STATUS_CARD_LIMITS.title, true), line(\"schedule\", raw.schedule, STATUS_CARD_LIMITS.schedule, true));\n if (!oneOf(ROUTINE_STATES, raw.state)) errors.push(`state is one of ${ROUTINE_STATES.join(\", \")}`);\n if (raw.cron !== undefined && !(typeof raw.cron === \"string\" && CRON.test(raw.cron.trim()))) errors.push(`cron is a cron line of at most ${STATUS_CARD_LIMITS.cron} characters`);\n if (raw.nextRunAt !== undefined && !(typeof raw.nextRunAt === \"number\" && Number.isSafeInteger(raw.nextRunAt) && raw.nextRunAt > 0 && raw.nextRunAt <= now + STATUS_CARD_LIMITS.nextRunMs)) errors.push(\"nextRunAt is a time in milliseconds, within a year\");\n const run = (field: string, value: unknown) => {\n if (!isObject(value)) return errors.push(`${field} is an object`);\n if (!(typeof value.at === \"number\" && Number.isSafeInteger(value.at) && value.at > 0 && value.at <= now + MESSAGE_CLOCK_SKEW_MS)) errors.push(`${field}.at is a time in milliseconds, not in the future`);\n if (!oneOf(RUN_RESULTS, value.result)) errors.push(`${field}.result is one of ${RUN_RESULTS.join(\", \")}`);\n errors.push(line(`${field}.summary`, value.summary, STATUS_CARD_LIMITS.line));\n };\n if (raw.lastRun !== undefined) run(\"lastRun\", raw.lastRun);\n if (raw.runs !== undefined) {\n if (!Array.isArray(raw.runs) || raw.runs.length > STATUS_CARD_LIMITS.runs) errors.push(`runs is a list of at most ${STATUS_CARD_LIMITS.runs}`);\n else raw.runs.forEach((entry, i) => run(`runs[${i}]`, entry));\n }\n }\n const error = errors.find((e): e is string => typeof e === \"string\");\n if (error) return { error };\n const card = readStatusCard(raw, now);\n return card ? { card } : { error: \"The card does not hold\" };\n}\n\n/** The progress a task shows, 0 to 100: its own, else its steps done of total; undefined when it says neither. */\nexport function taskProgress(card: Pick<TaskCard, \"progress\" | \"done\" | \"total\">): number | undefined {\n if (card.progress !== undefined) return card.progress;\n return card.done !== undefined && card.total ? Math.round(card.done / card.total * 100) : undefined;\n}\n\nconst TASK_MARK: Record<TaskStatus, string> = { queued: \"⏳\", running: \"🔄\", blocked: \"⛔\", done: \"✅\", failed: \"❌\", cancelled: \"🚫\" };\nconst STATUS_WORD: Record<TaskStatus, string> = { queued: \"Queued\", running: \"Running\", blocked: \"Blocked\", done: \"Done\", failed: \"Failed\", cancelled: \"Cancelled\" };\nconst RESULT_WORD: Record<RunResult, string> = { ok: \"ok\", failed: \"failed\", skipped: \"skipped\" };\n\n/** A time as the fallback text says it: in UTC, to the minute (\"2026-09-30 01:00 UTC\"). */\nexport function cardTimeUtc(ms: number): string {\n return `${new Date(ms).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\n/**\n * The text a card's message carries: what an app without cards shows, the DHT floor carries, the chat list previews\n * and search finds. Plain lines, the first naming the card, no bars or layout that would read badly elsewhere.\n */\nexport function statusCardText(card: StatusCard): string {\n // An app card names the app, and its url on a line of its own so an older app shows a link (WISP 405 § An app).\n if (card.kind === \"app\") {\n const named = `${card.title}${card.version ? ` ${card.version}` : \"\"}`;\n return [card.opened ? `🧩 Opened ${named} in this chat (Ghostly app)` : `🧩 ${named} (Ghostly app)`, ...(card.url ? [card.url] : [])].join(\"\\n\");\n }\n // One line: what an older app shows, and the chat list's line (WISP 405 § Usage).\n if (card.kind === \"usage\") {\n const left = `${usageLeft(card)}% left${card.window ? ` (${card.window})` : \"\"}`;\n return [`📊 ${card.label ?? \"Usage\"}`, ...(card.account ? [card.account] : []), left,\n ...(card.used !== undefined && card.limit !== undefined ? [`${card.used} of ${card.limit} used`] : []),\n ...(card.resetsAt ? [`resets ${cardTimeUtc(card.resetsAt)}`] : []),\n ...(card.windows ?? []).map((w) => `${w.window} ${w.left}%`)].join(\" · \");\n }\n // Buttons show with the bot's own text; this is only what goes when it gave none.\n if (card.kind === \"buttons\") return `Reply: ${card.buttons.map(b => b.label).join(\" / \")}`;\n if (card.kind === \"task\") {\n const progress = taskProgress(card);\n const state = [STATUS_WORD[card.status], ...(progress !== undefined ? [`${progress}%`] : []), ...(card.done !== undefined && card.total ? [`${card.done} of ${card.total} steps`] : [])].join(\" · \");\n // \"PR #612 +123 -45 (open, checks passing): https://…\": the state and the checks only when the card says them.\n const standing = card.pr && [...(card.pr.state ? [card.pr.state] : []), ...(card.pr.checks ? [`checks ${card.pr.checks}`] : [])].join(\", \");\n const pr = card.pr && [`PR${card.pr.number !== undefined ? ` #${card.pr.number}` : \"\"}`,\n ...(card.pr.additions !== undefined || card.pr.deletions !== undefined ? [`+${card.pr.additions ?? 0} -${card.pr.deletions ?? 0}`] : []),\n ...(standing ? [`(${standing})`] : [])].join(\" \") + `: ${card.pr.url}`;\n // \"Now:\" only while the task is going: a finished one's last step is not what it does now (the card hides it too).\n const now = card.step && ACTIVE_TASK_STATUSES.includes(card.status) ? [`Now: ${card.step}`] : [];\n return [`${TASK_MARK[card.status]} ${card.title}`, state, ...now, ...(pr ? [pr] : []),\n ...(card.tags?.length ? [`Tags: ${card.tags.join(\", \")}`] : []), ...(card.parent ? [`Part of: ${card.parent}`] : [])].join(\"\\n\");\n }\n const last = card.lastRun && `Last run: ${RESULT_WORD[card.lastRun.result]}, ${cardTimeUtc(card.lastRun.at)}${card.lastRun.summary ? ` · ${card.lastRun.summary}` : \"\"}`;\n return [`🔁 ${card.name}`, `${card.schedule} · ${card.state === \"active\" ? \"active\" : \"paused\"}`, ...(last ? [last] : []),\n ...(card.nextRunAt && card.state === \"active\" ? [`Next run: ${cardTimeUtc(card.nextRunAt)}`] : [])].join(\"\\n\");\n}\n\n/** Whether a message's edit number holds: a text's bound, or a card message's when the edit carries a card. */\nexport function cardEditNumber(e: unknown): e is number {\n return Number.isSafeInteger(e) && (e as number) >= 1 && (e as number) <= STATUS_CARD_LIMITS.edits;\n}\n","import { fromBase64Url } from \"./bytes\";\n\n/**\n * Videos. A video is an ordinary file (files/2, files/3 or a held item) whose announcement may also carry a\n * `video` description: how long it is, its size in pixels and a small JPEG of its first frame, so the\n * receiver shows a picture and a length before a byte of it has arrived. A peer that does not know `video`\n * ignores it and shows a file; a bad description only means no poster.\n */\nexport interface VideoMeta {\n /** Milliseconds. */\n duration: number;\n /** Pixels, as it plays (rotation applied). */\n width: number;\n height: number;\n /** A JPEG of an early frame, base64url, at most `VIDEO_LIMITS.maxPosterBytes`. */\n poster?: string;\n}\n\nexport const VIDEO_LIMITS = {\n /** The poster's bytes: with the rest of an offer, a frame stays far below the 60 KiB a session carries. */\n maxPosterBytes: 12 * 1024,\n /** The poster's longest side, in pixels, as the sender draws it. */\n posterEdge: 320,\n /** Ten hours: longer is a mistake, not a video. */\n maxDurationMs: 10 * 60 * 60_000,\n /** Pixels on either side (8K and a bit). */\n maxEdge: 16_384,\n} as const;\n\n/**\n * Video a player may be handed as it is. None of these runs anything in the page's origin, so received bytes\n * of these types are served with their type (see `safeBlobType`): WebKit will not play what is typed as bytes.\n */\nexport const PLAYABLE_VIDEO = /^video\\/(mp4|webm|quicktime|ogg|x-m4v)$/;\n\n/** Whether a file is a video, by its type: shown in a video bubble whether or not it came with a description. */\nexport function isVideoMime(mime: string): boolean {\n return mime.split(\";\")[0]!.trim().toLowerCase().startsWith(\"video/\");\n}\n\n/**\n * Whether a file is shown as a video (a poster, a player): a type a player may be handed. Other video types\n * (Matroska, AVI) are files, saved to be played elsewhere.\n */\nexport function isPlayableVideoType(mime: string): boolean {\n return PLAYABLE_VIDEO.test(mime.split(\";\")[0]!.trim().toLowerCase());\n}\n\nconst BASE64URL = /^[A-Za-z0-9_-]+$/;\nconst JPEG_START = [0xff, 0xd8, 0xff];\n\n/**\n * A peer's `video` description, or undefined when it is not one. It never refuses the file. A poster that is\n * too large or not a JPEG is dropped on its own; the length and size are kept.\n */\nexport function parseVideoMeta(value: unknown, mime?: string): VideoMeta | undefined {\n if (mime !== undefined && !isVideoMime(mime)) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { duration, width, height, poster } = value as { duration?: unknown; width?: unknown; height?: unknown; poster?: unknown };\n if (typeof duration !== \"number\" || !Number.isSafeInteger(duration) || duration <= 0 || duration > VIDEO_LIMITS.maxDurationMs) return undefined;\n const edge = (n: unknown): n is number => typeof n === \"number\" && Number.isSafeInteger(n) && n > 0 && n <= VIDEO_LIMITS.maxEdge;\n if (!edge(width) || !edge(height)) return undefined;\n const meta: VideoMeta = { duration, width, height };\n if (typeof poster === \"string\" && poster.length <= Math.ceil((VIDEO_LIMITS.maxPosterBytes * 4) / 3) && BASE64URL.test(poster)) {\n try {\n const bytes = fromBase64Url(poster);\n if (bytes.length <= VIDEO_LIMITS.maxPosterBytes && JPEG_START.every((b, i) => bytes[i] === b)) meta.poster = poster;\n } catch { /* no poster */ }\n }\n return meta;\n}\n\n/** `0:07`, `12:45`, `1:02:03`: as a player shows a video's length. */\nexport function formatVideoDuration(ms: number): string {\n const total = Math.max(0, Math.floor((Number.isFinite(ms) ? ms : 0) / 1000));\n const hours = Math.floor(total / 3600), minutes = Math.floor((total % 3600) / 60), seconds = String(total % 60).padStart(2, \"0\");\n return hours ? `${hours}:${String(minutes).padStart(2, \"0\")}:${seconds}` : `${minutes}:${seconds}`;\n}\n","import { formatVideoDuration, type VideoMeta } from \"./video\";\n\n/**\n * Voice messages. A voice message is an ordinary file (files/2, or a held item) whose\n * announcement also carries a `voice` description: how long it is and the shape of its\n * sound. The shape is measured once, while recording, so no player ever decodes audio\n * to draw it. A peer that does not know `voice` ignores it and shows an audio file.\n */\nexport interface VoiceMeta {\n /** Milliseconds. */\n duration: number;\n /** Loudness per bar, 0-255, in playing order. */\n peaks: number[];\n}\n\nexport const VOICE_LIMITS = {\n /** Fifteen minutes: at the bitrate recorded (32 kbit/s) about 3.6 MB, well inside a held item's 8 MiB. */\n maxDurationMs: 15 * 60_000,\n /** Bars drawn for a recording. */\n bars: 64,\n /** Bars accepted from a peer. */\n maxBars: 128,\n /** Asked of the recorder; opus speech is clear well below it. */\n audioBitsPerSecond: 32_000,\n} as const;\n\n/**\n * Audio a player may be handed as it is. None of these runs anything in the page's origin,\n * so received bytes of these types are served with their type (see `safeBlobType`).\n */\nexport const PLAYABLE_AUDIO = /^audio\\/(webm|ogg|opus|mp4|mpeg|mp3|aac|x-m4a|wav|x-wav|wave|flac|x-flac)$/;\n\n/**\n * Whether a file sent as a file (not a voice message) is shown with a player: a type a player may be handed.\n * Other audio types (MIDI, AIFF, WMA) are files, saved to be played elsewhere.\n */\nexport function isPlayableAudioType(mime: string): boolean {\n return PLAYABLE_AUDIO.test(baseMime(mime));\n}\n\n/** The part of a type before its parameters, in lower case: `audio/webm;codecs=opus` → `audio/webm`. */\nexport function baseMime(mime: string): string {\n return mime.split(\";\")[0]!.trim().toLowerCase();\n}\n\n/**\n * A peer's `voice` description, or undefined when it is not one. It never refuses the file:\n * a bad description only means the file is shown as a file.\n */\nexport function parseVoiceMeta(value: unknown, mime?: string): VoiceMeta | undefined {\n if (mime !== undefined && !PLAYABLE_AUDIO.test(baseMime(mime))) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { duration, peaks } = value as { duration?: unknown; peaks?: unknown };\n // A recorder stops a moment after the limit, never minutes after it.\n if (typeof duration !== \"number\" || !Number.isSafeInteger(duration) || duration <= 0 || duration > VOICE_LIMITS.maxDurationMs + 5_000) return undefined;\n if (!Array.isArray(peaks) || peaks.length === 0 || peaks.length > VOICE_LIMITS.maxBars) return undefined;\n if (!peaks.every((peak) => typeof peak === \"number\" && Number.isInteger(peak) && peak >= 0 && peak <= 255)) return undefined;\n return { duration, peaks: [...peaks] };\n}\n\n/**\n * How the level is metered while recording (apps/ui/src/lib/voiceRecorder.ts): every `intervalMs`, the RMS of the last\n * `window` samples an AnalyserNode holds (at a 48 kHz context, about 21 ms of sound).\n */\nexport const VOICE_METER = { intervalMs: 50, window: 1024, windowRate: 48_000 } as const;\n\n/** One reading of the meter, 0-1: the RMS of the samples, lifted because speech sits low on a linear scale. */\nexport function voiceLevel(samples: ArrayLike<number>): number {\n if (samples.length === 0) return 0;\n let sum = 0;\n for (let i = 0; i < samples.length; i++) sum += samples[i]! * samples[i]!;\n return Math.min(1, Math.sqrt(sum / samples.length) * 4);\n}\n\n/**\n * The readings the recorder would have taken of this sound (mono, -1 to 1), for a recording made elsewhere: one\n * every `intervalMs` of it, each over the window that ends there, as long in time as the analyser's.\n */\nexport function voiceLevelsOf(mono: Float32Array, sampleRate: number): number[] {\n if (!(sampleRate > 0) || mono.length === 0) return [];\n const window = Math.max(1, Math.round((VOICE_METER.window * sampleRate) / VOICE_METER.windowRate));\n const levels: number[] = [];\n for (let step = 1; ; step++) {\n const end = Math.round((step * VOICE_METER.intervalMs * sampleRate) / 1000);\n if (end > mono.length) break;\n levels.push(voiceLevel(mono.subarray(Math.max(0, end - window), end)));\n }\n return levels;\n}\n\n/** The waveform sent with a recording made elsewhere, drawn as the recorder draws its own. */\nexport function voicePeaksOf(mono: Float32Array, sampleRate: number, bars: number = VOICE_LIMITS.bars): number[] {\n return downsamplePeaks(voiceLevelsOf(mono, sampleRate), bars);\n}\n\n/** Below this a recording is silence, and is drawn flat rather than stretched to fill the bars. */\nconst QUIET = 0.02;\n\n/**\n * Bars from the loudness measured while recording (RMS, 0-1, oldest first): the loudest\n * sample of each stretch, scaled so the loudest bar is full height.\n */\nexport function downsamplePeaks(levels: ArrayLike<number>, bars: number = VOICE_LIMITS.bars): number[] {\n const count = Math.max(1, Math.min(VOICE_LIMITS.maxBars, Math.floor(bars)));\n const clean = Array.from(levels, (level) => (Number.isFinite(level) ? Math.min(1, Math.max(0, level)) : 0));\n if (clean.length === 0) return new Array<number>(count).fill(0);\n const loudest = Math.max(QUIET, ...clean);\n const out: number[] = [];\n for (let bar = 0; bar < count; bar++) {\n const from = Math.floor((bar * clean.length) / count);\n // Fewer samples than bars: each bar takes the sample under it.\n const to = Math.max(from + 1, Math.floor(((bar + 1) * clean.length) / count));\n let peak = 0;\n for (let i = from; i < to && i < clean.length; i++) peak = Math.max(peak, clean[i]!);\n out.push(Math.round((peak / loudest) * 255));\n }\n return out;\n}\n\n/**\n * What to record in, by preference: opus in WebM plays on Chromium, Firefox, Safari 15+ and\n * WebKitGTK; older WebKit (WKWebView on macOS before Safari 18.4) records only AAC in MP4,\n * which Chrome, Edge and Safari play. `recorder` is asked of MediaRecorder, `file` is sent.\n */\nexport const RECORDING_TYPES = [\"audio/webm;codecs=opus\", \"audio/ogg;codecs=opus\", \"audio/mp4;codecs=mp4a.40.2\", \"audio/mp4\", \"audio/webm\"] as const;\n\nexport function recordingMime(isTypeSupported: (mime: string) => boolean): { recorder: string; file: string } | null {\n for (const type of RECORDING_TYPES) {\n let supported = false;\n try { supported = isTypeSupported(type); } catch { /* an engine that throws for a type does not have it */ }\n if (supported) return { recorder: type, file: baseMime(type) };\n }\n return null;\n}\n\nconst EXTENSIONS: Record<string, string> = { \"audio/webm\": \"webm\", \"audio/ogg\": \"ogg\", \"audio/mp4\": \"m4a\", \"audio/x-m4a\": \"m4a\", \"audio/mpeg\": \"mp3\", \"audio/aac\": \"aac\", \"audio/wav\": \"wav\" };\n\nfunction voiceName(prefix: string, mime: string, timestamp: number): string {\n const date = new Date(timestamp);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const stamp = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}.${pad(date.getMinutes())}.${pad(date.getSeconds())}`;\n return `${prefix} ${stamp}.${EXTENSIONS[baseMime(mime)] ?? \"audio\"}`;\n}\n\n/** A name for the file, for whoever saves it or runs a Ghostly that shows it as a file. */\nexport function voiceFileName(mime: string, timestamp: number): string {\n return voiceName(\"Voice message\", mime, timestamp);\n}\n\n/**\n * The name a downloaded voice message is saved under, made here from the message's time and its\n * type (the sender's own name for it is not used): \"Ghostly voice 2026-09-27 14.01.30.webm\".\n */\nexport function voiceDownloadName(mime: string, timestamp: number): string {\n return voiceName(\"Ghostly voice\", mime, timestamp);\n}\n\n/** `0:07`, `12:45`: minutes and seconds, as a player shows them. */\nexport function formatVoiceDuration(ms: number): string {\n const total = Math.max(0, Math.floor((Number.isFinite(ms) ? ms : 0) / 1000));\n const minutes = Math.floor(total / 60);\n return `${minutes}:${String(total % 60).padStart(2, \"0\")}`;\n}\n\n/** The line a file message shows in the chat list and in notifications. */\nexport function fileMessageText(file: { name: string; voice?: VoiceMeta; video?: VideoMeta }): string {\n if (file.voice) return `🎤 Voice message (${formatVoiceDuration(file.voice.duration)})`;\n if (file.video) return `🎬 Video (${formatVideoDuration(file.video.duration)})`;\n return `📎 ${file.name}`;\n}\n","import {\n CHUNK_KIND,\n LIMITS,\n encodeControl,\n sendBody,\n type ChunkFrame,\n type FileFrame,\n type FrameChannel,\n type ResetFrame,\n} from \"./frames\";\nimport type { WireReply } from \"./replies\";\nimport type { ImageMeta } from \"./image\";\nimport { PLAYABLE_VIDEO, type VideoMeta } from \"./video\";\nimport { PLAYABLE_AUDIO, type VoiceMeta } from \"./voice\";\n\n/**\n * Files over the data link. A `file` frame announces name, size and type, the\n * bytes follow as chunks, and the receiver only keeps the file if exactly the\n * announced number of bytes arrived. Nothing touches Pkarr: files go peer to\n * peer or not at all.\n */\nexport interface FileInfo {\n /** Chosen by the sender. */\n id: string;\n name: string;\n size: number;\n mime: string;\n timestamp: number;\n /** A voice message: its length and the shape of its sound (files/2 and held items only). */\n voice?: VoiceMeta;\n /** A video: its length, size and a small first frame (optional; old apps ignore it). */\n video?: VideoMeta;\n /** A picture: its size as it is shown, so its box is laid out before it arrives (optional; old apps ignore it). */\n image?: ImageMeta;\n /** The message this file answers (`r`, WISP 401 § Replies): files/2, files/3 and held items; older apps ignore it. */\n reply?: WireReply;\n /** How many times it has been forwarded (`fw`, WISP 401 § Forwards): files/2, files/3 and held items; older apps ignore it. */\n forwarded?: number;\n}\n\n/** Where a platform puts incoming bytes: memory, IndexedDB, disk. */\nexport interface FileSink {\n write(chunk: Uint8Array): void | Promise<void>;\n close(digest?: string): void | Promise<void>;\n abort(): void;\n}\n\nexport interface FileTransferEvents {\n /** Return a sink to accept the file, null to refuse it, or the reason for refusing it. */\n onIncoming(file: FileInfo): FileSink | string | null;\n onProgress?(fileId: string, transferred: number, direction: \"in\" | \"out\"): void;\n onComplete?(fileId: string, direction: \"in\" | \"out\"): void;\n onFailed?(fileId: string, reason: string, direction: \"in\" | \"out\"): void;\n}\n\nconst FILE_ID = /^[A-Za-z0-9_-]{8,64}$/;\nconst MIME = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}\\/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$/i;\nconst MAX_NAME_LENGTH = 200;\n\n/**\n * Invisible and direction-changing characters: bidi overrides turn\n * \"invoice\\u202Efdp.exe\" into what reads as \"invoiceexe.pdf\", zero-width ones hide\n * text. Cf covers both; line and paragraph separators break the layout.\n */\nconst INVISIBLE = /[\\p{Cc}\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n\n/**\n * Names Windows keeps for devices, whatever the case, with any extension, and with spaces before the dot:\n * \"CON\", \"nul.txt\", \"COM1 .log\" open a device there, not a file.\n */\nconst WINDOWS_DEVICE = /^(CON|PRN|AUX|NUL|CONIN\\$|CONOUT\\$|COM[0-9¹²³]|LPT[0-9¹²³])\\s*(\\.|$)/i;\n\n/**\n * At most `MAX_NAME_LENGTH` characters. A longer name loses the end of its stem, not its extension (up to 15\n * characters after the last dot): \"…report.pdf\" stays a PDF to the system it is saved on.\n */\nfunction shorten(name: string): string {\n const chars = [...name];\n if (chars.length <= MAX_NAME_LENGTH) return name;\n const extension = [.../\\.[^.\\s]{1,15}$/u.exec(name)?.[0] ?? \"\"];\n return chars.slice(0, MAX_NAME_LENGTH - extension.length).join(\"\").trimEnd() + extension.join(\"\");\n}\n\n/** A file name is display text and a download suggestion, never a path. */\nexport function sanitizeFileName(name: string): string {\n const visible = name.replace(INVISIBLE, \"\").replace(/[/\\\\:]/g, \"\");\n // Whitespace before the dots must not hide them: \" .bashrc\" is a dotfile too.\n const clean = shorten(visible.replace(/^[\\s.]+/, \"\").trim());\n if (WINDOWS_DEVICE.test(clean)) return shorten(\"_\" + clean);\n return clean || \"file\";\n}\n\nexport function sanitizeMime(mime: string): string {\n return MIME.test(mime) ? mime.toLowerCase() : \"application/octet-stream\";\n}\n\n/** A size for people: B, KB, MB, GB, TB (powers of 1024), one decimal from KB up. */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let value = bytes / 1024, unit = 0;\n while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit++; }\n return `${value.toFixed(1)} ${units[unit]}`;\n}\n\n/** Raster images a UI may show inline. SVG is left out on purpose: it can carry scripts. */\nexport const PREVIEWABLE_IMAGE = /^image\\/(png|jpe?g|gif|webp)$/;\n\n/**\n * The type received bytes are served with. The peer picks the announced type,\n * and a blob: URL typed text/html or image/svg+xml would run in the app's\n * origin, so anything but a previewable image or playable audio or video is opaque bytes.\n * Audio and video keep their type because WebKit will not play what is typed as bytes.\n */\nexport function safeBlobType(mime: string): string {\n const clean = sanitizeMime(mime);\n return PREVIEWABLE_IMAGE.test(clean) || PLAYABLE_AUDIO.test(clean) || PLAYABLE_VIDEO.test(clean) ? clean : \"application/octet-stream\";\n}\n\ninterface Incoming {\n file: FileInfo;\n sink: FileSink;\n received: number;\n idleTimer?: ReturnType<typeof setTimeout>;\n /** Writes are chained so a slow sink cannot reorder chunks. */\n queue: Promise<void>;\n}\n\nexport class FileTransfers {\n private nextStreamId = 1;\n private readonly incoming = new Map<number, Incoming>();\n private readonly outgoing = new Map<number, { fileId: string; cancelled: boolean }>();\n\n constructor(\n private readonly channel: FrameChannel,\n private readonly events: FileTransferEvents,\n ) {}\n\n /** Streams a file to the peer. Resolves once every byte is handed to the channel. */\n async send(file: FileInfo, source: AsyncIterable<Uint8Array>): Promise<void> {\n if (file.size > LIMITS.maxFileBytes) throw new Error(\"File is too large\");\n const id = this.nextStreamId++;\n const state = { fileId: file.id, cancelled: false };\n this.outgoing.set(id, state);\n\n try {\n this.channel.send(\n encodeControl({\n t: \"file\",\n id,\n f: file.id,\n ts: file.timestamp,\n n: sanitizeFileName(file.name),\n s: file.size,\n m: sanitizeMime(file.mime),\n }),\n );\n let sent = 0;\n const counted = async function* (this: FileTransfers) {\n for await (const part of source) {\n sent += part.length;\n yield part;\n this.events.onProgress?.(file.id, sent, \"out\");\n }\n }.call(this);\n await sendBody(this.channel, CHUNK_KIND.fileBody, id, counted, () => state.cancelled);\n if (state.cancelled) throw new Error(\"The peer refused the file\");\n if (sent !== file.size) throw new Error(\"File changed while it was being sent\");\n this.events.onComplete?.(file.id, \"out\");\n } catch (error) {\n if (!state.cancelled) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id, d: \"f\", e: \"aborted\" }));\n } catch {\n // channel already closed\n }\n }\n this.events.onFailed?.(file.id, error instanceof Error ? error.message : String(error), \"out\");\n throw error;\n } finally {\n this.outgoing.delete(id);\n }\n }\n\n handleFile(frame: FileFrame): void {\n if (this.incoming.has(frame.id)) return;\n const refuse = (reason: string) => {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id: frame.id, d: \"f\", e: reason }));\n } catch {\n // channel already closed\n }\n };\n if (!FILE_ID.test(frame.f)) return refuse(\"invalid file id\");\n if (frame.s > LIMITS.maxFileBytes) return refuse(\"file too large\");\n if (this.incoming.size >= LIMITS.maxIncomingFilesPerPeer) return refuse(\"too many transfers\");\n\n const file: FileInfo = {\n id: frame.f,\n name: sanitizeFileName(frame.n),\n size: frame.s,\n mime: sanitizeMime(frame.m),\n timestamp: frame.ts,\n };\n const sink = this.events.onIncoming(file);\n if (!sink || typeof sink === \"string\") return refuse(sink || \"refused\");\n\n const entry: Incoming = { file, sink, received: 0, queue: Promise.resolve() };\n this.incoming.set(frame.id, entry);\n this.armIdleTimer(frame.id, entry);\n }\n\n handleChunk(chunk: ChunkFrame): void {\n const entry = this.incoming.get(chunk.id);\n if (!entry) return;\n\n entry.received += chunk.payload.length;\n if (entry.received > entry.file.size) return this.failIncoming(chunk.id, \"more data than announced\", true);\n\n if (chunk.payload.length > 0) {\n const payload = chunk.payload.slice();\n entry.queue = entry.queue.then(() => entry.sink.write(payload));\n this.events.onProgress?.(entry.file.id, entry.received, \"in\");\n }\n if (!chunk.end) return this.armIdleTimer(chunk.id, entry);\n\n if (entry.received !== entry.file.size) return this.failIncoming(chunk.id, \"incomplete file\", false);\n clearTimeout(entry.idleTimer);\n this.incoming.delete(chunk.id);\n entry.queue\n .then(() => entry.sink.close())\n .then(\n () => this.events.onComplete?.(entry.file.id, \"in\"),\n () => {\n entry.sink.abort();\n this.events.onFailed?.(entry.file.id, \"could not store the file\", \"in\");\n },\n );\n }\n\n handleReset(frame: ResetFrame): void {\n if (this.incoming.has(frame.id)) this.failIncoming(frame.id, frame.e || \"cancelled by the sender\", false);\n const sending = this.outgoing.get(frame.id);\n if (sending) sending.cancelled = true;\n }\n\n /** The link went away: nothing in flight can finish. */\n closeAll(): void {\n for (const id of [...this.incoming.keys()]) this.failIncoming(id, \"connection lost\", false);\n for (const sending of this.outgoing.values()) sending.cancelled = true;\n }\n\n private armIdleTimer(id: number, entry: Incoming): void {\n clearTimeout(entry.idleTimer);\n entry.idleTimer = setTimeout(() => this.failIncoming(id, \"transfer stalled\", true), LIMITS.bodyIdleTimeoutMs);\n }\n\n private failIncoming(id: number, reason: string, notifyPeer: boolean): void {\n const entry = this.incoming.get(id);\n if (!entry) return;\n clearTimeout(entry.idleTimer);\n this.incoming.delete(id);\n entry.sink.abort();\n if (notifyPeer) {\n try {\n this.channel.send(encodeControl({ t: \"rst\", id, d: \"f\", e: reason }));\n } catch {\n // channel already closed\n }\n }\n this.events.onFailed?.(entry.file.id, reason, \"in\");\n }\n}\n","/**\n * Pictures. A picture is an ordinary file (files/2, files/3 or a held item) whose announcement may also carry an\n * `image` description: its size in pixels as it is shown, so the receiver lays out its box before a byte of it has\n * arrived and the chat does not move when it loads. A peer that does not know `image` ignores it and shows the\n * picture as before; a bad description only means no box.\n *\n * The sender reads the size from the file's first bytes (`imageSize`): no decoding, the same on every platform.\n */\nexport interface ImageMeta {\n /** Pixels, as it is shown (EXIF orientation applied). */\n width: number;\n height: number;\n}\n\nexport const IMAGE_LIMITS = {\n /** Pixels on either side: the most a JPEG can say, well past what anyone sends as a picture. */\n maxEdge: 65_535,\n /** How much of a file is read for its size: past a JPEG's EXIF, thumbnails and colour profile. */\n headBytes: 256 * 1024,\n} as const;\n\n/** Whether a file is a picture, by its type. */\nexport function isImageMime(mime: string): boolean {\n return mime.split(\";\")[0]!.trim().toLowerCase().startsWith(\"image/\");\n}\n\nconst edge = (n: unknown): n is number => typeof n === \"number\" && Number.isSafeInteger(n) && n > 0 && n <= IMAGE_LIMITS.maxEdge;\n\n/** A peer's `image` description, or undefined when it is not one. It never refuses the file. */\nexport function parseImageMeta(value: unknown, mime?: string): ImageMeta | undefined {\n if (mime !== undefined && !isImageMime(mime)) return undefined;\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const { width, height } = value as { width?: unknown; height?: unknown };\n return edge(width) && edge(height) ? { width, height } : undefined;\n}\n\nconst u16be = (b: Uint8Array, at: number) => (b[at]! << 8) | b[at + 1]!;\nconst u16le = (b: Uint8Array, at: number) => b[at]! | (b[at + 1]! << 8);\nconst u24le = (b: Uint8Array, at: number) => b[at]! | (b[at + 1]! << 8) | (b[at + 2]! << 16);\nconst u32be = (b: Uint8Array, at: number) => ((b[at]! << 24) >>> 0) + (b[at + 1]! << 16) + (b[at + 2]! << 8) + b[at + 3]!;\nconst ascii = (b: Uint8Array, at: number, text: string) => [...text].every((c, i) => b[at + i] === c.charCodeAt(0));\n\nconst sized = (width: number, height: number): ImageMeta | undefined => (edge(width) && edge(height) ? { width, height } : undefined);\n\n/** SOFn markers: every frame type but DHT (C4), JPG (C8) and DAC (CC). */\nconst JPEG_FRAME = new Set([0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf]);\n\n/** The EXIF orientation in an APP1 segment's TIFF block (1 when it says none). */\nfunction exifOrientation(b: Uint8Array, tiff: number, end: number): number {\n if (tiff + 8 > end) return 1;\n const little = ascii(b, tiff, \"II\");\n if (!little && !ascii(b, tiff, \"MM\")) return 1;\n const u16 = (at: number) => (little ? u16le(b, at) : u16be(b, at));\n const u32 = (at: number) => (little ? (u16le(b, at) + u16le(b, at + 2) * 65536) : u32be(b, at));\n if (u16(tiff + 2) !== 42) return 1;\n const ifd = tiff + u32(tiff + 4);\n if (ifd + 2 > end) return 1;\n const count = u16(ifd);\n for (let i = 0; i < count; i++) {\n const entry = ifd + 2 + i * 12;\n if (entry + 12 > end) return 1;\n // Orientation, a SHORT: its value sits in the first two bytes of the value field.\n if (u16(entry) === 0x0112) { const value = u16(entry + 8); return value >= 1 && value <= 8 ? value : 1; }\n }\n return 1;\n}\n\nfunction jpegSize(b: Uint8Array): ImageMeta | undefined {\n let at = 2, orientation = 1;\n while (at + 4 <= b.length) {\n if (b[at] !== 0xff) return undefined;\n const marker = b[at + 1]!;\n // Fill bytes, then markers that stand alone.\n if (marker === 0xff) { at++; continue; }\n if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) { at += 2; continue; }\n // The image data or its end, and still no frame header.\n if (marker === 0xd9 || marker === 0xda) return undefined;\n const length = u16be(b, at + 2);\n if (length < 2) return undefined;\n const end = Math.min(b.length, at + 2 + length);\n if (marker === 0xe1 && ascii(b, at + 4, \"Exif\\0\\0\")) orientation = exifOrientation(b, at + 10, end);\n if (JPEG_FRAME.has(marker)) {\n if (at + 9 > b.length) return undefined;\n const height = u16be(b, at + 5), width = u16be(b, at + 7);\n // 5 to 8 turn it a quarter: shown, it is as wide as it is stored tall.\n return orientation >= 5 ? sized(height, width) : sized(width, height);\n }\n at += 2 + length;\n }\n return undefined;\n}\n\nfunction webpSize(b: Uint8Array): ImageMeta | undefined {\n if (b.length < 30) return undefined;\n if (ascii(b, 12, \"VP8 \")) {\n // A key frame: its start code, then 14-bit sizes (the top two bits are scaling).\n if (b[23] !== 0x9d || b[24] !== 0x01 || b[25] !== 0x2a) return undefined;\n return sized(u16le(b, 26) & 0x3fff, u16le(b, 28) & 0x3fff);\n }\n if (ascii(b, 12, \"VP8L\")) {\n if (b[20] !== 0x2f) return undefined;\n const bits = b[21]! | (b[22]! << 8) | (b[23]! << 16) | (b[24]! << 24);\n return sized((bits & 0x3fff) + 1, ((bits >>> 14) & 0x3fff) + 1);\n }\n if (ascii(b, 12, \"VP8X\")) return sized(u24le(b, 24) + 1, u24le(b, 27) + 1);\n return undefined;\n}\n\n/**\n * A picture's size as it is shown, read from its first bytes (PNG, JPEG, GIF, WebP), or undefined when they do not\n * say. Only the header is read: a JPEG whose frame header sits past `IMAGE_LIMITS.headBytes` goes without.\n */\nexport function imageSize(head: Uint8Array): ImageMeta | undefined {\n const b = head;\n try {\n if (b.length >= 24 && b[0] === 0x89 && ascii(b, 1, \"PNG\\r\\n\\x1a\\n\") && ascii(b, 12, \"IHDR\")) return sized(u32be(b, 16), u32be(b, 20));\n if (b.length >= 10 && (ascii(b, 0, \"GIF87a\") || ascii(b, 0, \"GIF89a\"))) return sized(u16le(b, 6), u16le(b, 8));\n if (b.length >= 4 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff) return jpegSize(b);\n if (b.length >= 16 && ascii(b, 0, \"RIFF\") && ascii(b, 8, \"WEBP\")) return webpSize(b);\n } catch { /* not a picture this reads */ }\n return undefined;\n}\n\n/**\n * The description a picture is sent with, read from the start of the file, or undefined when it is not a picture\n * or its first bytes do not say (it then goes without one).\n */\nexport async function readImageMeta(source: Blob, mime: string): Promise<ImageMeta | undefined> {\n if (!isImageMime(mime)) return undefined;\n try {\n return imageSize(new Uint8Array(await source.slice(0, IMAGE_LIMITS.headBytes).arrayBuffer()));\n } catch {\n return undefined;\n }\n}\n","import { formatFileSize, sanitizeFileName, sanitizeMime } from \"./files\";\nimport { parseImageMeta, type ImageMeta } from \"./image\";\nimport { parseVideoMeta, type VideoMeta } from \"./video\";\nimport { fileMessageText, formatVoiceDuration, parseVoiceMeta, type VoiceMeta } from \"./voice\";\n\n/**\n * Files and voice messages in groups (WISP 503 · Group Files). A file is announced as a group message carrying its\n * description; its bytes go over `files/3` between two members, from a member that holds it to one that asks for it.\n * The limits below are the profile's first numbers, kept in one place so they are easy to change.\n */\nexport const GROUP_FILE_LIMITS = {\n /** The largest file a group takes. */\n maxBytes: 100 * 1024 * 1024,\n /** A file this size or smaller is fetched without asking (voice messages always are, up to `VOICE_LIMITS`). */\n autoBytes: 8 * 1024 * 1024,\n /** What a device keeps of a group's files it fetched without asking; past it, every file is asked about. */\n autoBytesPerGroup: 256 * 1024 * 1024,\n /** Bytes of group files a device serves to others in a day, when serving is on. */\n serveBytesPerDay: 1024 * 1024 * 1024,\n /** Transfers a holder serves at once, all groups together. */\n serveAtOnce: 3,\n /** File announcements a member sends to one group in a minute. */\n announcePerMinute: 8,\n /** File announcements of one member of a group fetched by themselves in `announceWindowMs`; past it, kept for a Download. */\n announceTaken: 30,\n announceWindowMs: 10 * 60_000,\n /** Holders remembered per file. */\n holders: 8,\n /**\n * Message ids a `group-have` named before their message came (a member back from away hears who holds what as its\n * catch-up begins), remembered with their holders for when it does.\n */\n heardAhead: 256,\n /** Message ids in one `group-have`. */\n haveIds: 32,\n /**\n * `group-have` frames a private group's edge takes in `haveWindowMs`: what is to say is gathered for a second, then\n * goes. A community's, from a member, goes every `communityHaveEveryMs`.\n */\n haveFrames: 10,\n haveWindowMs: 10_000,\n communityHaveEveryMs: 60_000,\n /**\n * How long a want waits for an offer before the next holder is asked; a transfer that does not move for as long is\n * left for the next holder too.\n */\n wantWaitMs: 30_000,\n /** A file session closes this long after its last transfer ends. */\n sessionIdleMs: 60_000,\n /** File sessions opening at once, and open at once, all groups together. */\n sessionsOpening: 2,\n sessionsOpen: 4,\n /** The longest file name and type kept from a description. */\n nameChars: 255,\n mimeChars: 255,\n} as const;\n\n/** The `paired-groups` version an app announces when it takes group files: it answers wants and takes `group-have`. */\nexport const GROUP_VERSION_FILES = 6;\n\n/**\n * The frames of WISP 503 § Getting the bytes, on an edge between two apps that announced `GROUP_VERSION_FILES`:\n * `group-want {g, id}` asks a holder for the file of message `id`; `group-want-no {g, id, why}` is a holder's no;\n * `group-have {g, ids}` says which files a member keeps (in a community, an application frame `{t, ids}` instead).\n */\nexport const GROUP_WANT_FRAME = \"group-want\";\nexport const GROUP_WANT_NO_FRAME = \"group-want-no\";\nexport const GROUP_HAVE_FRAME = \"group-have\";\nexport type GroupWantRefusal = \"gone\" | \"busy\" | \"refused\";\nconst REFUSALS: readonly string[] = [\"gone\", \"busy\", \"refused\"];\n\n/** A group message's id, as a private group (`<s>:<e>:<n>`) or a community (`<s>:<e>:<h>:<n>`) names it. */\nconst MESSAGE_ID = /^[a-z0-9]{52}:\\d{1,15}(:[A-Za-z0-9_-]{1,64})?:\\d{1,15}$/;\nexport const isGroupMessageId = (value: unknown): value is string => typeof value === \"string\" && MESSAGE_ID.test(value);\n\nfunction frameOf(value: unknown, t: string, groupId?: string): Record<string, unknown> | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const f = value as Record<string, unknown>;\n return f.t === t && (groupId === undefined || f.g === groupId) ? f : null;\n}\n\n/** A `group-want` of group `groupId`: the message whose file is asked for, or null. */\nexport function readGroupWant(value: unknown, groupId: string): { id: string } | null {\n const f = frameOf(value, GROUP_WANT_FRAME, groupId);\n return f && isGroupMessageId(f.id) ? { id: f.id } : null;\n}\n\n/** A holder's `group-want-no` of group `groupId`, or null. A reason this app does not know reads as `busy`. */\nexport function readGroupWantNo(value: unknown, groupId: string): { id: string; why: GroupWantRefusal } | null {\n const f = frameOf(value, GROUP_WANT_NO_FRAME, groupId);\n if (!f || !isGroupMessageId(f.id)) return null;\n return { id: f.id, why: typeof f.why === \"string\" && REFUSALS.includes(f.why) ? f.why as GroupWantRefusal : \"busy\" };\n}\n\n/**\n * The message ids of a `group-have`: of group `groupId` on an edge, or (no `groupId`) a community's application frame.\n * At most `haveIds` message ids; a frame with more, or anything else, is null.\n */\nexport function readGroupHave(value: unknown, groupId?: string): string[] | null {\n const f = frameOf(value, GROUP_HAVE_FRAME, groupId);\n if (!f || !Array.isArray(f.ids) || !f.ids.length || f.ids.length > GROUP_FILE_LIMITS.haveIds || !f.ids.every(isGroupMessageId)) return null;\n return [...new Set(f.ids as string[])];\n}\n\n/** A group file's description as the author's app puts it in the message (WISP 503 § The announcement). */\nexport interface GroupFileMeta {\n name: string;\n mime: string;\n size: number;\n /** SHA-256 of the file, base64url: what every receiver checks the stored bytes against. */\n d: string;\n voice?: VoiceMeta;\n video?: VideoMeta;\n image?: ImageMeta;\n}\n\nconst DIGEST = /^[A-Za-z0-9_-]{43}$/;\n\n/**\n * A description read from the wire, cleaned: the name made a display name (never a path), the type untrusted, the\n * size within the group limit, the digest the right shape. A malformed voice, video or picture description is dropped\n * on its own and the file is still a file; anything else malformed is no file at all (null), and the text shows.\n */\nexport function readGroupFileMeta(value: unknown): GroupFileMeta | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const v = value as Record<string, unknown>;\n if (typeof v.name !== \"string\" || !v.name.trim() || [...v.name].length > GROUP_FILE_LIMITS.nameChars) return null;\n if (typeof v.mime !== \"string\" || v.mime.length > GROUP_FILE_LIMITS.mimeChars) return null;\n if (!Number.isSafeInteger(v.size) || (v.size as number) < 1 || (v.size as number) > GROUP_FILE_LIMITS.maxBytes) return null;\n if (typeof v.d !== \"string\" || !DIGEST.test(v.d)) return null;\n const mime = sanitizeMime(v.mime);\n const voice = v.voice === undefined ? undefined : parseVoiceMeta(v.voice, mime);\n const video = v.video === undefined ? undefined : parseVideoMeta(v.video, mime);\n const image = v.image === undefined ? undefined : parseImageMeta(v.image, mime);\n return { name: sanitizeFileName(v.name), mime, size: v.size as number, d: v.d,\n ...(voice ? { voice } : {}), ...(video ? { video } : {}), ...(image ? { image } : {}) };\n}\n\n/**\n * The text an announcement carries when its author wrote no caption: what an app without group files shows instead\n * of the file. English, as wire text is; an app that knows the file shows the file, not this line.\n */\nexport function groupFileFallback(file: Pick<GroupFileMeta, \"name\" | \"size\" | \"voice\" | \"video\">): string {\n if (file.voice) return `🎤 Voice message (${formatVoiceDuration(file.voice.duration)}): update Ghostly to hear it`;\n return `${fileMessageText(file)} (${formatFileSize(file.size)}): update Ghostly to get files in groups`;\n}\n\n/**\n * Whether a device fetches a file by itself (WISP 503 § Automatic and asked downloads): a voice message always, any\n * other file up to `autoBytes` while what it took that way from the group stays within `autoBytesPerGroup`.\n */\nexport function fetchesByItself(file: Pick<GroupFileMeta, \"size\" | \"voice\">, keptAutomatically: number,\n limits: { autoBytes: number; autoBytesPerGroup: number } = GROUP_FILE_LIMITS): boolean {\n if (file.voice) return true;\n return file.size <= limits.autoBytes && keptAutomatically + file.size <= limits.autoBytesPerGroup;\n}\n"],"mappings":"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"}
|