ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto

fix extension import: use bulkCreate and handle uploadId param

Backend fixes:
- Use SourceAccountRepository.bulkCreate() instead of non-existent upsertSourceAccount()
- Change redirectUrl from /results?uploadId= to /?uploadId=
- More efficient bulk insert instead of loop

Frontend fixes:
- Add useEffect to load results when uploadId param present
- Calls loadUploadResults(uploadId) automatically on page load
- Cleans up URL param after loading

Resolves:
- "sourceAccountRepo.upsertSourceAccount is not a function" error
- "No routes matched location /results?uploadId=..." routing error

byarielm.fyi 581ed00f 9ca73474

verified
Changed files
+159 -31
docs
packages
functions
web
src
+20 -12
docs/git-history.json
··· 1 1 [ 2 2 { 3 + "hash": "9ca734749fbaa014828f8437afc5e515610afd31", 4 + "short_hash": "9ca7347", 5 + "author": "Ariel M. Lighty", 6 + "date": "2025-12-26T13:37:24-05:00", 7 + "message": "update documentation: extension ready for testing after API response fix", 8 + "files_changed": 4 9 + }, 10 + { 3 11 "hash": "95636330f387598f55017eda668fb9f91ccde509", 4 12 "short_hash": "9563633", 5 13 "author": "Ariel M. Lighty", ··· 8 16 "files_changed": 2 9 17 }, 10 18 { 11 - "hash": "34bd9dcd1237971a87627b148c0452b8484e4871", 12 - "short_hash": "34bd9dc", 19 + "hash": "95636330f387598f55017eda668fb9f91ccde509", 20 + "short_hash": "9563633", 13 21 "author": "Ariel M. Lighty", 14 - "date": "2025-12-26T00:50:44-05:00", 15 - "message": "update documentation with current debugging status\n\nPLAN.md updates:\n- Added current status section with recent fixes and active work\n- Marked Phase 0 as complete\n- Marked Phase 1 as in progress (debugging)\n- Updated changelog with 2025-12-26 progress\n- Updated decision graph count to 288 nodes\n\nEXTENSION_STATUS.md updates:\n- Changed state from READY FOR TESTING to DEBUGGING\n- Added fixed issues section (NaN bug, database init)\n- Added active debugging section\n- Updated decision graph summary to 288 nodes\n- Added node references for recent fixes (#287-288)\n\nDecision graph:\n- Synced with latest nodes (288 total, 276 edges)\n- Tracked database initialization outcome", 16 - "files_changed": 4 22 + "date": "2025-12-26T13:35:52-05:00", 23 + "message": "fix extension api-client: unwrap ApiResponse.data structure\n\nBackend endpoints use successResponse() which wraps data in:\n { success: true, data: {...} }\n\nExtension was expecting flat response structure, causing:\n- uploadToATlast to return undefined (missing importId, redirectUrl)\n- checkSession to return wrapped object instead of user data\n- Invalid URL error: \"http://127.0.0.1:8888undefined\"\n\nFixed both uploadToATlast and checkSession to access apiResponse.data", 24 + "files_changed": 2 17 25 }, 18 26 { 19 - "hash": "a203bc343b1768cd51e08b51ed296eddb51d21b7", 20 - "short_hash": "a203bc3", 27 + "hash": "34bd9dcd1237971a87627b148c0452b8484e4871", 28 + "short_hash": "34bd9dc", 21 29 "author": "Ariel M. Lighty", 22 30 "date": "2025-12-26T00:50:44-05:00", 23 31 "message": "update documentation with current debugging status\n\nPLAN.md updates:\n- Added current status section with recent fixes and active work\n- Marked Phase 0 as complete\n- Marked Phase 1 as in progress (debugging)\n- Updated changelog with 2025-12-26 progress\n- Updated decision graph count to 288 nodes\n\nEXTENSION_STATUS.md updates:\n- Changed state from READY FOR TESTING to DEBUGGING\n- Added fixed issues section (NaN bug, database init)\n- Added active debugging section\n- Updated decision graph summary to 288 nodes\n- Added node references for recent fixes (#287-288)\n\nDecision graph:\n- Synced with latest nodes (288 total, 276 edges)\n- Tracked database initialization outcome", ··· 40 48 "files_changed": 12 41 49 }, 42 50 { 43 - "hash": "a8857408ab62183b99005fb7993d101319cfb929", 44 - "short_hash": "a885740", 51 + "hash": "69db377f7f6dd4e38677f2507ad660cdf1a66465", 52 + "short_hash": "69db377", 45 53 "author": "Ariel M. Lighty", 46 - "date": "2025-12-25T22:00:14-05:00", 47 - "message": "add host permissions for ATlast API servers\n\nFix CORS blocking extension health checks and API calls:\n- Add http://127.0.0.1:8888/* (dev)\n- Add http://localhost:8888/* (alt dev)\n- Add https://atlast.byarielm.fyi/* (prod)\n\nExtension can now make requests to ATlast servers without\nCORS errors.", 48 - "files_changed": 1 54 + "date": "2025-12-25T22:00:53-05:00", 55 + "message": "update decision graph", 56 + "files_changed": 2 49 57 }, 50 58 { 51 59 "hash": "c35fb0d83202607facc203dfe10325e8672ea67e",
+110
docs/graph-data.json
··· 3244 3244 "created_at": "2025-12-26T13:36:02.733197600-05:00", 3245 3245 "updated_at": "2025-12-26T13:36:02.733197600-05:00", 3246 3246 "metadata_json": "{\"branch\":\"master\",\"commit\":\"9563633\",\"confidence\":95}" 3247 + }, 3248 + { 3249 + "id": 296, 3250 + "change_id": "e2427bfe-84a1-4dee-adf4-28a9c1b739e2", 3251 + "node_type": "observation", 3252 + "title": "Extension upload flow fixed and ready for testing - API response unwrapping resolves undefined errors", 3253 + "description": null, 3254 + "status": "pending", 3255 + "created_at": "2025-12-26T13:37:35.844832-05:00", 3256 + "updated_at": "2025-12-26T13:37:35.844832-05:00", 3257 + "metadata_json": "{\"branch\":\"master\",\"commit\":\"9ca7347\",\"confidence\":95}" 3258 + }, 3259 + { 3260 + "id": 297, 3261 + "change_id": "74ea361f-577c-4058-b833-6666e777ee00", 3262 + "node_type": "goal", 3263 + "title": "Fix backend repository method error and missing frontend route", 3264 + "description": null, 3265 + "status": "pending", 3266 + "created_at": "2025-12-26T13:43:03.332690700-05:00", 3267 + "updated_at": "2025-12-26T13:43:03.332690700-05:00", 3268 + "metadata_json": "{\"branch\":\"master\",\"confidence\":90}" 3269 + }, 3270 + { 3271 + "id": 298, 3272 + "change_id": "c373be70-157a-420d-bc11-4364fe22d091", 3273 + "node_type": "observation", 3274 + "title": "Two issues: 1) SourceAccountRepository has getOrCreate/bulkCreate not upsertSourceAccount, 2) Router only has / route, no /results route", 3275 + "description": null, 3276 + "status": "pending", 3277 + "created_at": "2025-12-26T13:43:28.902663600-05:00", 3278 + "updated_at": "2025-12-26T13:43:28.902663600-05:00", 3279 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3280 + }, 3281 + { 3282 + "id": 299, 3283 + "change_id": "8edd7e11-54b4-4c5b-8379-37b1ec1e7d7d", 3284 + "node_type": "action", 3285 + "title": "Fix backend to use bulkCreate and frontend to handle uploadId param", 3286 + "description": null, 3287 + "status": "pending", 3288 + "created_at": "2025-12-26T13:44:28.406069900-05:00", 3289 + "updated_at": "2025-12-26T13:44:28.406069900-05:00", 3290 + "metadata_json": "{\"branch\":\"master\",\"confidence\":90}" 3291 + }, 3292 + { 3293 + "id": 300, 3294 + "change_id": "876412ec-a214-4bf7-b48a-b7706c698085", 3295 + "node_type": "outcome", 3296 + "title": "Fixed both issues: backend uses bulkCreate, redirects to /?uploadId, frontend loads results from uploadId param", 3297 + "description": null, 3298 + "status": "pending", 3299 + "created_at": "2025-12-26T13:45:58.309042200-05:00", 3300 + "updated_at": "2025-12-26T13:45:58.309042200-05:00", 3301 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 3247 3302 } 3248 3303 ], 3249 3304 "edges": [ ··· 6359 6414 "weight": 1.0, 6360 6415 "rationale": "Goal achieved", 6361 6416 "created_at": "2025-12-26T13:36:06.860603400-05:00" 6417 + }, 6418 + { 6419 + "id": 284, 6420 + "from_node_id": 295, 6421 + "to_node_id": 296, 6422 + "from_change_id": "ceaed4fe-5fd0-4542-8f3a-bd4640dfaadf", 6423 + "to_change_id": "e2427bfe-84a1-4dee-adf4-28a9c1b739e2", 6424 + "edge_type": "leads_to", 6425 + "weight": 1.0, 6426 + "rationale": "Documentation updated", 6427 + "created_at": "2025-12-26T13:37:37.858859900-05:00" 6428 + }, 6429 + { 6430 + "id": 285, 6431 + "from_node_id": 296, 6432 + "to_node_id": 297, 6433 + "from_change_id": "e2427bfe-84a1-4dee-adf4-28a9c1b739e2", 6434 + "to_change_id": "74ea361f-577c-4058-b833-6666e777ee00", 6435 + "edge_type": "leads_to", 6436 + "weight": 1.0, 6437 + "rationale": "New issues found during testing", 6438 + "created_at": "2025-12-26T13:43:05.419406600-05:00" 6439 + }, 6440 + { 6441 + "id": 286, 6442 + "from_node_id": 297, 6443 + "to_node_id": 298, 6444 + "from_change_id": "74ea361f-577c-4058-b833-6666e777ee00", 6445 + "to_change_id": "c373be70-157a-420d-bc11-4364fe22d091", 6446 + "edge_type": "leads_to", 6447 + "weight": 1.0, 6448 + "rationale": "Root cause analysis", 6449 + "created_at": "2025-12-26T13:43:31.032670900-05:00" 6450 + }, 6451 + { 6452 + "id": 287, 6453 + "from_node_id": 298, 6454 + "to_node_id": 299, 6455 + "from_change_id": "c373be70-157a-420d-bc11-4364fe22d091", 6456 + "to_change_id": "8edd7e11-54b4-4c5b-8379-37b1ec1e7d7d", 6457 + "edge_type": "leads_to", 6458 + "weight": 1.0, 6459 + "rationale": "Action to fix both issues", 6460 + "created_at": "2025-12-26T13:44:30.495551600-05:00" 6461 + }, 6462 + { 6463 + "id": 288, 6464 + "from_node_id": 299, 6465 + "to_node_id": 300, 6466 + "from_change_id": "8edd7e11-54b4-4c5b-8379-37b1ec1e7d7d", 6467 + "to_change_id": "876412ec-a214-4bf7-b48a-b7706c698085", 6468 + "edge_type": "leads_to", 6469 + "weight": 1.0, 6470 + "rationale": "Implementation complete", 6471 + "created_at": "2025-12-26T13:46:00.313229300-05:00" 6362 6472 } 6363 6473 ] 6364 6474 }
+11 -19
packages/functions/src/extension-import.ts
··· 60 60 61 61 console.log(`[extension-import] Created upload ${uploadId} for user ${context.did}`); 62 62 63 - // Save source accounts 64 - let savedCount = 0; 65 - for (const username of validatedData.usernames) { 66 - const normalizedUsername = normalize(username); 67 - 68 - try { 69 - await sourceAccountRepo.upsertSourceAccount( 70 - validatedData.platform, 71 - username, 72 - normalizedUsername 73 - ); 74 - savedCount++; 75 - } catch (error) { 76 - console.error(`[extension-import] Error saving username ${username}:`, error); 77 - // Continue with other usernames 78 - } 63 + // Save source accounts using bulk insert 64 + try { 65 + await sourceAccountRepo.bulkCreate( 66 + validatedData.platform, 67 + validatedData.usernames 68 + ); 69 + console.log(`[extension-import] Saved ${validatedData.usernames.length} source accounts`); 70 + } catch (error) { 71 + console.error('[extension-import] Error saving source accounts:', error); 72 + // Continue anyway - upload is created, frontend can still search 79 73 } 80 - 81 - console.log(`[extension-import] Saved ${savedCount}/${validatedData.usernames.length} source accounts`); 82 74 83 75 // Return upload data for frontend to search 84 76 const response: ExtensionImportResponse = { 85 77 importId: uploadId, 86 78 usernameCount: validatedData.usernames.length, 87 - redirectUrl: `/results?uploadId=${uploadId}` // Frontend will handle this 79 + redirectUrl: `/?uploadId=${uploadId}` // Frontend will load results from uploadId param 88 80 }; 89 81 90 82 return successResponse(response);
+18
packages/web/src/App.tsx
··· 326 326 handleExtensionImport(importId); 327 327 }, [session, currentDestinationAppId, setStatusMessage, setCurrentStep, setSearchResults, searchAllUsers, saveResults, error]); 328 328 329 + // Load results from uploadId URL parameter 330 + useEffect(() => { 331 + const urlParams = new URLSearchParams(window.location.search); 332 + const uploadId = urlParams.get('uploadId'); 333 + 334 + if (!uploadId || !session) { 335 + return; 336 + } 337 + 338 + // Load results for this upload 339 + loadUploadResults(uploadId); 340 + 341 + // Clean up URL parameter after loading 342 + const newUrl = new URL(window.location.href); 343 + newUrl.searchParams.delete('uploadId'); 344 + window.history.replaceState({}, '', newUrl); 345 + }, [session, loadUploadResults]); 346 + 329 347 return ( 330 348 <ErrorBoundary> 331 349 <div className="min-h-screen relative overflow-hidden">