Skip to content

Commit 935769e

Browse files
authored
Merge pull request #126 from Kzoeps/location-refactor
refactor(sdk-core): extract URI parsing/record helpers, fix blob upload and scope validation
2 parents 7ad471b + 5eda6ee commit 935769e

11 files changed

Lines changed: 383 additions & 244 deletions

File tree

.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?*
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`

packages/sdk-core/src/auth/permissions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,7 +1233,7 @@ export function validateScope(scope: string): {
12331233
const invalidPermissions: string[] = [];
12341234

12351235
// Pattern for valid permission prefixes
1236-
const validPrefixes = /^(atproto|transition:|account:|repo:|blob:?|rpc:|identity:|include:)/;
1236+
const validPrefixes = /^(atproto$|transition:|account:|repo[:?]|blob[:?]|rpc[:?]|identity:|include:)/;
12371237

12381238
for (const permission of permissions) {
12391239
if (!validPrefixes.test(permission)) {

packages/sdk-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export {
3838
parseAtUri,
3939
buildAtUri,
4040
extractRkeyFromUri,
41+
AT_URI_REGEX,
4142
isValidAtUri,
4243
createStrongRef,
4344
createStrongRefFromResult,

packages/sdk-core/src/lexicons/utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@
1111
import type { StrongRef } from "../services/hypercerts/types.js";
1212
import type { CreateResult, UpdateResult } from "../repository/types.js";
1313

14+
/**
15+
* Regular expression for parsing AT-URIs.
16+
*
17+
* AT-URIs follow the format: `at://{did}/{collection}/{rkey}`
18+
*
19+
* Capture groups:
20+
* - [1] did - The DID of the repository owner (e.g., "did:plc:abc123")
21+
* - [2] collection - The NSID of the record type (e.g., "org.hypercerts.claim.activity")
22+
* - [3] rkey - The record key (e.g., "3km2vj4kfqp2a")
23+
*
24+
* @example Direct regex usage
25+
* ```typescript
26+
* const match = AT_URI_REGEX.exec("at://did:plc:abc/org.hypercerts.claim.activity/xyz");
27+
* if (match) {
28+
* const [, did, collection, rkey] = match;
29+
* }
30+
* ```
31+
*
32+
* @example Validation
33+
* ```typescript
34+
* if (AT_URI_REGEX.test(userInput)) {
35+
* // Valid AT-URI format
36+
* }
37+
* ```
38+
*
39+
* @remarks
40+
* For most use cases, prefer using {@link parseAtUri} which provides
41+
* better error messages and returns a typed object.
42+
*/
43+
export const AT_URI_REGEX = /^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/;
44+
1445
/**
1546
* Components of an AT-URI (AT Protocol Uniform Resource Identifier).
1647
*

packages/sdk-core/src/repository/BlobOperationsImpl.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
* @packageDocumentation
88
*/
99

10-
import type { Agent, BlobRef } from "@atproto/api";
10+
import type { Agent } from "@atproto/api";
11+
import { BlobRef } from "@atproto/lexicon";
12+
import { CID } from "multiformats/cid";
1113
import { NetworkError } from "../core/errors.js";
1214
import type { BlobOperations } from "./interfaces.js";
1315

@@ -168,9 +170,21 @@ export class BlobOperationsImpl implements BlobOperations {
168170
throw new NetworkError(`SDS blob upload failed: ${response.statusText}`);
169171
}
170172

171-
const result = await response.json();
173+
// SDS returns { blob: { ref: { $link: string }, mimeType: string, size: number } }
174+
// which is a JSON-serialized blob ref, not a BlobRef instance.
175+
// Construct a BlobRef directly using CID.parse to preserve the size from the SDS response.
176+
const result = (await response.json()) as {
177+
blob: { ref: { $link: string }; mimeType: string; size: number };
178+
};
172179

173-
return result.blob;
180+
let cid: CID;
181+
try {
182+
cid = CID.parse(result.blob.ref.$link);
183+
} catch {
184+
throw new NetworkError("SDS blob upload returned an invalid blob reference");
185+
}
186+
187+
return new BlobRef(cid, result.blob.mimeType, result.blob.size);
174188
}
175189

176190
/**

0 commit comments

Comments
 (0)