A zero-dependency AT Protocol Personal Data Server written in JavaScript
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add jsconfig.json, fix type checking

+26 -10
+12
jsconfig.json
···
··· 1 + { 2 + "compilerOptions": { 3 + "target": "es2020", 4 + "lib": ["es2020", "dom"], 5 + "noEmit": true, 6 + "module": "esnext", 7 + "strict": false, 8 + "moduleResolution": "bundler", 9 + "resolveJsonModule": true 10 + }, 11 + "exclude": ["node_modules"] 12 + }
+14 -10
src/pds.js
··· 1 /** 2 * Minimal AT Protocol Personal Data Server (PDS) 3 * ··· 290 291 /** 292 * Create a CIDv1 (dag-cbor + sha-256) from raw bytes 293 - * @param {Uint8Array} bytes - Content to hash 294 * @returns {Promise<Uint8Array>} CID bytes (36 bytes: version + codec + multihash) 295 */ 296 export async function createCid(bytes) { ··· 449 /** 450 * Sign data with ECDSA P-256, returning low-S normalized signature 451 * @param {CryptoKey} privateKey - Web Crypto key from importPrivateKey 452 - * @param {Uint8Array} data - Data to sign 453 * @returns {Promise<Uint8Array>} 64-byte signature (r || s) 454 */ 455 export async function sign(privateKey, data) { ··· 1001 * Build a CAR (Content Addressable aRchive) file 1002 * @param {string} rootCid - Root CID string 1003 * @param {Array<{cid: string, data: Uint8Array}>} blocks - Blocks to include 1004 - * @returns {Uint8Array} CAR file bytes 1005 */ 1006 export function buildCarFile(rootCid, blocks) { 1007 const parts = []; ··· 1086 handler: (pds, _req, _url) => pds.handleRepoInfo(), 1087 }, 1088 '/xrpc/com.atproto.server.describeServer': { 1089 - handler: (pds, req, _url) => pds.handleDescribeServer(req), 1090 }, 1091 '/xrpc/com.atproto.server.createSession': { 1092 method: 'POST', ··· 1135 handler: (pds, _req, url) => pds.handleListRecords(url), 1136 }, 1137 '/xrpc/com.atproto.sync.getLatestCommit': { 1138 - handler: (pds, _req, _url) => pds.handleGetLatestCommit(), 1139 }, 1140 '/xrpc/com.atproto.sync.getRepoStatus': { 1141 handler: (pds, _req, _url) => pds.handleGetRepoStatus(), 1142 }, 1143 '/xrpc/com.atproto.sync.getRepo': { 1144 - handler: (pds, _req, _url) => pds.handleGetRepo(), 1145 }, 1146 '/xrpc/com.atproto.sync.getRecord': { 1147 handler: (pds, _req, url) => pds.handleSyncGetRecord(url), 1148 }, 1149 '/xrpc/com.atproto.sync.subscribeRepos': { 1150 - handler: (pds, req, url) => pds.handleSubscribeRepos(req, url), 1151 }, 1152 }; 1153 ··· 2386 * Verify auth and return DID from token 2387 * @param {Request} request - HTTP request with Authorization header 2388 * @param {Object} env - Environment with JWT_SECRET 2389 - * @returns {Promise<{did: string} | {error: Response}>} DID or error response 2390 */ 2391 - async function requireAuth(request, env) { 2392 const authHeader = request.headers.get('Authorization'); 2393 if (!authHeader || !authHeader.startsWith('Bearer ')) { 2394 return { 2395 error: Response.json( 2396 { 2397 error: 'AuthRequired', ··· 2406 const jwtSecret = env?.JWT_SECRET; 2407 if (!jwtSecret) { 2408 return { 2409 error: Response.json( 2410 { 2411 error: 'InternalServerError', ··· 2418 2419 try { 2420 const payload = await verifyAccessJwt(token, jwtSecret); 2421 - return { did: payload.sub }; 2422 } catch (err) { 2423 return { 2424 error: Response.json( 2425 { 2426 error: 'InvalidToken',
··· 1 + // @ts-check 2 /** 3 * Minimal AT Protocol Personal Data Server (PDS) 4 * ··· 291 292 /** 293 * Create a CIDv1 (dag-cbor + sha-256) from raw bytes 294 + * @param {Uint8Array<ArrayBuffer>} bytes - Content to hash 295 * @returns {Promise<Uint8Array>} CID bytes (36 bytes: version + codec + multihash) 296 */ 297 export async function createCid(bytes) { ··· 450 /** 451 * Sign data with ECDSA P-256, returning low-S normalized signature 452 * @param {CryptoKey} privateKey - Web Crypto key from importPrivateKey 453 + * @param {Uint8Array<ArrayBuffer>} data - Data to sign 454 * @returns {Promise<Uint8Array>} 64-byte signature (r || s) 455 */ 456 export async function sign(privateKey, data) { ··· 1002 * Build a CAR (Content Addressable aRchive) file 1003 * @param {string} rootCid - Root CID string 1004 * @param {Array<{cid: string, data: Uint8Array}>} blocks - Blocks to include 1005 + * @returns {Uint8Array<ArrayBuffer>} CAR file bytes 1006 */ 1007 export function buildCarFile(rootCid, blocks) { 1008 const parts = []; ··· 1087 handler: (pds, _req, _url) => pds.handleRepoInfo(), 1088 }, 1089 '/xrpc/com.atproto.server.describeServer': { 1090 + handler: async (pds, req, _url) => pds.handleDescribeServer(req), 1091 }, 1092 '/xrpc/com.atproto.server.createSession': { 1093 method: 'POST', ··· 1136 handler: (pds, _req, url) => pds.handleListRecords(url), 1137 }, 1138 '/xrpc/com.atproto.sync.getLatestCommit': { 1139 + handler: async (pds, _req, _url) => pds.handleGetLatestCommit(), 1140 }, 1141 '/xrpc/com.atproto.sync.getRepoStatus': { 1142 handler: (pds, _req, _url) => pds.handleGetRepoStatus(), 1143 }, 1144 '/xrpc/com.atproto.sync.getRepo': { 1145 + handler: async (pds, _req, _url) => pds.handleGetRepo(), 1146 }, 1147 '/xrpc/com.atproto.sync.getRecord': { 1148 handler: (pds, _req, url) => pds.handleSyncGetRecord(url), 1149 }, 1150 '/xrpc/com.atproto.sync.subscribeRepos': { 1151 + handler: async (pds, req, url) => pds.handleSubscribeRepos(req, url), 1152 }, 1153 }; 1154 ··· 2387 * Verify auth and return DID from token 2388 * @param {Request} request - HTTP request with Authorization header 2389 * @param {Object} env - Environment with JWT_SECRET 2390 + * @returns {Promise<{did:string|null, error:Response|null}>} DID or error response 2391 */ 2392 + async function requireAuth (request, env) { 2393 const authHeader = request.headers.get('Authorization'); 2394 if (!authHeader || !authHeader.startsWith('Bearer ')) { 2395 return { 2396 + did: null, 2397 error: Response.json( 2398 { 2399 error: 'AuthRequired', ··· 2408 const jwtSecret = env?.JWT_SECRET; 2409 if (!jwtSecret) { 2410 return { 2411 + did: null, 2412 error: Response.json( 2413 { 2414 error: 'InternalServerError', ··· 2421 2422 try { 2423 const payload = await verifyAccessJwt(token, jwtSecret); 2424 + return { did: payload.sub, error: null }; 2425 } catch (err) { 2426 return { 2427 + did: null, 2428 error: Response.json( 2429 { 2430 error: 'InvalidToken',