GET /xrpc/app.bsky.actor.searchActorsTypeahead typeahead.waow.tech
16
fork

Configure Feed

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

fix schema drift, unicode search, and error semantics

- schema.sql: avatar_cid → avatar_url to match production
- sanitize(): use unicode-aware \p{L}\p{N} instead of ASCII-only \w
- return 400 for empty query and limit>100 to match Bluesky's behavior

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

+11 -4
+1 -1
schema.sql
··· 2 2 did TEXT PRIMARY KEY, 3 3 handle TEXT NOT NULL, 4 4 display_name TEXT DEFAULT '', 5 - avatar_cid TEXT DEFAULT '', 5 + avatar_url TEXT DEFAULT '', 6 6 updated_at INTEGER NOT NULL DEFAULT (unixepoch()) 7 7 ); 8 8
+10 -3
src/index.ts
··· 27 27 return Response.json(data, { status, headers: CORS_HEADERS }); 28 28 } 29 29 30 - /** strip anything that could break FTS5 syntax */ 30 + /** strip anything that could break FTS5 syntax, preserving unicode letters/digits */ 31 31 function sanitize(q: string): string { 32 - return q.replace(/[^\w\s.-]/g, "").trim(); 32 + return q.replace(/[^\p{L}\p{N}\s.-]/gu, "").trim(); 33 33 } 34 34 35 35 interface ActorRow { ··· 123 123 const url = new URL(request.url); 124 124 const q = url.searchParams.get("q") || url.searchParams.get("term") || ""; 125 125 const limitParam = parseInt(url.searchParams.get("limit") || "10", 10); 126 - const limit = Math.max(1, Math.min(limitParam || 10, 100)); 126 + 127 + if (!q.trim()) { 128 + return json({ error: "InvalidRequest", message: "Error: Params must have the property \"q\"" }, 400); 129 + } 130 + if (limitParam > 100) { 131 + return json({ error: "InvalidRequest", message: "Error: limit must be <= 100" }, 400); 132 + } 127 133 134 + const limit = Math.max(1, limitParam || 10); 128 135 const term = sanitize(q); 129 136 if (!term) { 130 137 return json({ actors: [] });