import { ok } from '@atcute/client';
import type { OAuthUserAgent } from '@atcute/oauth-browser-client';
import { getRpc, createAuthRpc } from './client';
import { resolveActor } from './identity';
import { parseAtUri } from './records';
export interface PublicKeyRecord {
name: string;
key: string;
createdAt: string;
rkey: string;
uri: string;
}
export interface KnotRecord {
domain: string;
createdAt: string;
rkey: string;
uri: string;
}
export interface SpindleRecord {
instance: string;
createdAt: string;
rkey: string;
uri: string;
}
export const listPublicKeys = async (did: string): Promise<PublicKeyRecord[]> => {
const actor = await resolveActor(did);
const rpc = getRpc(actor.pds);
const keys: PublicKeyRecord[] = [];
let cursor: string | undefined;
try {
do {
const page = await ok(
rpc.get('com.atproto.repo.listRecords', {
params: {
repo: actor.did,
collection: 'sh.tangled.publicKey',
limit: 100,
cursor,
reverse: true,
},
}),
);
for (const r of page.records) {
const rkey = parseAtUri(r.uri).rkey;
const val = r.value as any;
keys.push({
name: val.name || '',
key: val.key || '',
createdAt: val.createdAt || '',
rkey,
uri: r.uri,
});
}
cursor = page.cursor;
} while (cursor);
} catch (err) {
console.error('Failed to list public keys from PDS:', err);
}
return keys;
};
export const addPublicKey = async (
agent: OAuthUserAgent,
name: string,
key: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.createRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.publicKey',
record: {
$type: 'sh.tangled.publicKey',
name,
key,
createdAt: new Date().toISOString(),
},
},
}),
);
};
export const deletePublicKey = async (
agent: OAuthUserAgent,
rkey: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.deleteRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.publicKey',
rkey,
},
}),
);
};
export const listKnots = async (did: string): Promise<KnotRecord[]> => {
const actor = await resolveActor(did);
const rpc = getRpc(actor.pds);
const knots: KnotRecord[] = [];
let cursor: string | undefined;
try {
do {
const page = await ok(
rpc.get('com.atproto.repo.listRecords', {
params: {
repo: actor.did,
collection: 'sh.tangled.knot',
limit: 100,
cursor,
reverse: true,
},
}),
);
for (const r of page.records) {
const rkey = parseAtUri(r.uri).rkey;
const val = r.value as any;
knots.push({
domain: rkey,
createdAt: val.createdAt || '',
rkey,
uri: r.uri,
});
}
cursor = page.cursor;
} while (cursor);
} catch (err) {
console.error('Failed to list knots from PDS:', err);
}
return knots;
};
export const registerKnot = async (
agent: OAuthUserAgent,
domain: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.putRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.knot',
rkey: domain,
record: {
$type: 'sh.tangled.knot',
createdAt: new Date().toISOString(),
},
},
}),
);
};
export const unregisterKnot = async (
agent: OAuthUserAgent,
domain: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.deleteRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.knot',
rkey: domain,
},
}),
);
};
export const listSpindles = async (did: string): Promise<SpindleRecord[]> => {
const actor = await resolveActor(did);
const rpc = getRpc(actor.pds);
const spindles: SpindleRecord[] = [];
let cursor: string | undefined;
try {
do {
const page = await ok(
rpc.get('com.atproto.repo.listRecords', {
params: {
repo: actor.did,
collection: 'sh.tangled.spindle',
limit: 100,
cursor,
reverse: true,
},
}),
);
for (const r of page.records) {
const rkey = parseAtUri(r.uri).rkey;
const val = r.value as any;
spindles.push({
instance: rkey,
createdAt: val.createdAt || '',
rkey,
uri: r.uri,
});
}
cursor = page.cursor;
} while (cursor);
} catch (err) {
console.error('Failed to list spindles from PDS:', err);
}
return spindles;
};
export const registerSpindle = async (
agent: OAuthUserAgent,
instance: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.putRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.spindle',
rkey: instance,
record: {
$type: 'sh.tangled.spindle',
createdAt: new Date().toISOString(),
},
},
}),
);
};
export const unregisterSpindle = async (
agent: OAuthUserAgent,
instance: string,
): Promise<void> => {
const rpc = createAuthRpc(agent);
await ok(
rpc.post('com.atproto.repo.deleteRecord', {
input: {
repo: agent.sub,
collection: 'sh.tangled.spindle',
rkey: instance,
},
}),
);
};