@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
package/dist/index.cjs CHANGED
@@ -1670,7 +1670,7 @@ class BlobOperationsImpl {
1670
1670
  throw new NetworkError("Failed to upload blob");
1671
1671
  }
1672
1672
  return {
1673
- ref: result.data.blob.ref,
1673
+ ref: { $link: result.data.blob.ref.toString() },
1674
1674
  mimeType: result.data.blob.mimeType,
1675
1675
  size: result.data.blob.size,
1676
1676
  };
@@ -2177,7 +2177,7 @@ class HypercertOperationsImpl extends eventemitter3.EventEmitter {
2177
2177
  if (uploadResult.success) {
2178
2178
  imageBlobRef = {
2179
2179
  $type: "blob",
2180
- ref: uploadResult.data.blob.ref,
2180
+ ref: { $link: uploadResult.data.blob.ref.toString() },
2181
2181
  mimeType: uploadResult.data.blob.mimeType,
2182
2182
  size: uploadResult.data.blob.size,
2183
2183
  };
@@ -2602,7 +2602,7 @@ class HypercertOperationsImpl extends eventemitter3.EventEmitter {
2602
2602
  if (uploadResult.success) {
2603
2603
  locationValue = {
2604
2604
  $type: "blob",
2605
- ref: uploadResult.data.blob.ref,
2605
+ ref: { $link: uploadResult.data.blob.ref.toString() },
2606
2606
  mimeType: uploadResult.data.blob.mimeType,
2607
2607
  size: uploadResult.data.blob.size,
2608
2608
  };
@@ -2887,7 +2887,7 @@ class HypercertOperationsImpl extends eventemitter3.EventEmitter {
2887
2887
  if (uploadResult.success) {
2888
2888
  coverPhotoRef = {
2889
2889
  $type: "blob",
2890
- ref: uploadResult.data.blob.ref,
2890
+ ref: { $link: uploadResult.data.blob.ref.toString() },
2891
2891
  mimeType: uploadResult.data.blob.mimeType,
2892
2892
  size: uploadResult.data.blob.size,
2893
2893
  };
@@ -3118,6 +3118,27 @@ class CollaboratorOperationsImpl {
3118
3118
  return "editor";
3119
3119
  return "viewer";
3120
3120
  }
3121
+ /**
3122
+ * Normalizes permissions from SDS API format to SDK format.
3123
+ *
3124
+ * The SDS API returns permissions as an object with boolean flags
3125
+ * (e.g., `{ read: true, create: true, update: false, ... }`).
3126
+ * This method ensures all expected fields are present with default values.
3127
+ *
3128
+ * @param permissions - Permissions object from SDS API
3129
+ * @returns Normalized permission flags object
3130
+ * @internal
3131
+ */
3132
+ parsePermissions(permissions) {
3133
+ return {
3134
+ read: permissions.read ?? false,
3135
+ create: permissions.create ?? false,
3136
+ update: permissions.update ?? false,
3137
+ delete: permissions.delete ?? false,
3138
+ admin: permissions.admin ?? false,
3139
+ owner: permissions.owner ?? false,
3140
+ };
3141
+ }
3121
3142
  /**
3122
3143
  * Grants repository access to a user.
3123
3144
  *
@@ -3194,7 +3215,10 @@ class CollaboratorOperationsImpl {
3194
3215
  /**
3195
3216
  * Lists all collaborators on the repository.
3196
3217
  *
3197
- * @returns Promise resolving to array of access grants
3218
+ * @param params - Optional pagination parameters
3219
+ * @param params.limit - Maximum number of results (1-100, default 50)
3220
+ * @param params.cursor - Pagination cursor from previous response
3221
+ * @returns Promise resolving to collaborators and optional cursor
3198
3222
  * @throws {@link NetworkError} if the list operation fails
3199
3223
  *
3200
3224
  * @remarks
@@ -3203,34 +3227,49 @@ class CollaboratorOperationsImpl {
3203
3227
  *
3204
3228
  * @example
3205
3229
  * ```typescript
3206
- * const collaborators = await repo.collaborators.list();
3230
+ * // Get first page
3231
+ * const page1 = await repo.collaborators.list({ limit: 10 });
3232
+ * console.log(`Found ${page1.collaborators.length} collaborators`);
3233
+ *
3234
+ * // Get next page if available
3235
+ * if (page1.cursor) {
3236
+ * const page2 = await repo.collaborators.list({ limit: 10, cursor: page1.cursor });
3237
+ * }
3207
3238
  *
3208
3239
  * // Filter active collaborators
3209
- * const active = collaborators.filter(c => !c.revokedAt);
3210
- *
3211
- * // Group by role
3212
- * const byRole = {
3213
- * owners: active.filter(c => c.role === "owner"),
3214
- * admins: active.filter(c => c.role === "admin"),
3215
- * editors: active.filter(c => c.role === "editor"),
3216
- * viewers: active.filter(c => c.role === "viewer"),
3217
- * };
3240
+ * const active = page1.collaborators.filter(c => !c.revokedAt);
3218
3241
  * ```
3219
3242
  */
3220
- async list() {
3221
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?repo=${encodeURIComponent(this.repoDid)}`, { method: "GET" });
3243
+ async list(params) {
3244
+ const queryParams = new URLSearchParams({
3245
+ repo: this.repoDid,
3246
+ });
3247
+ if (params?.limit !== undefined) {
3248
+ queryParams.set("limit", params.limit.toString());
3249
+ }
3250
+ if (params?.cursor) {
3251
+ queryParams.set("cursor", params.cursor);
3252
+ }
3253
+ const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.repo.listCollaborators?${queryParams.toString()}`, { method: "GET" });
3222
3254
  if (!response.ok) {
3223
3255
  throw new NetworkError(`Failed to list collaborators: ${response.statusText}`);
3224
3256
  }
3225
3257
  const data = await response.json();
3226
- return (data.collaborators || []).map((c) => ({
3227
- userDid: c.userDid,
3228
- role: this.permissionsToRole(c.permissions),
3229
- permissions: c.permissions,
3230
- grantedBy: c.grantedBy,
3231
- grantedAt: c.grantedAt,
3232
- revokedAt: c.revokedAt,
3233
- }));
3258
+ const collaborators = (data.collaborators || []).map((c) => {
3259
+ const permissions = this.parsePermissions(c.permissions);
3260
+ return {
3261
+ userDid: c.userDid,
3262
+ role: this.permissionsToRole(permissions),
3263
+ permissions: permissions,
3264
+ grantedBy: c.grantedBy,
3265
+ grantedAt: c.grantedAt,
3266
+ revokedAt: c.revokedAt,
3267
+ };
3268
+ });
3269
+ return {
3270
+ collaborators,
3271
+ cursor: data.cursor,
3272
+ };
3234
3273
  }
3235
3274
  /**
3236
3275
  * Checks if a user has any access to the repository.
@@ -3253,7 +3292,7 @@ class CollaboratorOperationsImpl {
3253
3292
  */
3254
3293
  async hasAccess(userDid) {
3255
3294
  try {
3256
- const collaborators = await this.list();
3295
+ const { collaborators } = await this.list();
3257
3296
  return collaborators.some((c) => c.userDid === userDid && !c.revokedAt);
3258
3297
  }
3259
3298
  catch {
@@ -3275,7 +3314,7 @@ class CollaboratorOperationsImpl {
3275
3314
  * ```
3276
3315
  */
3277
3316
  async getRole(userDid) {
3278
- const collaborators = await this.list();
3317
+ const { collaborators } = await this.list();
3279
3318
  const collab = collaborators.find((c) => c.userDid === userDid && !c.revokedAt);
3280
3319
  return collab?.role ?? null;
3281
3320
  }
@@ -3482,10 +3521,17 @@ class OrganizationOperationsImpl {
3482
3521
  * ```
3483
3522
  */
3484
3523
  async create(params) {
3524
+ const userDid = this.session.did || this.session.sub;
3525
+ if (!userDid) {
3526
+ throw new NetworkError("No authenticated user found");
3527
+ }
3485
3528
  const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.create`, {
3486
3529
  method: "POST",
3487
3530
  headers: { "Content-Type": "application/json" },
3488
- body: JSON.stringify(params),
3531
+ body: JSON.stringify({
3532
+ ...params,
3533
+ creatorDid: userDid,
3534
+ }),
3489
3535
  });
3490
3536
  if (!response.ok) {
3491
3537
  throw new NetworkError(`Failed to create organization: ${response.statusText}`);
@@ -3497,8 +3543,15 @@ class OrganizationOperationsImpl {
3497
3543
  name: data.name,
3498
3544
  description: data.description,
3499
3545
  createdAt: data.createdAt || new Date().toISOString(),
3500
- accessType: "owner",
3501
- permissions: { read: true, create: true, update: true, delete: true, admin: true, owner: true },
3546
+ accessType: data.accessType || "owner",
3547
+ permissions: data.permissions || {
3548
+ read: true,
3549
+ create: true,
3550
+ update: true,
3551
+ delete: true,
3552
+ admin: true,
3553
+ owner: true,
3554
+ },
3502
3555
  };
3503
3556
  }
3504
3557
  /**
@@ -3525,8 +3578,8 @@ class OrganizationOperationsImpl {
3525
3578
  */
3526
3579
  async get(did) {
3527
3580
  try {
3528
- const orgs = await this.list();
3529
- return orgs.find((o) => o.did === did) ?? null;
3581
+ const { organizations } = await this.list();
3582
+ return organizations.find((o) => o.did === did) ?? null;
3530
3583
  }
3531
3584
  catch {
3532
3585
  return null;
@@ -3535,7 +3588,10 @@ class OrganizationOperationsImpl {
3535
3588
  /**
3536
3589
  * Lists organizations the current user has access to.
3537
3590
  *
3538
- * @returns Promise resolving to array of organization info
3591
+ * @param params - Optional pagination parameters
3592
+ * @param params.limit - Maximum number of results (1-100, default 50)
3593
+ * @param params.cursor - Pagination cursor from previous response
3594
+ * @returns Promise resolving to organizations and optional cursor
3539
3595
  * @throws {@link NetworkError} if the list operation fails
3540
3596
  *
3541
3597
  * @remarks
@@ -3547,21 +3603,25 @@ class OrganizationOperationsImpl {
3547
3603
  *
3548
3604
  * @example
3549
3605
  * ```typescript
3550
- * const orgs = await repo.organizations.list();
3606
+ * // Get first page
3607
+ * const page1 = await repo.organizations.list({ limit: 20 });
3608
+ * console.log(`Found ${page1.organizations.length} organizations`);
3551
3609
  *
3552
- * // Filter by access type
3553
- * const owned = orgs.filter(o => o.accessType === "owner");
3554
- * const collaborated = orgs.filter(o => o.accessType === "collaborator");
3610
+ * // Get next page if available
3611
+ * if (page1.cursor) {
3612
+ * const page2 = await repo.organizations.list({ limit: 20, cursor: page1.cursor });
3613
+ * }
3555
3614
  *
3556
- * console.log(`You own ${owned.length} organizations`);
3557
- * console.log(`You collaborate on ${collaborated.length} organizations`);
3615
+ * // Filter by access type
3616
+ * const owned = page1.organizations.filter(o => o.accessType === "owner");
3617
+ * const shared = page1.organizations.filter(o => o.accessType === "shared");
3558
3618
  * ```
3559
3619
  *
3560
3620
  * @example Display organization details
3561
3621
  * ```typescript
3562
- * const orgs = await repo.organizations.list();
3622
+ * const { organizations } = await repo.organizations.list();
3563
3623
  *
3564
- * for (const org of orgs) {
3624
+ * for (const org of organizations) {
3565
3625
  * console.log(`${org.name} (@${org.handle})`);
3566
3626
  * console.log(` DID: ${org.did}`);
3567
3627
  * console.log(` Access: ${org.accessType}`);
@@ -3571,21 +3631,38 @@ class OrganizationOperationsImpl {
3571
3631
  * }
3572
3632
  * ```
3573
3633
  */
3574
- async list() {
3575
- const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.list?userDid=${encodeURIComponent(this.session.did || this.session.sub)}`, { method: "GET" });
3634
+ async list(params) {
3635
+ const userDid = this.session.did || this.session.sub;
3636
+ if (!userDid) {
3637
+ throw new NetworkError("No authenticated user found");
3638
+ }
3639
+ const queryParams = new URLSearchParams({
3640
+ userDid,
3641
+ });
3642
+ if (params?.limit !== undefined) {
3643
+ queryParams.set("limit", params.limit.toString());
3644
+ }
3645
+ if (params?.cursor) {
3646
+ queryParams.set("cursor", params.cursor);
3647
+ }
3648
+ const response = await this.session.fetchHandler(`${this.serverUrl}/xrpc/com.sds.organization.list?${queryParams.toString()}`, { method: "GET" });
3576
3649
  if (!response.ok) {
3577
3650
  throw new NetworkError(`Failed to list organizations: ${response.statusText}`);
3578
3651
  }
3579
3652
  const data = await response.json();
3580
- return (data.repositories || []).map((r) => ({
3653
+ const organizations = (data.organizations || []).map((r) => ({
3581
3654
  did: r.did,
3582
3655
  handle: r.handle,
3583
3656
  name: r.name,
3584
3657
  description: r.description,
3585
- createdAt: new Date().toISOString(), // SDS may not return this
3658
+ createdAt: r.createdAt || new Date().toISOString(),
3586
3659
  accessType: r.accessType,
3587
3660
  permissions: r.permissions,
3588
3661
  }));
3662
+ return {
3663
+ organizations,
3664
+ cursor: data.cursor,
3665
+ };
3589
3666
  }
3590
3667
  }
3591
3668
 
@@ -3697,6 +3774,9 @@ class Repository {
3697
3774
  this.logger = logger;
3698
3775
  // Create Agent with OAuth session
3699
3776
  this.agent = new api.Agent(session);
3777
+ // Configure Agent to use the specified server URL (PDS or SDS)
3778
+ // This ensures queries are routed to the correct server
3779
+ this.agent.api.xrpc.uri = new URL(serverUrl);
3700
3780
  this.lexiconRegistry.addToAgent(this.agent);
3701
3781
  // Register hypercert lexicons
3702
3782
  this.lexiconRegistry.registerMany(lexicon.HYPERCERT_LEXICONS);
@@ -4508,9 +4588,10 @@ const OrganizationSchema = zod.z.object({
4508
4588
  /**
4509
4589
  * How the current user relates to this organization.
4510
4590
  * - `"owner"`: User created or owns the organization
4511
- * - `"collaborator"`: User was invited to collaborate
4591
+ * - `"shared"`: User was invited to collaborate (has permissions)
4592
+ * - `"none"`: User has no access to this organization
4512
4593
  */
4513
- accessType: zod.z.enum(["owner", "collaborator"]),
4594
+ accessType: zod.z.enum(["owner", "shared", "none"]),
4514
4595
  });
4515
4596
  /**
4516
4597
  * Zod schema for collaborator data.