this repo has no description

style: apply consistent formatting across pds.js and tests

Standardize JSDoc type annotation spacing and improve multiline
formatting for better readability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Changed files
+179 -89
src
test
+126 -72
src/pds.js
··· 544 544 * @returns {Promise<Uint8Array>} CID bytes (36 bytes: version + codec + multihash) 545 545 */ 546 546 async function createCidWithCodec(bytes, codec) { 547 - const hash = await crypto.subtle.digest('SHA-256', /** @type {BufferSource} */(bytes)); 547 + const hash = await crypto.subtle.digest( 548 + 'SHA-256', 549 + /** @type {BufferSource} */ (bytes), 550 + ); 548 551 const hashBytes = new Uint8Array(hash); 549 552 550 553 // CIDv1: version(1) + codec + multihash(sha256) ··· 672 675 673 676 return crypto.subtle.importKey( 674 677 'pkcs8', 675 - /** @type {BufferSource} */(pkcs8), 678 + /** @type {BufferSource} */ (pkcs8), 676 679 { name: 'ECDSA', namedCurve: 'P-256' }, 677 680 false, 678 681 ['sign'], ··· 689 692 const signature = await crypto.subtle.sign( 690 693 { name: 'ECDSA', hash: 'SHA-256' }, 691 694 privateKey, 692 - /** @type {BufferSource} */(data), 695 + /** @type {BufferSource} */ (data), 693 696 ); 694 697 const sig = new Uint8Array(signature); 695 698 ··· 724 727 725 728 // Export private key as raw bytes 726 729 const privateJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey); 727 - const privateBytes = base64UrlDecode(/** @type {string} */(privateJwk.d)); 730 + const privateBytes = base64UrlDecode(/** @type {string} */ (privateJwk.d)); 728 731 729 732 // Export public key as compressed point 730 733 const publicRaw = await crypto.subtle.exportKey('raw', keyPair.publicKey); ··· 764 767 async function hmacSign(data, secret) { 765 768 const key = await crypto.subtle.importKey( 766 769 'raw', 767 - /** @type {BufferSource} */(new TextEncoder().encode(secret)), 770 + /** @type {BufferSource} */ (new TextEncoder().encode(secret)), 768 771 { name: 'HMAC', hash: 'SHA-256' }, 769 772 false, 770 773 ['sign'], ··· 772 775 const sig = await crypto.subtle.sign( 773 776 'HMAC', 774 777 key, 775 - /** @type {BufferSource} */(new TextEncoder().encode(data)), 778 + /** @type {BufferSource} */ (new TextEncoder().encode(data)), 776 779 ); 777 780 return base64UrlEncode(new Uint8Array(sig)); 778 781 } ··· 1558 1561 async getSigningKey() { 1559 1562 const hex = await this.state.storage.get('privateKey'); 1560 1563 if (!hex) return null; 1561 - return importPrivateKey(hexToBytes(/** @type {string} */(hex))); 1564 + return importPrivateKey(hexToBytes(/** @type {string} */ (hex))); 1562 1565 } 1563 1566 1564 1567 /** ··· 1576 1579 if (visited.has(cidStr)) return; 1577 1580 visited.add(cidStr); 1578 1581 1579 - const rows = /** @type {BlockRow[]} */ (this.sql 1580 - .exec(`SELECT data FROM blocks WHERE cid = ?`, cidStr) 1581 - .toArray()); 1582 + const rows = /** @type {BlockRow[]} */ ( 1583 + this.sql.exec(`SELECT data FROM blocks WHERE cid = ?`, cidStr).toArray() 1584 + ); 1582 1585 if (rows.length === 0) return; 1583 1586 1584 1587 const data = new Uint8Array(rows[0].data); ··· 1675 1678 const commit = { 1676 1679 did, 1677 1680 version: 3, 1678 - data: new CID(cidToBytes(/** @type {string} */(dataRoot))), // CID wrapped for explicit encoding 1681 + data: new CID(cidToBytes(/** @type {string} */ (dataRoot))), // CID wrapped for explicit encoding 1679 1682 rev, 1680 - prev: prevCommit?.cid ? new CID(cidToBytes(/** @type {string} */(prevCommit.cid))) : null, 1683 + prev: prevCommit?.cid 1684 + ? new CID(cidToBytes(/** @type {string} */ (prevCommit.cid))) 1685 + : null, 1681 1686 }; 1682 1687 1683 1688 // Sign commit (using dag-cbor encoder for CIDs) ··· 1718 1723 // Add commit block 1719 1724 newBlocks.push({ cid: commitCidStr, data: signedBytes }); 1720 1725 // Add MST node blocks (get all blocks referenced by commit.data) 1721 - const mstBlocks = this.collectMstBlocks(/** @type {string} */(dataRoot)); 1726 + const mstBlocks = this.collectMstBlocks(/** @type {string} */ (dataRoot)); 1722 1727 newBlocks.push(...mstBlocks); 1723 1728 1724 1729 // Sequence event with blocks - store complete event data including rev and time ··· 1740 1745 ); 1741 1746 1742 1747 // Broadcast to subscribers (both local and via default DO for relay) 1743 - const evtRows = /** @type {SeqEventRow[]} */ (this.sql 1744 - .exec(`SELECT * FROM seq_events ORDER BY seq DESC LIMIT 1`) 1745 - .toArray()); 1748 + const evtRows = /** @type {SeqEventRow[]} */ ( 1749 + this.sql 1750 + .exec(`SELECT * FROM seq_events ORDER BY seq DESC LIMIT 1`) 1751 + .toArray() 1752 + ); 1746 1753 if (evtRows.length > 0) { 1747 1754 this.broadcastEvent(evtRows[0]); 1748 1755 // Also forward to default DO for relay subscribers ··· 1760 1767 body: JSON.stringify({ ...row, evt: evtArray }), 1761 1768 }), 1762 1769 ) 1763 - .catch(() => { }); // Ignore forward errors 1770 + .catch(() => {}); // Ignore forward errors 1764 1771 } 1765 1772 } 1766 1773 ··· 1824 1831 const commit = { 1825 1832 did, 1826 1833 version: 3, 1827 - data: dataRoot ? new CID(cidToBytes(/** @type {string} */(dataRoot))) : null, 1834 + data: dataRoot 1835 + ? new CID(cidToBytes(/** @type {string} */ (dataRoot))) 1836 + : null, 1828 1837 rev, 1829 - prev: prevCommit?.cid ? new CID(cidToBytes(/** @type {string} */(prevCommit.cid))) : null, 1838 + prev: prevCommit?.cid 1839 + ? new CID(cidToBytes(/** @type {string} */ (prevCommit.cid))) 1840 + : null, 1830 1841 }; 1831 1842 1832 1843 // Sign commit ··· 1863 1874 const newBlocks = []; 1864 1875 newBlocks.push({ cid: commitCidStr, data: signedBytes }); 1865 1876 if (dataRoot) { 1866 - const mstBlocks = this.collectMstBlocks(/** @type {string} */(dataRoot)); 1877 + const mstBlocks = this.collectMstBlocks(/** @type {string} */ (dataRoot)); 1867 1878 newBlocks.push(...mstBlocks); 1868 1879 } 1869 1880 ··· 1883 1894 ); 1884 1895 1885 1896 // Broadcast to subscribers 1886 - const evtRows = /** @type {SeqEventRow[]} */ (this.sql 1887 - .exec(`SELECT * FROM seq_events ORDER BY seq DESC LIMIT 1`) 1888 - .toArray()); 1897 + const evtRows = /** @type {SeqEventRow[]} */ ( 1898 + this.sql 1899 + .exec(`SELECT * FROM seq_events ORDER BY seq DESC LIMIT 1`) 1900 + .toArray() 1901 + ); 1889 1902 if (evtRows.length > 0) { 1890 1903 this.broadcastEvent(evtRows[0]); 1891 1904 // Forward to default DO for relay subscribers ··· 1901 1914 body: JSON.stringify({ ...row, evt: evtArray }), 1902 1915 }), 1903 1916 ) 1904 - .catch(() => { }); // Ignore forward errors 1917 + .catch(() => {}); // Ignore forward errors 1905 1918 } 1906 1919 } 1907 1920 ··· 1920 1933 // Decode stored event to get ops, blocks, rev, and time 1921 1934 const evtData = cborDecode(new Uint8Array(evt.evt)); 1922 1935 /** @type {Array<{action: string, path: string, cid: CID|null}>} */ 1923 - const ops = evtData.ops.map((/** @type {{action: string, path: string, cid?: string}} */ op) => ({ 1924 - ...op, 1925 - cid: op.cid ? new CID(cidToBytes(op.cid)) : null, // Wrap in CID class for tag 42 encoding 1926 - })); 1936 + const ops = evtData.ops.map( 1937 + (/** @type {{action: string, path: string, cid?: string}} */ op) => ({ 1938 + ...op, 1939 + cid: op.cid ? new CID(cidToBytes(op.cid)) : null, // Wrap in CID class for tag 42 encoding 1940 + }), 1941 + ); 1927 1942 // Get blocks from stored event (already in CAR format) 1928 1943 const blocks = evtData.blocks || new Uint8Array(0); 1929 1944 ··· 1991 2006 if (!did) { 1992 2007 return new Response('User not found', { status: 404 }); 1993 2008 } 1994 - return new Response(/** @type {string} */(did), { headers: { 'Content-Type': 'text/plain' } }); 2009 + return new Response(/** @type {string} */ (did), { 2010 + headers: { 'Content-Type': 'text/plain' }, 2011 + }); 1995 2012 } 1996 2013 1997 2014 /** @param {Request} request */ ··· 2282 2299 // Check instance's own handle 2283 2300 const instanceDid = await this.getDid(); 2284 2301 if (instanceDid === did) { 2285 - return /** @type {string|null} */ (await this.state.storage.get('handle')); 2302 + return /** @type {string|null} */ ( 2303 + await this.state.storage.get('handle') 2304 + ); 2286 2305 } 2287 2306 return null; 2288 2307 } ··· 2372 2391 const did = await this.getDid(); 2373 2392 const repos = did 2374 2393 ? [{ did, head: null, rev: null }] 2375 - : registeredDids.map((/** @type {string} */ d) => ({ did: d, head: null, rev: null })); 2394 + : registeredDids.map((/** @type {string} */ d) => ({ 2395 + did: d, 2396 + head: null, 2397 + rev: null, 2398 + })); 2376 2399 return Response.json({ repos }); 2377 2400 } 2378 2401 ··· 2521 2544 } 2522 2545 const did = await this.getDid(); 2523 2546 const uri = `at://${did}/${collection}/${rkey}`; 2524 - const rows = /** @type {RecordRow[]} */ (this.sql 2525 - .exec(`SELECT cid, value FROM records WHERE uri = ?`, uri) 2526 - .toArray()); 2547 + const rows = /** @type {RecordRow[]} */ ( 2548 + this.sql 2549 + .exec(`SELECT cid, value FROM records WHERE uri = ?`, uri) 2550 + .toArray() 2551 + ); 2527 2552 if (rows.length === 0) { 2528 2553 return errorResponse('RecordNotFound', 'record not found', 404); 2529 2554 } ··· 2570 2595 const query = `SELECT uri, cid, value FROM records WHERE collection = ? ORDER BY rkey ${reverse ? 'DESC' : 'ASC'} LIMIT ?`; 2571 2596 const params = [collection, limit + 1]; 2572 2597 2573 - const rows = /** @type {RecordRow[]} */ (this.sql.exec(query, ...params).toArray()); 2598 + const rows = /** @type {RecordRow[]} */ ( 2599 + this.sql.exec(query, ...params).toArray() 2600 + ); 2574 2601 const hasMore = rows.length > limit; 2575 2602 const records = rows.slice(0, limit).map((r) => ({ 2576 2603 uri: r.uri, ··· 2585 2612 } 2586 2613 2587 2614 handleGetLatestCommit() { 2588 - const commits = /** @type {CommitRow[]} */ (this.sql 2589 - .exec(`SELECT cid, rev FROM commits ORDER BY seq DESC LIMIT 1`) 2590 - .toArray()); 2615 + const commits = /** @type {CommitRow[]} */ ( 2616 + this.sql 2617 + .exec(`SELECT cid, rev FROM commits ORDER BY seq DESC LIMIT 1`) 2618 + .toArray() 2619 + ); 2591 2620 if (commits.length === 0) { 2592 2621 return errorResponse('RepoNotFound', 'repo not found', 404); 2593 2622 } ··· 2596 2625 2597 2626 async handleGetRepoStatus() { 2598 2627 const did = await this.getDid(); 2599 - const commits = /** @type {CommitRow[]} */ (this.sql 2600 - .exec(`SELECT cid, rev FROM commits ORDER BY seq DESC LIMIT 1`) 2601 - .toArray()); 2628 + const commits = /** @type {CommitRow[]} */ ( 2629 + this.sql 2630 + .exec(`SELECT cid, rev FROM commits ORDER BY seq DESC LIMIT 1`) 2631 + .toArray() 2632 + ); 2602 2633 if (commits.length === 0 || !did) { 2603 2634 return errorResponse('RepoNotFound', 'repo not found', 404); 2604 2635 } ··· 2611 2642 } 2612 2643 2613 2644 handleGetRepo() { 2614 - const commits = /** @type {CommitRow[]} */ (this.sql 2615 - .exec(`SELECT cid FROM commits ORDER BY seq DESC LIMIT 1`) 2616 - .toArray()); 2645 + const commits = /** @type {CommitRow[]} */ ( 2646 + this.sql 2647 + .exec(`SELECT cid FROM commits ORDER BY seq DESC LIMIT 1`) 2648 + .toArray() 2649 + ); 2617 2650 if (commits.length === 0) { 2618 2651 return errorResponse('RepoNotFound', 'repo not found', 404); 2619 2652 } ··· 2625 2658 // Helper to get block data 2626 2659 /** @param {string} cid */ 2627 2660 const getBlock = (cid) => { 2628 - const rows = /** @type {BlockRow[]} */ (this.sql 2629 - .exec(`SELECT data FROM blocks WHERE cid = ?`, cid) 2630 - .toArray()); 2661 + const rows = /** @type {BlockRow[]} */ ( 2662 + this.sql.exec(`SELECT data FROM blocks WHERE cid = ?`, cid).toArray() 2663 + ); 2631 2664 return rows.length > 0 ? new Uint8Array(rows[0].data) : null; 2632 2665 }; 2633 2666 ··· 2680 2713 } 2681 2714 2682 2715 const car = buildCarFile(commitCid, blocksForCar); 2683 - return new Response(/** @type {BodyInit} */(car), { 2716 + return new Response(/** @type {BodyInit} */ (car), { 2684 2717 headers: { 'content-type': 'application/vnd.ipld.car' }, 2685 2718 }); 2686 2719 } ··· 2694 2727 } 2695 2728 const did = await this.getDid(); 2696 2729 const uri = `at://${did}/${collection}/${rkey}`; 2697 - const rows = /** @type {RecordRow[]} */ (this.sql 2698 - .exec(`SELECT cid FROM records WHERE uri = ?`, uri) 2699 - .toArray()); 2730 + const rows = /** @type {RecordRow[]} */ ( 2731 + this.sql.exec(`SELECT cid FROM records WHERE uri = ?`, uri).toArray() 2732 + ); 2700 2733 if (rows.length === 0) { 2701 2734 return errorResponse('RecordNotFound', 'record not found', 404); 2702 2735 } 2703 2736 const recordCid = rows[0].cid; 2704 2737 2705 2738 // Get latest commit 2706 - const commits = /** @type {CommitRow[]} */ (this.sql 2707 - .exec(`SELECT cid FROM commits ORDER BY seq DESC LIMIT 1`) 2708 - .toArray()); 2739 + const commits = /** @type {CommitRow[]} */ ( 2740 + this.sql 2741 + .exec(`SELECT cid FROM commits ORDER BY seq DESC LIMIT 1`) 2742 + .toArray() 2743 + ); 2709 2744 if (commits.length === 0) { 2710 2745 return errorResponse('RepoNotFound', 'no commits', 404); 2711 2746 } ··· 2721 2756 const addBlock = (cidStr) => { 2722 2757 if (included.has(cidStr)) return; 2723 2758 included.add(cidStr); 2724 - const blockRows = /** @type {BlockRow[]} */ (this.sql 2725 - .exec(`SELECT data FROM blocks WHERE cid = ?`, cidStr) 2726 - .toArray()); 2759 + const blockRows = /** @type {BlockRow[]} */ ( 2760 + this.sql.exec(`SELECT data FROM blocks WHERE cid = ?`, cidStr).toArray() 2761 + ); 2727 2762 if (blockRows.length > 0) { 2728 2763 blocks.push({ cid: cidStr, data: new Uint8Array(blockRows[0].data) }); 2729 2764 } ··· 2733 2768 addBlock(commitCid); 2734 2769 2735 2770 // Get commit to find data root 2736 - const commitRows = /** @type {BlockRow[]} */ (this.sql 2737 - .exec(`SELECT data FROM blocks WHERE cid = ?`, commitCid) 2738 - .toArray()); 2771 + const commitRows = /** @type {BlockRow[]} */ ( 2772 + this.sql 2773 + .exec(`SELECT data FROM blocks WHERE cid = ?`, commitCid) 2774 + .toArray() 2775 + ); 2739 2776 if (commitRows.length > 0) { 2740 2777 const commit = cborDecode(new Uint8Array(commitRows[0].data)); 2741 2778 if (commit.data) { ··· 2752 2789 addBlock(recordCid); 2753 2790 2754 2791 const car = buildCarFile(commitCid, blocks); 2755 - return new Response(/** @type {BodyInit} */(car), { 2792 + return new Response(/** @type {BodyInit} */ (car), { 2756 2793 headers: { 'content-type': 'application/vnd.ipld.car' }, 2757 2794 }); 2758 2795 } ··· 2797 2834 2798 2835 // Check size limits 2799 2836 if (size === 0) { 2800 - return errorResponse('InvalidRequest', 'Empty blobs are not allowed', 400); 2837 + return errorResponse( 2838 + 'InvalidRequest', 2839 + 'Empty blobs are not allowed', 2840 + 400, 2841 + ); 2801 2842 } 2802 2843 const MAX_BLOB_SIZE = 50 * 1024 * 1024; 2803 2844 if (size > MAX_BLOB_SIZE) { ··· 2851 2892 const cid = url.searchParams.get('cid'); 2852 2893 2853 2894 if (!did || !cid) { 2854 - return errorResponse('InvalidRequest', 'missing did or cid parameter', 400); 2895 + return errorResponse( 2896 + 'InvalidRequest', 2897 + 'missing did or cid parameter', 2898 + 400, 2899 + ); 2855 2900 } 2856 2901 2857 2902 // Validate CID format (CIDv1 base32lower: starts with 'b', 59 chars total) ··· 2862 2907 // Verify DID matches this DO 2863 2908 const myDid = await this.getDid(); 2864 2909 if (did !== myDid) { 2865 - return errorResponse('InvalidRequest', 'DID does not match this repo', 400); 2910 + return errorResponse( 2911 + 'InvalidRequest', 2912 + 'DID does not match this repo', 2913 + 400, 2914 + ); 2866 2915 } 2867 2916 2868 2917 // Look up blob metadata ··· 2909 2958 // Verify DID matches this DO 2910 2959 const myDid = await this.getDid(); 2911 2960 if (did !== myDid) { 2912 - return errorResponse('InvalidRequest', 'DID does not match this repo', 400); 2961 + return errorResponse( 2962 + 'InvalidRequest', 2963 + 'DID does not match this repo', 2964 + 400, 2965 + ); 2913 2966 } 2914 2967 2915 2968 // Query blobs with pagination (cursor is createdAt::cid for uniqueness) ··· 2954 3007 this.state.acceptWebSocket(server); 2955 3008 const cursor = url.searchParams.get('cursor'); 2956 3009 if (cursor) { 2957 - const events = /** @type {SeqEventRow[]} */ (this.sql 2958 - .exec( 2959 - `SELECT * FROM seq_events WHERE seq > ? ORDER BY seq`, 2960 - parseInt(cursor, 10), 2961 - ) 2962 - .toArray()); 3010 + const events = /** @type {SeqEventRow[]} */ ( 3011 + this.sql 3012 + .exec( 3013 + `SELECT * FROM seq_events WHERE seq > ? ORDER BY seq`, 3014 + parseInt(cursor, 10), 3015 + ) 3016 + .toArray() 3017 + ); 2963 3018 for (const evt of events) { 2964 3019 server.send(this.formatEvent(evt)); 2965 3020 } ··· 3018 3073 await this.env?.BLOBS?.delete(`${did}/${cid}`); 3019 3074 this.sql.exec('DELETE FROM blob WHERE cid = ?', cid); 3020 3075 } 3021 - 3022 3076 } 3023 3077 } 3024 3078
+5 -5
test/e2e.sh
··· 211 211 # Create a minimal valid PNG (1x1 transparent pixel) 212 212 # PNG signature + IHDR + IDAT + IEND 213 213 PNG_FILE=$(mktemp) 214 - printf '\x89PNG\r\n\x1a\n' > "$PNG_FILE" 215 - printf '\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89' >> "$PNG_FILE" 216 - printf '\x00\x00\x00\nIDATx\x9cc\x00\x01\x00\x00\x05\x00\x01\r\n-\xb4' >> "$PNG_FILE" 217 - printf '\x00\x00\x00\x00IEND\xaeB`\x82' >> "$PNG_FILE" 214 + printf '\x89PNG\r\n\x1a\n' >"$PNG_FILE" 215 + printf '\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89' >>"$PNG_FILE" 216 + printf '\x00\x00\x00\nIDATx\x9cc\x00\x01\x00\x00\x05\x00\x01\r\n-\xb4' >>"$PNG_FILE" 217 + printf '\x00\x00\x00\x00IEND\xaeB`\x82' >>"$PNG_FILE" 218 218 219 219 # uploadBlob requires auth 220 220 STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.repo.uploadBlob" \ ··· 261 261 BLOB_POST=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \ 262 262 -H "Authorization: Bearer $TOKEN" \ 263 263 -H "Content-Type: application/json" \ 264 - -d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"record\":{\"text\":\"post with image\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"embed\":{\"\$type\":\"app.bsky.embed.images\",\"images\":[{\"image\":{\"\$type\":\"blob\",\"ref\":{\"\$link\":\"$BLOB_CID\"},\"mimeType\":\"image/png\",\"size\":$(wc -c < "$PNG_FILE")},\"alt\":\"test\"}]}}}") 264 + -d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"record\":{\"text\":\"post with image\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"embed\":{\"\$type\":\"app.bsky.embed.images\",\"images\":[{\"image\":{\"\$type\":\"blob\",\"ref\":{\"\$link\":\"$BLOB_CID\"},\"mimeType\":\"image/png\",\"size\":$(wc -c <"$PNG_FILE")},\"alt\":\"test\"}]}}}") 265 265 BLOB_POST_URI=$(echo "$BLOB_POST" | jq -r '.uri') 266 266 BLOB_POST_RKEY=$(echo "$BLOB_POST_URI" | sed 's|.*/||') 267 267 [ "$BLOB_POST_URI" != "null" ] && [ -n "$BLOB_POST_URI" ] && pass "createRecord with blob ref" || fail "createRecord with blob"
+48 -12
test/pds.test.js
··· 615 615 616 616 test('detects WebP', () => { 617 617 const bytes = new Uint8Array([ 618 - 0x52, 0x49, 0x46, 0x46, // RIFF 619 - 0x00, 0x00, 0x00, 0x00, // size (ignored) 620 - 0x57, 0x45, 0x42, 0x50, // WEBP 618 + 0x52, 619 + 0x49, 620 + 0x46, 621 + 0x46, // RIFF 622 + 0x00, 623 + 0x00, 624 + 0x00, 625 + 0x00, // size (ignored) 626 + 0x57, 627 + 0x45, 628 + 0x42, 629 + 0x50, // WEBP 621 630 ]); 622 631 assert.strictEqual(sniffMimeType(bytes), 'image/webp'); 623 632 }); 624 633 625 634 test('detects MP4', () => { 626 635 const bytes = new Uint8Array([ 627 - 0x00, 0x00, 0x00, 0x18, // size 628 - 0x66, 0x74, 0x79, 0x70, // ftyp 629 - 0x69, 0x73, 0x6f, 0x6d, // isom brand 636 + 0x00, 637 + 0x00, 638 + 0x00, 639 + 0x18, // size 640 + 0x66, 641 + 0x74, 642 + 0x79, 643 + 0x70, // ftyp 644 + 0x69, 645 + 0x73, 646 + 0x6f, 647 + 0x6d, // isom brand 630 648 ]); 631 649 assert.strictEqual(sniffMimeType(bytes), 'video/mp4'); 632 650 }); 633 651 634 652 test('detects AVIF', () => { 635 653 const bytes = new Uint8Array([ 636 - 0x00, 0x00, 0x00, 0x1c, // size 637 - 0x66, 0x74, 0x79, 0x70, // ftyp 638 - 0x61, 0x76, 0x69, 0x66, // avif brand 654 + 0x00, 655 + 0x00, 656 + 0x00, 657 + 0x1c, // size 658 + 0x66, 659 + 0x74, 660 + 0x79, 661 + 0x70, // ftyp 662 + 0x61, 663 + 0x76, 664 + 0x69, 665 + 0x66, // avif brand 639 666 ]); 640 667 assert.strictEqual(sniffMimeType(bytes), 'image/avif'); 641 668 }); 642 669 643 670 test('detects HEIC', () => { 644 671 const bytes = new Uint8Array([ 645 - 0x00, 0x00, 0x00, 0x18, // size 646 - 0x66, 0x74, 0x79, 0x70, // ftyp 647 - 0x68, 0x65, 0x69, 0x63, // heic brand 672 + 0x00, 673 + 0x00, 674 + 0x00, 675 + 0x18, // size 676 + 0x66, 677 + 0x74, 678 + 0x79, 679 + 0x70, // ftyp 680 + 0x68, 681 + 0x65, 682 + 0x69, 683 + 0x63, // heic brand 648 684 ]); 649 685 assert.strictEqual(sniffMimeType(bytes), 'image/heic'); 650 686 });