// Auto-generated client-safe subset. Do not edit. // Import this in app components instead of hatk.generated.ts // to avoid pulling in server-only dependencies. export type { XrpcSchema } from './hatk.generated.ts' import type { XrpcSchema } from './hatk.generated.ts' export type { BskyActorProfile, Post, Postgate, Threadgate, BskyGraphFollow, GermnetworkDeclaration, ApplyWrites, CreateReport, DescribeCollections, DescribeFeeds, DescribeLabels, GetFeed, GetPreferences, GetRecord, GetRecords, PutPreference, SearchRecords, UploadBlob, OpensocialDeclaration, Search, GrainActorProfile, Comment, Favorite, Gallery, GrainGalleryItem, Block, GrainGraphFollow, MuteActor, UnmuteActor, Decline, GrainGroupItem, Submission, Photo, Exif, Story, AddSpaceMember, CreateGroup, CreatePoolComment, CreatePoolGallery, CreatePrivateGallery, DeleteAccount, DeleteGallery, DeletePoolComment, DeletePoolGallery, GetActorFavorites, GetActorProfile, GetBlocks, GetCameras, GetCommentThread, GetFollowers, GetFollowing, GetGallery, GetGalleryFavorites, GetGroup, GetGroupHost, GetGroupSubmissions, GetKnownFollowers, GetLocations, GetMutes, GetNotifications, GetPoolGallery, GetPrivateGallery, GetSitemap, GetSpaceMembers, GetSpaceSupport, GetStories, GetStory, GetStoryArchive, GetStoryAuthors, GetSuggestedFollows, JoinGroup, LeaveGroup, ListGroups, ListPoolFeed, ListPoolGalleries, ListPrivateGalleries, ListSharedGalleries, MarkStoriesViewed, PutPoolFavorite, SearchActorsTypeahead, SearchGalleries, SearchProfiles, RecordRegistry, CreateRecord, DeleteRecord, PutRecord, Nux, MutedWord, SavedFeed, StatusView, BskyActorDefsProfileView, BskyActorDefsViewerState, FeedViewPref, LabelersPref, InterestsPref, KnownFollowers, MutedWordsPref, SavedFeedsPref, ThreadViewPref, DeclaredAgePref, HiddenPostsPref, LabelerPrefItem, AdultContentPref, BskyAppStatePref, ContentLabelPref, ProfileViewBasic, SavedFeedsPrefV2, VerificationView, ProfileAssociated, VerificationPrefs, VerificationState, PersonalDetailsPref, BskyActorDefsProfileViewDetailed, BskyAppProgressGuide, LiveEventPreferences, ProfileAssociatedChat, ProfileAssociatedGerm, PostInteractionSettingsPref, ProfileAssociatedActivitySubscription, BskyEmbedDefsAspectRatio, ExternalView, External, ViewExternal, ImagesView, Image, ViewImage, RecordView, ViewRecord, ViewBlocked, ViewDetached, ViewNotFound, RecordWithMediaView, VideoView, Caption, PostView, BskyFeedDefsReplyRef, ReasonPin, BlockedPost, Interaction, BskyFeedDefsViewerState, FeedViewPost, NotFoundPost, ReasonRepost, BlockedAuthor, GeneratorView, ThreadContext, ThreadViewPost, ThreadgateView, SkeletonFeedPost, SkeletonReasonPin, GeneratorViewerState, SkeletonReasonRepost, Entity, PostReplyRef, TextSlice, DisableRule, ListRule, MentionRule, FollowerRule, FollowingRule, ListView, ListItemView, Relationship, ListViewBasic, NotFoundActor, ListViewerState, StarterPackView, StarterPackViewBasic, LabelerView, LabelerPolicies, LabelerViewerState, LabelerViewDetailed, Preference, Preferences, RecordDeleted, ChatPreference, ActivitySubscription, FilterablePreference, SubjectActivitySubscription, Tag, Link, Mention, ByteSlice, Label, SelfLabels, SelfLabel, LabelValueDefinition, LabelValueDefinitionStrings, GermnetworkDeclarationMessageMe, Create, Update, Delete, CreateResult, UpdateResult, DeleteResult, RepoRef, LabelDefinition, LabelLocale, Result, MentionLabel, EmbedInfo, SearchAspectRatio, SubscopeInfo, GrainActorDefsProfileView, GrainActorDefsProfileViewDetailed, GrainActorDefsMessageMe, GrainActorDefsViewerState, CommentView, GrainCommentDefsViewerState, GrainDefsAspectRatio, GalleryView, CrossPostInfo, GrainGalleryDefsViewerState, GroupView, GrainGroupDefsViewerState, SubmissionState, GroupRef, SubmissionView, Rule, PhotoView, ExifView, GalleryState, StoryView, GrainStoryDefsViewerState, CreatePoolGalleryPhotoInput, CreatePrivateGalleryPhotoInput, BlockItem, CameraItem, GetFollowersFollowerItem, GetFollowersViewerState, FollowingItem, GetFollowingViewerState, FavoriteItem, GetKnownFollowersFollowerItem, LocationItem, MuteItem, NotificationItem, PoolComment, PoolAuthor, PrivateGallery, PrivateItem, SitemapEntry, MemberView, StoryAuthor, SuggestedItem, PoolFeedItem, PoolGallery, PrivateGalleryRef, SharedGalleryRef, ProfileSearchResult } from './hatk.generated.ts' const _procedures = new Set(['dev.hatk.applyWrites', 'dev.hatk.createRecord', 'dev.hatk.createReport', 'dev.hatk.deleteRecord', 'dev.hatk.putPreference', 'dev.hatk.putRecord', 'social.grain.graph.muteActor', 'social.grain.graph.unmuteActor', 'social.grain.unspecced.addSpaceMember', 'social.grain.unspecced.createGroup', 'social.grain.unspecced.createPoolComment', 'social.grain.unspecced.createPoolGallery', 'social.grain.unspecced.createPrivateGallery', 'social.grain.unspecced.deleteAccount', 'social.grain.unspecced.deleteGallery', 'social.grain.unspecced.deletePoolComment', 'social.grain.unspecced.deletePoolGallery', 'social.grain.unspecced.joinGroup', 'social.grain.unspecced.leaveGroup', 'social.grain.unspecced.markStoriesViewed', 'social.grain.unspecced.putPoolFavorite']) const _blobInputs = new Set(['dev.hatk.uploadBlob']) type CallArg = XrpcSchema[K] extends { input: infer I } ? I : XrpcSchema[K] extends { params: infer P } ? P : Record type OutputOf = XrpcSchema[K]['output'] export async function callXrpc( nsid: K, arg?: CallArg, customFetch?: typeof globalThis.fetch, ): Promise> { if (typeof window === 'undefined' && !customFetch) { const bridge = (globalThis as any).__hatk_callXrpc if (!bridge) throw new Error('callXrpc: server bridge not available — is hatk initialized?') if (_procedures.has(nsid) || _blobInputs.has(nsid)) return bridge(nsid, {}, arg) as Promise> return bridge(nsid, arg) as Promise> } const _fetch = customFetch ?? globalThis.fetch // Use relative URL so SvelteKit's fetch can deduplicate server/client requests let path = `/xrpc/${nsid}` if (_blobInputs.has(nsid)) { const blob = arg as Blob | ArrayBuffer const ct = blob instanceof Blob ? blob.type : 'application/octet-stream' const res = await _fetch(path, { method: 'POST', headers: { 'Content-Type': ct }, body: blob }) if (typeof window !== 'undefined' && res.status === 401) { const _b = await res.json().catch(() => ({})); const _h = _b.handle ?? getViewer()?.handle; window.location.href = _h ? `/oauth/login?handle=${encodeURIComponent(_h)}` : '/oauth/login'; return new Promise(() => {}) as any } if (!res.ok) throw new Error(`XRPC ${nsid} failed: ${res.status}`) return res.json() as Promise> } if (_procedures.has(nsid)) { const res = await _fetch(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(arg) }) if (typeof window !== 'undefined' && res.status === 401) { const _b = await res.json().catch(() => ({})); const _h = _b.handle ?? getViewer()?.handle; window.location.href = _h ? `/oauth/login?handle=${encodeURIComponent(_h)}` : '/oauth/login'; return new Promise(() => {}) as any } if (!res.ok) throw new Error(`XRPC ${nsid} failed: ${res.status}`) return res.json() as Promise> } const params = new URLSearchParams() for (const [k, v] of Object.entries(arg || {})) { if (v == null) continue if (Array.isArray(v)) for (const item of v) params.append(k, String(item)) else params.set(k, String(v)) } const qs = params.toString() if (qs) path += `?${qs}` const res = await _fetch(path) if (typeof window !== 'undefined' && res.status === 401) { const _b = await res.json().catch(() => ({})); const _h = _b.handle ?? getViewer()?.handle; window.location.href = _h ? `/oauth/login?handle=${encodeURIComponent(_h)}` : '/oauth/login'; return new Promise(() => {}) as any } if (!res.ok) throw new Error(`XRPC ${nsid} failed: ${res.status}`) return res.json() as Promise> } export function getViewer(): { did: string; handle: string } | null { return (globalThis as any).__hatk_viewer ?? null } // ─── Auth Helpers ──────────────────────────────────────────────────── export async function login(handle: string): Promise { const res = await fetch(`/oauth/login?handle=${encodeURIComponent(handle)}`, { redirect: 'manual' }) if (res.type === 'opaqueredirect') { window.location.href = `/oauth/login?handle=${encodeURIComponent(handle)}` return } if (res.ok) return const body = await res.json().catch(() => ({ error: 'Login failed' })) throw new Error(body.error || 'Login failed') } export async function logout(): Promise { ;(globalThis as any).__hatk_viewer = null await fetch('/auth/logout', { method: 'POST' }).catch(() => {}) } export function viewerDid(): string | null { if (typeof window === 'undefined') return null const viewer = (globalThis as any).__hatk_viewer return viewer?.did ?? null } // Expose viewer for getViewer() bridge ;(globalThis as any).__hatk_auth = { viewerDid } // ─── Server Helpers ────────────────────────────────────────────────── export async function parseViewer(cookies: { get(name: string): string | undefined }): Promise<{ did: string; handle?: string } | null> { const parseSessionCookie = (globalThis as any).__hatk_parseSessionCookie if (!parseSessionCookie) return null const cookieValue = cookies.get('__hatk_session') if (!cookieValue) return null try { const request = new Request('http://localhost', { headers: { cookie: `__hatk_session=${cookieValue}` } }) const viewer = await parseSessionCookie(request) if (viewer) (globalThis as any).__hatk_viewer = viewer return viewer } catch { return null } }