Skip to content

Commit 9d033f1

Browse files
committed
Merge branch 'develop' into local-dev-docs
2 parents 28d1340 + 23634e2 commit 9d033f1

49 files changed

Lines changed: 3913 additions & 1510 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.beads/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Dolt database directory and lock file
2+
dolt/
3+
dolt-access.lock
4+
15
# SQLite databases
26
*.db
37
*.db?*

.changeset/add-richtext-utility.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,24 @@
44

55
Add RichText utility functions for auto-detecting facets from text
66

7+
**0.x Versioning Note:** This is a non-breaking addition. No existing APIs are modified or removed.
8+
79
New utility functions to simplify creating rich text facets:
810

911
- `createFacetsFromText(text, agent?)` - async function that auto-detects URLs, hashtags, and @mentions. If an agent is
10-
provided, resolves mentions to DIDs.
11-
- `createFacetsFromTextSync(text)` - sync function for fast detection without mention resolution
12+
provided, resolves mentions to DIDs; otherwise mentions use the handle string as the DID.
13+
- `createFacetsFromTextSync(text)` - sync function for fast detection without mention resolution (mentions use handle as
14+
DID)
1215
- Re-exports `RichText` class from `@atproto/api` for advanced use cases
16+
17+
**Usage:**
18+
19+
```typescript
20+
import { createFacetsFromText, createFacetsFromTextSync } from "@hypercerts-org/sdk-core";
21+
22+
// Sync (no DID resolution)
23+
const facetsSync = createFacetsFromTextSync("Check out #hypercerts by @alice");
24+
25+
// Async with DID resolution (requires authenticated agent)
26+
const facetsAsync = await createFacetsFromText("Check out #hypercerts by @alice", agent);
27+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
---
4+
5+
Add `isValidDid()` utility function for DID format validation
6+
7+
- Validates DID format (did:method:identifier) with support for numeric method names per W3C spec
8+
- Exported from `@hypercerts-org/sdk-core` for consumer use
9+
- `BlobOperationsImpl` constructor now validates `repoDid` and throws `ValidationError` for invalid formats
10+
11+
> **⚠️ Potentially breaking:** callers that previously passed invalid DID strings to `BlobOperationsImpl` (directly or
12+
> via `Repository`) will now receive a `ValidationError` at construction time instead of silently accepting the value.
13+
> Use `isValidDid(repoDid)` to check before constructing if needed.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
"@hypercerts-org/sdk-react": minor
4+
---
5+
6+
Add dual profile system with separate Bluesky and Certified profile operations, plus upsert methods
7+
8+
**Core SDK (`@hypercerts-org/sdk-core`):**
9+
10+
**Breaking Changes:**
11+
12+
The profile API has been completely redesigned to support two profile types:
13+
14+
- **Removed generic profile methods:**
15+
-`profile.get()`
16+
-`profile.create(params)`
17+
-`profile.update(params)`
18+
19+
- **Removed deprecated profile types:**
20+
-`HypercertProfile` - Use `CertifiedProfileRecord` instead
21+
-`CreateHypercertProfileParams` - Use `CreateCertifiedProfileParams` instead
22+
-`UpdateHypercertProfileParams` - Use `UpdateCertifiedProfileParams` instead
23+
-`HypercertProfileParams` - Use specific create/update types instead
24+
-`CreateProfileParams` - Use `CreateCertifiedProfileParams` instead
25+
26+
- **Removed unused type export:**
27+
-`JsonBlobRef` - No longer used in SDK. This type was removed because:
28+
- It was too "snowflaky" - required converting `BlobRef` instances to JSON format unnecessarily
29+
- The actual `BlobRef` object works perfectly fine for all use cases
30+
- It created flaky tests where we manually created mock JSON objects that weren't representative of actual
31+
`JsonBlobRef` values
32+
- Internal implementation now uses `BlobRef` instances directly throughout
33+
- Users who need this type for advanced use cases can import it directly from `@atproto/lexicon`
34+
35+
- **Added profile-specific methods:**
36+
-`profile.getBskyProfile()` - Get Bluesky profile (app.bsky.actor.profile)
37+
-`profile.createBskyProfile(params)` - Create Bluesky profile
38+
-`profile.updateBskyProfile(params)` - Update Bluesky profile
39+
-`profile.getCertifiedProfile()` - Get Certified profile (app.certified.actor.profile) **[Returns `null` if
40+
profile doesn't exist]**
41+
-`profile.createCertifiedProfile(params)` - Create Certified profile
42+
-`profile.updateCertifiedProfile(params)` - Update Certified profile
43+
-`profile.upsertBskyProfile(params)` - Create or update Bluesky profile **[New]**
44+
-`profile.upsertCertifiedProfile(params)` - Create or update Certified profile **[New]**
45+
46+
**Features:**
47+
48+
- **Bluesky profiles** (`app.bsky.actor.profile`):
49+
- Standard AT Protocol profiles
50+
- Avatar/banner returned as CDN URLs (`https://cdn.bsky.app/...`)
51+
- Includes Bluesky-specific fields (labels, pinnedPost, etc.)
52+
53+
- **Certified profiles** (`app.certified.actor.profile`):
54+
- Hypercerts-specific profiles with additional fields
55+
- Avatar/banner returned as PDS blob URLs (`https://pds.../xrpc/...`)
56+
- **`getCertifiedProfile()` returns `null` if profile doesn't exist** (not an error - common for new users)
57+
- Supports `pronouns` field (max 20 graphemes)
58+
- Supports `website` field
59+
- Images stored using `HypercertImageRecord` format internally (smallImage/largeImage wrappers)
60+
61+
- **Upsert methods** (Recommended for most use cases):
62+
- `upsertBskyProfile(params)` - Automatically creates or updates Bluesky profile
63+
- `upsertCertifiedProfile(params)` - Automatically creates or updates Certified profile
64+
- Simpler DX - no need to check if profile exists first
65+
- Perfect for "save profile" operations
66+
67+
- **New types:**
68+
- `BskyProfile` - Type for Bluesky profiles (alias for `AppBskyActorDefs.ProfileViewDetailed`)
69+
- `CertifiedProfile` - Type for Certified profiles
70+
- `CertifiedProfileRecord` - Record type for Certified profiles (replaces `HypercertProfile`)
71+
- `CreateBskyProfileParams`, `UpdateBskyProfileParams`
72+
- `CreateCertifiedProfileParams`, `UpdateCertifiedProfileParams` (replace `CreateHypercertProfileParams`,
73+
`UpdateHypercertProfileParams`)
74+
75+
**Migration Guide:**
76+
77+
```typescript
78+
// BEFORE (old API - removed)
79+
const profile = await repo.profile.get();
80+
await repo.profile.create({ displayName: "Alice" });
81+
await repo.profile.update({ displayName: "New Name" });
82+
83+
// AFTER - Recommended: Use upsert (works for both create and update)
84+
await repo.profile.upsertCertifiedProfile({
85+
displayName: "Alice",
86+
pronouns: "she/her",
87+
website: "https://alice.com",
88+
});
89+
90+
// AFTER - Advanced: Explicit create/update for fine control
91+
const certProfile = await repo.profile.getCertifiedProfile();
92+
if (!certProfile) {
93+
await repo.profile.createCertifiedProfile({
94+
displayName: "Alice",
95+
pronouns: "she/her",
96+
});
97+
} else {
98+
await repo.profile.updateCertifiedProfile({
99+
displayName: "New Name",
100+
});
101+
}
102+
103+
// Getting profiles - handle null case
104+
const profile = await repo.profile.getCertifiedProfile();
105+
if (profile) {
106+
console.log(profile.displayName);
107+
} else {
108+
console.log("User hasn't created a profile yet");
109+
}
110+
111+
// Type migrations
112+
import type {
113+
CertifiedProfileRecord, // was: HypercertProfile
114+
CreateCertifiedProfileParams, // was: CreateHypercertProfileParams
115+
UpdateCertifiedProfileParams, // was: UpdateHypercertProfileParams
116+
} from "@hypercerts-org/sdk-core/types";
117+
```
118+
119+
**React SDK (`@hypercerts-org/sdk-react`):**
120+
121+
**Breaking Changes:**
122+
123+
- `useProfile` hook renamed `update` to `save` and `isUpdating` to `isSaving`
124+
- `save()` now uses upsert internally - works for first-time profile creation too
125+
126+
```typescript
127+
// BEFORE
128+
const { update, isUpdating } = useProfile();
129+
await update({ displayName: "Alice" });
130+
131+
// AFTER
132+
const { save, isSaving } = useProfile();
133+
await save({ displayName: "Alice" }); // Works even if profile doesn't exist!
134+
```
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
---
4+
5+
Fix `addContribution` to properly update hypercerts with contributor references
6+
7+
**Breaking Changes:**
8+
9+
The `addContribution` method signature has changed to align with the `create()` method's contribution handling:
10+
11+
- `hypercertUri` is now **required** (was optional)
12+
- `contributors` now accepts `Array<ContributorIdentityParams>` (was `string[]`)
13+
- Supports DIDs, StrongRefs, or inline contributor creation params
14+
- `contributionDetails` parameter **replaces** separate `role`/`description` params
15+
- Supports inline role strings, StrongRefs, or inline contribution creation params
16+
- Added optional `weight` parameter for contribution weighting
17+
- Added optional `onProgress` callback for progress tracking
18+
- Returns `UpdateResult` instead of `CreateResult` (since it updates the hypercert)
19+
20+
**What Changed:**
21+
22+
The method now correctly:
23+
24+
- Creates or references contributionDetails records
25+
- Creates or references contributorInformation records
26+
- **Updates the hypercert's `contributors` array** with the new entries (this was the bug)
27+
- Supports batch addition of multiple contributors in one call
28+
- Preserves existing contributors when adding new ones
29+
30+
**Migration:**
31+
32+
```typescript
33+
// Before (0.10.0-beta.7 and earlier):
34+
await repo.hypercerts.addContribution({
35+
hypercertUri: "at://...", // optional
36+
contributors: ["did:plc:user1"],
37+
role: "Developer",
38+
description: "Built features",
39+
});
40+
41+
// After (0.10.0-beta.8+):
42+
await repo.hypercerts.addContribution({
43+
hypercertUri: "at://...", // required
44+
contributors: ["did:plc:user1"], // or StrongRef or create params
45+
contributionDetails: "Developer", // or StrongRef or create params object
46+
weight: "1.0", // optional
47+
});
48+
49+
// With detailed contribution record:
50+
await repo.hypercerts.addContribution({
51+
hypercertUri: "at://...",
52+
contributors: [
53+
{
54+
identifier: "did:plc:user1",
55+
displayName: "Alice",
56+
image: avatarBlob,
57+
},
58+
],
59+
contributionDetails: {
60+
role: "Developer",
61+
contributionDescription: "Built features",
62+
startDate: "2024-01-01",
63+
endDate: "2024-06-30",
64+
},
65+
weight: "2.0",
66+
});
67+
```
68+
69+
**Additional Breaking Change:**
70+
71+
The `update()` method signature has been corrected to accept actual record fields:
72+
73+
- Now accepts `UpdateHypercertParams` (fields from `HypercertClaim` record schema)
74+
- Previously accepted `Partial<CreateHypercertParams>` (SDK input format)
75+
76+
**Why this change:**
77+
78+
- Prevents invalid fields like `contributions` being added to records
79+
- Allows updating `contributors` array directly (which exists in the schema)
80+
- Type-safe - can only update fields that actually exist in the record
81+
- Validation now works correctly
82+
83+
**Migration for update():**
84+
85+
Most code should continue to work since common fields like `title`, `description`, `startDate`, etc. exist in both
86+
formats.
87+
88+
If you were using SDK input fields that don't exist in records (e.g., `contributions`), you'll need to update:
89+
90+
```typescript
91+
// Before:
92+
await repo.hypercerts.update({
93+
uri: hypercertUri,
94+
updates: {
95+
contributions: [...], // ❌ Invalid - doesn't exist in record
96+
},
97+
});
98+
99+
// After: Use the actual record field
100+
await repo.hypercerts.update({
101+
uri: hypercertUri,
102+
updates: {
103+
contributors: [...], // ✅ Valid - exists in record schema
104+
},
105+
});
106+
107+
// Or use the new addContribution method
108+
await repo.hypercerts.addContribution({
109+
hypercertUri: hypercertUri,
110+
contributors: ["did:plc:user1"],
111+
contributionDetails: "Developer",
112+
});
113+
```
114+
115+
**Implementation Details:**
116+
117+
- Added `UpdateHypercertParams` type for type-safe record updates
118+
- Added `buildContributorEntries()` helper to resolve and build contributor entries
119+
- Added `attachContributorsToHypercert()` helper to update hypercerts with new contributors
120+
- Refactored `processContributors()` to reuse `buildContributorEntries()` for consistency
121+
- Removed unused `createContributionsWithProgress()` method
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@hypercerts-org/sdk-core": minor
3+
---
4+
5+
Refactor internal URI parsing and blob upload operations
6+
7+
**Breaking:** `BlobOperationsImpl.upload()` now returns AT Protocol's `BlobRef` type instead of a plain
8+
`{ ref, mimeType, size }` object. Callers should access blob properties via `BlobRef` methods (e.g.
9+
`result.ref.toString()` for the CID string). SDS uploads now return a proper `BlobRef` instance with `ref`, `mimeType`,
10+
and `size` correctly populated from the server response.
11+
12+
- Fix `validateScope` permission prefix regex to correctly accept query-param style scopes (e.g. `repo?action=create`,
13+
`blob?accept=video/*`, `rpc?lxm=*`) and reject bare `atproto` with a suffix
14+
- Export `AT_URI_REGEX` from `@hypercerts-org/sdk-core` for direct regex usage
15+
- Consolidate AT-URI parsing in HypercertOperationsImpl using `parseAtUri()` utility
16+
- Add internal `fetchRecord<T>()` and `saveRecord()` helpers to reduce code duplication
17+
- Fix `AT_URI_REGEX` rkey capture group to use `[^/]+` instead of `.+` to prevent over-matching
18+
- Fix `fetchRecord` to throw `NetworkError` when CID is absent instead of silently using an empty string
19+
- Fix `saveRecord` error message formatting (was passing two arguments to `NetworkError`)
20+
- Remove dead `parseAndValidateUri` method
21+
- Eliminate redundant network fetch in `updateProject` by passing pre-fetched record to `updateCollectionRecord`

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ coverage.json
1818
npm-debug.log*
1919
yarn-debug.log*
2020
yarn-error.log*
21+
package-lock.json
2122
.DS_Store
2223
/.idea
2324
stats.html

0 commit comments

Comments
 (0)