Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144import { isDid, type Did } from "@atcute/lexicons/syntax";import { buildUrl, toResponseError } from "#lib/api/_request.ts";import { nsidOf } from "#lib/api/syntax.ts";import type { AuthorizationResponse } from "#lib/auth.svelte.ts";import { sentence } from "#lib/format.ts";import type { Fetch } from "#lib/fetch.ts";
const BEGIN_SIGNUP = nsidOf("org.tangled.temp.account.beginSignup");const RESEND_SIGNUP = nsidOf("org.tangled.temp.account.resendSignup");const GET_SIGNUP = nsidOf("org.tangled.temp.account.getSignup");const COMPLETE_SIGNUP = nsidOf("org.tangled.temp.account.completeSignup");
const SERVER_FAILURE = "Unable to reach Tangled. Try again in a moment.";
export class SignupError extends Error { constructor( message: string, readonly code?: string ) { super(message); }}
const signupError = async (response: Response, fallback: string): Promise<SignupError> => { if (response.status >= 500) return new SignupError(SERVER_FAILURE); const err = await toResponseError(response); // "XRPCError" is toResponseError's sentinel for non-JSON bodies, not a service code const code = err.error && err.error !== "XRPCError" ? err.error : undefined; if (err.description) return new SignupError(sentence(err.description), code); return new SignupError(code ?? fallback, code);};
// open endpoints: no service auth, the turnstile challenge guards beginSignupexport const beginSignup = async ( serviceUrl: string, email: string, turnstileToken: string, fetchFn: Fetch = fetch): Promise<void> => { const response = await fetchFn(buildUrl(serviceUrl, BEGIN_SIGNUP), { method: "POST", headers: { "content-type": "application/json", accept: "application/json" }, body: JSON.stringify({ email, turnstileToken }) }); if (!response.ok) { throw await signupError( response, `Unable to send the link (HTTP ${response.status}). Try again in a moment.` ); }};
export const resendSignup = async ( serviceUrl: string, email: string, fetchFn: Fetch = fetch): Promise<void> => { const response = await fetchFn(buildUrl(serviceUrl, RESEND_SIGNUP), { method: "POST", headers: { "content-type": "application/json", accept: "application/json" }, body: JSON.stringify({ email }) }); if (!response.ok) { throw await signupError( response, `Unable to resend the email (HTTP ${response.status}). Try again in a moment.` ); }};
export const getSignup = async ( serviceUrl: string, token: string, fetchFn: Fetch = fetch): Promise<{ email: string; pds?: string }> => { const response = await fetchFn(buildUrl(serviceUrl, GET_SIGNUP, { token }), { headers: { accept: "application/json" } }); if (!response.ok) { throw await signupError(response, `Unable to check the link (HTTP ${response.status}).`); } const body = (await response.json().catch(() => null)) as { email?: unknown; pds?: unknown; } | null; if (typeof body?.email !== "string") { throw new SignupError("Unable to check the link. Try again in a moment."); } // a deliberi without the field leaves the page to sign in through the pds return typeof body.pds === "string" ? { email: body.email, pds: body.pds } : { email: body.email };};
export interface CompleteSignupInput { token: string; username: string; password: string; requestUri?: string;}
export interface CompletedSignup { did: Did; handle: string; authorization?: AuthorizationResponse;}
const isAuthorization = (value: unknown): value is AuthorizationResponse => !!value && typeof value === "object" && ["code", "state", "iss"].every( (key) => typeof (value as Record<string, unknown>)[key] === "string" );
export const completeSignup = async ( serviceUrl: string, input: CompleteSignupInput, fetchFn: Fetch = fetch): Promise<CompletedSignup> => { const response = await fetchFn(buildUrl(serviceUrl, COMPLETE_SIGNUP), { method: "POST", headers: { "content-type": "application/json", accept: "application/json" }, body: JSON.stringify(input) }); if (!response.ok) { throw await signupError( response, `Unable to create the account (HTTP ${response.status}). Try again in a moment.` ); } // a successful signup must return both fields; anything else is a broken // response, and the pages must not render "Account created" for it const body = (await response.json().catch(() => null)) as { did?: unknown; handle?: unknown; authorization?: unknown; } | null; if (!isDid(body?.did) || typeof body.handle !== "string") { throw new Error("Unable to create the account. Try again in a moment."); } const { did, handle, authorization } = body; return isAuthorization(authorization) ? { did, handle, authorization } : { did, handle };};