https://domlink.deployments.hotsocket.fyi/
1// skinny atproto types file for supporting constellation.ts
2
3const DEFAULT_SERVICE = "https://api.bsky.app";
4
5/** Type used to imply that a parameter will be run through {@link ValidateNSID} */
6export type NSID = string;
7const NSIDExpression = /^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)$/;
8export function ValidateNSID(nsid: string): string | null {
9 return NSIDExpression.test(nsid) ? nsid : null;
10}
11
12export type Handle = string;
13export function StripHandle(handle: Handle) {
14 return handle.replace("@", "");
15}
16export type DID = `did:${"web"|"plc"}:${string}`;
17export function ValidateDID(did: string): string | null {
18 const parts = did.split(":");
19 const isValid = parts.length == 3 && parts[0] == "did" && (parts[1] == "plc" || parts[1] == "web") && parts[2].length > 0;
20 return isValid ? did : null;
21}
22
23export type AtURIString = string; //`at://${string}/${string}/${string}`;
24export class AtURI {
25 readonly authority: string | null;
26 readonly collection: string | null;
27 readonly rkey: string | null;
28 static fromString(uri: AtURIString): AtURI {
29 const parts = uri.split("/").slice(2);
30 return new AtURI(ValidateDID(parts[0]), ValidateNSID(parts[1]), parts[2]);
31 }
32 constructor(authority: string | null, collection: string | null = null, rkey: string | null = null) {
33 this.authority = authority;
34 this.collection = collection;
35 this.rkey = rkey;
36 }
37 /**
38 * Converts URI to at:// URI.
39 * @returns The string form of this URI, unless if any parts are specified without any preceding elements.
40 * @example ```
41 * // Invalid collection NSID, returns null.
42 * new AtURI("at://did:web:example.com/cheese/abc123").toString()
43 * // Invalid 'authority' DID, returns null.
44 * new AtURI("at://not-a-did/com.example.nsid").toString()
45 * // All good and happy, returns the string fed in.
46 * new AtURI("at://did:web:example.com/com.example.nsid/abc123").toString()
47 * ```
48 */
49 toString(): string | null {
50 const ret: (string|null)[] = ["at://"];
51 // using `?? ""` to have a "bad" value to find
52 if (this.authority) {
53 ret.push(this.authority ?? "");
54 } else ret.push(null);
55 if (this.collection) {
56 if (ret.indexOf(null) != -1) {return null;}
57 ret.push("/");
58 ret.push(this.collection ?? "");
59 } else ret.push(null);
60 if (this.rkey) {
61 if (ret.indexOf(null) != -1) {return null;}
62 ret.push("/");
63 ret.push(this.rkey ?? "");
64 }
65 return ret.join("");
66 }
67}
68
69export type RecordResponse<T> = {
70 cid: string;
71 uri: AtURIString;
72 value: T;
73}
74
75// technically you can cast it to whatever you want but i feel like using a generic(?) makes it cleaner
76/** Calls an XRPC "query" method (HTTP GET)
77 * @param service Defaults to the {@link https://api.bsky.app/ Bluesky (PBC) API} service.
78*/
79export async function XQuery<T>(method: string, params: Record<string, string | number | null> | null = null, service: string = DEFAULT_SERVICE) {
80 let QueryURL = `${service}/xrpc/${method}`;
81 if (params) {
82 const usp = new URLSearchParams();
83 for (const key in params) {
84 if (params[key]) {
85 usp.append(key, params[key].toString());
86 }
87 }
88 QueryURL += "?" + usp.toString();
89 }
90 return (await (await fetch(QueryURL)).json()) as T;
91}
92/** Calls com.atproto.repo.getRecord with XQuery */
93export async function GetRecord<T>(uri: AtURI, service: string = "https://slingshot.microcosm.blue") {
94 return await XQuery<RecordResponse<T>>("com.atproto.repo.getRecord", {
95 repo: uri.authority!,
96 collection: uri.collection!,
97 rkey: uri.rkey!
98 }, service);
99}
100type ListRecordsResponse<T> = {
101 cursor: string;
102 records: RecordResponse<T>[];
103};
104export async function ListRecords<T>(repo: string, collection: NSID, service: string, limit: number = 50) {
105 return await XQuery<ListRecordsResponse<T>>("com.atproto.repo.listRecords", {
106 repo: repo,
107 collection: collection,
108 limit: limit
109 }, service);
110}