mirror of
https://github.com/versia-pub/api.git
synced 2026-03-13 04:09:15 +01:00
refactor: 🚚 Rename Lysand to Versia
This commit is contained in:
parent
5b119949dc
commit
c9f4885c72
27 changed files with 112 additions and 112 deletions
296
client/versia/base.ts
Normal file
296
client/versia/base.ts
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
import { DEFAULT_UA } from "./constants";
|
||||
|
||||
type HttpVerb = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
||||
type ConvertibleObject = {
|
||||
[key: string]:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| File
|
||||
| undefined
|
||||
| null
|
||||
| ConvertibleObject[]
|
||||
| ConvertibleObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Output of a request. Contains the data and headers.
|
||||
* @template ReturnType The type of the data returned by the request.
|
||||
*/
|
||||
export interface Output<ReturnType> {
|
||||
data: ReturnType;
|
||||
ok: boolean;
|
||||
raw: Response;
|
||||
}
|
||||
|
||||
const objectToFormData = (obj: ConvertibleObject): FormData => {
|
||||
return Object.keys(obj).reduce((formData, key) => {
|
||||
if (obj[key] === undefined || obj[key] === null) {
|
||||
return formData;
|
||||
}
|
||||
if (obj[key] instanceof File) {
|
||||
formData.append(key, obj[key] as Blob);
|
||||
return formData;
|
||||
}
|
||||
if (Array.isArray(obj[key])) {
|
||||
(obj[key] as ConvertibleObject[]).forEach((item, index) => {
|
||||
if (item instanceof File) {
|
||||
formData.append(`${key}[${index}]`, item as Blob);
|
||||
return;
|
||||
}
|
||||
formData.append(`${key}[${index}]`, String(item));
|
||||
});
|
||||
|
||||
return formData;
|
||||
}
|
||||
if (typeof obj[key] === "object") {
|
||||
const nested = objectToFormData(obj[key] as ConvertibleObject);
|
||||
|
||||
for (const [nestedKey, value] of nested.entries()) {
|
||||
formData.append(`${key}[${nestedKey}]`, value);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
formData.append(key, String(obj[key]));
|
||||
return formData;
|
||||
}, new FormData());
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper around Error, useful for detecting if an error
|
||||
* is due to a failed request.
|
||||
*
|
||||
* Throws if the request returns invalid or unexpected data.
|
||||
*/
|
||||
export class ResponseError<
|
||||
ReturnType = {
|
||||
error?: string;
|
||||
},
|
||||
> extends Error {
|
||||
constructor(
|
||||
public response: Output<ReturnType>,
|
||||
message: string,
|
||||
) {
|
||||
super(message);
|
||||
this.name = "ResponseError";
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseClient {
|
||||
constructor(
|
||||
protected baseUrl: URL,
|
||||
private accessToken?: string,
|
||||
public globalCatch: (error: ResponseError) => void = () => {
|
||||
// Do nothing by default
|
||||
},
|
||||
) {}
|
||||
|
||||
get url(): URL {
|
||||
return this.baseUrl;
|
||||
}
|
||||
|
||||
get token(): string | undefined {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
private async request<ReturnType>(
|
||||
request: Request,
|
||||
): Promise<Output<ReturnType>> {
|
||||
const result = await fetch(request);
|
||||
const isJson = result.headers
|
||||
.get("Content-Type")
|
||||
?.includes("application/json");
|
||||
|
||||
if (!result.ok) {
|
||||
const error = isJson ? await result.json() : await result.text();
|
||||
throw new ResponseError(
|
||||
{
|
||||
data: error,
|
||||
ok: false,
|
||||
raw: result,
|
||||
},
|
||||
`Request failed (${result.status}): ${
|
||||
error.error || error.message || result.statusText
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
data: isJson ? await result.json() : (await result.text()) || null,
|
||||
ok: true,
|
||||
raw: result,
|
||||
};
|
||||
}
|
||||
|
||||
private constructRequest(
|
||||
path: string,
|
||||
method: HttpVerb,
|
||||
body?: object | FormData,
|
||||
extra?: RequestInit,
|
||||
): Request {
|
||||
const headers = new Headers({
|
||||
"User-Agent": DEFAULT_UA,
|
||||
});
|
||||
|
||||
if (this.accessToken) {
|
||||
headers.set("Authorization", `Bearer ${this.accessToken}`);
|
||||
}
|
||||
if (body) {
|
||||
if (!(body instanceof FormData)) {
|
||||
headers.set("Content-Type", "application/json; charset=utf-8");
|
||||
} // else: let FormData set the content type, as it knows best (boundary, etc.)
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(extra?.headers || {})) {
|
||||
headers.set(key, value);
|
||||
}
|
||||
|
||||
return new Request(new URL(path, this.baseUrl).toString(), {
|
||||
method,
|
||||
headers,
|
||||
body: body
|
||||
? body instanceof FormData
|
||||
? body
|
||||
: JSON.stringify(body)
|
||||
: undefined,
|
||||
...extra,
|
||||
});
|
||||
}
|
||||
|
||||
public get<ReturnType>(
|
||||
path: string,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(path, "GET", undefined, extra),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public post<ReturnType>(
|
||||
path: string,
|
||||
body?: object,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(path, "POST", body, extra),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public postForm<ReturnType>(
|
||||
path: string,
|
||||
body: FormData | ConvertibleObject,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(
|
||||
path,
|
||||
"POST",
|
||||
body instanceof FormData ? body : objectToFormData(body),
|
||||
extra,
|
||||
),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public put<ReturnType>(
|
||||
path: string,
|
||||
body?: object,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(path, "PUT", body, extra),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public putForm<ReturnType>(
|
||||
path: string,
|
||||
body: FormData | ConvertibleObject,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(
|
||||
path,
|
||||
"PUT",
|
||||
body instanceof FormData ? body : objectToFormData(body),
|
||||
extra,
|
||||
),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public patch<ReturnType>(
|
||||
path: string,
|
||||
body?: object,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(path, "PATCH", body, extra),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public patchForm<ReturnType>(
|
||||
path: string,
|
||||
body: FormData | ConvertibleObject,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(
|
||||
path,
|
||||
"PATCH",
|
||||
body instanceof FormData ? body : objectToFormData(body),
|
||||
extra,
|
||||
),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public delete<ReturnType>(
|
||||
path: string,
|
||||
body?: object,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(path, "DELETE", body, extra),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
public deleteForm<ReturnType>(
|
||||
path: string,
|
||||
body: FormData | ConvertibleObject,
|
||||
extra?: RequestInit,
|
||||
): Promise<Output<ReturnType>> {
|
||||
return this.request<ReturnType>(
|
||||
this.constructRequest(
|
||||
path,
|
||||
"DELETE",
|
||||
body instanceof FormData ? body : objectToFormData(body),
|
||||
extra,
|
||||
),
|
||||
).catch((e) => {
|
||||
this.globalCatch(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
}
|
||||
2795
client/versia/client.ts
Normal file
2795
client/versia/client.ts
Normal file
File diff suppressed because it is too large
Load diff
5
client/versia/constants.ts
Normal file
5
client/versia/constants.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import pkg from "../package.json" with { type: "json" };
|
||||
|
||||
export const NO_REDIRECT = "urn:ietf:wg:oauth:2.0:oob";
|
||||
export const DEFAULT_SCOPE = ["read", "write", "follow"];
|
||||
export const DEFAULT_UA = `VersiaClient/${pkg.version} (+${pkg.homepage})`;
|
||||
Loading…
Add table
Add a link
Reference in a new issue