alinea
Version:
Headless git-based CMS
198 lines (196 loc) • 6.16 kB
JavaScript
import "../chunks/chunk-NZLE2WMY.js";
// src/core/Client.ts
import { AbortController, fetch } from "@alinea/iso";
import { HandleAction } from "alinea/backend/HandleAction";
import { AuthResultType } from "alinea/cloud/AuthResult";
import { HttpError } from "./HttpError.js";
import { getScope } from "./Scope.js";
import { ReadonlyTree } from "./source/Tree.js";
import { base64 } from "./util/Encoding.js";
var Client = class _Client {
#options;
constructor(options) {
this.#options = options;
}
get url() {
return this.#options.url;
}
authStatus() {
return this.#requestJson({
action: HandleAction.Auth,
auth: "status"
}).then(this.#failOnHttpError);
}
logout = async () => {
const endSession = this.#options.unauthorized;
await this.#request({
action: HandleAction.Auth,
auth: "logout"
}).then((res) => this.#failOnHttpError(res, false)).then(endSession);
};
previewToken(request) {
return this.#requestJson(
{ action: HandleAction.PreviewToken },
{
method: "POST",
body: JSON.stringify(request)
}
).then(this.#failOnHttpError);
}
prepareUpload(file) {
return this.#requestJson(
{ action: HandleAction.Upload },
{
method: "POST",
body: JSON.stringify({ filename: file })
}
).then(this.#failOnHttpError);
}
user() {
return this.#requestJson({ action: HandleAction.User }).then(this.#failOnHttpError).then((user) => user ?? void 0);
}
resolve(query) {
const scope = getScope(this.#options.config);
const body = scope.stringify(query);
return this.#requestJson(
{ action: HandleAction.Resolve },
{ method: "POST", body }
).then(this.#failOnHttpError);
}
mutate(mutations) {
return this.#requestJson(
{ action: HandleAction.Mutate },
{ method: "POST", body: JSON.stringify(mutations) }
).then(this.#failOnHttpError);
}
authenticate(applyAuth, unauthorized) {
return new _Client({ ...this.#options, applyAuth, unauthorized });
}
// History
revisions(file) {
return this.#requestJson({ action: HandleAction.History, file }).then(this.#failOnHttpError);
}
revisionData(file, revisionId) {
return this.#requestJson({
action: HandleAction.History,
file,
revisionId
}).then(this.#failOnHttpError).then((res) => res ?? void 0);
}
// Source
getTreeIfDifferent(sha) {
return this.#requestJson({
action: HandleAction.Tree,
sha
}).then(this.#failOnHttpError).then((tree) => tree ? new ReadonlyTree(tree) : void 0);
}
async *getBlobs(shas) {
if (shas.length === 0) return;
const response = await this.#request(
{ action: HandleAction.Blob },
{
method: "POST",
body: JSON.stringify({ shas }),
headers: {
"content-type": "application/json",
accept: "multipart/form-data"
}
}
).then((response2) => this.#failOnHttpError(response2, false));
const form = await response.formData();
for (const [key, value] of form.entries()) {
if (value instanceof Blob) {
const sha = key.slice(0, 40);
const blob = new Uint8Array(await value.arrayBuffer());
yield [sha, blob];
}
}
}
// Commit
write(request) {
return this.#requestJson(
{ action: HandleAction.Commit },
{ method: "POST", body: JSON.stringify(request) }
).then(this.#failOnHttpError);
}
// Drafts
getDraft(key) {
return this.#requestJson({ action: HandleAction.Draft, key }).then(this.#failOnHttpError).then(
(draft) => draft ? { ...draft, draft: base64.parse(draft.draft) } : void 0
);
}
storeDraft(draft) {
return this.#requestJson(
{ action: HandleAction.Draft },
{
method: "POST",
body: JSON.stringify({ ...draft, draft: base64.stringify(draft.draft) })
}
).then((res) => this.#failOnHttpError(res, false));
}
#request(params, init = {}, retry = false) {
const { url, applyAuth = (v) => v, unauthorized } = this.#options;
const controller = new AbortController();
const signal = controller.signal;
const location = `${url}?${new URLSearchParams(params).toString()}`;
const promise = fetch(location, {
...applyAuth(init),
signal
}).catch((err) => {
throw new HttpError(
500,
`${err} @ ${init?.method || "GET"} action ${params.action}`
);
}).then(async (res) => {
if (res.ok) return res;
const isJson = res.headers.get("content-type")?.includes("application/json");
let errorMessage;
if (isJson) {
const body = await res.json();
if (res.status === 401 && body.type === AuthResultType.NeedsRefresh) {
if (!retry) return this.#request(params, init, true);
}
if ("error" in body && typeof body.error === "string")
errorMessage = body.error;
else errorMessage = JSON.stringify(body, null, 2);
} else {
errorMessage = await res.text();
}
errorMessage = errorMessage.replace(/\s+/g, " ").slice(0, 1024);
if (res.status === 401) unauthorized?.();
throw new HttpError(
res.status,
`${errorMessage} @ ${init?.method || "GET"} action ${params.action}`
);
});
const cancel = () => controller.abort();
function cancelify(promise2) {
const t = promise2.then.bind(promise2);
const c = promise2.catch.bind(promise2);
return Object.assign(promise2, {
cancel,
then: (...args) => cancelify(t(...args)),
catch: (...args) => cancelify(c(...args))
});
}
return cancelify(promise);
}
#requestJson(params, init) {
return this.#request(params, {
...init,
headers: {
...init?.headers,
"content-type": "application/json",
accept: "application/json"
}
});
}
async #failOnHttpError(res, expectJson = true) {
if (res.ok) return expectJson ? res.json() : res;
const text = await res.text();
throw new HttpError(res.status, text || res.statusText);
}
};
export {
Client
};