@hypercerts-org/sdk-core 0.5.0-beta.0 → 0.7.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.
Files changed (57) hide show
  1. package/README.md +130 -8
  2. package/dist/index.cjs +93 -15
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +64 -1
  5. package/dist/index.mjs +93 -16
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +9 -5
  8. package/.turbo/turbo-build.log +0 -40
  9. package/.turbo/turbo-test.log +0 -119
  10. package/CHANGELOG.md +0 -62
  11. package/eslint.config.mjs +0 -22
  12. package/rollup.config.js +0 -75
  13. package/src/auth/OAuthClient.ts +0 -497
  14. package/src/core/SDK.ts +0 -410
  15. package/src/core/config.ts +0 -243
  16. package/src/core/errors.ts +0 -257
  17. package/src/core/interfaces.ts +0 -324
  18. package/src/core/types.ts +0 -282
  19. package/src/errors.ts +0 -57
  20. package/src/index.ts +0 -107
  21. package/src/lexicons.ts +0 -64
  22. package/src/repository/BlobOperationsImpl.ts +0 -199
  23. package/src/repository/CollaboratorOperationsImpl.ts +0 -442
  24. package/src/repository/HypercertOperationsImpl.ts +0 -1146
  25. package/src/repository/LexiconRegistry.ts +0 -332
  26. package/src/repository/OrganizationOperationsImpl.ts +0 -282
  27. package/src/repository/ProfileOperationsImpl.ts +0 -281
  28. package/src/repository/RecordOperationsImpl.ts +0 -340
  29. package/src/repository/Repository.ts +0 -482
  30. package/src/repository/interfaces.ts +0 -909
  31. package/src/repository/types.ts +0 -111
  32. package/src/services/hypercerts/types.ts +0 -87
  33. package/src/storage/InMemorySessionStore.ts +0 -127
  34. package/src/storage/InMemoryStateStore.ts +0 -146
  35. package/src/storage.ts +0 -63
  36. package/src/testing/index.ts +0 -67
  37. package/src/testing/mocks.ts +0 -142
  38. package/src/testing/stores.ts +0 -285
  39. package/src/testing.ts +0 -64
  40. package/src/types.ts +0 -86
  41. package/tests/auth/OAuthClient.test.ts +0 -164
  42. package/tests/core/SDK.test.ts +0 -176
  43. package/tests/core/errors.test.ts +0 -81
  44. package/tests/repository/BlobOperationsImpl.test.ts +0 -155
  45. package/tests/repository/CollaboratorOperationsImpl.test.ts +0 -438
  46. package/tests/repository/HypercertOperationsImpl.test.ts +0 -652
  47. package/tests/repository/LexiconRegistry.test.ts +0 -192
  48. package/tests/repository/OrganizationOperationsImpl.test.ts +0 -240
  49. package/tests/repository/ProfileOperationsImpl.test.ts +0 -254
  50. package/tests/repository/RecordOperationsImpl.test.ts +0 -375
  51. package/tests/repository/Repository.test.ts +0 -149
  52. package/tests/utils/fixtures.ts +0 -117
  53. package/tests/utils/mocks.ts +0 -109
  54. package/tests/utils/repository-fixtures.ts +0 -78
  55. package/tsconfig.json +0 -11
  56. package/tsconfig.tsbuildinfo +0 -1
  57. package/vitest.config.ts +0 -30
@@ -1,442 +0,0 @@
1
- /**
2
- * CollaboratorOperationsImpl - SDS collaborator management operations.
3
- *
4
- * This module provides the implementation for managing collaborator
5
- * access on Shared Data Server (SDS) repositories.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
- import { NetworkError } from "../core/errors.js";
11
- import type { CollaboratorPermissions, Session } from "../core/types.js";
12
- import type { CollaboratorOperations } from "./interfaces.js";
13
- import type { RepositoryRole, RepositoryAccessGrant } from "./types.js";
14
-
15
- /**
16
- * Implementation of collaborator operations for SDS access control.
17
- *
18
- * This class manages access permissions for shared repositories on
19
- * Shared Data Servers (SDS). It provides role-based access control
20
- * with predefined permission sets.
21
- *
22
- * @remarks
23
- * This class is typically not instantiated directly. Access it through
24
- * {@link Repository.collaborators} on an SDS-connected repository.
25
- *
26
- * **Role Hierarchy**:
27
- * - `viewer`: Read-only access
28
- * - `editor`: Read + Create + Update
29
- * - `admin`: All permissions except ownership transfer
30
- * - `owner`: Full control including ownership management
31
- *
32
- * **SDS API Endpoints Used**:
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
38
- *
39
- * @example
40
- * ```typescript
41
- * // Get SDS repository
42
- * const sdsRepo = sdk.repository(session, { server: "sds" });
43
- *
44
- * // Grant editor access
45
- * await sdsRepo.collaborators.grant({
46
- * userDid: "did:plc:new-user",
47
- * role: "editor",
48
- * });
49
- *
50
- * // List all collaborators
51
- * const collaborators = await sdsRepo.collaborators.list();
52
- *
53
- * // Check specific user
54
- * const hasAccess = await sdsRepo.collaborators.hasAccess("did:plc:someone");
55
- * const role = await sdsRepo.collaborators.getRole("did:plc:someone");
56
- * ```
57
- *
58
- * @internal
59
- */
60
- export class CollaboratorOperationsImpl implements CollaboratorOperations {
61
- /**
62
- * Creates a new CollaboratorOperationsImpl.
63
- *
64
- * @param session - Authenticated OAuth session with fetchHandler
65
- * @param repoDid - DID of the repository to manage
66
- * @param serverUrl - SDS server URL
67
- *
68
- * @internal
69
- */
70
- constructor(
71
- private session: Session,
72
- private repoDid: string,
73
- private serverUrl: string,
74
- ) {}
75
-
76
- /**
77
- * Converts a role to its corresponding permissions object.
78
- *
79
- * @param role - The role to convert
80
- * @returns Permission flags for the role
81
- * @internal
82
- */
83
- private roleToPermissions(role: RepositoryRole): CollaboratorPermissions {
84
- switch (role) {
85
- case "viewer":
86
- return { read: true, create: false, update: false, delete: false, admin: false, owner: false };
87
- case "editor":
88
- return { read: true, create: true, update: true, delete: false, admin: false, owner: false };
89
- case "admin":
90
- return { read: true, create: true, update: true, delete: true, admin: true, owner: false };
91
- case "owner":
92
- return { read: true, create: true, update: true, delete: true, admin: true, owner: true };
93
- }
94
- }
95
-
96
- /**
97
- * Determines the role from a permissions object.
98
- *
99
- * @param permissions - The permissions to analyze
100
- * @returns The highest role matching the permissions
101
- * @internal
102
- */
103
- private permissionsToRole(permissions: CollaboratorPermissions): RepositoryRole {
104
- if (permissions.owner) return "owner";
105
- if (permissions.admin) return "admin";
106
- if (permissions.create || permissions.update) return "editor";
107
- return "viewer";
108
- }
109
-
110
- /**
111
- * Converts a permission string array to a permissions object.
112
- *
113
- * The SDS API returns permissions as an array of strings (e.g., ["read", "create"]).
114
- * This method converts them to the boolean flag format used by the SDK.
115
- *
116
- * @param permissionArray - Array of permission strings from SDS API
117
- * @returns Permission flags object
118
- * @internal
119
- */
120
- private parsePermissions(permissionArray: string[]): CollaboratorPermissions {
121
- return {
122
- read: permissionArray.includes("read"),
123
- create: permissionArray.includes("create"),
124
- update: permissionArray.includes("update"),
125
- delete: permissionArray.includes("delete"),
126
- admin: permissionArray.includes("admin"),
127
- owner: permissionArray.includes("owner"),
128
- };
129
- }
130
-
131
- /**
132
- * Grants repository access to a user.
133
- *
134
- * @param params - Grant parameters
135
- * @param params.userDid - DID of the user to grant access to
136
- * @param params.role - Role to assign (determines permissions)
137
- * @throws {@link NetworkError} if the grant operation fails
138
- *
139
- * @remarks
140
- * If the user already has access, their permissions are updated
141
- * to the new role.
142
- *
143
- * @example
144
- * ```typescript
145
- * // Grant viewer access
146
- * await repo.collaborators.grant({
147
- * userDid: "did:plc:viewer-user",
148
- * role: "viewer",
149
- * });
150
- *
151
- * // Upgrade to editor
152
- * await repo.collaborators.grant({
153
- * userDid: "did:plc:viewer-user",
154
- * role: "editor",
155
- * });
156
- * ```
157
- */
158
- async grant(params: { userDid: string; role: RepositoryRole }): Promise<void> {
159
- const permissions = this.roleToPermissions(params.role);
160
-
161
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.grantAccess`, {
162
- method: "POST",
163
- headers: { "Content-Type": "application/json" },
164
- body: JSON.stringify({
165
- repo: this.repoDid,
166
- userDid: params.userDid,
167
- permissions,
168
- }),
169
- });
170
-
171
- if (!response.ok) {
172
- throw new NetworkError(`Failed to grant access: ${response.statusText}`);
173
- }
174
- }
175
-
176
- /**
177
- * Revokes repository access from a user.
178
- *
179
- * @param params - Revoke parameters
180
- * @param params.userDid - DID of the user to revoke access from
181
- * @throws {@link NetworkError} if the revoke operation fails
182
- *
183
- * @remarks
184
- * - Cannot revoke access from the repository owner
185
- * - Revoked access is recorded with a `revokedAt` timestamp
186
- *
187
- * @example
188
- * ```typescript
189
- * await repo.collaborators.revoke({
190
- * userDid: "did:plc:former-collaborator",
191
- * });
192
- * ```
193
- */
194
- async revoke(params: { userDid: string }): Promise<void> {
195
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.revokeAccess`, {
196
- method: "POST",
197
- headers: { "Content-Type": "application/json" },
198
- body: JSON.stringify({
199
- repo: this.repoDid,
200
- userDid: params.userDid,
201
- }),
202
- });
203
-
204
- if (!response.ok) {
205
- throw new NetworkError(`Failed to revoke access: ${response.statusText}`);
206
- }
207
- }
208
-
209
- /**
210
- * Lists all collaborators on the repository.
211
- *
212
- * @param params - Optional pagination parameters
213
- * @param params.limit - Maximum number of results (1-100, default 50)
214
- * @param params.cursor - Pagination cursor from previous response
215
- * @returns Promise resolving to collaborators and optional cursor
216
- * @throws {@link NetworkError} if the list operation fails
217
- *
218
- * @remarks
219
- * The list includes both active and revoked collaborators.
220
- * Check `revokedAt` to filter active collaborators.
221
- *
222
- * @example
223
- * ```typescript
224
- * // Get first page
225
- * const page1 = await repo.collaborators.list({ limit: 10 });
226
- * console.log(`Found ${page1.collaborators.length} collaborators`);
227
- *
228
- * // Get next page if available
229
- * if (page1.cursor) {
230
- * const page2 = await repo.collaborators.list({ limit: 10, cursor: page1.cursor });
231
- * }
232
- *
233
- * // Filter active collaborators
234
- * const active = page1.collaborators.filter(c => !c.revokedAt);
235
- * ```
236
- */
237
- async list(params?: { limit?: number; cursor?: string }): Promise<{
238
- collaborators: RepositoryAccessGrant[];
239
- cursor?: string;
240
- }> {
241
- const queryParams = new URLSearchParams({
242
- repo: this.repoDid,
243
- });
244
-
245
- if (params?.limit !== undefined) {
246
- queryParams.set("limit", params.limit.toString());
247
- }
248
-
249
- if (params?.cursor) {
250
- queryParams.set("cursor", params.cursor);
251
- }
252
-
253
- const response = await this.session.fetchHandler(
254
- `${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?${queryParams.toString()}`,
255
- { method: "GET" },
256
- );
257
-
258
- if (!response.ok) {
259
- throw new NetworkError(`Failed to list collaborators: ${response.statusText}`);
260
- }
261
-
262
- const data = await response.json();
263
- const collaborators = (data.collaborators || []).map(
264
- (c: {
265
- userDid: string;
266
- permissions: string[]; // SDS API returns string array
267
- grantedBy: string;
268
- grantedAt: string;
269
- revokedAt?: string;
270
- }) => {
271
- const permissions = this.parsePermissions(c.permissions);
272
- return {
273
- userDid: c.userDid,
274
- role: this.permissionsToRole(permissions),
275
- permissions: permissions,
276
- grantedBy: c.grantedBy,
277
- grantedAt: c.grantedAt,
278
- revokedAt: c.revokedAt,
279
- };
280
- },
281
- );
282
-
283
- return {
284
- collaborators,
285
- cursor: data.cursor,
286
- };
287
- }
288
-
289
- /**
290
- * Checks if a user has any access to the repository.
291
- *
292
- * @param userDid - DID of the user to check
293
- * @returns Promise resolving to `true` if user has active access
294
- *
295
- * @remarks
296
- * Returns `false` if:
297
- * - User was never granted access
298
- * - User's access was revoked
299
- * - The list operation fails (error is suppressed)
300
- *
301
- * @example
302
- * ```typescript
303
- * if (await repo.collaborators.hasAccess("did:plc:someone")) {
304
- * console.log("User has access");
305
- * }
306
- * ```
307
- */
308
- async hasAccess(userDid: string): Promise<boolean> {
309
- try {
310
- const { collaborators } = await this.list();
311
- return collaborators.some((c) => c.userDid === userDid && !c.revokedAt);
312
- } catch {
313
- return false;
314
- }
315
- }
316
-
317
- /**
318
- * Gets the role assigned to a user.
319
- *
320
- * @param userDid - DID of the user to check
321
- * @returns Promise resolving to the user's role, or `null` if no active access
322
- *
323
- * @example
324
- * ```typescript
325
- * const role = await repo.collaborators.getRole("did:plc:someone");
326
- * if (role === "admin" || role === "owner") {
327
- * // User can manage other collaborators
328
- * }
329
- * ```
330
- */
331
- async getRole(userDid: string): Promise<RepositoryRole | null> {
332
- const { collaborators } = await this.list();
333
- const collab = collaborators.find((c) => c.userDid === userDid && !c.revokedAt);
334
- return collab?.role ?? null;
335
- }
336
-
337
- /**
338
- * Gets the current user's permissions for this repository.
339
- *
340
- * @returns Promise resolving to the permission flags
341
- * @throws {@link NetworkError} if the request fails
342
- *
343
- * @remarks
344
- * This is useful for checking what actions the current user can perform
345
- * before attempting operations that might fail due to insufficient permissions.
346
- *
347
- * @example
348
- * ```typescript
349
- * const permissions = await repo.collaborators.getPermissions();
350
- *
351
- * if (permissions.admin) {
352
- * // Show admin UI
353
- * console.log("You can manage collaborators");
354
- * }
355
- *
356
- * if (permissions.create) {
357
- * console.log("You can create records");
358
- * }
359
- * ```
360
- *
361
- * @example Conditional UI rendering
362
- * ```typescript
363
- * const permissions = await repo.collaborators.getPermissions();
364
- *
365
- * // Show/hide UI elements based on permissions
366
- * const canEdit = permissions.update;
367
- * const canDelete = permissions.delete;
368
- * const isAdmin = permissions.admin;
369
- * const isOwner = permissions.owner;
370
- * ```
371
- */
372
- async getPermissions(): Promise<CollaboratorPermissions> {
373
- const response = await this.session.fetchHandler(
374
- `${this.serverUrl}/xrpc/com.sds.repo.getPermissions?repo=${encodeURIComponent(this.repoDid)}`,
375
- { method: "GET" },
376
- );
377
-
378
- if (!response.ok) {
379
- throw new NetworkError(`Failed to get permissions: ${response.statusText}`);
380
- }
381
-
382
- const data = await response.json();
383
- return data.permissions as CollaboratorPermissions;
384
- }
385
-
386
- /**
387
- * Transfers repository ownership to another user.
388
- *
389
- * @param params - Transfer parameters
390
- * @param params.newOwnerDid - DID of the user to transfer ownership to
391
- * @throws {@link NetworkError} if the transfer fails
392
- *
393
- * @remarks
394
- * **IMPORTANT**: This action is irreversible. Once ownership is transferred:
395
- * - The new owner gains full control of the repository
396
- * - Your role will be changed to admin (or specified role)
397
- * - You cannot transfer ownership back without the new owner's approval
398
- *
399
- * **Requirements**:
400
- * - You must be the current owner
401
- * - The new owner must have an existing account
402
- * - The new owner will be notified of the ownership transfer
403
- *
404
- * @example
405
- * ```typescript
406
- * // Transfer ownership to another user
407
- * await repo.collaborators.transferOwnership({
408
- * newOwnerDid: "did:plc:new-owner",
409
- * });
410
- *
411
- * console.log("Ownership transferred successfully");
412
- * // You are now an admin, not the owner
413
- * ```
414
- *
415
- * @example With confirmation
416
- * ```typescript
417
- * const confirmTransfer = await askUser(
418
- * "Are you sure you want to transfer ownership? This cannot be undone."
419
- * );
420
- *
421
- * if (confirmTransfer) {
422
- * await repo.collaborators.transferOwnership({
423
- * newOwnerDid: "did:plc:new-owner",
424
- * });
425
- * }
426
- * ```
427
- */
428
- async transferOwnership(params: { newOwnerDid: string }): Promise<void> {
429
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.transferOwnership`, {
430
- method: "POST",
431
- headers: { "Content-Type": "application/json" },
432
- body: JSON.stringify({
433
- repo: this.repoDid,
434
- newOwner: params.newOwnerDid,
435
- }),
436
- });
437
-
438
- if (!response.ok) {
439
- throw new NetworkError(`Failed to transfer ownership: ${response.statusText}`);
440
- }
441
- }
442
- }