Publish the unified MIT-licensed Stuffbox SDK source

Hop-State: A_06FPAF0M83E7T0JJ7EP2WQ0
Hop-Proposal: R_06FPAF04S0D4G8TA8TNK3C0
Hop-Task: T_06FPADTV2YD4PD5GMTK3150
Hop-Attempt: AT_06FPADTV2ZYKEY8GH3JTK98
This commit is contained in:
2026-07-15 03:04:24 -07:00
committed by Hop
parent 0718af136c
commit 9e1ed2c9cb
26 changed files with 4128 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
import {
StuffboxClient,
createPkcePair,
generateState,
type Asset,
type StuffboxScope,
type TokenSet,
} from "@stuffbox/sdk";
const stuffbox = new StuffboxClient({ baseUrl: "https://stuffbox.xyz" });
export interface PendingConnection {
clientId: string;
callbackUrl: string;
codeVerifier: string;
state: string;
}
interface BeginConnectionInput {
callbackUrl: string;
scopes: readonly StuffboxScope[];
clientId?: string;
savePending(value: PendingConnection): Promise<void>;
}
export async function beginConnection(
input: BeginConnectionInput,
): Promise<string> {
const pkce = await createPkcePair();
const state = generateState();
const common = {
callbackUrl: input.callbackUrl,
codeChallenge: pkce.challenge,
scopes: input.scopes,
state,
};
const connection = await stuffbox.createConnectionRequest(
input.clientId
? { ...common, clientId: input.clientId }
: { ...common, selfHosted: true },
);
await input.savePending({
clientId: connection.clientId,
callbackUrl: connection.callbackUrl,
codeVerifier: pkce.verifier,
state,
});
return connection.authorizationUrl;
}
interface FinishConnectionInput {
code: string;
returnedState: string;
pending: PendingConnection;
}
export async function finishConnection(
input: FinishConnectionInput,
): Promise<TokenSet> {
if (input.returnedState !== input.pending.state) {
throw new Error("Stuffbox connection state did not match");
}
return stuffbox.exchangeAuthorizationCode({
clientId: input.pending.clientId,
code: input.code,
codeVerifier: input.pending.codeVerifier,
redirectUri: input.pending.callbackUrl,
});
}
export async function loadEntireLibrary(accessToken: string): Promise<Asset[]> {
const media = new StuffboxClient({
baseUrl: "https://stuffbox.xyz",
accessToken,
});
const assets: Asset[] = [];
let cursor: string | undefined;
do {
const page = await media.listAssets({
limit: 100,
...(cursor ? { cursor } : {}),
});
assets.push(...page.items);
cursor = page.nextCursor;
} while (cursor);
return assets;
}