@hypercerts-org/sdk-core 0.10.0-beta.8 → 0.10.0-beta.9

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,337 @@
1
1
  # @hypercerts-org/sdk-core
2
2
 
3
+ ## 0.10.0-beta.9
4
+
5
+ ### Minor Changes
6
+
7
+ - [#128](https://github.com/hypercerts-org/hypercerts-sdk/pull/128)
8
+ [`d10642f`](https://github.com/hypercerts-org/hypercerts-sdk/commit/d10642ff3647513f03db4b73ca2e9ab7a06fe955) Thanks
9
+ [@Kzoeps](https://github.com/Kzoeps)! - **BREAKING CHANGE (pre-1.0):** Add strict URI validation for attachment
10
+ content strings
11
+
12
+ This introduces a breaking behavioral change for 0.x consumers:
13
+ - Add `isValidUri` utility to validate URI strings have a proper scheme (supports http, https, at://, ipfs://, and any
14
+ RFC 3986-compliant scheme)
15
+ - **`addAttachment` now throws `ValidationError`** when content strings are not valid URIs (e.g., plain text like
16
+ `"not-a-uri"`)
17
+ - Export `isValidUri` from the public API for consumer use
18
+
19
+ **Migration Guide:** Existing code that passes plain text or non-URI strings to `addAttachment` will now fail with a
20
+ `ValidationError`. To migrate:
21
+ 1. Ensure all content strings passed to `addAttachment` are valid URIs
22
+ 2. Use the new `isValidUri` utility to validate strings before passing them
23
+ 3. Convert plain text content to proper URI format (e.g., data URIs, IPFS URIs, or HTTP URLs)
24
+
25
+ **Compatibility Note:** Consumers relying on the previous lenient behavior that accepted non-URI strings must update
26
+ their code. The validation now strictly enforces that attachment content must be a valid URI with a recognized scheme.
27
+
28
+ - [#127](https://github.com/hypercerts-org/hypercerts-sdk/pull/127)
29
+ [`5662f3f`](https://github.com/hypercerts-org/hypercerts-sdk/commit/5662f3f03d286a55c2623223b40ebc0542c1dcca) Thanks
30
+ [@Kzoeps](https://github.com/Kzoeps)! - Auto-detect user's PDS URL from OAuth session instead of requiring static
31
+ configuration
32
+
33
+ **Breaking Changes:**
34
+ - `servers.pds` config option has been removed. The user's PDS URL is now automatically detected from the OAuth
35
+ session's token info (`tokenInfo.aud`) during `callback()` and `restoreSession()`. This means the SDK correctly
36
+ routes operations to each user's actual PDS regardless of which server they're hosted on.
37
+ - A new `handleResolver` config option replaces `servers.pds` for its handle resolution role during OAuth
38
+ authorization. This is optional — if omitted, DNS-based resolution is used.
39
+ - `getAccountEmail()` no longer requires `servers.pds` to be configured, since it uses the session's fetch handler
40
+ which internally routes to the correct PDS.
41
+ - `sdk.repository()` is now async (returns `Promise<Repository>`). On cache miss it automatically resolves the PDS
42
+ from the session's token info, so callers no longer need to manually call `resolveSessionPds()` first.
43
+
44
+ **New APIs:**
45
+ - `sdk.resolveSessionPds(session)` — Manually resolve and cache a session's PDS URL. Useful for pre-warming the cache
46
+ or for sessions created outside the SDK's auth flow.
47
+
48
+ **Migration:**
49
+
50
+ ```typescript
51
+ // Before
52
+ const sdk = createATProtoSDK({
53
+ oauth: { ... },
54
+ servers: { pds: "https://bsky.social", sds: "https://sds.example.com" },
55
+ });
56
+
57
+ // After
58
+ const sdk = createATProtoSDK({
59
+ oauth: { ... },
60
+ handleResolver: "https://bsky.social", // optional, for handle resolution only
61
+ servers: { sds: "https://sds.example.com" },
62
+ });
63
+ ```
64
+
65
+ - [#137](https://github.com/hypercerts-org/hypercerts-sdk/pull/137)
66
+ [`6f914e5`](https://github.com/hypercerts-org/hypercerts-sdk/commit/6f914e5a1f76ede52af4b8b75cf71dc935314dd7) Thanks
67
+ [@aspiers](https://github.com/aspiers)! - Add `isValidDid()` utility function for DID format validation
68
+ - Validates DID format (did:method:identifier) with support for numeric method names per W3C spec
69
+ - Exported from `@hypercerts-org/sdk-core` for consumer use
70
+ - `BlobOperationsImpl` constructor now validates `repoDid` and throws `ValidationError` for invalid formats
71
+
72
+ > **⚠️ Potentially breaking:** callers that previously passed invalid DID strings to `BlobOperationsImpl` (directly or
73
+ > via `Repository`) will now receive a `ValidationError` at construction time instead of silently accepting the value.
74
+ > Use `isValidDid(repoDid)` to check before constructing if needed.
75
+
76
+ - [#125](https://github.com/hypercerts-org/hypercerts-sdk/pull/125)
77
+ [`a493f3b`](https://github.com/hypercerts-org/hypercerts-sdk/commit/a493f3b05174eeef104c83f7be14749f1c0185a2) Thanks
78
+ [@Kzoeps](https://github.com/Kzoeps)! - Add dual profile system with separate Bluesky and Certified profile
79
+ operations, plus upsert methods
80
+
81
+ **Core SDK (`@hypercerts-org/sdk-core`):**
82
+
83
+ **Breaking Changes:**
84
+
85
+ The profile API has been completely redesigned to support two profile types:
86
+ - **Removed generic profile methods:**
87
+ - ❌ `profile.get()`
88
+ - ❌ `profile.create(params)`
89
+ - ❌ `profile.update(params)`
90
+ - **Removed deprecated profile types:**
91
+ - ❌ `HypercertProfile` - Use `CertifiedProfileRecord` instead
92
+ - ❌ `CreateHypercertProfileParams` - Use `CreateCertifiedProfileParams` instead
93
+ - ❌ `UpdateHypercertProfileParams` - Use `UpdateCertifiedProfileParams` instead
94
+ - ❌ `HypercertProfileParams` - Use specific create/update types instead
95
+ - ❌ `CreateProfileParams` - Use `CreateCertifiedProfileParams` instead
96
+ - **Removed unused type export:**
97
+ - ❌ `JsonBlobRef` - No longer used in SDK. This type was removed because:
98
+ - It was too "snowflaky" - required converting `BlobRef` instances to JSON format unnecessarily
99
+ - The actual `BlobRef` object works perfectly fine for all use cases
100
+ - It created flaky tests where we manually created mock JSON objects that weren't representative of actual
101
+ `JsonBlobRef` values
102
+ - Internal implementation now uses `BlobRef` instances directly throughout
103
+ - Users who need this type for advanced use cases can import it directly from `@atproto/lexicon`
104
+ - **Added profile-specific methods:**
105
+ - ✅ `profile.getBskyProfile()` - Get Bluesky profile (app.bsky.actor.profile)
106
+ - ✅ `profile.createBskyProfile(params)` - Create Bluesky profile
107
+ - ✅ `profile.updateBskyProfile(params)` - Update Bluesky profile
108
+ - ✅ `profile.getCertifiedProfile()` - Get Certified profile (app.certified.actor.profile) **[Returns `null` if
109
+ profile doesn't exist]**
110
+ - ✅ `profile.createCertifiedProfile(params)` - Create Certified profile
111
+ - ✅ `profile.updateCertifiedProfile(params)` - Update Certified profile
112
+ - ✅ `profile.upsertBskyProfile(params)` - Create or update Bluesky profile **[New]**
113
+ - ✅ `profile.upsertCertifiedProfile(params)` - Create or update Certified profile **[New]**
114
+
115
+ **Features:**
116
+ - **Bluesky profiles** (`app.bsky.actor.profile`):
117
+ - Standard AT Protocol profiles
118
+ - Avatar/banner returned as CDN URLs (`https://cdn.bsky.app/...`)
119
+ - Includes Bluesky-specific fields (labels, pinnedPost, etc.)
120
+ - **Certified profiles** (`app.certified.actor.profile`):
121
+ - Hypercerts-specific profiles with additional fields
122
+ - Avatar/banner returned as PDS blob URLs (`https://pds.../xrpc/...`)
123
+ - **`getCertifiedProfile()` returns `null` if profile doesn't exist** (not an error - common for new users)
124
+ - Supports `pronouns` field (max 20 graphemes)
125
+ - Supports `website` field
126
+ - Images stored using `HypercertImageRecord` format internally (smallImage/largeImage wrappers)
127
+ - **Upsert methods** (Recommended for most use cases):
128
+ - `upsertBskyProfile(params)` - Automatically creates or updates Bluesky profile
129
+ - `upsertCertifiedProfile(params)` - Automatically creates or updates Certified profile
130
+ - Simpler DX - no need to check if profile exists first
131
+ - Perfect for "save profile" operations
132
+ - **New types:**
133
+ - `BskyProfile` - Type for Bluesky profiles (alias for `AppBskyActorDefs.ProfileViewDetailed`)
134
+ - `CertifiedProfile` - Type for Certified profiles
135
+ - `CertifiedProfileRecord` - Record type for Certified profiles (replaces `HypercertProfile`)
136
+ - `CreateBskyProfileParams`, `UpdateBskyProfileParams`
137
+ - `CreateCertifiedProfileParams`, `UpdateCertifiedProfileParams` (replace `CreateHypercertProfileParams`,
138
+ `UpdateHypercertProfileParams`)
139
+
140
+ **Migration Guide:**
141
+
142
+ ```typescript
143
+ // BEFORE (old API - removed)
144
+ const profile = await repo.profile.get();
145
+ await repo.profile.create({ displayName: "Alice" });
146
+ await repo.profile.update({ displayName: "New Name" });
147
+
148
+ // AFTER - Recommended: Use upsert (works for both create and update)
149
+ await repo.profile.upsertCertifiedProfile({
150
+ displayName: "Alice",
151
+ pronouns: "she/her",
152
+ website: "https://alice.com",
153
+ });
154
+
155
+ // AFTER - Advanced: Explicit create/update for fine control
156
+ const certProfile = await repo.profile.getCertifiedProfile();
157
+ if (!certProfile) {
158
+ await repo.profile.createCertifiedProfile({
159
+ displayName: "Alice",
160
+ pronouns: "she/her",
161
+ });
162
+ } else {
163
+ await repo.profile.updateCertifiedProfile({
164
+ displayName: "New Name",
165
+ });
166
+ }
167
+
168
+ // Getting profiles - handle null case
169
+ const profile = await repo.profile.getCertifiedProfile();
170
+ if (profile) {
171
+ console.log(profile.displayName);
172
+ } else {
173
+ console.log("User hasn't created a profile yet");
174
+ }
175
+
176
+ // Type migrations
177
+ import type {
178
+ CertifiedProfileRecord, // was: HypercertProfile
179
+ CreateCertifiedProfileParams, // was: CreateHypercertProfileParams
180
+ UpdateCertifiedProfileParams, // was: UpdateHypercertProfileParams
181
+ } from "@hypercerts-org/sdk-core/types";
182
+ ```
183
+
184
+ **React SDK (`@hypercerts-org/sdk-react`):**
185
+
186
+ **Breaking Changes:**
187
+ - `useProfile` hook renamed `update` to `save` and `isUpdating` to `isSaving`
188
+ - `save()` now uses upsert internally - works for first-time profile creation too
189
+
190
+ ```typescript
191
+ // BEFORE
192
+ const { update, isUpdating } = useProfile();
193
+ await update({ displayName: "Alice" });
194
+
195
+ // AFTER
196
+ const { save, isSaving } = useProfile();
197
+ await save({ displayName: "Alice" }); // Works even if profile doesn't exist!
198
+ ```
199
+
200
+ - [#130](https://github.com/hypercerts-org/hypercerts-sdk/pull/130)
201
+ [`a9701cd`](https://github.com/hypercerts-org/hypercerts-sdk/commit/a9701cd47e2743207ff6bb6968eef7d9fc4db17c) Thanks
202
+ [@Kzoeps](https://github.com/Kzoeps)! - Fix `addContribution` to properly update hypercerts with contributor
203
+ references
204
+
205
+ **Breaking Changes:**
206
+
207
+ The `addContribution` method signature has changed to align with the `create()` method's contribution handling:
208
+ - `hypercertUri` is now **required** (was optional)
209
+ - `contributors` now accepts `Array<ContributorIdentityParams>` (was `string[]`)
210
+ - Supports DIDs, StrongRefs, or inline contributor creation params
211
+ - `contributionDetails` parameter **replaces** separate `role`/`description` params
212
+ - Supports inline role strings, StrongRefs, or inline contribution creation params
213
+ - Added optional `weight` parameter for contribution weighting
214
+ - Added optional `onProgress` callback for progress tracking
215
+ - Returns `UpdateResult` instead of `CreateResult` (since it updates the hypercert)
216
+
217
+ **What Changed:**
218
+
219
+ The method now correctly:
220
+ - Creates or references contributionDetails records
221
+ - Creates or references contributorInformation records
222
+ - **Updates the hypercert's `contributors` array** with the new entries (this was the bug)
223
+ - Supports batch addition of multiple contributors in one call
224
+ - Preserves existing contributors when adding new ones
225
+
226
+ **Migration:**
227
+
228
+ ```typescript
229
+ // Before (0.10.0-beta.7 and earlier):
230
+ await repo.hypercerts.addContribution({
231
+ hypercertUri: "at://...", // optional
232
+ contributors: ["did:plc:user1"],
233
+ role: "Developer",
234
+ description: "Built features",
235
+ });
236
+
237
+ // After (0.10.0-beta.8+):
238
+ await repo.hypercerts.addContribution({
239
+ hypercertUri: "at://...", // required
240
+ contributors: ["did:plc:user1"], // or StrongRef or create params
241
+ contributionDetails: "Developer", // or StrongRef or create params object
242
+ weight: "1.0", // optional
243
+ });
244
+
245
+ // With detailed contribution record:
246
+ await repo.hypercerts.addContribution({
247
+ hypercertUri: "at://...",
248
+ contributors: [
249
+ {
250
+ identifier: "did:plc:user1",
251
+ displayName: "Alice",
252
+ image: avatarBlob,
253
+ },
254
+ ],
255
+ contributionDetails: {
256
+ role: "Developer",
257
+ contributionDescription: "Built features",
258
+ startDate: "2024-01-01",
259
+ endDate: "2024-06-30",
260
+ },
261
+ weight: "2.0",
262
+ });
263
+ ```
264
+
265
+ **Additional Breaking Change:**
266
+
267
+ The `update()` method signature has been corrected to accept actual record fields:
268
+ - Now accepts `UpdateHypercertParams` (fields from `HypercertClaim` record schema)
269
+ - Previously accepted `Partial<CreateHypercertParams>` (SDK input format)
270
+
271
+ **Why this change:**
272
+ - Prevents invalid fields like `contributions` being added to records
273
+ - Allows updating `contributors` array directly (which exists in the schema)
274
+ - Type-safe - can only update fields that actually exist in the record
275
+ - Validation now works correctly
276
+
277
+ **Migration for update():**
278
+
279
+ Most code should continue to work since common fields like `title`, `description`, `startDate`, etc. exist in both
280
+ formats.
281
+
282
+ If you were using SDK input fields that don't exist in records (e.g., `contributions`), you'll need to update:
283
+
284
+ ```typescript
285
+ // Before:
286
+ await repo.hypercerts.update({
287
+ uri: hypercertUri,
288
+ updates: {
289
+ contributions: [...], // ❌ Invalid - doesn't exist in record
290
+ },
291
+ });
292
+
293
+ // After: Use the actual record field
294
+ await repo.hypercerts.update({
295
+ uri: hypercertUri,
296
+ updates: {
297
+ contributors: [...], // ✅ Valid - exists in record schema
298
+ },
299
+ });
300
+
301
+ // Or use the new addContribution method
302
+ await repo.hypercerts.addContribution({
303
+ hypercertUri: hypercertUri,
304
+ contributors: ["did:plc:user1"],
305
+ contributionDetails: "Developer",
306
+ });
307
+ ```
308
+
309
+ **Implementation Details:**
310
+ - Added `UpdateHypercertParams` type for type-safe record updates
311
+ - Added `buildContributorEntries()` helper to resolve and build contributor entries
312
+ - Added `attachContributorsToHypercert()` helper to update hypercerts with new contributors
313
+ - Refactored `processContributors()` to reuse `buildContributorEntries()` for consistency
314
+ - Removed unused `createContributionsWithProgress()` method
315
+
316
+ - [#126](https://github.com/hypercerts-org/hypercerts-sdk/pull/126)
317
+ [`5db01ee`](https://github.com/hypercerts-org/hypercerts-sdk/commit/5db01ee2baee53a0fd1c4e1ee1e32f2c7e41c30e) Thanks
318
+ [@Kzoeps](https://github.com/Kzoeps)! - Refactor internal URI parsing and blob upload operations
319
+
320
+ **Breaking:** `BlobOperationsImpl.upload()` now returns AT Protocol's `BlobRef` type instead of a plain
321
+ `{ ref, mimeType, size }` object. Callers should access blob properties via `BlobRef` methods (e.g.
322
+ `result.ref.toString()` for the CID string). SDS uploads now return a proper `BlobRef` instance with `ref`,
323
+ `mimeType`, and `size` correctly populated from the server response.
324
+ - Fix `validateScope` permission prefix regex to correctly accept query-param style scopes (e.g. `repo?action=create`,
325
+ `blob?accept=video/*`, `rpc?lxm=*`) and reject bare `atproto` with a suffix
326
+ - Export `AT_URI_REGEX` from `@hypercerts-org/sdk-core` for direct regex usage
327
+ - Consolidate AT-URI parsing in HypercertOperationsImpl using `parseAtUri()` utility
328
+ - Add internal `fetchRecord<T>()` and `saveRecord()` helpers to reduce code duplication
329
+ - Fix `AT_URI_REGEX` rkey capture group to use `[^/]+` instead of `.+` to prevent over-matching
330
+ - Fix `fetchRecord` to throw `NetworkError` when CID is absent instead of silently using an empty string
331
+ - Fix `saveRecord` error message formatting (was passing two arguments to `NetworkError`)
332
+ - Remove dead `parseAndValidateUri` method
333
+ - Eliminate redundant network fetch in `updateProject` by passing pre-fetched record to `updateCollectionRecord`
334
+
3
335
  ## 0.10.0-beta.8
4
336
 
5
337
  ### Minor Changes