diff --git a/xrpc/deno.json b/xrpc/deno.json index 8ab09e8..9448832 100644 --- a/xrpc/deno.json +++ b/xrpc/deno.json @@ -1,6 +1,6 @@ { "name": "@atp/xrpc", - "version": "0.1.0-alpha.7", + "version": "0.1.0-alpha.8", "exports": "./mod.ts", "license": "MIT", "imports": { diff --git a/xrpc/tests/client_test.ts b/xrpc/tests/client_test.ts index 2952149..50d43dc 100644 --- a/xrpc/tests/client_test.ts +++ b/xrpc/tests/client_test.ts @@ -146,6 +146,44 @@ Deno.test("keeps call as a compatibility alias for xrpc", async () => { assertEquals(result.data, { value: "ok" }); }); +Deno.test("validates blob refs parsed from JSON responses", async () => { + const cid = "bafkreidgvpkjawlxzizq3pzkupkkm4o5mdujl6fpvl5twvyevezdsz3zua"; + const method = l.procedure( + "io.example.uploadBlob", + l.params(), + l.payload("application/octet-stream"), + l.jsonPayload({ blob: l.blob() }), + ); + + const client = new Client((url, init) => { + assertEquals(url, "/xrpc/io.example.uploadBlob"); + assertEquals(init.method, "post"); + assertEquals( + new Headers(init.headers).get("content-type"), + "application/octet-stream", + ); + return Promise.resolve( + Response.json({ + blob: { + $type: "blob", + ref: { $link: cid }, + mimeType: "video/mp4", + size: 1234, + }, + }), + ); + }); + + const result = await client.call(method, { + body: new Uint8Array([1, 2, 3]), + }); + + assertEquals(result.data.blob.$type, "blob"); + assertEquals(result.data.blob.ref.toString(), cid); + assertEquals(result.data.blob.mimeType, "video/mp4"); + assertEquals(result.data.blob.size, 1234); +}); + Deno.test("serializes params using schema transforms", async () => { const method = l.query( "io.example.query", diff --git a/xrpc/util.ts b/xrpc/util.ts index 21a1f9c..b05ac74 100644 --- a/xrpc/util.ts +++ b/xrpc/util.ts @@ -1,9 +1,5 @@ -import { - jsonStringToLex, - type LexXrpcProcedure, - type LexXrpcQuery, - stringifyLex, -} from "@atp/lexicon"; +import type { LexXrpcProcedure, LexXrpcQuery } from "@atp/lexicon"; +import { lexParse, lexStringify } from "@atp/lex/json"; import { type CallOptions, type ErrorResponseBody, @@ -301,7 +297,7 @@ export function encodeMethodCallBody( return new TextEncoder().encode(String(data)); } if (contentType.startsWith("application/json")) { - const json = stringifyLex(data); + const json = lexStringify(data); // Server would return a 400 error if the JSON is invalid (e.g. trying to // JSONify a function, or an object that implements toJSON() poorly). if (json === undefined) { @@ -361,7 +357,7 @@ export function httpResponseBodyParse( if (mimeType) { if (mimeType.includes("application/json")) { const str = new TextDecoder().decode(data); - return jsonStringToLex(str); + return lexParse(str); } if (mimeType.startsWith("text/")) { return new TextDecoder().decode(data);