@hypercerts-org/sdk-core 0.2.0-beta.0 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-test.log +27 -27
- package/CHANGELOG.md +9 -3
- package/README.md +48 -5
- package/dist/index.cjs +110 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +29 -10
- package/dist/index.mjs +110 -10
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +29 -10
- package/package.json +2 -2
- package/src/repository/CollaboratorOperationsImpl.ts +114 -6
- package/src/repository/OrganizationOperationsImpl.ts +4 -4
- package/src/repository/interfaces.ts +29 -0
- package/tests/repository/CollaboratorOperationsImpl.test.ts +123 -8
- package/tests/repository/OrganizationOperationsImpl.test.ts +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -1242,16 +1242,6 @@ interface HypercertWithMetadata {
|
|
|
1242
1242
|
record: HypercertClaim;
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
|
-
/**
|
|
1246
|
-
* Repository interfaces - Operation contracts for repository functionality.
|
|
1247
|
-
*
|
|
1248
|
-
* This module defines the interfaces for all repository operations,
|
|
1249
|
-
* providing clear contracts for record management, blob handling,
|
|
1250
|
-
* profile management, and domain-specific operations.
|
|
1251
|
-
*
|
|
1252
|
-
* @packageDocumentation
|
|
1253
|
-
*/
|
|
1254
|
-
|
|
1255
1245
|
/**
|
|
1256
1246
|
* Parameters for creating a new hypercert.
|
|
1257
1247
|
*
|
|
@@ -1998,6 +1988,17 @@ interface HypercertOperations extends EventEmitter<HypercertEvents> {
|
|
|
1998
1988
|
* const hasAccess = await repo.collaborators.hasAccess("did:plc:someone");
|
|
1999
1989
|
* const role = await repo.collaborators.getRole("did:plc:someone");
|
|
2000
1990
|
*
|
|
1991
|
+
* // Get current user permissions
|
|
1992
|
+
* const permissions = await repo.collaborators.getPermissions();
|
|
1993
|
+
* if (permissions.admin) {
|
|
1994
|
+
* // Can manage collaborators
|
|
1995
|
+
* }
|
|
1996
|
+
*
|
|
1997
|
+
* // Transfer ownership
|
|
1998
|
+
* await repo.collaborators.transferOwnership({
|
|
1999
|
+
* newOwnerDid: "did:plc:new-owner",
|
|
2000
|
+
* });
|
|
2001
|
+
*
|
|
2001
2002
|
* // Revoke access
|
|
2002
2003
|
* await repo.collaborators.revoke({ userDid: "did:plc:former-user" });
|
|
2003
2004
|
* ```
|
|
@@ -2043,6 +2044,24 @@ interface CollaboratorOperations {
|
|
|
2043
2044
|
* @returns Promise resolving to role, or `null` if no access
|
|
2044
2045
|
*/
|
|
2045
2046
|
getRole(userDid: string): Promise<RepositoryRole | null>;
|
|
2047
|
+
/**
|
|
2048
|
+
* Gets the current user's permissions for this repository.
|
|
2049
|
+
*
|
|
2050
|
+
* @returns Promise resolving to permission flags
|
|
2051
|
+
*/
|
|
2052
|
+
getPermissions(): Promise<CollaboratorPermissions>;
|
|
2053
|
+
/**
|
|
2054
|
+
* Transfers repository ownership to another user.
|
|
2055
|
+
*
|
|
2056
|
+
* **WARNING**: This action is irreversible. The new owner will have
|
|
2057
|
+
* full control of the repository.
|
|
2058
|
+
*
|
|
2059
|
+
* @param params - Transfer parameters
|
|
2060
|
+
* @param params.newOwnerDid - DID of the user to transfer ownership to
|
|
2061
|
+
*/
|
|
2062
|
+
transferOwnership(params: {
|
|
2063
|
+
newOwnerDid: string;
|
|
2064
|
+
}): Promise<void>;
|
|
2046
2065
|
}
|
|
2047
2066
|
/**
|
|
2048
2067
|
* Organization operations for SDS organization management.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypercerts-org/sdk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.0",
|
|
4
4
|
"description": "Framework-agnostic ATProto SDK core for authentication, repository operations, and lexicon management",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"repository": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@atproto/oauth-client-node": "^0.3.10",
|
|
76
76
|
"eventemitter3": "^5.0.1",
|
|
77
77
|
"zod": "^3.24.4",
|
|
78
|
-
"@hypercerts-org/lexicon": "0.
|
|
78
|
+
"@hypercerts-org/lexicon": "0.4.0-beta.0"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"test": "vitest",
|
|
@@ -30,9 +30,11 @@ import type { RepositoryRole, RepositoryAccessGrant } from "./types.js";
|
|
|
30
30
|
* - `owner`: Full control including ownership management
|
|
31
31
|
*
|
|
32
32
|
* **SDS API Endpoints Used**:
|
|
33
|
-
* - `com.
|
|
34
|
-
* - `com.
|
|
35
|
-
* - `com.
|
|
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
|
|
36
38
|
*
|
|
37
39
|
* @example
|
|
38
40
|
* ```typescript
|
|
@@ -135,7 +137,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
|
|
|
135
137
|
async grant(params: { userDid: string; role: RepositoryRole }): Promise<void> {
|
|
136
138
|
const permissions = this.roleToPermissions(params.role);
|
|
137
139
|
|
|
138
|
-
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.
|
|
140
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.grantAccess`, {
|
|
139
141
|
method: "POST",
|
|
140
142
|
headers: { "Content-Type": "application/json" },
|
|
141
143
|
body: JSON.stringify({
|
|
@@ -169,7 +171,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
|
|
|
169
171
|
* ```
|
|
170
172
|
*/
|
|
171
173
|
async revoke(params: { userDid: string }): Promise<void> {
|
|
172
|
-
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.
|
|
174
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.revokeAccess`, {
|
|
173
175
|
method: "POST",
|
|
174
176
|
headers: { "Content-Type": "application/json" },
|
|
175
177
|
body: JSON.stringify({
|
|
@@ -211,7 +213,7 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
|
|
|
211
213
|
*/
|
|
212
214
|
async list(): Promise<RepositoryAccessGrant[]> {
|
|
213
215
|
const response = await this.session.fetchHandler(
|
|
214
|
-
`${this.serverUrl}/xrpc/com.
|
|
216
|
+
`${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?repo=${encodeURIComponent(this.repoDid)}`,
|
|
215
217
|
{ method: "GET" },
|
|
216
218
|
);
|
|
217
219
|
|
|
@@ -285,4 +287,110 @@ export class CollaboratorOperationsImpl implements CollaboratorOperations {
|
|
|
285
287
|
const collab = collaborators.find((c) => c.userDid === userDid && !c.revokedAt);
|
|
286
288
|
return collab?.role ?? null;
|
|
287
289
|
}
|
|
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
|
+
}
|
|
288
396
|
}
|
|
@@ -29,8 +29,8 @@ import type { OrganizationInfo } from "./types.js";
|
|
|
29
29
|
* {@link Repository.organizations} on an SDS-connected repository.
|
|
30
30
|
*
|
|
31
31
|
* **SDS API Endpoints Used**:
|
|
32
|
-
* - `com.
|
|
33
|
-
* - `com.
|
|
32
|
+
* - `com.sds.organization.create`: Create a new organization
|
|
33
|
+
* - `com.sds.organization.list`: List accessible organizations
|
|
34
34
|
*
|
|
35
35
|
* **Access Types**:
|
|
36
36
|
* - `"owner"`: User created or owns the organization
|
|
@@ -109,7 +109,7 @@ export class OrganizationOperationsImpl implements OrganizationOperations {
|
|
|
109
109
|
* ```
|
|
110
110
|
*/
|
|
111
111
|
async create(params: { name: string; description?: string; handle?: string }): Promise<OrganizationInfo> {
|
|
112
|
-
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.
|
|
112
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.create`, {
|
|
113
113
|
method: "POST",
|
|
114
114
|
headers: { "Content-Type": "application/json" },
|
|
115
115
|
body: JSON.stringify(params),
|
|
@@ -203,7 +203,7 @@ export class OrganizationOperationsImpl implements OrganizationOperations {
|
|
|
203
203
|
*/
|
|
204
204
|
async list(): Promise<OrganizationInfo[]> {
|
|
205
205
|
const response = await this.session.fetchHandler(
|
|
206
|
-
`${this.serverUrl}/xrpc/com.
|
|
206
|
+
`${this.serverUrl}/xrpc/com.sds.organization.list?userDid=${encodeURIComponent(this.session.did || this.session.sub)}`,
|
|
207
207
|
{ method: "GET" },
|
|
208
208
|
);
|
|
209
209
|
|
|
@@ -773,6 +773,17 @@ export interface HypercertOperations extends EventEmitter<HypercertEvents> {
|
|
|
773
773
|
* const hasAccess = await repo.collaborators.hasAccess("did:plc:someone");
|
|
774
774
|
* const role = await repo.collaborators.getRole("did:plc:someone");
|
|
775
775
|
*
|
|
776
|
+
* // Get current user permissions
|
|
777
|
+
* const permissions = await repo.collaborators.getPermissions();
|
|
778
|
+
* if (permissions.admin) {
|
|
779
|
+
* // Can manage collaborators
|
|
780
|
+
* }
|
|
781
|
+
*
|
|
782
|
+
* // Transfer ownership
|
|
783
|
+
* await repo.collaborators.transferOwnership({
|
|
784
|
+
* newOwnerDid: "did:plc:new-owner",
|
|
785
|
+
* });
|
|
786
|
+
*
|
|
776
787
|
* // Revoke access
|
|
777
788
|
* await repo.collaborators.revoke({ userDid: "did:plc:former-user" });
|
|
778
789
|
* ```
|
|
@@ -817,6 +828,24 @@ export interface CollaboratorOperations {
|
|
|
817
828
|
* @returns Promise resolving to role, or `null` if no access
|
|
818
829
|
*/
|
|
819
830
|
getRole(userDid: string): Promise<RepositoryRole | null>;
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Gets the current user's permissions for this repository.
|
|
834
|
+
*
|
|
835
|
+
* @returns Promise resolving to permission flags
|
|
836
|
+
*/
|
|
837
|
+
getPermissions(): Promise<import("../core/types.js").CollaboratorPermissions>;
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Transfers repository ownership to another user.
|
|
841
|
+
*
|
|
842
|
+
* **WARNING**: This action is irreversible. The new owner will have
|
|
843
|
+
* full control of the repository.
|
|
844
|
+
*
|
|
845
|
+
* @param params - Transfer parameters
|
|
846
|
+
* @param params.newOwnerDid - DID of the user to transfer ownership to
|
|
847
|
+
*/
|
|
848
|
+
transferOwnership(params: { newOwnerDid: string }): Promise<void>;
|
|
820
849
|
}
|
|
821
850
|
|
|
822
851
|
/**
|
|
@@ -28,7 +28,7 @@ describe("CollaboratorOperationsImpl", () => {
|
|
|
28
28
|
await collaboratorOps.grant({ userDid: "did:plc:newuser", role: "viewer" });
|
|
29
29
|
|
|
30
30
|
expect(mockSession.fetchHandler).toHaveBeenCalledWith(
|
|
31
|
-
`${serverUrl}/xrpc/com.
|
|
31
|
+
`${serverUrl}/xrpc/com.sds.repo.grantAccess`,
|
|
32
32
|
expect.objectContaining({
|
|
33
33
|
method: "POST",
|
|
34
34
|
body: expect.stringContaining('"read":true'),
|
|
@@ -88,9 +88,7 @@ describe("CollaboratorOperationsImpl", () => {
|
|
|
88
88
|
statusText: "Forbidden",
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
await expect(
|
|
92
|
-
collaboratorOps.grant({ userDid: "did:plc:newuser", role: "viewer" }),
|
|
93
|
-
).rejects.toThrow(NetworkError);
|
|
91
|
+
await expect(collaboratorOps.grant({ userDid: "did:plc:newuser", role: "viewer" })).rejects.toThrow(NetworkError);
|
|
94
92
|
});
|
|
95
93
|
});
|
|
96
94
|
|
|
@@ -104,7 +102,7 @@ describe("CollaboratorOperationsImpl", () => {
|
|
|
104
102
|
await collaboratorOps.revoke({ userDid: "did:plc:revokeduser" });
|
|
105
103
|
|
|
106
104
|
expect(mockSession.fetchHandler).toHaveBeenCalledWith(
|
|
107
|
-
`${serverUrl}/xrpc/com.
|
|
105
|
+
`${serverUrl}/xrpc/com.sds.repo.revokeAccess`,
|
|
108
106
|
expect.objectContaining({
|
|
109
107
|
method: "POST",
|
|
110
108
|
}),
|
|
@@ -121,9 +119,7 @@ describe("CollaboratorOperationsImpl", () => {
|
|
|
121
119
|
statusText: "Not Found",
|
|
122
120
|
});
|
|
123
121
|
|
|
124
|
-
await expect(
|
|
125
|
-
collaboratorOps.revoke({ userDid: "did:plc:user" }),
|
|
126
|
-
).rejects.toThrow(NetworkError);
|
|
122
|
+
await expect(collaboratorOps.revoke({ userDid: "did:plc:user" })).rejects.toThrow(NetworkError);
|
|
127
123
|
});
|
|
128
124
|
});
|
|
129
125
|
|
|
@@ -320,4 +316,123 @@ describe("CollaboratorOperationsImpl", () => {
|
|
|
320
316
|
expect(result).toBeNull();
|
|
321
317
|
});
|
|
322
318
|
});
|
|
319
|
+
|
|
320
|
+
describe("getPermissions", () => {
|
|
321
|
+
it("should get current user permissions successfully", async () => {
|
|
322
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
323
|
+
ok: true,
|
|
324
|
+
json: async () => ({
|
|
325
|
+
permissions: {
|
|
326
|
+
read: true,
|
|
327
|
+
create: true,
|
|
328
|
+
update: true,
|
|
329
|
+
delete: false,
|
|
330
|
+
admin: false,
|
|
331
|
+
owner: false,
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const result = await collaboratorOps.getPermissions();
|
|
337
|
+
|
|
338
|
+
expect(mockSession.fetchHandler).toHaveBeenCalledWith(
|
|
339
|
+
`${serverUrl}/xrpc/com.sds.repo.getPermissions?repo=${encodeURIComponent(repoDid)}`,
|
|
340
|
+
expect.objectContaining({
|
|
341
|
+
method: "GET",
|
|
342
|
+
}),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
expect(result.read).toBe(true);
|
|
346
|
+
expect(result.create).toBe(true);
|
|
347
|
+
expect(result.update).toBe(true);
|
|
348
|
+
expect(result.delete).toBe(false);
|
|
349
|
+
expect(result.admin).toBe(false);
|
|
350
|
+
expect(result.owner).toBe(false);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should handle owner permissions", async () => {
|
|
354
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
355
|
+
ok: true,
|
|
356
|
+
json: async () => ({
|
|
357
|
+
permissions: {
|
|
358
|
+
read: true,
|
|
359
|
+
create: true,
|
|
360
|
+
update: true,
|
|
361
|
+
delete: true,
|
|
362
|
+
admin: true,
|
|
363
|
+
owner: true,
|
|
364
|
+
},
|
|
365
|
+
}),
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const result = await collaboratorOps.getPermissions();
|
|
369
|
+
|
|
370
|
+
expect(result.owner).toBe(true);
|
|
371
|
+
expect(result.admin).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should throw NetworkError on failure", async () => {
|
|
375
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
376
|
+
ok: false,
|
|
377
|
+
statusText: "Forbidden",
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
await expect(collaboratorOps.getPermissions()).rejects.toThrow(NetworkError);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
describe("transferOwnership", () => {
|
|
385
|
+
it("should transfer ownership successfully", async () => {
|
|
386
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
387
|
+
ok: true,
|
|
388
|
+
json: async () => ({}),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
await collaboratorOps.transferOwnership({ newOwnerDid: "did:plc:new-owner" });
|
|
392
|
+
|
|
393
|
+
expect(mockSession.fetchHandler).toHaveBeenCalledWith(
|
|
394
|
+
`${serverUrl}/xrpc/com.sds.repo.transferOwnership`,
|
|
395
|
+
expect.objectContaining({
|
|
396
|
+
method: "POST",
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const body = JSON.parse(mockSession.fetchHandler.mock.calls[0][1].body);
|
|
401
|
+
expect(body.repo).toBe(repoDid);
|
|
402
|
+
expect(body.newOwner).toBe("did:plc:new-owner");
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("should throw NetworkError on failure", async () => {
|
|
406
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
407
|
+
ok: false,
|
|
408
|
+
statusText: "Forbidden",
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await expect(collaboratorOps.transferOwnership({ newOwnerDid: "did:plc:new-owner" })).rejects.toThrow(
|
|
412
|
+
NetworkError,
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("should throw NetworkError when not owner", async () => {
|
|
417
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
418
|
+
ok: false,
|
|
419
|
+
statusText: "Forbidden: Only the owner can transfer ownership",
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
await expect(collaboratorOps.transferOwnership({ newOwnerDid: "did:plc:new-owner" })).rejects.toThrow(
|
|
423
|
+
NetworkError,
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should throw NetworkError when new owner does not exist", async () => {
|
|
428
|
+
mockSession.fetchHandler.mockResolvedValue({
|
|
429
|
+
ok: false,
|
|
430
|
+
statusText: "Not Found: New owner DID not found",
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await expect(collaboratorOps.transferOwnership({ newOwnerDid: "did:plc:nonexistent" })).rejects.toThrow(
|
|
434
|
+
NetworkError,
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
323
438
|
});
|
|
@@ -44,7 +44,7 @@ describe("OrganizationOperationsImpl", () => {
|
|
|
44
44
|
expect(result.permissions.owner).toBe(true);
|
|
45
45
|
|
|
46
46
|
expect(mockSession.fetchHandler).toHaveBeenCalledWith(
|
|
47
|
-
`${serverUrl}/xrpc/com.
|
|
47
|
+
`${serverUrl}/xrpc/com.sds.organization.create`,
|
|
48
48
|
expect.objectContaining({
|
|
49
49
|
method: "POST",
|
|
50
50
|
headers: { "Content-Type": "application/json" },
|