@@ -30,9 +30,11 @@ import type { RepositoryRole, RepositoryAccessGrant } from "./types.js";
3030 * - `owner`: Full control including ownership management
3131 *
3232 * **SDS API Endpoints Used**:
33- * - `com.atproto.sds.grantAccess`: Grant access to a user
34- * - `com.atproto.sds.revokeAccess`: Revoke access from a user
35- * - `com.atproto.sds.listCollaborators`: List all collaborators
33+ * - `com.sds.repo.grantAccess`: Grant access to a user
34+ * - `com.sds.repo.revokeAccess`: Revoke access from a user
35+ * - `com.sds.repo.listCollaborators`: List all collaborators
36+ * - `com.sds.repo.getPermissions`: Get current user's permissions
37+ * - `com.sds.repo.transferOwnership`: Transfer repository ownership
3638 *
3739 * @example
3840 * ```typescript
@@ -135,7 +137,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
135137 async grant ( params : { userDid : string ; role : RepositoryRole } ) : Promise < void > {
136138 const permissions = this . roleToPermissions ( params . role ) ;
137139
138- const response = await this . session . fetchHandler ( `${ this . serverUrl } /xrpc/com.atproto. sds.grantAccess` , {
140+ const response = await this . session . fetchHandler ( `${ this . serverUrl } /xrpc/com.sds.repo .grantAccess` , {
139141 method : "POST" ,
140142 headers : { "Content-Type" : "application/json" } ,
141143 body : JSON . stringify ( {
@@ -169,7 +171,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
169171 * ```
170172 */
171173 async revoke ( params : { userDid : string } ) : Promise < void > {
172- const response = await this . session . fetchHandler ( `${ this . serverUrl } /xrpc/com.atproto. sds.revokeAccess` , {
174+ const response = await this . session . fetchHandler ( `${ this . serverUrl } /xrpc/com.sds.repo .revokeAccess` , {
173175 method : "POST" ,
174176 headers : { "Content-Type" : "application/json" } ,
175177 body : JSON . stringify ( {
@@ -211,7 +213,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
211213 */
212214 async list ( ) : Promise < RepositoryAccessGrant [ ] > {
213215 const response = await this . session . fetchHandler (
214- `${ this . serverUrl } /xrpc/com.atproto. sds.listCollaborators?repo=${ encodeURIComponent ( this . repoDid ) } ` ,
216+ `${ this . serverUrl } /xrpc/com.sds.repo .listCollaborators?repo=${ encodeURIComponent ( this . repoDid ) } ` ,
215217 { method : "GET" } ,
216218 ) ;
217219
@@ -285,4 +287,110 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
285287 const collab = collaborators . find ( ( c ) => c . userDid === userDid && ! c . revokedAt ) ;
286288 return collab ?. role ?? null ;
287289 }
290+
291+ /**
292+ * Gets the current user's permissions for this repository.
293+ *
294+ * @returns Promise resolving to the permission flags
295+ * @throws {@link NetworkError } if the request fails
296+ *
297+ * @remarks
298+ * This is useful for checking what actions the current user can perform
299+ * before attempting operations that might fail due to insufficient permissions.
300+ *
301+ * @example
302+ * ```typescript
303+ * const permissions = await repo.collaborators.getPermissions();
304+ *
305+ * if (permissions.admin) {
306+ * // Show admin UI
307+ * console.log("You can manage collaborators");
308+ * }
309+ *
310+ * if (permissions.create) {
311+ * console.log("You can create records");
312+ * }
313+ * ```
314+ *
315+ * @example Conditional UI rendering
316+ * ```typescript
317+ * const permissions = await repo.collaborators.getPermissions();
318+ *
319+ * // Show/hide UI elements based on permissions
320+ * const canEdit = permissions.update;
321+ * const canDelete = permissions.delete;
322+ * const isAdmin = permissions.admin;
323+ * const isOwner = permissions.owner;
324+ * ```
325+ */
326+ async getPermissions ( ) : Promise < CollaboratorPermissions > {
327+ const response = await this . session . fetchHandler (
328+ `${ this . serverUrl } /xrpc/com.sds.repo.getPermissions?repo=${ encodeURIComponent ( this . repoDid ) } ` ,
329+ { method : "GET" } ,
330+ ) ;
331+
332+ if ( ! response . ok ) {
333+ throw new NetworkError ( `Failed to get permissions: ${ response . statusText } ` ) ;
334+ }
335+
336+ const data = await response . json ( ) ;
337+ return data . permissions as CollaboratorPermissions ;
338+ }
339+
340+ /**
341+ * Transfers repository ownership to another user.
342+ *
343+ * @param params - Transfer parameters
344+ * @param params.newOwnerDid - DID of the user to transfer ownership to
345+ * @throws {@link NetworkError } if the transfer fails
346+ *
347+ * @remarks
348+ * **IMPORTANT**: This action is irreversible. Once ownership is transferred:
349+ * - The new owner gains full control of the repository
350+ * - Your role will be changed to admin (or specified role)
351+ * - You cannot transfer ownership back without the new owner's approval
352+ *
353+ * **Requirements**:
354+ * - You must be the current owner
355+ * - The new owner must have an existing account
356+ * - The new owner will be notified of the ownership transfer
357+ *
358+ * @example
359+ * ```typescript
360+ * // Transfer ownership to another user
361+ * await repo.collaborators.transferOwnership({
362+ * newOwnerDid: "did:plc:new-owner",
363+ * });
364+ *
365+ * console.log("Ownership transferred successfully");
366+ * // You are now an admin, not the owner
367+ * ```
368+ *
369+ * @example With confirmation
370+ * ```typescript
371+ * const confirmTransfer = await askUser(
372+ * "Are you sure you want to transfer ownership? This cannot be undone."
373+ * );
374+ *
375+ * if (confirmTransfer) {
376+ * await repo.collaborators.transferOwnership({
377+ * newOwnerDid: "did:plc:new-owner",
378+ * });
379+ * }
380+ * ```
381+ */
382+ async transferOwnership ( params : { newOwnerDid : string } ) : Promise < void > {
383+ const response = await this . session . fetchHandler ( `${ this . serverUrl } /xrpc/com.sds.repo.transferOwnership` , {
384+ method : "POST" ,
385+ headers : { "Content-Type" : "application/json" } ,
386+ body : JSON . stringify ( {
387+ repo : this . repoDid ,
388+ newOwner : params . newOwnerDid ,
389+ } ) ,
390+ } ) ;
391+
392+ if ( ! response . ok ) {
393+ throw new NetworkError ( `Failed to transfer ownership: ${ response . statusText } ` ) ;
394+ }
395+ }
288396}
0 commit comments