@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
260 lines (259 loc) • 9.02 kB
JavaScript
var u = Object.defineProperty, f = (n, e, t) => e in n ? u(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, d = (n, e, t) => f(n, typeof e != "symbol" ? e + "" : e, t);
class p {
constructor(e) {
d(this, "config"), d(this, "requestInterceptors", []), d(this, "responseInterceptors", []), d(this, "errorHandlers", []), this.config = e;
}
// Configuration methods
setConfig(e) {
this.config = { ...this.config, ...e };
}
setOrganizationContext(e) {
this.config.organizationId = e;
}
clearOrganizationContext() {
delete this.config.organizationId;
}
// Interceptor methods
addRequestInterceptor(e) {
this.requestInterceptors.push(e);
}
addResponseInterceptor(e) {
this.responseInterceptors.push(e);
}
addErrorHandler(e) {
this.errorHandlers.push(e);
}
// Main request method
async request(e, t = {}) {
const s = this.buildUrl(e, t.params);
let r = this.buildRequestConfig(t);
for (const o of this.requestInterceptors)
r = await o(r);
try {
let i = await this.executeRequest(s, r);
for (const a of this.responseInterceptors)
i = await a(i);
return await this.parseResponse(i);
} catch (o) {
const i = this.createAPIError(o);
for (const a of this.errorHandlers)
a(i);
throw i;
}
}
// Convenience methods
async get(e, t) {
return this.request(e, { ...t, method: "GET" });
}
async post(e, t, s) {
return this.request(e, { ...s, method: "POST", body: t });
}
async put(e, t, s) {
return this.request(e, { ...s, method: "PUT", body: t });
}
async patch(e, t, s) {
return this.request(e, { ...s, method: "PATCH", body: t });
}
async delete(e, t) {
return this.request(e, { ...t, method: "DELETE" });
}
// Private helper methods
buildUrl(e, t) {
const s = new URL(e, this.config.baseUrl);
if (t)
for (const [r, o] of Object.entries(t))
o != null && s.searchParams.set(r, String(o));
return s.toString();
}
buildRequestConfig(e) {
const t = {
"Content-Type": "application/json",
...this.config.headers,
...e.headers
};
return this.config.apiKey ? t.Authorization = `Bearer ${this.config.apiKey}` : this.config.publishableKey && (t["X-Publishable-Key"] = this.config.publishableKey), (e.organizationId || this.config.organizationId) && (t["X-Organization-Id"] = e.organizationId || this.config.organizationId), {
method: "GET",
timeout: this.config.timeout || 3e4,
retries: this.config.retries || 3,
...e,
headers: t
};
}
async executeRequest(e, t) {
const s = new AbortController(), r = t.timeout ? setTimeout(() => s.abort(), t.timeout) : null;
try {
const o = {
method: t.method,
headers: t.headers,
signal: s.signal
};
t.body && t.method !== "GET" && (o.body = typeof t.body == "string" ? t.body : JSON.stringify(t.body));
const i = await fetch(e, o);
return r && clearTimeout(r), i;
} catch (o) {
throw r && clearTimeout(r), o;
}
}
async parseResponse(e) {
const t = e.headers.get("Content-Type") || "";
let s;
if (t.includes("application/json") ? s = await e.json() : s = await e.text(), !e.ok)
throw new Error(`HTTP ${e.status}: ${e.statusText}`, {
cause: { status: e.status, data: s }
});
return s && typeof s == "object" && "success" in s ? s : {
success: !0,
data: s
};
}
createAPIError(e) {
if (e.cause && typeof e.cause == "object") {
const { status: t, data: s } = e.cause;
if (s && typeof s == "object" && "errors" in s)
return {
code: s.code || `HTTP_${t}`,
message: s.message || e.message,
details: s.details
};
}
return {
code: "UNKNOWN_ERROR",
message: e.message || "An unknown error occurred",
details: { originalError: e }
};
}
}
const w = (n) => new p(n), g = (n) => n && typeof n == "object" && "code" in n && "message" in n, m = (n) => g(n) ? n : {
code: "UNKNOWN_ERROR",
message: n?.message || "An unknown error occurred",
details: { originalError: n }
}, b = async (n, e = 3, t = 1e3) => {
let s;
for (let r = 1; r <= e; r++)
try {
return await n();
} catch (o) {
if (s = o, r === e) break;
const i = t * Math.pow(2, r - 1);
await new Promise((a) => setTimeout(a, i));
}
throw s;
}, y = (n, e, t = 3e5) => {
const s = /* @__PURE__ */ new Map();
return (async () => {
const r = s.get(e);
if (r && r.expires > Date.now())
return r.data;
const o = await n();
return s.set(e, { data: o, expires: Date.now() + t }), o;
})();
}, R = async (n, e = 5, t = 100) => {
const s = [];
for (let r = 0; r < n.length; r += e) {
const o = n.slice(r, r + e), i = await Promise.all(o.map((a) => a()));
s.push(...i), r + e < n.length && t > 0 && await new Promise((a) => setTimeout(a, t));
}
return s;
}, E = async (n, e, t, s = {}) => {
const r = new FormData();
if (r.append("file", t), s.additionalData)
for (const [o, i] of Object.entries(s.additionalData))
r.append(o, String(i));
return new Promise((o, i) => {
const a = new XMLHttpRequest();
s.onProgress && a.upload.addEventListener("progress", (c) => {
if (c.lengthComputable) {
const h = c.loaded / c.total * 100;
s.onProgress(h);
}
}), s.abortSignal && s.abortSignal.addEventListener("abort", () => {
a.abort(), i(new Error("Upload aborted"));
}), a.addEventListener("load", async () => {
try {
const c = JSON.parse(a.responseText);
a.status >= 200 && a.status < 300 ? o(c) : i(new Error(`Upload failed: ${c.message || a.statusText}`));
} catch {
i(new Error("Failed to parse upload response"));
}
}), a.addEventListener("error", () => {
i(new Error("Upload failed"));
}), a.open("POST", `${n.config.baseUrl}${e}`);
const l = n.buildRequestConfig({}).headers || {};
for (const [c, h] of Object.entries(l))
c.toLowerCase() !== "content-type" && a.setRequestHeader(c, h);
a.send(r);
});
}, P = async (n, e, t, s = {}) => {
const r = await fetch(`${n.config.baseUrl}${e}`, {
headers: n.buildRequestConfig({}).headers,
signal: s.abortSignal
});
if (!r.ok)
throw new Error(`Download failed: ${r.statusText}`);
const o = await r.blob(), i = window.URL.createObjectURL(o), a = document.createElement("a");
a.style.display = "none", a.href = i, a.download = t || "download", document.body.appendChild(a), a.click(), window.URL.revokeObjectURL(i), document.body.removeChild(a);
};
class I {
constructor(e, t = {}) {
this.url = e, this.config = t, d(this, "ws", null), d(this, "reconnectAttempts", 0), d(this, "maxReconnectAttempts", 5), d(this, "reconnectDelay", 1e3), d(this, "messageQueue", []), d(this, "eventHandlers", /* @__PURE__ */ new Map());
}
connect() {
return new Promise((e, t) => {
try {
const s = new URL(this.url);
this.config.token && s.searchParams.set("token", this.config.token), this.config.organizationId && s.searchParams.set("organizationId", this.config.organizationId), this.ws = new WebSocket(s.toString()), this.ws.onopen = () => {
for (this.reconnectAttempts = 0; this.messageQueue.length > 0; ) {
const r = this.messageQueue.shift();
this.ws.send(JSON.stringify(r));
}
e();
}, this.ws.onmessage = (r) => {
try {
const o = JSON.parse(r.data);
this.handleMessage(o);
} catch (o) {
console.error("Failed to parse WebSocket message:", o);
}
}, this.ws.onclose = () => {
this.ws = null, this.config.autoReconnect !== !1 && this.reconnectAttempts < this.maxReconnectAttempts && setTimeout(() => {
this.reconnectAttempts++, this.connect();
}, this.reconnectDelay * Math.pow(2, this.reconnectAttempts));
}, this.ws.onerror = (r) => {
t(r);
};
} catch (s) {
t(s);
}
});
}
disconnect() {
this.ws && (this.ws.close(), this.ws = null);
}
send(e) {
this.ws && this.ws.readyState === WebSocket.OPEN ? this.ws.send(JSON.stringify(e)) : this.messageQueue.push(e);
}
on(e, t) {
return this.eventHandlers.has(e) || this.eventHandlers.set(e, /* @__PURE__ */ new Set()), this.eventHandlers.get(e).add(t), () => {
this.eventHandlers.get(e)?.delete(t);
};
}
handleMessage(e) {
const { type: t, payload: s } = e;
if (this.eventHandlers.has(t))
for (const r of this.eventHandlers.get(t))
r(s);
}
}
export {
p as APIClient,
I as APIWebSocket,
R as batchRequests,
w as createAPIClient,
P as downloadFile,
m as handleAPIError,
g as isAPIError,
b as retryRequest,
E as uploadFile,
y as withCache
};
//# sourceMappingURL=api.js.map