import { isCid, isDatetime, isDid, isNsid, isRecordKey, isTid, parseCanonicalResourceUri } from "@atcute/lexicons/syntax"; import type { CanonicalResourceUri, Cid as RawCid, Datetime as RawDatetime, Did as RawDid, Nsid as RawNsid, RecordKey as RawRecordKey, ResourceUri as RawResourceUri, Tid as RawTid } from "@atcute/lexicons/syntax"; export type { RawCid, RawDatetime, RawResourceUri }; // asserting wrappers over atcute's syntax guards for xrpc input boundaries. // untrusted strings (user input, route params, storage) get rejected here // instead of traveling to the server and failing as a 400. declare const tag: unique symbol; type Lewitype = T & { readonly [tag]: N }; export type Did = Lewitype; export type Nsid = Lewitype; export type Rkey = Lewitype; export type Tid = Lewitype; export type Cid = Lewitype; export type AtUri = Lewitype; export type Datetime = Lewitype; export type KnotOrigin = Lewitype; export type GitOid = Lewitype; export type LabelKey = Lewitype; export type Capability = Lewitype; export type ProxyPath = Lewitype; const invalid = (kind: string, value: string): Error => new Error(`"${value}" is not a valid ${kind}.`); export const didOf = (value: string): Did => { if (!isDid(value)) throw invalid("DID", value); return value as Did; }; export const nsidOf = (value: string): Nsid => { if (!isNsid(value)) throw invalid("NSID", value); return value as Nsid; }; export const recordKeyOf = (value: string): Rkey => { if (!isRecordKey(value)) throw invalid("rkey", value); return value as Rkey; }; export const tidOf = (value: string): Tid => { if (!isTid(value)) throw invalid("TID", value); return value as Tid; }; export const cidOf = (value: string): Cid => { if (!isCid(value)) throw invalid("CID", value); return value as Cid; }; export const atUriOf = (value: string): AtUri => { parseCanonicalResourceUri(value); return value as AtUri; }; export const datetimeOf = (value: string): Datetime => { if (!isDatetime(value)) throw invalid("datetime", value); return value as Datetime; }; export const knotOf = (value: string): KnotOrigin => { const parsed = URL.parse(`https://${value}`); if (parsed === null || parsed.host !== value || parsed.pathname !== "/") throw invalid("knot origin", value); return value as KnotOrigin; }; const GIT_OID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/; export const gitOidOf = (value: string): GitOid => { if (!GIT_OID.test(value)) throw invalid("git OID", value); return value as GitOid; }; const LABEL_KEY = /^(?!did:)[A-Za-z0-9._~-]{1,512}$/; export const labelKeyOf = (value: string): LabelKey => { if (!LABEL_KEY.test(value)) throw invalid("label key", value); return value as LabelKey; }; const CAPABILITY = /^\S+$/; export const capabilityOf = (value: string): Capability => { if (value.length === 0 || value.length > 512 || !CAPABILITY.test(value)) throw invalid("cap", value); return value as Capability; }; const PROXY_PATH = /^_[A-Za-z0-9.-]+$/; export const proxyPathOf = (value: string): ProxyPath => { if (!PROXY_PATH.test(value)) throw invalid("proxy path", value); return value as ProxyPath; }; export type XrpcPath = Nsid | ProxyPath; export const xrpcPathOf = (value: string): XrpcPath => value.startsWith("_") ? proxyPathOf(value) : nsidOf(value);