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