+79
-1
src/pds.js
+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
+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
+
})