this repo has no description

feat: add getRecord endpoint with CBOR decoder

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

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

Changed files
+124 -2
src
test
+79 -1
src/pds.js
··· 59 return new Uint8Array(parts) 60 } 61 62 // === CID GENERATION === 63 // dag-cbor (0x71) + sha-256 (0x12) + 32 bytes 64 ··· 534 return Response.json({ error: err.message }, { status: 500 }) 535 } 536 } 537 return new Response('pds running', { status: 200 }) 538 } 539 } ··· 555 556 // Export utilities for testing 557 export { 558 - cborEncode, createCid, cidToString, base32Encode, createTid, 559 generateKeyPair, importPrivateKey, sign, bytesToHex, hexToBytes, 560 getKeyDepth 561 }
··· 59 return new Uint8Array(parts) 60 } 61 62 + function cborDecode(bytes) { 63 + let offset = 0 64 + 65 + function read() { 66 + const initial = bytes[offset++] 67 + const major = initial >> 5 68 + const info = initial & 0x1f 69 + 70 + let length = info 71 + if (info === 24) length = bytes[offset++] 72 + else if (info === 25) { length = (bytes[offset++] << 8) | bytes[offset++] } 73 + else if (info === 26) { 74 + length = (bytes[offset++] << 24) | (bytes[offset++] << 16) | (bytes[offset++] << 8) | bytes[offset++] 75 + } 76 + 77 + switch (major) { 78 + case 0: return length // unsigned int 79 + case 1: return -1 - length // negative int 80 + case 2: { // byte string 81 + const data = bytes.slice(offset, offset + length) 82 + offset += length 83 + return data 84 + } 85 + case 3: { // text string 86 + const data = new TextDecoder().decode(bytes.slice(offset, offset + length)) 87 + offset += length 88 + return data 89 + } 90 + case 4: { // array 91 + const arr = [] 92 + for (let i = 0; i < length; i++) arr.push(read()) 93 + return arr 94 + } 95 + case 5: { // map 96 + const obj = {} 97 + for (let i = 0; i < length; i++) { 98 + const key = read() 99 + obj[key] = read() 100 + } 101 + return obj 102 + } 103 + case 7: { // special 104 + if (info === 20) return false 105 + if (info === 21) return true 106 + if (info === 22) return null 107 + return undefined 108 + } 109 + } 110 + } 111 + 112 + return read() 113 + } 114 + 115 // === CID GENERATION === 116 // dag-cbor (0x71) + sha-256 (0x12) + 32 bytes 117 ··· 587 return Response.json({ error: err.message }, { status: 500 }) 588 } 589 } 590 + if (url.pathname === '/xrpc/com.atproto.repo.getRecord') { 591 + const collection = url.searchParams.get('collection') 592 + const rkey = url.searchParams.get('rkey') 593 + 594 + if (!collection || !rkey) { 595 + return Response.json({ error: 'missing collection or rkey' }, { status: 400 }) 596 + } 597 + 598 + const did = await this.getDid() 599 + const uri = `at://${did}/${collection}/${rkey}` 600 + 601 + const rows = this.sql.exec( 602 + `SELECT cid, value FROM records WHERE uri = ?`, uri 603 + ).toArray() 604 + 605 + if (rows.length === 0) { 606 + return Response.json({ error: 'record not found' }, { status: 404 }) 607 + } 608 + 609 + const row = rows[0] 610 + // Decode CBOR for response 611 + const value = cborDecode(row.value) 612 + 613 + return Response.json({ uri, cid: row.cid, value }) 614 + } 615 return new Response('pds running', { status: 200 }) 616 } 617 } ··· 633 634 // Export utilities for testing 635 export { 636 + cborEncode, cborDecode, createCid, cidToString, base32Encode, createTid, 637 generateKeyPair, importPrivateKey, sign, bytesToHex, hexToBytes, 638 getKeyDepth 639 }
+45 -1
test/pds.test.js
··· 1 import { test, describe } from 'node:test' 2 import assert from 'node:assert' 3 import { 4 - cborEncode, createCid, cidToString, base32Encode, createTid, 5 generateKeyPair, importPrivateKey, sign, bytesToHex, hexToBytes, 6 getKeyDepth 7 } from '../src/pds.js' ··· 221 assert.ok(depth >= 0) 222 }) 223 })
··· 1 import { test, describe } from 'node:test' 2 import assert from 'node:assert' 3 import { 4 + cborEncode, cborDecode, createCid, cidToString, base32Encode, createTid, 5 generateKeyPair, importPrivateKey, sign, bytesToHex, hexToBytes, 6 getKeyDepth 7 } from '../src/pds.js' ··· 221 assert.ok(depth >= 0) 222 }) 223 }) 224 + 225 + describe('CBOR Decoding', () => { 226 + test('decodes what encode produces (roundtrip)', () => { 227 + const original = { hello: 'world', num: 42 } 228 + const encoded = cborEncode(original) 229 + const decoded = cborDecode(encoded) 230 + assert.deepStrictEqual(decoded, original) 231 + }) 232 + 233 + test('decodes null', () => { 234 + const encoded = cborEncode(null) 235 + const decoded = cborDecode(encoded) 236 + assert.strictEqual(decoded, null) 237 + }) 238 + 239 + test('decodes booleans', () => { 240 + assert.strictEqual(cborDecode(cborEncode(true)), true) 241 + assert.strictEqual(cborDecode(cborEncode(false)), false) 242 + }) 243 + 244 + test('decodes integers', () => { 245 + assert.strictEqual(cborDecode(cborEncode(0)), 0) 246 + assert.strictEqual(cborDecode(cborEncode(42)), 42) 247 + assert.strictEqual(cborDecode(cborEncode(255)), 255) 248 + assert.strictEqual(cborDecode(cborEncode(-1)), -1) 249 + assert.strictEqual(cborDecode(cborEncode(-10)), -10) 250 + }) 251 + 252 + test('decodes strings', () => { 253 + assert.strictEqual(cborDecode(cborEncode('hello')), 'hello') 254 + assert.strictEqual(cborDecode(cborEncode('')), '') 255 + }) 256 + 257 + test('decodes arrays', () => { 258 + assert.deepStrictEqual(cborDecode(cborEncode([1, 2, 3])), [1, 2, 3]) 259 + assert.deepStrictEqual(cborDecode(cborEncode([])), []) 260 + }) 261 + 262 + test('decodes nested structures', () => { 263 + const original = { arr: [1, { nested: true }], str: 'test' } 264 + const decoded = cborDecode(cborEncode(original)) 265 + assert.deepStrictEqual(decoded, original) 266 + }) 267 + })