This repository has no description
0

Configure Feed

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

formatting and linting

+241 -197
+10
.agent/logs/20260122_open_collection_removal_logic.md
··· 55 55 ``` 56 56 57 57 **Design Notes:** 58 + 58 59 - No `removedBy` field - user identity is derived from the AT-URI of the removal record 59 60 - No `reason` field - kept the schema minimal and focused 60 61 - Uses StrongRef to ensure content-addressable references (URI + CID) ··· 64 65 ### Removal Permissions 65 66 66 67 **For OPEN Collections:** 68 + 67 69 - ✅ Collection author can remove any card (including cards added by others) 68 70 - ✅ Users can only remove cards they themselves added 69 71 - ❌ Non-authors cannot remove cards added by others ··· 235 237 ## Files Modified 236 238 237 239 ### Created 240 + 238 241 1. `src/modules/atproto/infrastructure/lexicons/collectionLinkRemoval.json` - Lexicon schema 239 242 2. `src/modules/atproto/infrastructure/mappers/CollectionLinkRemovalMapper.ts` - Mapper for removal records 240 243 241 244 ### Modified 245 + 242 246 1. `src/modules/cards/domain/Collection.ts` - Updated `removeCard()` method with proper permissions 243 247 2. `src/modules/cards/domain/services/CardCollectionService.ts` - Updated removal logic with publishing decisions 244 248 3. `src/modules/cards/application/ports/ICollectionPublisher.ts` - Added `publishCollectionLinkRemoval` method signature ··· 249 253 8. `src/modules/cards/tests/application/RemoveCardFromCollectionUseCase.test.ts` - Updated/added tests 250 254 251 255 ### Generated 256 + 252 257 - TypeScript types regenerated via `npm run lexgen` 253 258 254 259 ## Test Coverage 255 260 256 261 ### Test Results 262 + 257 263 - ✅ 20 tests passed in RemoveCardFromCollectionUseCase 258 264 - ✅ 2 tests skipped (CLOSED collections with collaborators - out of scope) 259 265 - ✅ 148 total collection-related tests passed ··· 268 274 ### Skipped Tests (Out of Scope) 269 275 270 276 The following tests were skipped as CLOSED collection scenarios with collaborators are deferred: 277 + 271 278 - `should allow collaborator to remove cards from closed collection` 272 279 - `should handle mixed collection permissions when removing cards` 273 280 ··· 283 290 ## Future Work 284 291 285 292 ### CLOSED Collections with Collaborators 293 + 286 294 Deferred for future implementation: 295 + 287 296 - How collaborators can remove cards from CLOSED collections 288 297 - Whether collaborators can remove any card or only their own 289 298 - Permission rules for multi-user CLOSED collection scenarios 290 299 291 300 ### Potential Enhancements 301 + 292 302 - Add a `reason` field to collectionLinkRemoval for moderation use cases 293 303 - Implement bulk removal operations 294 304 - Add notifications when cards are removed from collections
+6 -2
src/modules/atproto/application/useCases/ProcessCollectionFirehoseEventUseCase.ts
··· 85 85 const result = await this.createCollectionUseCase.execute({ 86 86 name: request.record.name, 87 87 description: request.record.description, 88 - accessType: request.record.accessType as CollectionAccessType | undefined, 88 + accessType: request.record.accessType as 89 + | CollectionAccessType 90 + | undefined, 89 91 curatorId: authorDid, 90 92 publishedRecordId: publishedRecordId, 91 93 }); ··· 170 172 collectionId: collectionIdResult.value.getStringValue(), 171 173 name: request.record.name, 172 174 description: request.record.description, 173 - accessType: request.record.accessType as CollectionAccessType | undefined, 175 + accessType: request.record.accessType as 176 + | CollectionAccessType 177 + | undefined, 174 178 curatorId: authorDid, 175 179 publishedRecordId: publishedRecordId, 176 180 });
+1 -2
src/modules/atproto/application/useCases/ProcessCollectionLinkRemovalFirehoseEventUseCase.ts
··· 19 19 const ENABLE_FIREHOSE_LOGGING = true; 20 20 21 21 export class ProcessCollectionLinkRemovalFirehoseEventUseCase 22 - implements 23 - UseCase<ProcessCollectionLinkRemovalFirehoseEventDTO, Result<void>> 22 + implements UseCase<ProcessCollectionLinkRemovalFirehoseEventDTO, Result<void>> 24 23 { 25 24 constructor( 26 25 private atUriResolutionService: IAtUriResolutionService,
+3 -1
src/modules/atproto/application/useCases/ProcessFirehoseEventUseCase.ts
··· 137 137 !removalRecord.removedAt 138 138 ) { 139 139 return err( 140 - new ValidationError('Invalid collection link removal record structure'), 140 + new ValidationError( 141 + 'Invalid collection link removal record structure', 142 + ), 141 143 ); 142 144 } 143 145 }
+42 -42
src/modules/atproto/infrastructure/lexicon/index.ts
··· 7 7 type Options as XrpcOptions, 8 8 type AuthVerifier, 9 9 type StreamAuthVerifier, 10 - } from '@atproto/xrpc-server' 11 - import { schemas } from './lexicons.js' 10 + } from '@atproto/xrpc-server'; 11 + import { schemas } from './lexicons.js'; 12 12 13 13 export function createServer(options?: XrpcOptions): Server { 14 - return new Server(options) 14 + return new Server(options); 15 15 } 16 16 17 17 export class Server { 18 - xrpc: XrpcServer 19 - network: NetworkNS 20 - com: ComNS 18 + xrpc: XrpcServer; 19 + network: NetworkNS; 20 + com: ComNS; 21 21 22 22 constructor(options?: XrpcOptions) { 23 - this.xrpc = createXrpcServer(schemas, options) 24 - this.network = new NetworkNS(this) 25 - this.com = new ComNS(this) 23 + this.xrpc = createXrpcServer(schemas, options); 24 + this.network = new NetworkNS(this); 25 + this.com = new ComNS(this); 26 26 } 27 27 } 28 28 29 29 export class NetworkNS { 30 - _server: Server 31 - cosmik: NetworkCosmikNS 30 + _server: Server; 31 + cosmik: NetworkCosmikNS; 32 32 33 33 constructor(server: Server) { 34 - this._server = server 35 - this.cosmik = new NetworkCosmikNS(server) 34 + this._server = server; 35 + this.cosmik = new NetworkCosmikNS(server); 36 36 } 37 37 } 38 38 39 39 export class NetworkCosmikNS { 40 - _server: Server 40 + _server: Server; 41 41 42 42 constructor(server: Server) { 43 - this._server = server 43 + this._server = server; 44 44 } 45 45 } 46 46 47 47 export class ComNS { 48 - _server: Server 49 - atproto: ComAtprotoNS 48 + _server: Server; 49 + atproto: ComAtprotoNS; 50 50 51 51 constructor(server: Server) { 52 - this._server = server 53 - this.atproto = new ComAtprotoNS(server) 52 + this._server = server; 53 + this.atproto = new ComAtprotoNS(server); 54 54 } 55 55 } 56 56 57 57 export class ComAtprotoNS { 58 - _server: Server 59 - repo: ComAtprotoRepoNS 58 + _server: Server; 59 + repo: ComAtprotoRepoNS; 60 60 61 61 constructor(server: Server) { 62 - this._server = server 63 - this.repo = new ComAtprotoRepoNS(server) 62 + this._server = server; 63 + this.repo = new ComAtprotoRepoNS(server); 64 64 } 65 65 } 66 66 67 67 export class ComAtprotoRepoNS { 68 - _server: Server 68 + _server: Server; 69 69 70 70 constructor(server: Server) { 71 - this._server = server 71 + this._server = server; 72 72 } 73 73 } 74 74 75 75 type SharedRateLimitOpts<T> = { 76 - name: string 77 - calcKey?: (ctx: T) => string | null 78 - calcPoints?: (ctx: T) => number 79 - } 76 + name: string; 77 + calcKey?: (ctx: T) => string | null; 78 + calcPoints?: (ctx: T) => number; 79 + }; 80 80 type RouteRateLimitOpts<T> = { 81 - durationMs: number 82 - points: number 83 - calcKey?: (ctx: T) => string | null 84 - calcPoints?: (ctx: T) => number 85 - } 86 - type HandlerOpts = { blobLimit?: number } 87 - type HandlerRateLimitOpts<T> = SharedRateLimitOpts<T> | RouteRateLimitOpts<T> 81 + durationMs: number; 82 + points: number; 83 + calcKey?: (ctx: T) => string | null; 84 + calcPoints?: (ctx: T) => number; 85 + }; 86 + type HandlerOpts = { blobLimit?: number }; 87 + type HandlerRateLimitOpts<T> = SharedRateLimitOpts<T> | RouteRateLimitOpts<T>; 88 88 type ConfigOf<Auth, Handler, ReqCtx> = 89 89 | Handler 90 90 | { 91 - auth?: Auth 92 - opts?: HandlerOpts 93 - rateLimit?: HandlerRateLimitOpts<ReqCtx> | HandlerRateLimitOpts<ReqCtx>[] 94 - handler: Handler 95 - } 91 + auth?: Auth; 92 + opts?: HandlerOpts; 93 + rateLimit?: HandlerRateLimitOpts<ReqCtx> | HandlerRateLimitOpts<ReqCtx>[]; 94 + handler: Handler; 95 + }; 96 96 type ExtractAuth<AV extends AuthVerifier | StreamAuthVerifier> = Extract< 97 97 Awaited<ReturnType<AV>>, 98 98 { credentials: unknown } 99 - > 99 + >;
+9 -9
src/modules/atproto/infrastructure/lexicon/lexicons.ts
··· 6 6 Lexicons, 7 7 ValidationError, 8 8 type ValidationResult, 9 - } from '@atproto/lexicon' 10 - import { type $Typed, is$typed, maybe$typed } from './util.js' 9 + } from '@atproto/lexicon'; 10 + import { type $Typed, is$typed, maybe$typed } from './util.js'; 11 11 12 12 export const schemaDict = { 13 13 NetworkCosmikCard: { ··· 337 337 }, 338 338 }, 339 339 }, 340 - } as const satisfies Record<string, LexiconDoc> 341 - export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 342 - export const lexicons: Lexicons = new Lexicons(schemas) 340 + } as const satisfies Record<string, LexiconDoc>; 341 + export const schemas = Object.values(schemaDict) satisfies LexiconDoc[]; 342 + export const lexicons: Lexicons = new Lexicons(schemas); 343 343 344 344 export function validate<T extends { $type: string }>( 345 345 v: unknown, 346 346 id: string, 347 347 hash: string, 348 348 requiredType: true, 349 - ): ValidationResult<T> 349 + ): ValidationResult<T>; 350 350 export function validate<T extends { $type?: string }>( 351 351 v: unknown, 352 352 id: string, 353 353 hash: string, 354 354 requiredType?: false, 355 - ): ValidationResult<T> 355 + ): ValidationResult<T>; 356 356 export function validate( 357 357 v: unknown, 358 358 id: string, ··· 366 366 error: new ValidationError( 367 367 `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 368 368 ), 369 - } 369 + }; 370 370 } 371 371 372 372 export const ids = { ··· 376 376 NetworkCosmikCollectionLinkRemoval: 'network.cosmik.collectionLinkRemoval', 377 377 NetworkCosmikDefs: 'network.cosmik.defs', 378 378 ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', 379 - } as const 379 + } as const;
+12 -12
src/modules/atproto/infrastructure/lexicon/types/com/atproto/repo/strongRef.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../../lexicons' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../../lexicons'; 7 7 import { 8 8 type $Typed, 9 9 is$typed as _is$typed, 10 10 type OmitKey, 11 - } from '../../../../util' 11 + } from '../../../../util'; 12 12 13 13 const is$typed = _is$typed, 14 - validate = _validate 15 - const id = 'com.atproto.repo.strongRef' 14 + validate = _validate; 15 + const id = 'com.atproto.repo.strongRef'; 16 16 17 17 export interface Main { 18 - $type?: 'com.atproto.repo.strongRef' 19 - cid: string 20 - uri: string 18 + $type?: 'com.atproto.repo.strongRef'; 19 + cid: string; 20 + uri: string; 21 21 } 22 22 23 - const hashMain = 'main' 23 + const hashMain = 'main'; 24 24 25 25 export function isMain<V>(v: V) { 26 - return is$typed(v, id, hashMain) 26 + return is$typed(v, id, hashMain); 27 27 } 28 28 29 29 export function validateMain<V>(v: V) { 30 - return validate<Main & V>(v, id, hashMain) 30 + return validate<Main & V>(v, id, hashMain); 31 31 }
+49 -45
src/modules/atproto/infrastructure/lexicon/types/network/cosmik/card.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js' 9 - import type * as NetworkCosmikDefs from './defs.js' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../lexicons'; 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../util'; 12 + import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js'; 13 + import type * as NetworkCosmikDefs from './defs.js'; 10 14 11 15 const is$typed = _is$typed, 12 - validate = _validate 13 - const id = 'network.cosmik.card' 16 + validate = _validate; 17 + const id = 'network.cosmik.card'; 14 18 15 19 export interface Record { 16 - $type: 'network.cosmik.card' 20 + $type: 'network.cosmik.card'; 17 21 /** The type of card */ 18 - type: 'URL' | 'NOTE' | (string & {}) 19 - content: $Typed<UrlContent> | $Typed<NoteContent> | { $type: string } 22 + type: 'URL' | 'NOTE' | (string & {}); 23 + content: $Typed<UrlContent> | $Typed<NoteContent> | { $type: string }; 20 24 /** Optional URL associated with the card. Required for URL cards, optional for NOTE cards. */ 21 - url?: string 22 - parentCard?: ComAtprotoRepoStrongRef.Main 25 + url?: string; 26 + parentCard?: ComAtprotoRepoStrongRef.Main; 23 27 /** Timestamp when this card was created (usually set by PDS). */ 24 - createdAt?: string 25 - originalCard?: ComAtprotoRepoStrongRef.Main 26 - provenance?: NetworkCosmikDefs.Provenance 27 - [k: string]: unknown 28 + createdAt?: string; 29 + originalCard?: ComAtprotoRepoStrongRef.Main; 30 + provenance?: NetworkCosmikDefs.Provenance; 31 + [k: string]: unknown; 28 32 } 29 33 30 - const hashRecord = 'main' 34 + const hashRecord = 'main'; 31 35 32 36 export function isRecord<V>(v: V) { 33 - return is$typed(v, id, hashRecord) 37 + return is$typed(v, id, hashRecord); 34 38 } 35 39 36 40 export function validateRecord<V>(v: V) { 37 - return validate<Record & V>(v, id, hashRecord, true) 41 + return validate<Record & V>(v, id, hashRecord, true); 38 42 } 39 43 40 44 /** Content structure for a URL card. */ 41 45 export interface UrlContent { 42 - $type?: 'network.cosmik.card#urlContent' 46 + $type?: 'network.cosmik.card#urlContent'; 43 47 /** The URL being saved */ 44 - url: string 45 - metadata?: UrlMetadata 48 + url: string; 49 + metadata?: UrlMetadata; 46 50 } 47 51 48 - const hashUrlContent = 'urlContent' 52 + const hashUrlContent = 'urlContent'; 49 53 50 54 export function isUrlContent<V>(v: V) { 51 - return is$typed(v, id, hashUrlContent) 55 + return is$typed(v, id, hashUrlContent); 52 56 } 53 57 54 58 export function validateUrlContent<V>(v: V) { 55 - return validate<UrlContent & V>(v, id, hashUrlContent) 59 + return validate<UrlContent & V>(v, id, hashUrlContent); 56 60 } 57 61 58 62 /** Content structure for a note card. */ 59 63 export interface NoteContent { 60 - $type?: 'network.cosmik.card#noteContent' 64 + $type?: 'network.cosmik.card#noteContent'; 61 65 /** The note text content */ 62 - text: string 66 + text: string; 63 67 } 64 68 65 - const hashNoteContent = 'noteContent' 69 + const hashNoteContent = 'noteContent'; 66 70 67 71 export function isNoteContent<V>(v: V) { 68 - return is$typed(v, id, hashNoteContent) 72 + return is$typed(v, id, hashNoteContent); 69 73 } 70 74 71 75 export function validateNoteContent<V>(v: V) { 72 - return validate<NoteContent & V>(v, id, hashNoteContent) 76 + return validate<NoteContent & V>(v, id, hashNoteContent); 73 77 } 74 78 75 79 /** Metadata about a URL. */ 76 80 export interface UrlMetadata { 77 - $type?: 'network.cosmik.card#urlMetadata' 81 + $type?: 'network.cosmik.card#urlMetadata'; 78 82 /** Title of the page */ 79 - title?: string 83 + title?: string; 80 84 /** Description of the page */ 81 - description?: string 85 + description?: string; 82 86 /** Author of the content */ 83 - author?: string 87 + author?: string; 84 88 /** When the content was published */ 85 - publishedDate?: string 89 + publishedDate?: string; 86 90 /** Name of the site */ 87 - siteName?: string 91 + siteName?: string; 88 92 /** URL of a representative image */ 89 - imageUrl?: string 93 + imageUrl?: string; 90 94 /** Type of content (e.g., 'video', 'article') */ 91 - type?: string 95 + type?: string; 92 96 /** When the metadata was retrieved */ 93 - retrievedAt?: string 97 + retrievedAt?: string; 94 98 /** Digital Object Identifier (DOI) for academic content */ 95 - doi?: string 99 + doi?: string; 96 100 /** International Standard Book Number (ISBN) for books */ 97 - isbn?: string 101 + isbn?: string; 98 102 } 99 103 100 - const hashUrlMetadata = 'urlMetadata' 104 + const hashUrlMetadata = 'urlMetadata'; 101 105 102 106 export function isUrlMetadata<V>(v: V) { 103 - return is$typed(v, id, hashUrlMetadata) 107 + return is$typed(v, id, hashUrlMetadata); 104 108 } 105 109 106 110 export function validateUrlMetadata<V>(v: V) { 107 - return validate<UrlMetadata & V>(v, id, hashUrlMetadata) 111 + return validate<UrlMetadata & V>(v, id, hashUrlMetadata); 108 112 }
+21 -17
src/modules/atproto/infrastructure/lexicon/types/network/cosmik/collection.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../lexicons'; 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../util'; 8 12 9 13 const is$typed = _is$typed, 10 - validate = _validate 11 - const id = 'network.cosmik.collection' 14 + validate = _validate; 15 + const id = 'network.cosmik.collection'; 12 16 13 17 export interface Record { 14 - $type: 'network.cosmik.collection' 18 + $type: 'network.cosmik.collection'; 15 19 /** Name of the collection */ 16 - name: string 20 + name: string; 17 21 /** Description of the collection */ 18 - description?: string 22 + description?: string; 19 23 /** Access control for the collection */ 20 - accessType: 'OPEN' | 'CLOSED' | (string & {}) 24 + accessType: 'OPEN' | 'CLOSED' | (string & {}); 21 25 /** List of collaborator DIDs who can add cards to closed collections */ 22 - collaborators?: string[] 26 + collaborators?: string[]; 23 27 /** Timestamp when this collection was created (usually set by PDS). */ 24 - createdAt?: string 28 + createdAt?: string; 25 29 /** Timestamp when this collection was last updated. */ 26 - updatedAt?: string 27 - [k: string]: unknown 30 + updatedAt?: string; 31 + [k: string]: unknown; 28 32 } 29 33 30 - const hashRecord = 'main' 34 + const hashRecord = 'main'; 31 35 32 36 export function isRecord<V>(v: V) { 33 - return is$typed(v, id, hashRecord) 37 + return is$typed(v, id, hashRecord); 34 38 } 35 39 36 40 export function validateRecord<V>(v: V) { 37 - return validate<Record & V>(v, id, hashRecord, true) 41 + return validate<Record & V>(v, id, hashRecord, true); 38 42 }
+24 -20
src/modules/atproto/infrastructure/lexicon/types/network/cosmik/collectionLink.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js' 9 - import type * as NetworkCosmikDefs from './defs.js' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../lexicons'; 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../util'; 12 + import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js'; 13 + import type * as NetworkCosmikDefs from './defs.js'; 10 14 11 15 const is$typed = _is$typed, 12 - validate = _validate 13 - const id = 'network.cosmik.collectionLink' 16 + validate = _validate; 17 + const id = 'network.cosmik.collectionLink'; 14 18 15 19 export interface Record { 16 - $type: 'network.cosmik.collectionLink' 17 - collection: ComAtprotoRepoStrongRef.Main 18 - card: ComAtprotoRepoStrongRef.Main 19 - originalCard?: ComAtprotoRepoStrongRef.Main 20 + $type: 'network.cosmik.collectionLink'; 21 + collection: ComAtprotoRepoStrongRef.Main; 22 + card: ComAtprotoRepoStrongRef.Main; 23 + originalCard?: ComAtprotoRepoStrongRef.Main; 20 24 /** DID of the user who added the card to the collection */ 21 - addedBy: string 25 + addedBy: string; 22 26 /** Timestamp when the card was added to the collection. */ 23 - addedAt: string 27 + addedAt: string; 24 28 /** Timestamp when this link record was created (usually set by PDS). */ 25 - createdAt?: string 26 - provenance?: NetworkCosmikDefs.Provenance 27 - [k: string]: unknown 29 + createdAt?: string; 30 + provenance?: NetworkCosmikDefs.Provenance; 31 + [k: string]: unknown; 28 32 } 29 33 30 - const hashRecord = 'main' 34 + const hashRecord = 'main'; 31 35 32 36 export function isRecord<V>(v: V) { 33 - return is$typed(v, id, hashRecord) 37 + return is$typed(v, id, hashRecord); 34 38 } 35 39 36 40 export function validateRecord<V>(v: V) { 37 - return validate<Record & V>(v, id, hashRecord, true) 41 + return validate<Record & V>(v, id, hashRecord, true); 38 42 }
+19 -15
src/modules/atproto/infrastructure/lexicon/types/network/cosmik/collectionLinkRemoval.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../lexicons'; 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../util'; 12 + import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js'; 9 13 10 14 const is$typed = _is$typed, 11 - validate = _validate 12 - const id = 'network.cosmik.collectionLinkRemoval' 15 + validate = _validate; 16 + const id = 'network.cosmik.collectionLinkRemoval'; 13 17 14 18 export interface Record { 15 - $type: 'network.cosmik.collectionLinkRemoval' 16 - collection: ComAtprotoRepoStrongRef.Main 17 - removedLink: ComAtprotoRepoStrongRef.Main 19 + $type: 'network.cosmik.collectionLinkRemoval'; 20 + collection: ComAtprotoRepoStrongRef.Main; 21 + removedLink: ComAtprotoRepoStrongRef.Main; 18 22 /** Timestamp when the link was removed from the collection. */ 19 - removedAt: string 20 - [k: string]: unknown 23 + removedAt: string; 24 + [k: string]: unknown; 21 25 } 22 26 23 - const hashRecord = 'main' 27 + const hashRecord = 'main'; 24 28 25 29 export function isRecord<V>(v: V) { 26 - return is$typed(v, id, hashRecord) 30 + return is$typed(v, id, hashRecord); 27 31 } 28 32 29 33 export function validateRecord<V>(v: V) { 30 - return validate<Record & V>(v, id, hashRecord, true) 34 + return validate<Record & V>(v, id, hashRecord, true); 31 35 }
+16 -12
src/modules/atproto/infrastructure/lexicon/types/network/cosmik/defs.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 - import { CID } from 'multiformats/cid' 6 - import { validate as _validate } from '../../../lexicons' 7 - import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 - import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js' 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon'; 5 + import { CID } from 'multiformats/cid'; 6 + import { validate as _validate } from '../../../lexicons'; 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../util'; 12 + import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef.js'; 9 13 10 14 const is$typed = _is$typed, 11 - validate = _validate 12 - const id = 'network.cosmik.defs' 15 + validate = _validate; 16 + const id = 'network.cosmik.defs'; 13 17 14 18 /** Represents the provenance or source of a record. */ 15 19 export interface Provenance { 16 - $type?: 'network.cosmik.defs#provenance' 17 - via?: ComAtprotoRepoStrongRef.Main 20 + $type?: 'network.cosmik.defs#provenance'; 21 + via?: ComAtprotoRepoStrongRef.Main; 18 22 } 19 23 20 - const hashProvenance = 'provenance' 24 + const hashProvenance = 'provenance'; 21 25 22 26 export function isProvenance<V>(v: V) { 23 - return is$typed(v, id, hashProvenance) 27 + return is$typed(v, id, hashProvenance); 24 28 } 25 29 26 30 export function validateProvenance<V>(v: V) { 27 - return validate<Provenance & V>(v, id, hashProvenance) 31 + return validate<Provenance & V>(v, id, hashProvenance); 28 32 }
+16 -16
src/modules/atproto/infrastructure/lexicon/util.ts
··· 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 4 5 - import { type ValidationResult } from '@atproto/lexicon' 5 + import { type ValidationResult } from '@atproto/lexicon'; 6 6 7 7 export type OmitKey<T, K extends keyof T> = { 8 - [K2 in keyof T as K2 extends K ? never : K2]: T[K2] 9 - } 8 + [K2 in keyof T as K2 extends K ? never : K2]: T[K2]; 9 + }; 10 10 11 - export type $Typed<V, T extends string = string> = V & { $type: T } 12 - export type Un$Typed<V extends { $type?: string }> = OmitKey<V, '$type'> 11 + export type $Typed<V, T extends string = string> = V & { $type: T }; 12 + export type Un$Typed<V extends { $type?: string }> = OmitKey<V, '$type'>; 13 13 14 14 export type $Type<Id extends string, Hash extends string> = Hash extends 'main' 15 15 ? Id 16 - : `${Id}#${Hash}` 16 + : `${Id}#${Hash}`; 17 17 18 18 function isObject<V>(v: V): v is V & object { 19 - return v != null && typeof v === 'object' 19 + return v != null && typeof v === 'object'; 20 20 } 21 21 22 22 function is$type<Id extends string, Hash extends string>( ··· 31 31 $type.length === id.length + 1 + hash.length && 32 32 $type.charCodeAt(id.length) === 35 /* '#' */ && 33 33 $type.startsWith(id) && 34 - $type.endsWith(hash) 34 + $type.endsWith(hash); 35 35 } 36 36 37 37 export type $TypedObject< ··· 39 39 Id extends string, 40 40 Hash extends string, 41 41 > = V extends { 42 - $type: $Type<Id, Hash> 42 + $type: $Type<Id, Hash>; 43 43 } 44 44 ? V 45 45 : V extends { $type?: string } 46 46 ? V extends { $type?: infer T extends $Type<Id, Hash> } 47 47 ? V & { $type: T } 48 48 : never 49 - : V & { $type: $Type<Id, Hash> } 49 + : V & { $type: $Type<Id, Hash> }; 50 50 51 51 export function is$typed<V, Id extends string, Hash extends string>( 52 52 v: V, 53 53 id: Id, 54 54 hash: Hash, 55 55 ): v is $TypedObject<V, Id, Hash> { 56 - return isObject(v) && '$type' in v && is$type(v.$type, id, hash) 56 + return isObject(v) && '$type' in v && is$type(v.$type, id, hash); 57 57 } 58 58 59 59 export function maybe$typed<V, Id extends string, Hash extends string>( ··· 64 64 return ( 65 65 isObject(v) && 66 66 ('$type' in v ? v.$type === undefined || is$type(v.$type, id, hash) : true) 67 - ) 67 + ); 68 68 } 69 69 70 - export type Validator<R = unknown> = (v: unknown) => ValidationResult<R> 70 + export type Validator<R = unknown> = (v: unknown) => ValidationResult<R>; 71 71 export type ValidatorParam<V extends Validator> = 72 - V extends Validator<infer R> ? R : never 72 + V extends Validator<infer R> ? R : never; 73 73 74 74 /** 75 75 * Utility function that allows to convert a "validate*" utility function into a ··· 77 77 */ 78 78 export function asPredicate<V extends Validator>(validate: V) { 79 79 return function <T>(v: T): v is T & ValidatorParam<V> { 80 - return validate(v).success 81 - } 80 + return validate(v).success; 81 + }; 82 82 }
+6 -1
src/modules/cards/domain/ICardQueryRepository.ts
··· 59 59 export type CollectionCardQueryResultDTO = UrlCardView; 60 60 // Raw data from repository - minimal, just what's stored 61 61 export interface WithCollections { 62 - collections: { id: string; name: string; authorId: string; accessType: string }[]; 62 + collections: { 63 + id: string; 64 + name: string; 65 + authorId: string; 66 + accessType: string; 67 + }[]; 63 68 } 64 69 65 70 export interface WithLibraries {
+1 -2
src/modules/cards/infrastructure/http/routes/collectionRoutes.ts
··· 51 51 router.get( 52 52 '/contributed/:identifier', 53 53 authMiddleware.optionalAuth(), 54 - (req, res) => 55 - getOpenCollectionsWithContributorController.execute(req, res), 54 + (req, res) => getOpenCollectionsWithContributorController.execute(req, res), 56 55 ); 57 56 58 57 // GET /api/collections/at/:handle/:recordKey - Get collection by AT URI
+6 -1
src/modules/cards/tests/utils/InMemoryCardQueryRepository.ts
··· 110 110 111 111 // Find collections this card belongs to by querying the collection repository 112 112 const allCollections = this.collectionRepository.getAllCollections(); 113 - const collections: { id: string; name: string; authorId: string; accessType: string }[] = []; 113 + const collections: { 114 + id: string; 115 + name: string; 116 + authorId: string; 117 + accessType: string; 118 + }[] = []; 114 119 115 120 for (const collection of allCollections) { 116 121 if (