@hypercerts-org/sdk-core 0.4.0-beta.0 → 0.6.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 (62) hide show
  1. package/README.md +459 -79
  2. package/dist/index.cjs +128 -47
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +28 -9
  5. package/dist/index.mjs +128 -47
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types.cjs +3 -2
  8. package/dist/types.cjs.map +1 -1
  9. package/dist/types.d.ts +28 -9
  10. package/dist/types.mjs +3 -2
  11. package/dist/types.mjs.map +1 -1
  12. package/package.json +9 -5
  13. package/.turbo/turbo-build.log +0 -328
  14. package/.turbo/turbo-test.log +0 -118
  15. package/CHANGELOG.md +0 -22
  16. package/eslint.config.mjs +0 -22
  17. package/rollup.config.js +0 -75
  18. package/src/auth/OAuthClient.ts +0 -497
  19. package/src/core/SDK.ts +0 -410
  20. package/src/core/config.ts +0 -243
  21. package/src/core/errors.ts +0 -257
  22. package/src/core/interfaces.ts +0 -324
  23. package/src/core/types.ts +0 -281
  24. package/src/errors.ts +0 -57
  25. package/src/index.ts +0 -107
  26. package/src/lexicons.ts +0 -64
  27. package/src/repository/BlobOperationsImpl.ts +0 -199
  28. package/src/repository/CollaboratorOperationsImpl.ts +0 -396
  29. package/src/repository/HypercertOperationsImpl.ts +0 -1146
  30. package/src/repository/LexiconRegistry.ts +0 -332
  31. package/src/repository/OrganizationOperationsImpl.ts +0 -234
  32. package/src/repository/ProfileOperationsImpl.ts +0 -281
  33. package/src/repository/RecordOperationsImpl.ts +0 -340
  34. package/src/repository/Repository.ts +0 -482
  35. package/src/repository/interfaces.ts +0 -897
  36. package/src/repository/types.ts +0 -111
  37. package/src/services/hypercerts/types.ts +0 -87
  38. package/src/storage/InMemorySessionStore.ts +0 -127
  39. package/src/storage/InMemoryStateStore.ts +0 -146
  40. package/src/storage.ts +0 -63
  41. package/src/testing/index.ts +0 -67
  42. package/src/testing/mocks.ts +0 -142
  43. package/src/testing/stores.ts +0 -285
  44. package/src/testing.ts +0 -64
  45. package/src/types.ts +0 -86
  46. package/tests/auth/OAuthClient.test.ts +0 -164
  47. package/tests/core/SDK.test.ts +0 -176
  48. package/tests/core/errors.test.ts +0 -81
  49. package/tests/repository/BlobOperationsImpl.test.ts +0 -154
  50. package/tests/repository/CollaboratorOperationsImpl.test.ts +0 -438
  51. package/tests/repository/HypercertOperationsImpl.test.ts +0 -652
  52. package/tests/repository/LexiconRegistry.test.ts +0 -192
  53. package/tests/repository/OrganizationOperationsImpl.test.ts +0 -242
  54. package/tests/repository/ProfileOperationsImpl.test.ts +0 -254
  55. package/tests/repository/RecordOperationsImpl.test.ts +0 -375
  56. package/tests/repository/Repository.test.ts +0 -149
  57. package/tests/utils/fixtures.ts +0 -117
  58. package/tests/utils/mocks.ts +0 -109
  59. package/tests/utils/repository-fixtures.ts +0 -78
  60. package/tsconfig.json +0 -11
  61. package/tsconfig.tsbuildinfo +0 -1
  62. package/vitest.config.ts +0 -30
@@ -1,396 +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
- * Grants repository access to a user.
112
- *
113
- * @param params - Grant parameters
114
- * @param params.userDid - DID of the user to grant access to
115
- * @param params.role - Role to assign (determines permissions)
116
- * @throws {@link NetworkError} if the grant operation fails
117
- *
118
- * @remarks
119
- * If the user already has access, their permissions are updated
120
- * to the new role.
121
- *
122
- * @example
123
- * ```typescript
124
- * // Grant viewer access
125
- * await repo.collaborators.grant({
126
- * userDid: "did:plc:viewer-user",
127
- * role: "viewer",
128
- * });
129
- *
130
- * // Upgrade to editor
131
- * await repo.collaborators.grant({
132
- * userDid: "did:plc:viewer-user",
133
- * role: "editor",
134
- * });
135
- * ```
136
- */
137
- async grant(params: { userDid: string; role: RepositoryRole }): Promise<void> {
138
- const permissions = this.roleToPermissions(params.role);
139
-
140
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.grantAccess`, {
141
- method: "POST",
142
- headers: { "Content-Type": "application/json" },
143
- body: JSON.stringify({
144
- repo: this.repoDid,
145
- userDid: params.userDid,
146
- permissions,
147
- }),
148
- });
149
-
150
- if (!response.ok) {
151
- throw new NetworkError(`Failed to grant access: ${response.statusText}`);
152
- }
153
- }
154
-
155
- /**
156
- * Revokes repository access from a user.
157
- *
158
- * @param params - Revoke parameters
159
- * @param params.userDid - DID of the user to revoke access from
160
- * @throws {@link NetworkError} if the revoke operation fails
161
- *
162
- * @remarks
163
- * - Cannot revoke access from the repository owner
164
- * - Revoked access is recorded with a `revokedAt` timestamp
165
- *
166
- * @example
167
- * ```typescript
168
- * await repo.collaborators.revoke({
169
- * userDid: "did:plc:former-collaborator",
170
- * });
171
- * ```
172
- */
173
- async revoke(params: { userDid: string }): Promise<void> {
174
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.revokeAccess`, {
175
- method: "POST",
176
- headers: { "Content-Type": "application/json" },
177
- body: JSON.stringify({
178
- repo: this.repoDid,
179
- userDid: params.userDid,
180
- }),
181
- });
182
-
183
- if (!response.ok) {
184
- throw new NetworkError(`Failed to revoke access: ${response.statusText}`);
185
- }
186
- }
187
-
188
- /**
189
- * Lists all collaborators on the repository.
190
- *
191
- * @returns Promise resolving to array of access grants
192
- * @throws {@link NetworkError} if the list operation fails
193
- *
194
- * @remarks
195
- * The list includes both active and revoked collaborators.
196
- * Check `revokedAt` to filter active collaborators.
197
- *
198
- * @example
199
- * ```typescript
200
- * const collaborators = await repo.collaborators.list();
201
- *
202
- * // Filter active collaborators
203
- * const active = collaborators.filter(c => !c.revokedAt);
204
- *
205
- * // Group by role
206
- * const byRole = {
207
- * owners: active.filter(c => c.role === "owner"),
208
- * admins: active.filter(c => c.role === "admin"),
209
- * editors: active.filter(c => c.role === "editor"),
210
- * viewers: active.filter(c => c.role === "viewer"),
211
- * };
212
- * ```
213
- */
214
- async list(): Promise<RepositoryAccessGrant[]> {
215
- const response = await this.session.fetchHandler(
216
- `${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?repo=${encodeURIComponent(this.repoDid)}`,
217
- { method: "GET" },
218
- );
219
-
220
- if (!response.ok) {
221
- throw new NetworkError(`Failed to list collaborators: ${response.statusText}`);
222
- }
223
-
224
- const data = await response.json();
225
- return (data.collaborators || []).map(
226
- (c: {
227
- userDid: string;
228
- permissions: CollaboratorPermissions;
229
- grantedBy: string;
230
- grantedAt: string;
231
- revokedAt?: string;
232
- }) => ({
233
- userDid: c.userDid,
234
- role: this.permissionsToRole(c.permissions),
235
- permissions: c.permissions,
236
- grantedBy: c.grantedBy,
237
- grantedAt: c.grantedAt,
238
- revokedAt: c.revokedAt,
239
- }),
240
- );
241
- }
242
-
243
- /**
244
- * Checks if a user has any access to the repository.
245
- *
246
- * @param userDid - DID of the user to check
247
- * @returns Promise resolving to `true` if user has active access
248
- *
249
- * @remarks
250
- * Returns `false` if:
251
- * - User was never granted access
252
- * - User's access was revoked
253
- * - The list operation fails (error is suppressed)
254
- *
255
- * @example
256
- * ```typescript
257
- * if (await repo.collaborators.hasAccess("did:plc:someone")) {
258
- * console.log("User has access");
259
- * }
260
- * ```
261
- */
262
- async hasAccess(userDid: string): Promise<boolean> {
263
- try {
264
- const collaborators = await this.list();
265
- return collaborators.some((c) => c.userDid === userDid && !c.revokedAt);
266
- } catch {
267
- return false;
268
- }
269
- }
270
-
271
- /**
272
- * Gets the role assigned to a user.
273
- *
274
- * @param userDid - DID of the user to check
275
- * @returns Promise resolving to the user's role, or `null` if no active access
276
- *
277
- * @example
278
- * ```typescript
279
- * const role = await repo.collaborators.getRole("did:plc:someone");
280
- * if (role === "admin" || role === "owner") {
281
- * // User can manage other collaborators
282
- * }
283
- * ```
284
- */
285
- async getRole(userDid: string): Promise<RepositoryRole | null> {
286
- const collaborators = await this.list();
287
- const collab = collaborators.find((c) => c.userDid === userDid && !c.revokedAt);
288
- return collab?.role ?? null;
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
- }
396
- }