A social knowledge tool for researchers built on ATProto

formatting and linting

Changed files
+23 -10
docs
src
modules
cards
infrastructure
http
+21 -8
docs/unauthed-use-cases.md
··· 9 9 ## Why This Pattern Makes Sense 10 10 11 11 ### Business Reality 12 + 12 13 - Many operations serve both user types (e.g., viewing public collections) 13 14 - Authenticated users often get enhanced functionality or additional data 14 15 - Same core business logic with context-dependent variations 15 16 16 17 ### Architectural Benefits 18 + 17 19 - **Single Responsibility**: One use case per business operation 18 20 - **Explicit Dependencies**: Clear what context is needed 19 21 - **Clean Layer Separation**: Each layer handles its concerns appropriately ··· 22 24 ## Implementation Pattern 23 25 24 26 ### Controller Layer 27 + 25 28 Extract authentication context from HTTP layer and pass it down: 26 29 27 30 ```typescript ··· 34 37 ``` 35 38 36 39 ### Use Case Layer 40 + 37 41 Accept optional caller context and pass it to domain services: 38 42 39 43 ```typescript ··· 53 57 ``` 54 58 55 59 ### Domain Service Layer 60 + 56 61 Make decisions based on caller identity: 57 62 58 63 ```typescript ··· 71 76 72 77 ```typescript 73 78 export class GetCollectionPageUseCase { 74 - async execute(query: GetCollectionPageQuery): Promise<Result<GetCollectionPageResult>> { 79 + async execute( 80 + query: GetCollectionPageQuery, 81 + ): Promise<Result<GetCollectionPageResult>> { 75 82 // Core business logic remains the same 76 83 const collection = await this.getCollection(query.collectionId); 77 - 84 + 78 85 // Context affects behavior where needed 79 86 const profile = await this.profileService.getProfile( 80 87 collection.authorId.value, 81 - query.callerDid // undefined for unauth users 88 + query.callerDid, // undefined for unauth users 82 89 ); 83 - 90 + 84 91 // Business rules can vary based on context 85 - if (this.isPrivateCollection(collection) && !this.canAccess(collection, query.callerDid)) { 92 + if ( 93 + this.isPrivateCollection(collection) && 94 + !this.canAccess(collection, query.callerDid) 95 + ) { 86 96 return err(new UnauthorizedError()); 87 97 } 88 - 98 + 89 99 return ok(result); 90 100 } 91 101 } ··· 94 104 ## Common Use Cases 95 105 96 106 This pattern works well for: 107 + 97 108 - **Profile viewing** (public vs private details) 98 109 - **Content feeds** (personalized vs generic) 99 110 - **Collection browsing** (access control based on caller) ··· 102 113 ## When to Split Use Cases 103 114 104 115 Consider separate use cases when: 116 + 105 117 - **Fundamentally different operations**: `LoginUseCase` vs `GetPublicDataUseCase` 106 118 - **Complex branching**: If auth/unauth paths are completely different 107 119 - **Security isolation**: When you want to ensure certain code paths never run for unauthenticated users ··· 120 132 121 133 ```typescript 122 134 // In routes 123 - router.get('/:id', 135 + router.get( 136 + '/:id', 124 137 authMiddleware.optionalAuthentication(), // Sets req.did if token present 125 - (req, res) => controller.execute(req, res) 138 + (req, res) => controller.execute(req, res), 126 139 ); 127 140 ``` 128 141
+2 -2
src/modules/cards/infrastructure/http/controllers/GetUrlCardViewController.ts
··· 17 17 return this.badRequest(res, 'Card ID is required'); 18 18 } 19 19 20 - const result = await this.getUrlCardViewUseCase.execute({ 20 + const result = await this.getUrlCardViewUseCase.execute({ 21 21 cardId, 22 - callerDid 22 + callerDid, 23 23 }); 24 24 25 25 if (result.isErr()) {