Mirror of https://github.com/improsocial/impro An extensible Bluesky client for web impro.social
6

Configure Feed

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

Revert simple handle resolution

Grace Kind (Jul 16, 2026, 4:56 PM -0500) 1792f32d 6964d365

+13 -35
+2 -7
src/js/atproto.js
··· 18 18 return aliases.includes(atHandle); 19 19 } 20 20 21 - async function resolveHandleUnverified(handle) { 21 + export async function resolveHandle(handle) { 22 22 const params = new URLSearchParams({ 23 23 handle, 24 24 }); ··· 46 46 } 47 47 48 48 export async function resolveIdentity(handle) { 49 - const did = await resolveHandleUnverified(handle); 49 + const did = await resolveHandle(handle); 50 50 if (!did) return null; 51 51 const didDoc = await resolveDid(did); 52 52 if (!didDocReferencesHandle(didDoc, handle)) { 53 53 throw new Error(`DID doc for ${did} does not reference handle: ${handle}`); 54 54 } 55 55 return { did, didDoc }; 56 - } 57 - 58 - export async function resolveHandle(handle) { 59 - const result = await resolveIdentity(handle); 60 - return result?.did ?? null; 61 56 } 62 57 63 58 export async function getServiceEndpointForHandle(handle) {
+11 -28
tests/unit/specs/atproto.test.js
··· 72 72 }); 73 73 74 74 describe("resolveHandle", () => { 75 - it("returns the verified did", async () => { 75 + it("returns the did from the handle resolver without fetching the DID doc", async () => { 76 76 const did = "did:plc:aaaa"; 77 77 stubDid(did); 78 - stubPlcDoc(did, { 79 - alsoKnownAs: ["at://alice.example"], 80 - service: [], 81 - }); 82 78 assert.deepEqual(await resolveHandle("alice.example"), did); 79 + assert( 80 + !globalThis.fetch.calls.some((call) => 81 + call.url.startsWith("https://plc.directory/"), 82 + ), 83 + "resolveHandle should not hit plc.directory", 84 + ); 83 85 }); 84 86 85 - it("throws on verification failure — callers cannot silently use a spoofed mapping", async () => { 86 - stubDid("did:plc:aaaa"); 87 - stubPlcDoc("did:plc:aaaa", { 88 - alsoKnownAs: ["at://mallory.example"], 89 - service: [], 90 - }); 91 - await assert.rejects(() => resolveHandle("alice.example")); 87 + it("returns null when the handle does not resolve", async () => { 88 + stubDid(null); 89 + assert.deepEqual(await resolveHandle("nope.example"), null); 92 90 }); 93 91 }); 94 92 ··· 111 109 }); 112 110 113 111 describe("IdentityResolver.resolveHandle", () => { 114 - it("caches the verified DID and does not re-verify", async () => { 112 + it("caches the resolved DID", async () => { 115 113 const did = "did:plc:aaaa"; 116 114 stubDid(did); 117 - stubPlcDoc(did, { 118 - alsoKnownAs: ["at://alice.example"], 119 - service: [], 120 - }); 121 115 const resolver = new IdentityResolver(); 122 116 const first = await resolver.resolveHandle("alice.example"); 123 117 const callsAfterFirst = globalThis.fetch.calls.length; ··· 125 119 assert.deepEqual(first, did); 126 120 assert.deepEqual(second, did); 127 121 assert.deepEqual(globalThis.fetch.calls.length, callsAfterFirst); 128 - }); 129 - 130 - it("does not cache when verification fails, so retries are possible", async () => { 131 - stubDid("did:plc:aaaa"); 132 - stubPlcDoc("did:plc:aaaa", { 133 - alsoKnownAs: ["at://someone-else.example"], 134 - service: [], 135 - }); 136 - const resolver = new IdentityResolver(); 137 - await assert.rejects(() => resolver.resolveHandle("alice.example")); 138 - assert(!resolver.handleToDidMap.has("alice.example")); 139 122 }); 140 123 }); 141 124 });