···11+import 'package:atproto_core/atproto_core.dart';
22+33+/// Callback type for deleting a record
44+typedef DeleteRecordCallback = Future<void> Function({
55+ required String repo,
66+ required String collection,
77+ required String rkey,
88+});
99+1010+/// Adapter for Bluesky repository operations
1111+///
1212+/// Handles Bluesky-specific repository operations like cross-post management
1313+/// and URI conversions between Spark and Bluesky namespaces.
1414+class BskyRepoAdapter {
1515+ const BskyRepoAdapter();
1616+1717+ // ============================================================================
1818+ // Bluesky Cross-Post Handling
1919+ // ============================================================================
2020+2121+ /// Build a Bluesky counterpart URI from a Spark post URI
2222+ ///
2323+ /// Converts `at://did/so.sprk.feed.post/rkey` to `at://did/app.bsky.feed.post/rkey`
2424+ AtUri buildBlueskyCounterpartUri(AtUri sparkUri) {
2525+ final did = sparkUri.hostname;
2626+ final rkey = sparkUri.rkey;
2727+ return AtUri.parse('at://$did/app.bsky.feed.post/$rkey');
2828+ }
2929+3030+ /// Delete the Bluesky counterpart of a Spark post
3131+ ///
3232+ /// This is a best-effort operation - errors are caught and returned as false.
3333+ /// Returns true if deletion was successful, false otherwise.
3434+ ///
3535+ /// [deleteRecord] is a callback that performs the actual deletion (typically atproto.repo.deleteRecord)
3636+ Future<bool> deleteBlueskyCounterpart(
3737+ DeleteRecordCallback deleteRecord,
3838+ AtUri sparkUri,
3939+ ) async {
4040+ try {
4141+ final blueskyUri = buildBlueskyCounterpartUri(sparkUri);
4242+ await deleteRecord(
4343+ repo: blueskyUri.hostname,
4444+ collection: blueskyUri.collection.toString(),
4545+ rkey: blueskyUri.rkey,
4646+ );
4747+ return true;
4848+ } catch (e) {
4949+ // Ignore errors like 404 – it simply means the counterpart does not exist.
5050+ return false;
5151+ }
5252+ }
5353+5454+ /// Check if a URI is a Bluesky feed post
5555+ bool isBlueskyFeedPost(AtUri uri) {
5656+ return uri.collection.toString() == 'app.bsky.feed.post';
5757+ }
5858+5959+ /// Check if a URI is a Spark feed post
6060+ bool isSparkFeedPost(AtUri uri) {
6161+ return uri.collection.toString() == 'so.sprk.feed.post';
6262+ }
6363+}
6464+6565+/// Singleton instance of the Bluesky repo adapter
6666+///
6767+/// Use this instance for all Bluesky repository operations:
6868+/// ```dart
6969+/// final blueskyUri = bskyRepoAdapter.buildBlueskyCounterpartUri(sparkUri);
7070+/// await bskyRepoAdapter.deleteBlueskyCounterpart(repoService, sparkUri);
7171+/// ```
7272+const bskyRepoAdapter = BskyRepoAdapter();