this repo has no description

feat: add CID generation with SHA-256 and test framework

- Add createCid, cidToString, base32Encode utilities
- Set up Node.js built-in test runner (zero dependencies)
- Add comprehensive tests for CBOR encoding and CID generation
- Export utility functions for testing

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

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

.wrangler/state/v3/do/atproto-pds-PersonalDataServer/d67d37442b3ca7789897149c593e43bcf1fd6a9933be25596eb0cbe4c6a0783e.sqlite-shm

This is a binary file and will not be displayed.

.wrangler/state/v3/do/atproto-pds-PersonalDataServer/d67d37442b3ca7789897149c593e43bcf1fd6a9933be25596eb0cbe4c6a0783e.sqlite-wal

This is a binary file and will not be displayed.

+107
.wrangler/tmp/dev-qpR91k/pds.js
··· 28 28 }); 29 29 30 30 // src/pds.js 31 + function cborEncode(value) { 32 + const parts = []; 33 + function encode(val) { 34 + if (val === null) { 35 + parts.push(246); 36 + } else if (val === true) { 37 + parts.push(245); 38 + } else if (val === false) { 39 + parts.push(244); 40 + } else if (typeof val === "number") { 41 + encodeInteger(val); 42 + } else if (typeof val === "string") { 43 + const bytes = new TextEncoder().encode(val); 44 + encodeHead(3, bytes.length); 45 + parts.push(...bytes); 46 + } else if (val instanceof Uint8Array) { 47 + encodeHead(2, val.length); 48 + parts.push(...val); 49 + } else if (Array.isArray(val)) { 50 + encodeHead(4, val.length); 51 + for (const item of val) encode(item); 52 + } else if (typeof val === "object") { 53 + const keys = Object.keys(val).sort(); 54 + encodeHead(5, keys.length); 55 + for (const key of keys) { 56 + encode(key); 57 + encode(val[key]); 58 + } 59 + } 60 + } 61 + __name(encode, "encode"); 62 + function encodeHead(majorType, length) { 63 + const mt = majorType << 5; 64 + if (length < 24) { 65 + parts.push(mt | length); 66 + } else if (length < 256) { 67 + parts.push(mt | 24, length); 68 + } else if (length < 65536) { 69 + parts.push(mt | 25, length >> 8, length & 255); 70 + } else if (length < 4294967296) { 71 + parts.push(mt | 26, length >> 24 & 255, length >> 16 & 255, length >> 8 & 255, length & 255); 72 + } 73 + } 74 + __name(encodeHead, "encodeHead"); 75 + function encodeInteger(n) { 76 + if (n >= 0) { 77 + encodeHead(0, n); 78 + } else { 79 + encodeHead(1, -n - 1); 80 + } 81 + } 82 + __name(encodeInteger, "encodeInteger"); 83 + encode(value); 84 + return new Uint8Array(parts); 85 + } 86 + __name(cborEncode, "cborEncode"); 87 + async function createCid(bytes) { 88 + const hash = await crypto.subtle.digest("SHA-256", bytes); 89 + const hashBytes = new Uint8Array(hash); 90 + const cid = new Uint8Array(2 + 2 + 32); 91 + cid[0] = 1; 92 + cid[1] = 113; 93 + cid[2] = 18; 94 + cid[3] = 32; 95 + cid.set(hashBytes, 4); 96 + return cid; 97 + } 98 + __name(createCid, "createCid"); 99 + function cidToString(cid) { 100 + return "b" + base32Encode(cid); 101 + } 102 + __name(cidToString, "cidToString"); 103 + function base32Encode(bytes) { 104 + const alphabet = "abcdefghijklmnopqrstuvwxyz234567"; 105 + let result = ""; 106 + let bits = 0; 107 + let value = 0; 108 + for (const byte of bytes) { 109 + value = value << 8 | byte; 110 + bits += 8; 111 + while (bits >= 5) { 112 + bits -= 5; 113 + result += alphabet[value >> bits & 31]; 114 + } 115 + } 116 + if (bits > 0) { 117 + result += alphabet[value << 5 - bits & 31]; 118 + } 119 + return result; 120 + } 121 + __name(base32Encode, "base32Encode"); 31 122 var PersonalDataServer = class { 32 123 static { 33 124 __name(this, "PersonalDataServer"); ··· 37 128 this.sql = state.storage.sql; 38 129 } 39 130 async fetch(request) { 131 + const url = new URL(request.url); 132 + if (url.pathname === "/test/cbor") { 133 + const encoded = cborEncode({ hello: "world", num: 42 }); 134 + return new Response(encoded, { 135 + headers: { "content-type": "application/cbor" } 136 + }); 137 + } 138 + if (url.pathname === "/test/cid") { 139 + const data = cborEncode({ test: "data" }); 140 + const cid = await createCid(data); 141 + return Response.json({ cid: cidToString(cid) }); 142 + } 40 143 return new Response("pds running", { status: 200 }); 41 144 } 42 145 }; ··· 225 328 export { 226 329 PersonalDataServer, 227 330 __INTERNAL_WRANGLER_MIDDLEWARE__, 331 + base32Encode, 332 + cborEncode, 333 + cidToString, 334 + createCid, 228 335 middleware_loader_entry_default as default 229 336 }; 230 337 //# sourceMappingURL=pds.js.map
+2 -2
.wrangler/tmp/dev-qpR91k/pds.js.map
··· 2 2 "version": 3, 3 3 "sources": ["../bundle-MaCAbF/checked-fetch.js", "../../../src/pds.js", "../../../../../.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts", "../../../../../.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts", "../bundle-MaCAbF/middleware-insertion-facade.js", "../../../../../.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/common.ts", "../bundle-MaCAbF/middleware-loader.entry.ts"], 4 4 "sourceRoot": "/Users/chadmiller/code/pds-experiment/.wrangler/tmp/dev-qpR91k", 5 - "sourcesContent": ["const urls = new Set();\n\nfunction checkURL(request, init) {\n\tconst url =\n\t\trequest instanceof URL\n\t\t\t? request\n\t\t\t: new URL(\n\t\t\t\t\t(typeof request === \"string\"\n\t\t\t\t\t\t? new Request(request, init)\n\t\t\t\t\t\t: request\n\t\t\t\t\t).url\n\t\t\t\t);\n\tif (url.port && url.port !== \"443\" && url.protocol === \"https:\") {\n\t\tif (!urls.has(url.toString())) {\n\t\t\turls.add(url.toString());\n\t\t\tconsole.warn(\n\t\t\t\t`WARNING: known issue with \\`fetch()\\` requests to custom HTTPS ports in published Workers:\\n` +\n\t\t\t\t\t` - ${url.toString()} - the custom port will be ignored when the Worker is published using the \\`wrangler deploy\\` command.\\n`\n\t\t\t);\n\t\t}\n\t}\n}\n\nglobalThis.fetch = new Proxy(globalThis.fetch, {\n\tapply(target, thisArg, argArray) {\n\t\tconst [request, init] = argArray;\n\t\tcheckURL(request, init);\n\t\treturn Reflect.apply(target, thisArg, argArray);\n\t},\n});\n", "export class PersonalDataServer {\n constructor(state, env) {\n this.state = state\n this.sql = state.storage.sql\n }\n\n async fetch(request) {\n return new Response('pds running', { status: 200 })\n }\n}\n\nexport default {\n async fetch(request, env) {\n const url = new URL(request.url)\n const did = url.searchParams.get('did')\n\n if (!did) {\n return new Response('missing did param', { status: 400 })\n }\n\n const id = env.PDS.idFromName(did)\n const pds = env.PDS.get(id)\n return pds.fetch(request)\n }\n}\n", "import type { Middleware } from \"./common\";\n\nconst drainBody: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} finally {\n\t\ttry {\n\t\t\tif (request.body !== null && !request.bodyUsed) {\n\t\t\t\tconst reader = request.body.getReader();\n\t\t\t\twhile (!(await reader.read()).done) {}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(\"Failed to drain the unused request body.\", e);\n\t\t}\n\t}\n};\n\nexport default drainBody;\n", "import type { Middleware } from \"./common\";\n\ninterface JsonError {\n\tmessage?: string;\n\tname?: string;\n\tstack?: string;\n\tcause?: JsonError;\n}\n\nfunction reduceError(e: any): JsonError {\n\treturn {\n\t\tname: e?.name,\n\t\tmessage: e?.message ?? String(e),\n\t\tstack: e?.stack,\n\t\tcause: e?.cause === undefined ? undefined : reduceError(e.cause),\n\t};\n}\n\n// See comment in `bundle.ts` for details on why this is needed\nconst jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} catch (e: any) {\n\t\tconst error = reduceError(e);\n\t\treturn Response.json(error, {\n\t\t\tstatus: 500,\n\t\t\theaders: { \"MF-Experimental-Error-Stack\": \"true\" },\n\t\t});\n\t}\n};\n\nexport default jsonError;\n", "\t\t\t\timport worker, * as OTHER_EXPORTS from \"/Users/chadmiller/code/pds-experiment/src/pds.js\";\n\t\t\t\timport * as __MIDDLEWARE_0__ from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts\";\nimport * as __MIDDLEWARE_1__ from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts\";\n\n\t\t\t\texport * from \"/Users/chadmiller/code/pds-experiment/src/pds.js\";\n\t\t\t\tconst MIDDLEWARE_TEST_INJECT = \"__INJECT_FOR_TESTING_WRANGLER_MIDDLEWARE__\";\n\t\t\t\texport const __INTERNAL_WRANGLER_MIDDLEWARE__ = [\n\t\t\t\t\t\n\t\t\t\t\t__MIDDLEWARE_0__.default,__MIDDLEWARE_1__.default\n\t\t\t\t]\n\t\t\t\texport default worker;", "export type Awaitable<T> = T | Promise<T>;\n// TODO: allow dispatching more events?\nexport type Dispatcher = (\n\ttype: \"scheduled\",\n\tinit: { cron?: string }\n) => Awaitable<void>;\n\nexport type IncomingRequest = Request<\n\tunknown,\n\tIncomingRequestCfProperties<unknown>\n>;\n\nexport interface MiddlewareContext {\n\tdispatch: Dispatcher;\n\tnext(request: IncomingRequest, env: any): Awaitable<Response>;\n}\n\nexport type Middleware = (\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tmiddlewareCtx: MiddlewareContext\n) => Awaitable<Response>;\n\nconst __facade_middleware__: Middleware[] = [];\n\n// The register functions allow for the insertion of one or many middleware,\n// We register internal middleware first in the stack, but have no way of controlling\n// the order that addMiddleware is run in service workers so need an internal function.\nexport function __facade_register__(...args: (Middleware | Middleware[])[]) {\n\t__facade_middleware__.push(...args.flat());\n}\nexport function __facade_registerInternal__(\n\t...args: (Middleware | Middleware[])[]\n) {\n\t__facade_middleware__.unshift(...args.flat());\n}\n\nfunction __facade_invokeChain__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tmiddlewareChain: Middleware[]\n): Awaitable<Response> {\n\tconst [head, ...tail] = middlewareChain;\n\tconst middlewareCtx: MiddlewareContext = {\n\t\tdispatch,\n\t\tnext(newRequest, newEnv) {\n\t\t\treturn __facade_invokeChain__(newRequest, newEnv, ctx, dispatch, tail);\n\t\t},\n\t};\n\treturn head(request, env, ctx, middlewareCtx);\n}\n\nexport function __facade_invoke__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tfinalMiddleware: Middleware\n): Awaitable<Response> {\n\treturn __facade_invokeChain__(request, env, ctx, dispatch, [\n\t\t...__facade_middleware__,\n\t\tfinalMiddleware,\n\t]);\n}\n", "// This loads all middlewares exposed on the middleware object and then starts\n// the invocation chain. The big idea is that we can add these to the middleware\n// export dynamically through wrangler, or we can potentially let users directly\n// add them as a sort of \"plugin\" system.\n\nimport ENTRY, { __INTERNAL_WRANGLER_MIDDLEWARE__ } from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\nimport { __facade_invoke__, __facade_register__, Dispatcher } from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/common.ts\";\nimport type { WorkerEntrypointConstructor } from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\n\n// Preserve all the exports from the worker\nexport * from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\n\nclass __Facade_ScheduledController__ implements ScheduledController {\n\treadonly #noRetry: ScheduledController[\"noRetry\"];\n\n\tconstructor(\n\t\treadonly scheduledTime: number,\n\t\treadonly cron: string,\n\t\tnoRetry: ScheduledController[\"noRetry\"]\n\t) {\n\t\tthis.#noRetry = noRetry;\n\t}\n\n\tnoRetry() {\n\t\tif (!(this instanceof __Facade_ScheduledController__)) {\n\t\t\tthrow new TypeError(\"Illegal invocation\");\n\t\t}\n\t\t// Need to call native method immediately in case uncaught error thrown\n\t\tthis.#noRetry();\n\t}\n}\n\nfunction wrapExportedHandler(worker: ExportedHandler): ExportedHandler {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn worker;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\tconst fetchDispatcher: ExportedHandlerFetchHandler = function (\n\t\trequest,\n\t\tenv,\n\t\tctx\n\t) {\n\t\tif (worker.fetch === undefined) {\n\t\t\tthrow new Error(\"Handler does not export a fetch() function.\");\n\t\t}\n\t\treturn worker.fetch(request, env, ctx);\n\t};\n\n\treturn {\n\t\t...worker,\n\t\tfetch(request, env, ctx) {\n\t\t\tconst dispatcher: Dispatcher = function (type, init) {\n\t\t\t\tif (type === \"scheduled\" && worker.scheduled !== undefined) {\n\t\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t\t() => {}\n\t\t\t\t\t);\n\t\t\t\t\treturn worker.scheduled(controller, env, ctx);\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn __facade_invoke__(request, env, ctx, dispatcher, fetchDispatcher);\n\t\t},\n\t};\n}\n\nfunction wrapWorkerEntrypoint(\n\tklass: WorkerEntrypointConstructor\n): WorkerEntrypointConstructor {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn klass;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\t// `extend`ing `klass` here so other RPC methods remain callable\n\treturn class extends klass {\n\t\t#fetchDispatcher: ExportedHandlerFetchHandler<Record<string, unknown>> = (\n\t\t\trequest,\n\t\t\tenv,\n\t\t\tctx\n\t\t) => {\n\t\t\tthis.env = env;\n\t\t\tthis.ctx = ctx;\n\t\t\tif (super.fetch === undefined) {\n\t\t\t\tthrow new Error(\"Entrypoint class does not define a fetch() function.\");\n\t\t\t}\n\t\t\treturn super.fetch(request);\n\t\t};\n\n\t\t#dispatcher: Dispatcher = (type, init) => {\n\t\t\tif (type === \"scheduled\" && super.scheduled !== undefined) {\n\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\tDate.now(),\n\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t() => {}\n\t\t\t\t);\n\t\t\t\treturn super.scheduled(controller);\n\t\t\t}\n\t\t};\n\n\t\tfetch(request: Request<unknown, IncomingRequestCfProperties>) {\n\t\t\treturn __facade_invoke__(\n\t\t\t\trequest,\n\t\t\t\tthis.env,\n\t\t\t\tthis.ctx,\n\t\t\t\tthis.#dispatcher,\n\t\t\t\tthis.#fetchDispatcher\n\t\t\t);\n\t\t}\n\t};\n}\n\nlet WRAPPED_ENTRY: ExportedHandler | WorkerEntrypointConstructor | undefined;\nif (typeof ENTRY === \"object\") {\n\tWRAPPED_ENTRY = wrapExportedHandler(ENTRY);\n} else if (typeof ENTRY === \"function\") {\n\tWRAPPED_ENTRY = wrapWorkerEntrypoint(ENTRY);\n}\nexport default WRAPPED_ENTRY;\n"], 6 - "mappings": "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", 5 + "sourcesContent": ["const urls = new Set();\n\nfunction checkURL(request, init) {\n\tconst url =\n\t\trequest instanceof URL\n\t\t\t? request\n\t\t\t: new URL(\n\t\t\t\t\t(typeof request === \"string\"\n\t\t\t\t\t\t? new Request(request, init)\n\t\t\t\t\t\t: request\n\t\t\t\t\t).url\n\t\t\t\t);\n\tif (url.port && url.port !== \"443\" && url.protocol === \"https:\") {\n\t\tif (!urls.has(url.toString())) {\n\t\t\turls.add(url.toString());\n\t\t\tconsole.warn(\n\t\t\t\t`WARNING: known issue with \\`fetch()\\` requests to custom HTTPS ports in published Workers:\\n` +\n\t\t\t\t\t` - ${url.toString()} - the custom port will be ignored when the Worker is published using the \\`wrangler deploy\\` command.\\n`\n\t\t\t);\n\t\t}\n\t}\n}\n\nglobalThis.fetch = new Proxy(globalThis.fetch, {\n\tapply(target, thisArg, argArray) {\n\t\tconst [request, init] = argArray;\n\t\tcheckURL(request, init);\n\t\treturn Reflect.apply(target, thisArg, argArray);\n\t},\n});\n", "// === CBOR ENCODING ===\n// Minimal deterministic CBOR (RFC 8949) - sorted keys, minimal integers\n\nfunction cborEncode(value) {\n const parts = []\n\n function encode(val) {\n if (val === null) {\n parts.push(0xf6) // null\n } else if (val === true) {\n parts.push(0xf5) // true\n } else if (val === false) {\n parts.push(0xf4) // false\n } else if (typeof val === 'number') {\n encodeInteger(val)\n } else if (typeof val === 'string') {\n const bytes = new TextEncoder().encode(val)\n encodeHead(3, bytes.length) // major type 3 = text string\n parts.push(...bytes)\n } else if (val instanceof Uint8Array) {\n encodeHead(2, val.length) // major type 2 = byte string\n parts.push(...val)\n } else if (Array.isArray(val)) {\n encodeHead(4, val.length) // major type 4 = array\n for (const item of val) encode(item)\n } else if (typeof val === 'object') {\n // Sort keys for deterministic encoding\n const keys = Object.keys(val).sort()\n encodeHead(5, keys.length) // major type 5 = map\n for (const key of keys) {\n encode(key)\n encode(val[key])\n }\n }\n }\n\n function encodeHead(majorType, length) {\n const mt = majorType << 5\n if (length < 24) {\n parts.push(mt | length)\n } else if (length < 256) {\n parts.push(mt | 24, length)\n } else if (length < 65536) {\n parts.push(mt | 25, length >> 8, length & 0xff)\n } else if (length < 4294967296) {\n parts.push(mt | 26, (length >> 24) & 0xff, (length >> 16) & 0xff, (length >> 8) & 0xff, length & 0xff)\n }\n }\n\n function encodeInteger(n) {\n if (n >= 0) {\n encodeHead(0, n) // major type 0 = unsigned int\n } else {\n encodeHead(1, -n - 1) // major type 1 = negative int\n }\n }\n\n encode(value)\n return new Uint8Array(parts)\n}\n\n// === CID GENERATION ===\n// dag-cbor (0x71) + sha-256 (0x12) + 32 bytes\n\nasync function createCid(bytes) {\n const hash = await crypto.subtle.digest('SHA-256', bytes)\n const hashBytes = new Uint8Array(hash)\n\n // CIDv1: version(1) + codec(dag-cbor=0x71) + multihash(sha256)\n // Multihash: hash-type(0x12) + length(0x20=32) + digest\n const cid = new Uint8Array(2 + 2 + 32)\n cid[0] = 0x01 // CIDv1\n cid[1] = 0x71 // dag-cbor codec\n cid[2] = 0x12 // sha-256\n cid[3] = 0x20 // 32 bytes\n cid.set(hashBytes, 4)\n\n return cid\n}\n\nfunction cidToString(cid) {\n // base32lower encoding for CIDv1\n return 'b' + base32Encode(cid)\n}\n\nfunction base32Encode(bytes) {\n const alphabet = 'abcdefghijklmnopqrstuvwxyz234567'\n let result = ''\n let bits = 0\n let value = 0\n\n for (const byte of bytes) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n bits -= 5\n result += alphabet[(value >> bits) & 31]\n }\n }\n\n if (bits > 0) {\n result += alphabet[(value << (5 - bits)) & 31]\n }\n\n return result\n}\n\nexport class PersonalDataServer {\n constructor(state, env) {\n this.state = state\n this.sql = state.storage.sql\n }\n\n async fetch(request) {\n const url = new URL(request.url)\n if (url.pathname === '/test/cbor') {\n const encoded = cborEncode({ hello: 'world', num: 42 })\n return new Response(encoded, {\n headers: { 'content-type': 'application/cbor' }\n })\n }\n if (url.pathname === '/test/cid') {\n const data = cborEncode({ test: 'data' })\n const cid = await createCid(data)\n return Response.json({ cid: cidToString(cid) })\n }\n return new Response('pds running', { status: 200 })\n }\n}\n\nexport default {\n async fetch(request, env) {\n const url = new URL(request.url)\n const did = url.searchParams.get('did')\n\n if (!did) {\n return new Response('missing did param', { status: 400 })\n }\n\n const id = env.PDS.idFromName(did)\n const pds = env.PDS.get(id)\n return pds.fetch(request)\n }\n}\n\n// Export utilities for testing\nexport { cborEncode, createCid, cidToString, base32Encode }\n", "import type { Middleware } from \"./common\";\n\nconst drainBody: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} finally {\n\t\ttry {\n\t\t\tif (request.body !== null && !request.bodyUsed) {\n\t\t\t\tconst reader = request.body.getReader();\n\t\t\t\twhile (!(await reader.read()).done) {}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(\"Failed to drain the unused request body.\", e);\n\t\t}\n\t}\n};\n\nexport default drainBody;\n", "import type { Middleware } from \"./common\";\n\ninterface JsonError {\n\tmessage?: string;\n\tname?: string;\n\tstack?: string;\n\tcause?: JsonError;\n}\n\nfunction reduceError(e: any): JsonError {\n\treturn {\n\t\tname: e?.name,\n\t\tmessage: e?.message ?? String(e),\n\t\tstack: e?.stack,\n\t\tcause: e?.cause === undefined ? undefined : reduceError(e.cause),\n\t};\n}\n\n// See comment in `bundle.ts` for details on why this is needed\nconst jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} catch (e: any) {\n\t\tconst error = reduceError(e);\n\t\treturn Response.json(error, {\n\t\t\tstatus: 500,\n\t\t\theaders: { \"MF-Experimental-Error-Stack\": \"true\" },\n\t\t});\n\t}\n};\n\nexport default jsonError;\n", "\t\t\t\timport worker, * as OTHER_EXPORTS from \"/Users/chadmiller/code/pds-experiment/src/pds.js\";\n\t\t\t\timport * as __MIDDLEWARE_0__ from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts\";\nimport * as __MIDDLEWARE_1__ from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts\";\n\n\t\t\t\texport * from \"/Users/chadmiller/code/pds-experiment/src/pds.js\";\n\t\t\t\tconst MIDDLEWARE_TEST_INJECT = \"__INJECT_FOR_TESTING_WRANGLER_MIDDLEWARE__\";\n\t\t\t\texport const __INTERNAL_WRANGLER_MIDDLEWARE__ = [\n\t\t\t\t\t\n\t\t\t\t\t__MIDDLEWARE_0__.default,__MIDDLEWARE_1__.default\n\t\t\t\t]\n\t\t\t\texport default worker;", "export type Awaitable<T> = T | Promise<T>;\n// TODO: allow dispatching more events?\nexport type Dispatcher = (\n\ttype: \"scheduled\",\n\tinit: { cron?: string }\n) => Awaitable<void>;\n\nexport type IncomingRequest = Request<\n\tunknown,\n\tIncomingRequestCfProperties<unknown>\n>;\n\nexport interface MiddlewareContext {\n\tdispatch: Dispatcher;\n\tnext(request: IncomingRequest, env: any): Awaitable<Response>;\n}\n\nexport type Middleware = (\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tmiddlewareCtx: MiddlewareContext\n) => Awaitable<Response>;\n\nconst __facade_middleware__: Middleware[] = [];\n\n// The register functions allow for the insertion of one or many middleware,\n// We register internal middleware first in the stack, but have no way of controlling\n// the order that addMiddleware is run in service workers so need an internal function.\nexport function __facade_register__(...args: (Middleware | Middleware[])[]) {\n\t__facade_middleware__.push(...args.flat());\n}\nexport function __facade_registerInternal__(\n\t...args: (Middleware | Middleware[])[]\n) {\n\t__facade_middleware__.unshift(...args.flat());\n}\n\nfunction __facade_invokeChain__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tmiddlewareChain: Middleware[]\n): Awaitable<Response> {\n\tconst [head, ...tail] = middlewareChain;\n\tconst middlewareCtx: MiddlewareContext = {\n\t\tdispatch,\n\t\tnext(newRequest, newEnv) {\n\t\t\treturn __facade_invokeChain__(newRequest, newEnv, ctx, dispatch, tail);\n\t\t},\n\t};\n\treturn head(request, env, ctx, middlewareCtx);\n}\n\nexport function __facade_invoke__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tfinalMiddleware: Middleware\n): Awaitable<Response> {\n\treturn __facade_invokeChain__(request, env, ctx, dispatch, [\n\t\t...__facade_middleware__,\n\t\tfinalMiddleware,\n\t]);\n}\n", "// This loads all middlewares exposed on the middleware object and then starts\n// the invocation chain. The big idea is that we can add these to the middleware\n// export dynamically through wrangler, or we can potentially let users directly\n// add them as a sort of \"plugin\" system.\n\nimport ENTRY, { __INTERNAL_WRANGLER_MIDDLEWARE__ } from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\nimport { __facade_invoke__, __facade_register__, Dispatcher } from \"/Users/chadmiller/.npm/_npx/32026684e21afda6/node_modules/wrangler/templates/middleware/common.ts\";\nimport type { WorkerEntrypointConstructor } from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\n\n// Preserve all the exports from the worker\nexport * from \"/Users/chadmiller/code/pds-experiment/.wrangler/tmp/bundle-MaCAbF/middleware-insertion-facade.js\";\n\nclass __Facade_ScheduledController__ implements ScheduledController {\n\treadonly #noRetry: ScheduledController[\"noRetry\"];\n\n\tconstructor(\n\t\treadonly scheduledTime: number,\n\t\treadonly cron: string,\n\t\tnoRetry: ScheduledController[\"noRetry\"]\n\t) {\n\t\tthis.#noRetry = noRetry;\n\t}\n\n\tnoRetry() {\n\t\tif (!(this instanceof __Facade_ScheduledController__)) {\n\t\t\tthrow new TypeError(\"Illegal invocation\");\n\t\t}\n\t\t// Need to call native method immediately in case uncaught error thrown\n\t\tthis.#noRetry();\n\t}\n}\n\nfunction wrapExportedHandler(worker: ExportedHandler): ExportedHandler {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn worker;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\tconst fetchDispatcher: ExportedHandlerFetchHandler = function (\n\t\trequest,\n\t\tenv,\n\t\tctx\n\t) {\n\t\tif (worker.fetch === undefined) {\n\t\t\tthrow new Error(\"Handler does not export a fetch() function.\");\n\t\t}\n\t\treturn worker.fetch(request, env, ctx);\n\t};\n\n\treturn {\n\t\t...worker,\n\t\tfetch(request, env, ctx) {\n\t\t\tconst dispatcher: Dispatcher = function (type, init) {\n\t\t\t\tif (type === \"scheduled\" && worker.scheduled !== undefined) {\n\t\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t\t() => {}\n\t\t\t\t\t);\n\t\t\t\t\treturn worker.scheduled(controller, env, ctx);\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn __facade_invoke__(request, env, ctx, dispatcher, fetchDispatcher);\n\t\t},\n\t};\n}\n\nfunction wrapWorkerEntrypoint(\n\tklass: WorkerEntrypointConstructor\n): WorkerEntrypointConstructor {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn klass;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\t// `extend`ing `klass` here so other RPC methods remain callable\n\treturn class extends klass {\n\t\t#fetchDispatcher: ExportedHandlerFetchHandler<Record<string, unknown>> = (\n\t\t\trequest,\n\t\t\tenv,\n\t\t\tctx\n\t\t) => {\n\t\t\tthis.env = env;\n\t\t\tthis.ctx = ctx;\n\t\t\tif (super.fetch === undefined) {\n\t\t\t\tthrow new Error(\"Entrypoint class does not define a fetch() function.\");\n\t\t\t}\n\t\t\treturn super.fetch(request);\n\t\t};\n\n\t\t#dispatcher: Dispatcher = (type, init) => {\n\t\t\tif (type === \"scheduled\" && super.scheduled !== undefined) {\n\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\tDate.now(),\n\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t() => {}\n\t\t\t\t);\n\t\t\t\treturn super.scheduled(controller);\n\t\t\t}\n\t\t};\n\n\t\tfetch(request: Request<unknown, IncomingRequestCfProperties>) {\n\t\t\treturn __facade_invoke__(\n\t\t\t\trequest,\n\t\t\t\tthis.env,\n\t\t\t\tthis.ctx,\n\t\t\t\tthis.#dispatcher,\n\t\t\t\tthis.#fetchDispatcher\n\t\t\t);\n\t\t}\n\t};\n}\n\nlet WRAPPED_ENTRY: ExportedHandler | WorkerEntrypointConstructor | undefined;\nif (typeof ENTRY === \"object\") {\n\tWRAPPED_ENTRY = wrapExportedHandler(ENTRY);\n} else if (typeof ENTRY === \"function\") {\n\tWRAPPED_ENTRY = wrapWorkerEntrypoint(ENTRY);\n}\nexport default WRAPPED_ENTRY;\n"], 6 + "mappings": "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", 7 7 "names": [] 8 8 }
+2 -1
package.json
··· 2 2 "name": "cloudflare-pds", 3 3 "version": "0.1.0", 4 4 "private": true, 5 + "type": "module", 5 6 "scripts": { 6 7 "dev": "wrangler dev", 7 8 "deploy": "wrangler deploy", 8 - "test": "node test/run.js" 9 + "test": "node --test test/*.test.js" 9 10 } 10 11 }
+54
src/pds.js
··· 59 59 return new Uint8Array(parts) 60 60 } 61 61 62 + // === CID GENERATION === 63 + // dag-cbor (0x71) + sha-256 (0x12) + 32 bytes 64 + 65 + async function createCid(bytes) { 66 + const hash = await crypto.subtle.digest('SHA-256', bytes) 67 + const hashBytes = new Uint8Array(hash) 68 + 69 + // CIDv1: version(1) + codec(dag-cbor=0x71) + multihash(sha256) 70 + // Multihash: hash-type(0x12) + length(0x20=32) + digest 71 + const cid = new Uint8Array(2 + 2 + 32) 72 + cid[0] = 0x01 // CIDv1 73 + cid[1] = 0x71 // dag-cbor codec 74 + cid[2] = 0x12 // sha-256 75 + cid[3] = 0x20 // 32 bytes 76 + cid.set(hashBytes, 4) 77 + 78 + return cid 79 + } 80 + 81 + function cidToString(cid) { 82 + // base32lower encoding for CIDv1 83 + return 'b' + base32Encode(cid) 84 + } 85 + 86 + function base32Encode(bytes) { 87 + const alphabet = 'abcdefghijklmnopqrstuvwxyz234567' 88 + let result = '' 89 + let bits = 0 90 + let value = 0 91 + 92 + for (const byte of bytes) { 93 + value = (value << 8) | byte 94 + bits += 8 95 + while (bits >= 5) { 96 + bits -= 5 97 + result += alphabet[(value >> bits) & 31] 98 + } 99 + } 100 + 101 + if (bits > 0) { 102 + result += alphabet[(value << (5 - bits)) & 31] 103 + } 104 + 105 + return result 106 + } 107 + 62 108 export class PersonalDataServer { 63 109 constructor(state, env) { 64 110 this.state = state ··· 73 119 headers: { 'content-type': 'application/cbor' } 74 120 }) 75 121 } 122 + if (url.pathname === '/test/cid') { 123 + const data = cborEncode({ test: 'data' }) 124 + const cid = await createCid(data) 125 + return Response.json({ cid: cidToString(cid) }) 126 + } 76 127 return new Response('pds running', { status: 200 }) 77 128 } 78 129 } ··· 91 142 return pds.fetch(request) 92 143 } 93 144 } 145 + 146 + // Export utilities for testing 147 + export { cborEncode, createCid, cidToString, base32Encode }
+115
test/pds.test.js
··· 1 + import { test, describe } from 'node:test' 2 + import assert from 'node:assert' 3 + import { cborEncode, createCid, cidToString, base32Encode } from '../src/pds.js' 4 + 5 + describe('CBOR Encoding', () => { 6 + test('encodes simple map', () => { 7 + const encoded = cborEncode({ hello: 'world', num: 42 }) 8 + // Expected: a2 65 68 65 6c 6c 6f 65 77 6f 72 6c 64 63 6e 75 6d 18 2a 9 + const expected = new Uint8Array([ 10 + 0xa2, 0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x65, 0x77, 0x6f, 0x72, 0x6c, 0x64, 11 + 0x63, 0x6e, 0x75, 0x6d, 0x18, 0x2a 12 + ]) 13 + assert.deepStrictEqual(encoded, expected) 14 + }) 15 + 16 + test('encodes null', () => { 17 + const encoded = cborEncode(null) 18 + assert.deepStrictEqual(encoded, new Uint8Array([0xf6])) 19 + }) 20 + 21 + test('encodes booleans', () => { 22 + assert.deepStrictEqual(cborEncode(true), new Uint8Array([0xf5])) 23 + assert.deepStrictEqual(cborEncode(false), new Uint8Array([0xf4])) 24 + }) 25 + 26 + test('encodes small integers', () => { 27 + assert.deepStrictEqual(cborEncode(0), new Uint8Array([0x00])) 28 + assert.deepStrictEqual(cborEncode(1), new Uint8Array([0x01])) 29 + assert.deepStrictEqual(cborEncode(23), new Uint8Array([0x17])) 30 + }) 31 + 32 + test('encodes integers >= 24', () => { 33 + assert.deepStrictEqual(cborEncode(24), new Uint8Array([0x18, 0x18])) 34 + assert.deepStrictEqual(cborEncode(255), new Uint8Array([0x18, 0xff])) 35 + }) 36 + 37 + test('encodes negative integers', () => { 38 + assert.deepStrictEqual(cborEncode(-1), new Uint8Array([0x20])) 39 + assert.deepStrictEqual(cborEncode(-10), new Uint8Array([0x29])) 40 + }) 41 + 42 + test('encodes strings', () => { 43 + const encoded = cborEncode('hello') 44 + // 0x65 = text string of length 5 45 + assert.deepStrictEqual(encoded, new Uint8Array([0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f])) 46 + }) 47 + 48 + test('encodes byte strings', () => { 49 + const bytes = new Uint8Array([1, 2, 3]) 50 + const encoded = cborEncode(bytes) 51 + // 0x43 = byte string of length 3 52 + assert.deepStrictEqual(encoded, new Uint8Array([0x43, 1, 2, 3])) 53 + }) 54 + 55 + test('encodes arrays', () => { 56 + const encoded = cborEncode([1, 2, 3]) 57 + // 0x83 = array of length 3 58 + assert.deepStrictEqual(encoded, new Uint8Array([0x83, 0x01, 0x02, 0x03])) 59 + }) 60 + 61 + test('sorts map keys deterministically', () => { 62 + const encoded1 = cborEncode({ z: 1, a: 2 }) 63 + const encoded2 = cborEncode({ a: 2, z: 1 }) 64 + assert.deepStrictEqual(encoded1, encoded2) 65 + // First key should be 'a' (0x61) 66 + assert.strictEqual(encoded1[1], 0x61) 67 + }) 68 + }) 69 + 70 + describe('Base32 Encoding', () => { 71 + test('encodes bytes to base32lower', () => { 72 + const bytes = new Uint8Array([0x01, 0x71, 0x12, 0x20]) 73 + const encoded = base32Encode(bytes) 74 + assert.strictEqual(typeof encoded, 'string') 75 + assert.match(encoded, /^[a-z2-7]+$/) 76 + }) 77 + }) 78 + 79 + describe('CID Generation', () => { 80 + test('creates CIDv1 with dag-cbor codec', async () => { 81 + const data = cborEncode({ test: 'data' }) 82 + const cid = await createCid(data) 83 + 84 + assert.strictEqual(cid.length, 36) // 2 prefix + 2 multihash header + 32 hash 85 + assert.strictEqual(cid[0], 0x01) // CIDv1 86 + assert.strictEqual(cid[1], 0x71) // dag-cbor 87 + assert.strictEqual(cid[2], 0x12) // sha-256 88 + assert.strictEqual(cid[3], 0x20) // 32 bytes 89 + }) 90 + 91 + test('cidToString returns base32lower with b prefix', async () => { 92 + const data = cborEncode({ test: 'data' }) 93 + const cid = await createCid(data) 94 + const cidStr = cidToString(cid) 95 + 96 + assert.strictEqual(cidStr[0], 'b') 97 + assert.match(cidStr, /^b[a-z2-7]+$/) 98 + }) 99 + 100 + test('same input produces same CID', async () => { 101 + const data1 = cborEncode({ test: 'data' }) 102 + const data2 = cborEncode({ test: 'data' }) 103 + const cid1 = cidToString(await createCid(data1)) 104 + const cid2 = cidToString(await createCid(data2)) 105 + 106 + assert.strictEqual(cid1, cid2) 107 + }) 108 + 109 + test('different input produces different CID', async () => { 110 + const cid1 = cidToString(await createCid(cborEncode({ a: 1 }))) 111 + const cid2 = cidToString(await createCid(cborEncode({ a: 2 }))) 112 + 113 + assert.notStrictEqual(cid1, cid2) 114 + }) 115 + })