@hypercerts-org/sdk-core 0.2.0-beta.0 → 0.5.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 +13 -301
- package/.turbo/turbo-test.log +30 -29
- package/CHANGELOG.md +49 -3
- package/README.md +473 -50
- package/dist/index.cjs +232 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +57 -19
- package/dist/index.mjs +232 -55
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs +3 -2
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +57 -19
- package/dist/types.mjs +3 -2
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/core/types.ts +3 -2
- package/src/repository/BlobOperationsImpl.ts +1 -1
- package/src/repository/CollaboratorOperationsImpl.ts +184 -30
- package/src/repository/HypercertOperationsImpl.ts +3 -3
- package/src/repository/OrganizationOperationsImpl.ts +70 -22
- package/src/repository/interfaces.ts +45 -4
- package/src/repository/types.ts +1 -1
- package/tests/repository/BlobOperationsImpl.test.ts +10 -9
- package/tests/repository/CollaboratorOperationsImpl.test.ts +138 -23
- package/tests/repository/OrganizationOperationsImpl.test.ts +18 -20
package/dist/index.d.ts
CHANGED
|
@@ -766,9 +766,10 @@ declare const OrganizationSchema: z.ZodObject<{
|
|
|
766
766
|
/**
|
|
767
767
|
* How the current user relates to this organization.
|
|
768
768
|
* - `"owner"`: User created or owns the organization
|
|
769
|
-
* - `"
|
|
769
|
+
* - `"shared"`: User was invited to collaborate (has permissions)
|
|
770
|
+
* - `"none"`: User has no access to this organization
|
|
770
771
|
*/
|
|
771
|
-
accessType: z.ZodEnum<["owner", "
|
|
772
|
+
accessType: z.ZodEnum<["owner", "shared", "none"]>;
|
|
772
773
|
}, "strip", z.ZodTypeAny, {
|
|
773
774
|
did: string;
|
|
774
775
|
handle: string;
|
|
@@ -782,7 +783,7 @@ declare const OrganizationSchema: z.ZodObject<{
|
|
|
782
783
|
admin: boolean;
|
|
783
784
|
owner: boolean;
|
|
784
785
|
};
|
|
785
|
-
accessType: "owner" | "
|
|
786
|
+
accessType: "owner" | "shared" | "none";
|
|
786
787
|
description?: string | undefined;
|
|
787
788
|
}, {
|
|
788
789
|
did: string;
|
|
@@ -797,7 +798,7 @@ declare const OrganizationSchema: z.ZodObject<{
|
|
|
797
798
|
admin: boolean;
|
|
798
799
|
owner: boolean;
|
|
799
800
|
};
|
|
800
|
-
accessType: "owner" | "
|
|
801
|
+
accessType: "owner" | "shared" | "none";
|
|
801
802
|
description?: string | undefined;
|
|
802
803
|
}>;
|
|
803
804
|
/**
|
|
@@ -1022,7 +1023,7 @@ interface OrganizationInfo {
|
|
|
1022
1023
|
name: string;
|
|
1023
1024
|
description?: string;
|
|
1024
1025
|
createdAt: string;
|
|
1025
|
-
accessType: "owner" | "
|
|
1026
|
+
accessType: "owner" | "shared" | "none";
|
|
1026
1027
|
permissions: CollaboratorPermissions;
|
|
1027
1028
|
collaboratorCount?: number;
|
|
1028
1029
|
profile?: {
|
|
@@ -1089,16 +1090,6 @@ interface HypercertWithMetadata {
|
|
|
1089
1090
|
record: HypercertClaim;
|
|
1090
1091
|
}
|
|
1091
1092
|
|
|
1092
|
-
/**
|
|
1093
|
-
* Repository interfaces - Operation contracts for repository functionality.
|
|
1094
|
-
*
|
|
1095
|
-
* This module defines the interfaces for all repository operations,
|
|
1096
|
-
* providing clear contracts for record management, blob handling,
|
|
1097
|
-
* profile management, and domain-specific operations.
|
|
1098
|
-
*
|
|
1099
|
-
* @packageDocumentation
|
|
1100
|
-
*/
|
|
1101
|
-
|
|
1102
1093
|
/**
|
|
1103
1094
|
* Parameters for creating a new hypercert.
|
|
1104
1095
|
*
|
|
@@ -1845,6 +1836,17 @@ interface HypercertOperations extends EventEmitter<HypercertEvents> {
|
|
|
1845
1836
|
* const hasAccess = await repo.collaborators.hasAccess("did:plc:someone");
|
|
1846
1837
|
* const role = await repo.collaborators.getRole("did:plc:someone");
|
|
1847
1838
|
*
|
|
1839
|
+
* // Get current user permissions
|
|
1840
|
+
* const permissions = await repo.collaborators.getPermissions();
|
|
1841
|
+
* if (permissions.admin) {
|
|
1842
|
+
* // Can manage collaborators
|
|
1843
|
+
* }
|
|
1844
|
+
*
|
|
1845
|
+
* // Transfer ownership
|
|
1846
|
+
* await repo.collaborators.transferOwnership({
|
|
1847
|
+
* newOwnerDid: "did:plc:new-owner",
|
|
1848
|
+
* });
|
|
1849
|
+
*
|
|
1848
1850
|
* // Revoke access
|
|
1849
1851
|
* await repo.collaborators.revoke({ userDid: "did:plc:former-user" });
|
|
1850
1852
|
* ```
|
|
@@ -1873,9 +1875,18 @@ interface CollaboratorOperations {
|
|
|
1873
1875
|
/**
|
|
1874
1876
|
* Lists all collaborators on the repository.
|
|
1875
1877
|
*
|
|
1876
|
-
* @
|
|
1878
|
+
* @param params - Optional pagination parameters
|
|
1879
|
+
* @param params.limit - Maximum number of results (1-100, default 50)
|
|
1880
|
+
* @param params.cursor - Pagination cursor from previous response
|
|
1881
|
+
* @returns Promise resolving to collaborators and optional cursor
|
|
1877
1882
|
*/
|
|
1878
|
-
list(
|
|
1883
|
+
list(params?: {
|
|
1884
|
+
limit?: number;
|
|
1885
|
+
cursor?: string;
|
|
1886
|
+
}): Promise<{
|
|
1887
|
+
collaborators: RepositoryAccessGrant[];
|
|
1888
|
+
cursor?: string;
|
|
1889
|
+
}>;
|
|
1879
1890
|
/**
|
|
1880
1891
|
* Checks if a user has any access to the repository.
|
|
1881
1892
|
*
|
|
@@ -1890,6 +1901,24 @@ interface CollaboratorOperations {
|
|
|
1890
1901
|
* @returns Promise resolving to role, or `null` if no access
|
|
1891
1902
|
*/
|
|
1892
1903
|
getRole(userDid: string): Promise<RepositoryRole | null>;
|
|
1904
|
+
/**
|
|
1905
|
+
* Gets the current user's permissions for this repository.
|
|
1906
|
+
*
|
|
1907
|
+
* @returns Promise resolving to permission flags
|
|
1908
|
+
*/
|
|
1909
|
+
getPermissions(): Promise<CollaboratorPermissions>;
|
|
1910
|
+
/**
|
|
1911
|
+
* Transfers repository ownership to another user.
|
|
1912
|
+
*
|
|
1913
|
+
* **WARNING**: This action is irreversible. The new owner will have
|
|
1914
|
+
* full control of the repository.
|
|
1915
|
+
*
|
|
1916
|
+
* @param params - Transfer parameters
|
|
1917
|
+
* @param params.newOwnerDid - DID of the user to transfer ownership to
|
|
1918
|
+
*/
|
|
1919
|
+
transferOwnership(params: {
|
|
1920
|
+
newOwnerDid: string;
|
|
1921
|
+
}): Promise<void>;
|
|
1893
1922
|
}
|
|
1894
1923
|
/**
|
|
1895
1924
|
* Organization operations for SDS organization management.
|
|
@@ -1936,9 +1965,18 @@ interface OrganizationOperations {
|
|
|
1936
1965
|
/**
|
|
1937
1966
|
* Lists organizations the current user has access to.
|
|
1938
1967
|
*
|
|
1939
|
-
* @
|
|
1968
|
+
* @param params - Optional pagination parameters
|
|
1969
|
+
* @param params.limit - Maximum number of results (1-100, default 50)
|
|
1970
|
+
* @param params.cursor - Pagination cursor from previous response
|
|
1971
|
+
* @returns Promise resolving to organizations and optional cursor
|
|
1940
1972
|
*/
|
|
1941
|
-
list(
|
|
1973
|
+
list(params?: {
|
|
1974
|
+
limit?: number;
|
|
1975
|
+
cursor?: string;
|
|
1976
|
+
}): Promise<{
|
|
1977
|
+
organizations: OrganizationInfo[];
|
|
1978
|
+
cursor?: string;
|
|
1979
|
+
}>;
|
|
1942
1980
|
}
|
|
1943
1981
|
|
|
1944
1982
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1669,7 +1669,7 @@ class BlobOperationsImpl {
|
|
|
1669
1669
|
throw new NetworkError("Failed to upload blob");
|
|
1670
1670
|
}
|
|
1671
1671
|
return {
|
|
1672
|
-
ref: result.data.blob.ref,
|
|
1672
|
+
ref: { $link: result.data.blob.ref.toString() },
|
|
1673
1673
|
mimeType: result.data.blob.mimeType,
|
|
1674
1674
|
size: result.data.blob.size,
|
|
1675
1675
|
};
|
|
@@ -2176,7 +2176,7 @@ class HypercertOperationsImpl extends EventEmitter {
|
|
|
2176
2176
|
if (uploadResult.success) {
|
|
2177
2177
|
imageBlobRef = {
|
|
2178
2178
|
$type: "blob",
|
|
2179
|
-
ref: uploadResult.data.blob.ref,
|
|
2179
|
+
ref: { $link: uploadResult.data.blob.ref.toString() },
|
|
2180
2180
|
mimeType: uploadResult.data.blob.mimeType,
|
|
2181
2181
|
size: uploadResult.data.blob.size,
|
|
2182
2182
|
};
|
|
@@ -2601,7 +2601,7 @@ class HypercertOperationsImpl extends EventEmitter {
|
|
|
2601
2601
|
if (uploadResult.success) {
|
|
2602
2602
|
locationValue = {
|
|
2603
2603
|
$type: "blob",
|
|
2604
|
-
ref: uploadResult.data.blob.ref,
|
|
2604
|
+
ref: { $link: uploadResult.data.blob.ref.toString() },
|
|
2605
2605
|
mimeType: uploadResult.data.blob.mimeType,
|
|
2606
2606
|
size: uploadResult.data.blob.size,
|
|
2607
2607
|
};
|
|
@@ -2886,7 +2886,7 @@ class HypercertOperationsImpl extends EventEmitter {
|
|
|
2886
2886
|
if (uploadResult.success) {
|
|
2887
2887
|
coverPhotoRef = {
|
|
2888
2888
|
$type: "blob",
|
|
2889
|
-
ref: uploadResult.data.blob.ref,
|
|
2889
|
+
ref: { $link: uploadResult.data.blob.ref.toString() },
|
|
2890
2890
|
mimeType: uploadResult.data.blob.mimeType,
|
|
2891
2891
|
size: uploadResult.data.blob.size,
|
|
2892
2892
|
};
|
|
@@ -3040,9 +3040,11 @@ class HypercertOperationsImpl extends EventEmitter {
|
|
|
3040
3040
|
* - `owner`: Full control including ownership management
|
|
3041
3041
|
*
|
|
3042
3042
|
* **SDS API Endpoints Used**:
|
|
3043
|
-
* - `com.
|
|
3044
|
-
* - `com.
|
|
3045
|
-
* - `com.
|
|
3043
|
+
* - `com.sds.repo.grantAccess`: Grant access to a user
|
|
3044
|
+
* - `com.sds.repo.revokeAccess`: Revoke access from a user
|
|
3045
|
+
* - `com.sds.repo.listCollaborators`: List all collaborators
|
|
3046
|
+
* - `com.sds.repo.getPermissions`: Get current user's permissions
|
|
3047
|
+
* - `com.sds.repo.transferOwnership`: Transfer repository ownership
|
|
3046
3048
|
*
|
|
3047
3049
|
* @example
|
|
3048
3050
|
* ```typescript
|
|
@@ -3115,6 +3117,26 @@ class CollaboratorOperationsImpl {
|
|
|
3115
3117
|
return "editor";
|
|
3116
3118
|
return "viewer";
|
|
3117
3119
|
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Converts a permission string array to a permissions object.
|
|
3122
|
+
*
|
|
3123
|
+
* The SDS API returns permissions as an array of strings (e.g., ["read", "create"]).
|
|
3124
|
+
* This method converts them to the boolean flag format used by the SDK.
|
|
3125
|
+
*
|
|
3126
|
+
* @param permissionArray - Array of permission strings from SDS API
|
|
3127
|
+
* @returns Permission flags object
|
|
3128
|
+
* @internal
|
|
3129
|
+
*/
|
|
3130
|
+
parsePermissions(permissionArray) {
|
|
3131
|
+
return {
|
|
3132
|
+
read: permissionArray.includes("read"),
|
|
3133
|
+
create: permissionArray.includes("create"),
|
|
3134
|
+
update: permissionArray.includes("update"),
|
|
3135
|
+
delete: permissionArray.includes("delete"),
|
|
3136
|
+
admin: permissionArray.includes("admin"),
|
|
3137
|
+
owner: permissionArray.includes("owner"),
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3118
3140
|
/**
|
|
3119
3141
|
* Grants repository access to a user.
|
|
3120
3142
|
*
|
|
@@ -3144,7 +3166,7 @@ class CollaboratorOperationsImpl {
|
|
|
3144
3166
|
*/
|
|
3145
3167
|
async grant(params) {
|
|
3146
3168
|
const permissions = this.roleToPermissions(params.role);
|
|
3147
|
-
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.
|
|
3169
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.grantAccess`, {
|
|
3148
3170
|
method: "POST",
|
|
3149
3171
|
headers: { "Content-Type": "application/json" },
|
|
3150
3172
|
body: JSON.stringify({
|
|
@@ -3176,7 +3198,7 @@ class CollaboratorOperationsImpl {
|
|
|
3176
3198
|
* ```
|
|
3177
3199
|
*/
|
|
3178
3200
|
async revoke(params) {
|
|
3179
|
-
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.
|
|
3201
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.revokeAccess`, {
|
|
3180
3202
|
method: "POST",
|
|
3181
3203
|
headers: { "Content-Type": "application/json" },
|
|
3182
3204
|
body: JSON.stringify({
|
|
@@ -3191,7 +3213,10 @@ class CollaboratorOperationsImpl {
|
|
|
3191
3213
|
/**
|
|
3192
3214
|
* Lists all collaborators on the repository.
|
|
3193
3215
|
*
|
|
3194
|
-
* @
|
|
3216
|
+
* @param params - Optional pagination parameters
|
|
3217
|
+
* @param params.limit - Maximum number of results (1-100, default 50)
|
|
3218
|
+
* @param params.cursor - Pagination cursor from previous response
|
|
3219
|
+
* @returns Promise resolving to collaborators and optional cursor
|
|
3195
3220
|
* @throws {@link NetworkError} if the list operation fails
|
|
3196
3221
|
*
|
|
3197
3222
|
* @remarks
|
|
@@ -3200,34 +3225,49 @@ class CollaboratorOperationsImpl {
|
|
|
3200
3225
|
*
|
|
3201
3226
|
* @example
|
|
3202
3227
|
* ```typescript
|
|
3203
|
-
*
|
|
3228
|
+
* // Get first page
|
|
3229
|
+
* const page1 = await repo.collaborators.list({ limit: 10 });
|
|
3230
|
+
* console.log(`Found ${page1.collaborators.length} collaborators`);
|
|
3231
|
+
*
|
|
3232
|
+
* // Get next page if available
|
|
3233
|
+
* if (page1.cursor) {
|
|
3234
|
+
* const page2 = await repo.collaborators.list({ limit: 10, cursor: page1.cursor });
|
|
3235
|
+
* }
|
|
3204
3236
|
*
|
|
3205
3237
|
* // Filter active collaborators
|
|
3206
|
-
* const active = collaborators.filter(c => !c.revokedAt);
|
|
3207
|
-
*
|
|
3208
|
-
* // Group by role
|
|
3209
|
-
* const byRole = {
|
|
3210
|
-
* owners: active.filter(c => c.role === "owner"),
|
|
3211
|
-
* admins: active.filter(c => c.role === "admin"),
|
|
3212
|
-
* editors: active.filter(c => c.role === "editor"),
|
|
3213
|
-
* viewers: active.filter(c => c.role === "viewer"),
|
|
3214
|
-
* };
|
|
3238
|
+
* const active = page1.collaborators.filter(c => !c.revokedAt);
|
|
3215
3239
|
* ```
|
|
3216
3240
|
*/
|
|
3217
|
-
async list() {
|
|
3218
|
-
const
|
|
3241
|
+
async list(params) {
|
|
3242
|
+
const queryParams = new URLSearchParams({
|
|
3243
|
+
repo: this.repoDid,
|
|
3244
|
+
});
|
|
3245
|
+
if (params?.limit !== undefined) {
|
|
3246
|
+
queryParams.set("limit", params.limit.toString());
|
|
3247
|
+
}
|
|
3248
|
+
if (params?.cursor) {
|
|
3249
|
+
queryParams.set("cursor", params.cursor);
|
|
3250
|
+
}
|
|
3251
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?${queryParams.toString()}`, { method: "GET" });
|
|
3219
3252
|
if (!response.ok) {
|
|
3220
3253
|
throw new NetworkError(`Failed to list collaborators: ${response.statusText}`);
|
|
3221
3254
|
}
|
|
3222
3255
|
const data = await response.json();
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3256
|
+
const collaborators = (data.collaborators || []).map((c) => {
|
|
3257
|
+
const permissions = this.parsePermissions(c.permissions);
|
|
3258
|
+
return {
|
|
3259
|
+
userDid: c.userDid,
|
|
3260
|
+
role: this.permissionsToRole(permissions),
|
|
3261
|
+
permissions: permissions,
|
|
3262
|
+
grantedBy: c.grantedBy,
|
|
3263
|
+
grantedAt: c.grantedAt,
|
|
3264
|
+
revokedAt: c.revokedAt,
|
|
3265
|
+
};
|
|
3266
|
+
});
|
|
3267
|
+
return {
|
|
3268
|
+
collaborators,
|
|
3269
|
+
cursor: data.cursor,
|
|
3270
|
+
};
|
|
3231
3271
|
}
|
|
3232
3272
|
/**
|
|
3233
3273
|
* Checks if a user has any access to the repository.
|
|
@@ -3250,7 +3290,7 @@ class CollaboratorOperationsImpl {
|
|
|
3250
3290
|
*/
|
|
3251
3291
|
async hasAccess(userDid) {
|
|
3252
3292
|
try {
|
|
3253
|
-
const collaborators = await this.list();
|
|
3293
|
+
const { collaborators } = await this.list();
|
|
3254
3294
|
return collaborators.some((c) => c.userDid === userDid && !c.revokedAt);
|
|
3255
3295
|
}
|
|
3256
3296
|
catch {
|
|
@@ -3272,10 +3312,108 @@ class CollaboratorOperationsImpl {
|
|
|
3272
3312
|
* ```
|
|
3273
3313
|
*/
|
|
3274
3314
|
async getRole(userDid) {
|
|
3275
|
-
const collaborators = await this.list();
|
|
3315
|
+
const { collaborators } = await this.list();
|
|
3276
3316
|
const collab = collaborators.find((c) => c.userDid === userDid && !c.revokedAt);
|
|
3277
3317
|
return collab?.role ?? null;
|
|
3278
3318
|
}
|
|
3319
|
+
/**
|
|
3320
|
+
* Gets the current user's permissions for this repository.
|
|
3321
|
+
*
|
|
3322
|
+
* @returns Promise resolving to the permission flags
|
|
3323
|
+
* @throws {@link NetworkError} if the request fails
|
|
3324
|
+
*
|
|
3325
|
+
* @remarks
|
|
3326
|
+
* This is useful for checking what actions the current user can perform
|
|
3327
|
+
* before attempting operations that might fail due to insufficient permissions.
|
|
3328
|
+
*
|
|
3329
|
+
* @example
|
|
3330
|
+
* ```typescript
|
|
3331
|
+
* const permissions = await repo.collaborators.getPermissions();
|
|
3332
|
+
*
|
|
3333
|
+
* if (permissions.admin) {
|
|
3334
|
+
* // Show admin UI
|
|
3335
|
+
* console.log("You can manage collaborators");
|
|
3336
|
+
* }
|
|
3337
|
+
*
|
|
3338
|
+
* if (permissions.create) {
|
|
3339
|
+
* console.log("You can create records");
|
|
3340
|
+
* }
|
|
3341
|
+
* ```
|
|
3342
|
+
*
|
|
3343
|
+
* @example Conditional UI rendering
|
|
3344
|
+
* ```typescript
|
|
3345
|
+
* const permissions = await repo.collaborators.getPermissions();
|
|
3346
|
+
*
|
|
3347
|
+
* // Show/hide UI elements based on permissions
|
|
3348
|
+
* const canEdit = permissions.update;
|
|
3349
|
+
* const canDelete = permissions.delete;
|
|
3350
|
+
* const isAdmin = permissions.admin;
|
|
3351
|
+
* const isOwner = permissions.owner;
|
|
3352
|
+
* ```
|
|
3353
|
+
*/
|
|
3354
|
+
async getPermissions() {
|
|
3355
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.getPermissions?repo=${encodeURIComponent(this.repoDid)}`, { method: "GET" });
|
|
3356
|
+
if (!response.ok) {
|
|
3357
|
+
throw new NetworkError(`Failed to get permissions: ${response.statusText}`);
|
|
3358
|
+
}
|
|
3359
|
+
const data = await response.json();
|
|
3360
|
+
return data.permissions;
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Transfers repository ownership to another user.
|
|
3364
|
+
*
|
|
3365
|
+
* @param params - Transfer parameters
|
|
3366
|
+
* @param params.newOwnerDid - DID of the user to transfer ownership to
|
|
3367
|
+
* @throws {@link NetworkError} if the transfer fails
|
|
3368
|
+
*
|
|
3369
|
+
* @remarks
|
|
3370
|
+
* **IMPORTANT**: This action is irreversible. Once ownership is transferred:
|
|
3371
|
+
* - The new owner gains full control of the repository
|
|
3372
|
+
* - Your role will be changed to admin (or specified role)
|
|
3373
|
+
* - You cannot transfer ownership back without the new owner's approval
|
|
3374
|
+
*
|
|
3375
|
+
* **Requirements**:
|
|
3376
|
+
* - You must be the current owner
|
|
3377
|
+
* - The new owner must have an existing account
|
|
3378
|
+
* - The new owner will be notified of the ownership transfer
|
|
3379
|
+
*
|
|
3380
|
+
* @example
|
|
3381
|
+
* ```typescript
|
|
3382
|
+
* // Transfer ownership to another user
|
|
3383
|
+
* await repo.collaborators.transferOwnership({
|
|
3384
|
+
* newOwnerDid: "did:plc:new-owner",
|
|
3385
|
+
* });
|
|
3386
|
+
*
|
|
3387
|
+
* console.log("Ownership transferred successfully");
|
|
3388
|
+
* // You are now an admin, not the owner
|
|
3389
|
+
* ```
|
|
3390
|
+
*
|
|
3391
|
+
* @example With confirmation
|
|
3392
|
+
* ```typescript
|
|
3393
|
+
* const confirmTransfer = await askUser(
|
|
3394
|
+
* "Are you sure you want to transfer ownership? This cannot be undone."
|
|
3395
|
+
* );
|
|
3396
|
+
*
|
|
3397
|
+
* if (confirmTransfer) {
|
|
3398
|
+
* await repo.collaborators.transferOwnership({
|
|
3399
|
+
* newOwnerDid: "did:plc:new-owner",
|
|
3400
|
+
* });
|
|
3401
|
+
* }
|
|
3402
|
+
* ```
|
|
3403
|
+
*/
|
|
3404
|
+
async transferOwnership(params) {
|
|
3405
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.transferOwnership`, {
|
|
3406
|
+
method: "POST",
|
|
3407
|
+
headers: { "Content-Type": "application/json" },
|
|
3408
|
+
body: JSON.stringify({
|
|
3409
|
+
repo: this.repoDid,
|
|
3410
|
+
newOwner: params.newOwnerDid,
|
|
3411
|
+
}),
|
|
3412
|
+
});
|
|
3413
|
+
if (!response.ok) {
|
|
3414
|
+
throw new NetworkError(`Failed to transfer ownership: ${response.statusText}`);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3279
3417
|
}
|
|
3280
3418
|
|
|
3281
3419
|
/**
|
|
@@ -3302,8 +3440,8 @@ class CollaboratorOperationsImpl {
|
|
|
3302
3440
|
* {@link Repository.organizations} on an SDS-connected repository.
|
|
3303
3441
|
*
|
|
3304
3442
|
* **SDS API Endpoints Used**:
|
|
3305
|
-
* - `com.
|
|
3306
|
-
* - `com.
|
|
3443
|
+
* - `com.sds.organization.create`: Create a new organization
|
|
3444
|
+
* - `com.sds.organization.list`: List accessible organizations
|
|
3307
3445
|
*
|
|
3308
3446
|
* **Access Types**:
|
|
3309
3447
|
* - `"owner"`: User created or owns the organization
|
|
@@ -3381,10 +3519,17 @@ class OrganizationOperationsImpl {
|
|
|
3381
3519
|
* ```
|
|
3382
3520
|
*/
|
|
3383
3521
|
async create(params) {
|
|
3384
|
-
const
|
|
3522
|
+
const userDid = this.session.did || this.session.sub;
|
|
3523
|
+
if (!userDid) {
|
|
3524
|
+
throw new NetworkError("No authenticated user found");
|
|
3525
|
+
}
|
|
3526
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.create`, {
|
|
3385
3527
|
method: "POST",
|
|
3386
3528
|
headers: { "Content-Type": "application/json" },
|
|
3387
|
-
body: JSON.stringify(
|
|
3529
|
+
body: JSON.stringify({
|
|
3530
|
+
...params,
|
|
3531
|
+
creatorDid: userDid,
|
|
3532
|
+
}),
|
|
3388
3533
|
});
|
|
3389
3534
|
if (!response.ok) {
|
|
3390
3535
|
throw new NetworkError(`Failed to create organization: ${response.statusText}`);
|
|
@@ -3396,8 +3541,15 @@ class OrganizationOperationsImpl {
|
|
|
3396
3541
|
name: data.name,
|
|
3397
3542
|
description: data.description,
|
|
3398
3543
|
createdAt: data.createdAt || new Date().toISOString(),
|
|
3399
|
-
accessType: "owner",
|
|
3400
|
-
permissions:
|
|
3544
|
+
accessType: data.accessType || "owner",
|
|
3545
|
+
permissions: data.permissions || {
|
|
3546
|
+
read: true,
|
|
3547
|
+
create: true,
|
|
3548
|
+
update: true,
|
|
3549
|
+
delete: true,
|
|
3550
|
+
admin: true,
|
|
3551
|
+
owner: true,
|
|
3552
|
+
},
|
|
3401
3553
|
};
|
|
3402
3554
|
}
|
|
3403
3555
|
/**
|
|
@@ -3424,8 +3576,8 @@ class OrganizationOperationsImpl {
|
|
|
3424
3576
|
*/
|
|
3425
3577
|
async get(did) {
|
|
3426
3578
|
try {
|
|
3427
|
-
const
|
|
3428
|
-
return
|
|
3579
|
+
const { organizations } = await this.list();
|
|
3580
|
+
return organizations.find((o) => o.did === did) ?? null;
|
|
3429
3581
|
}
|
|
3430
3582
|
catch {
|
|
3431
3583
|
return null;
|
|
@@ -3434,7 +3586,10 @@ class OrganizationOperationsImpl {
|
|
|
3434
3586
|
/**
|
|
3435
3587
|
* Lists organizations the current user has access to.
|
|
3436
3588
|
*
|
|
3437
|
-
* @
|
|
3589
|
+
* @param params - Optional pagination parameters
|
|
3590
|
+
* @param params.limit - Maximum number of results (1-100, default 50)
|
|
3591
|
+
* @param params.cursor - Pagination cursor from previous response
|
|
3592
|
+
* @returns Promise resolving to organizations and optional cursor
|
|
3438
3593
|
* @throws {@link NetworkError} if the list operation fails
|
|
3439
3594
|
*
|
|
3440
3595
|
* @remarks
|
|
@@ -3446,21 +3601,25 @@ class OrganizationOperationsImpl {
|
|
|
3446
3601
|
*
|
|
3447
3602
|
* @example
|
|
3448
3603
|
* ```typescript
|
|
3449
|
-
*
|
|
3604
|
+
* // Get first page
|
|
3605
|
+
* const page1 = await repo.organizations.list({ limit: 20 });
|
|
3606
|
+
* console.log(`Found ${page1.organizations.length} organizations`);
|
|
3450
3607
|
*
|
|
3451
|
-
* //
|
|
3452
|
-
*
|
|
3453
|
-
*
|
|
3608
|
+
* // Get next page if available
|
|
3609
|
+
* if (page1.cursor) {
|
|
3610
|
+
* const page2 = await repo.organizations.list({ limit: 20, cursor: page1.cursor });
|
|
3611
|
+
* }
|
|
3454
3612
|
*
|
|
3455
|
-
*
|
|
3456
|
-
*
|
|
3613
|
+
* // Filter by access type
|
|
3614
|
+
* const owned = page1.organizations.filter(o => o.accessType === "owner");
|
|
3615
|
+
* const shared = page1.organizations.filter(o => o.accessType === "shared");
|
|
3457
3616
|
* ```
|
|
3458
3617
|
*
|
|
3459
3618
|
* @example Display organization details
|
|
3460
3619
|
* ```typescript
|
|
3461
|
-
* const
|
|
3620
|
+
* const { organizations } = await repo.organizations.list();
|
|
3462
3621
|
*
|
|
3463
|
-
* for (const org of
|
|
3622
|
+
* for (const org of organizations) {
|
|
3464
3623
|
* console.log(`${org.name} (@${org.handle})`);
|
|
3465
3624
|
* console.log(` DID: ${org.did}`);
|
|
3466
3625
|
* console.log(` Access: ${org.accessType}`);
|
|
@@ -3470,21 +3629,38 @@ class OrganizationOperationsImpl {
|
|
|
3470
3629
|
* }
|
|
3471
3630
|
* ```
|
|
3472
3631
|
*/
|
|
3473
|
-
async list() {
|
|
3474
|
-
const
|
|
3632
|
+
async list(params) {
|
|
3633
|
+
const userDid = this.session.did || this.session.sub;
|
|
3634
|
+
if (!userDid) {
|
|
3635
|
+
throw new NetworkError("No authenticated user found");
|
|
3636
|
+
}
|
|
3637
|
+
const queryParams = new URLSearchParams({
|
|
3638
|
+
userDid,
|
|
3639
|
+
});
|
|
3640
|
+
if (params?.limit !== undefined) {
|
|
3641
|
+
queryParams.set("limit", params.limit.toString());
|
|
3642
|
+
}
|
|
3643
|
+
if (params?.cursor) {
|
|
3644
|
+
queryParams.set("cursor", params.cursor);
|
|
3645
|
+
}
|
|
3646
|
+
const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.list?${queryParams.toString()}`, { method: "GET" });
|
|
3475
3647
|
if (!response.ok) {
|
|
3476
3648
|
throw new NetworkError(`Failed to list organizations: ${response.statusText}`);
|
|
3477
3649
|
}
|
|
3478
3650
|
const data = await response.json();
|
|
3479
|
-
|
|
3651
|
+
const organizations = (data.organizations || []).map((r) => ({
|
|
3480
3652
|
did: r.did,
|
|
3481
3653
|
handle: r.handle,
|
|
3482
3654
|
name: r.name,
|
|
3483
3655
|
description: r.description,
|
|
3484
|
-
createdAt: new Date().toISOString(),
|
|
3656
|
+
createdAt: r.createdAt || new Date().toISOString(),
|
|
3485
3657
|
accessType: r.accessType,
|
|
3486
3658
|
permissions: r.permissions,
|
|
3487
3659
|
}));
|
|
3660
|
+
return {
|
|
3661
|
+
organizations,
|
|
3662
|
+
cursor: data.cursor,
|
|
3663
|
+
};
|
|
3488
3664
|
}
|
|
3489
3665
|
}
|
|
3490
3666
|
|
|
@@ -4407,9 +4583,10 @@ const OrganizationSchema = z.object({
|
|
|
4407
4583
|
/**
|
|
4408
4584
|
* How the current user relates to this organization.
|
|
4409
4585
|
* - `"owner"`: User created or owns the organization
|
|
4410
|
-
* - `"
|
|
4586
|
+
* - `"shared"`: User was invited to collaborate (has permissions)
|
|
4587
|
+
* - `"none"`: User has no access to this organization
|
|
4411
4588
|
*/
|
|
4412
|
-
accessType: z.enum(["owner", "
|
|
4589
|
+
accessType: z.enum(["owner", "shared", "none"]),
|
|
4413
4590
|
});
|
|
4414
4591
|
/**
|
|
4415
4592
|
* Zod schema for collaborator data.
|