1import { requestUrl } from "obsidian";2
3const API_BASE = "https://api.sublimated.com";4
5export interface Version {6 id: string;7 number: number;8 authorName: string;9 authorType: string;10 message: string | null;11 createdAt: string;12}13
14export class AuthRequiredError extends Error {15 constructor(public status: number) {16 super(`auth required (${status})`);17 this.name = "AuthRequiredError";18 }19}20
21// Raw wire format from api.sublimated.com22interface RawActor {23 username: string | null;24 name: string | null;25 role: string;26 actor_type: string;27}28
29interface RawVersion {30 id: string;31 item_id: string;32 version: number;33 content_hash: string;34 blob_sha1: string | null;35 size_bytes: number;36 message: string | null;37 name: string | null;38 created_at: string;39 actors: RawActor[];40}41
42interface ApiEnvelope<T> {43 ok: boolean;44 data?: T;45 error?: { type: string; message?: string };46}47
48export interface TestConnectionResult {49 ok: boolean;50 username?: string;51 error?: string;52}53
54export class SublimatedApi {55 constructor(private token: string) {}56
57 private async request<T>(path: string): Promise<T> {58 const headers: Record<string, string> = {59 Accept: "application/json",60 };61 if (this.token) {62 headers.Authorization = `Bearer ${this.token}`;63 }64 const response = await requestUrl({65 url: `${API_BASE}${path}`,66 headers,67 throw: false,68 });69
70 if (response.status === 401 || response.status === 403) {71 throw new AuthRequiredError(response.status);72 }73 if (response.status >= 400) {74 throw new Error(75 `API error ${response.status}: ${response.text}`76 );77 }78
79 const body = response.json as ApiEnvelope<T>;80 if (!body.ok || body.data === undefined) {81 const msg = body.error?.message || body.error?.type || "unknown error";82 throw new Error(`API error: ${msg}`);83 }84 return body.data;85 }86
87 async getVersions(88 spaceSlug: string,89 itemId: string90 ): Promise<Version[]> {91 const raw = await this.request<RawVersion[]>(92 `/v1/spaces/${encodeURIComponent(spaceSlug)}/items/${encodeURIComponent(itemId)}/versions`93 );94 return raw.map(mapVersion).reverse();95 }96
97 async testConnection(): Promise<TestConnectionResult> {98 try {99 const data = await this.request<{ username: string }>(100 "/v1/me"101 );102 return { ok: true, username: data.username };103 } catch (e) {104 return {105 ok: false,106 error: e instanceof Error ? e.message : "Unknown error",107 };108 }109 }110}111
112function mapVersion(r: RawVersion): Version {113 // pick the author actor; fall back to the first actor or a placeholder114 const author =115 r.actors.find((a) => a.role === "author") ?? r.actors[0] ?? null;116 return {117 id: r.id,118 number: r.version,119 authorName: author?.name ?? author?.username ?? "unknown",120 authorType: author?.actor_type ?? "person",121 message: r.message,122 createdAt: r.created_at,123 };124} v4
commit 3ef869c
message edit api.ts via macOS
author anton
date Apr 11, 2026
size 2.8 KB