mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-24 09:02:00 +00:00
feat(headers): add support for custom HTTP headers in clients
* Introduced `headers` property in `ClientConfig` interface. * Updated `HeaderSpecClient` and `ResolveSpecClient` to utilize custom headers. * Implemented `mergeHeaders` function to handle case-insensitive header merging. * Added tests for custom header functionality in clients.
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
@@ -34,6 +34,8 @@ export declare function buildHeaders(options: Options): Record<string, string>;
|
||||
export declare interface ClientConfig {
|
||||
baseUrl: string;
|
||||
token?: string;
|
||||
/** Custom HTTP headers. Token and HeaderSpec query options take precedence. */
|
||||
headers?: Record<string, string>;
|
||||
}
|
||||
|
||||
export declare interface Column {
|
||||
|
||||
Vendored
+125
-110
@@ -1,29 +1,47 @@
|
||||
import { v4 as l } from "uuid";
|
||||
const d = /* @__PURE__ */ new Map();
|
||||
function E(n) {
|
||||
const e = n.baseUrl;
|
||||
let t = d.get(e);
|
||||
return t || (t = new g(n), d.set(e, t)), t;
|
||||
function u(...r) {
|
||||
const e = {};
|
||||
for (const t of r)
|
||||
for (const [s, n] of Object.entries(t)) {
|
||||
for (const i of Object.keys(e))
|
||||
i.toLowerCase() === s.toLowerCase() && delete e[i];
|
||||
Object.defineProperty(e, s, { value: n, enumerable: !0, configurable: !0, writable: !0 });
|
||||
}
|
||||
return e;
|
||||
}
|
||||
class g {
|
||||
function m(r) {
|
||||
return u(
|
||||
{ "Content-Type": "application/json" },
|
||||
r.headers ?? {},
|
||||
r.token ? { Authorization: `Bearer ${r.token}` } : {}
|
||||
);
|
||||
}
|
||||
function p(r) {
|
||||
const e = Object.entries(m(r)).map(([t, s]) => [t.toLowerCase(), s]).sort(([t], [s]) => t.localeCompare(s));
|
||||
return JSON.stringify([r.baseUrl, e]);
|
||||
}
|
||||
const f = /* @__PURE__ */ new Map();
|
||||
function v(r) {
|
||||
const e = p(r);
|
||||
let t = f.get(e);
|
||||
return t || (t = new y(r), f.set(e, t)), t;
|
||||
}
|
||||
class y {
|
||||
constructor(e) {
|
||||
this.config = e;
|
||||
this.config = { ...e, headers: { ...e.headers } };
|
||||
}
|
||||
buildUrl(e, t, s) {
|
||||
let r = `${this.config.baseUrl}/${e}/${t}`;
|
||||
return s && (r += `/${s}`), r;
|
||||
let n = `${this.config.baseUrl}/${e}/${t}`;
|
||||
return s && (n += `/${s}`), n;
|
||||
}
|
||||
baseHeaders() {
|
||||
const e = {
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
return this.config.token && (e.Authorization = `Bearer ${this.config.token}`), e;
|
||||
return m(this.config);
|
||||
}
|
||||
async fetchWithError(e, t) {
|
||||
const s = await fetch(e, t), r = await s.json();
|
||||
const s = await fetch(e, t), n = await s.json();
|
||||
if (!s.ok)
|
||||
throw new Error(r.error?.message || "An error occurred");
|
||||
return r;
|
||||
throw new Error(n.error?.message || "An error occurred");
|
||||
return n;
|
||||
}
|
||||
async getMetadata(e, t) {
|
||||
const s = this.buildUrl(e, t);
|
||||
@@ -32,11 +50,11 @@ class g {
|
||||
headers: this.baseHeaders()
|
||||
});
|
||||
}
|
||||
async read(e, t, s, r) {
|
||||
async read(e, t, s, n) {
|
||||
const i = typeof s == "number" || typeof s == "string" ? String(s) : void 0, a = this.buildUrl(e, t, i), c = {
|
||||
operation: "read",
|
||||
id: Array.isArray(s) ? s : void 0,
|
||||
options: r
|
||||
options: n
|
||||
};
|
||||
return this.fetchWithError(a, {
|
||||
method: "POST",
|
||||
@@ -44,11 +62,11 @@ class g {
|
||||
body: JSON.stringify(c)
|
||||
});
|
||||
}
|
||||
async create(e, t, s, r) {
|
||||
async create(e, t, s, n) {
|
||||
const i = this.buildUrl(e, t), a = {
|
||||
operation: "create",
|
||||
data: s,
|
||||
options: r
|
||||
options: n
|
||||
};
|
||||
return this.fetchWithError(i, {
|
||||
method: "POST",
|
||||
@@ -56,10 +74,10 @@ class g {
|
||||
body: JSON.stringify(a)
|
||||
});
|
||||
}
|
||||
async update(e, t, s, r, i) {
|
||||
const a = typeof r == "number" || typeof r == "string" ? String(r) : void 0, c = this.buildUrl(e, t, a), o = {
|
||||
async update(e, t, s, n, i) {
|
||||
const a = typeof n == "number" || typeof n == "string" ? String(n) : void 0, c = this.buildUrl(e, t, a), o = {
|
||||
operation: "update",
|
||||
id: Array.isArray(r) ? r : void 0,
|
||||
id: Array.isArray(n) ? n : void 0,
|
||||
data: s,
|
||||
options: i
|
||||
};
|
||||
@@ -70,23 +88,23 @@ class g {
|
||||
});
|
||||
}
|
||||
async delete(e, t, s) {
|
||||
const r = this.buildUrl(e, t, String(s)), i = {
|
||||
const n = this.buildUrl(e, t, String(s)), i = {
|
||||
operation: "delete"
|
||||
};
|
||||
return this.fetchWithError(r, {
|
||||
return this.fetchWithError(n, {
|
||||
method: "POST",
|
||||
headers: this.baseHeaders(),
|
||||
body: JSON.stringify(i)
|
||||
});
|
||||
}
|
||||
}
|
||||
const f = /* @__PURE__ */ new Map();
|
||||
function _(n) {
|
||||
const e = n.url;
|
||||
let t = f.get(e);
|
||||
return t || (t = new p(n), f.set(e, t)), t;
|
||||
const b = /* @__PURE__ */ new Map();
|
||||
function O(r) {
|
||||
const e = r.url;
|
||||
let t = b.get(e);
|
||||
return t || (t = new S(r), b.set(e, t)), t;
|
||||
}
|
||||
class p {
|
||||
class S {
|
||||
constructor(e) {
|
||||
this.ws = null, this.messageHandlers = /* @__PURE__ */ new Map(), this.subscriptions = /* @__PURE__ */ new Map(), this.eventListeners = {}, this.state = "disconnected", this.reconnectAttempts = 0, this.reconnectTimer = null, this.heartbeatTimer = null, this.isManualClose = !1, this.config = {
|
||||
url: e.url,
|
||||
@@ -110,12 +128,12 @@ class p {
|
||||
this.handleMessage(s.data);
|
||||
}, this.ws.onerror = (s) => {
|
||||
this.log("WebSocket error:", s);
|
||||
const r = new Error("WebSocket connection error");
|
||||
this.emit("error", r), t(r);
|
||||
const n = new Error("WebSocket connection error");
|
||||
this.emit("error", n), t(n);
|
||||
}, this.ws.onclose = (s) => {
|
||||
this.log("WebSocket closed:", s.code, s.reason), this.stopHeartbeat(), this.setState("disconnected"), this.emit("disconnect", s), this.config.reconnect && !this.isManualClose && this.reconnectAttempts < this.config.maxReconnectAttempts && (this.reconnectAttempts++, this.log(`Reconnection attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts}`), this.setState("reconnecting"), this.reconnectTimer = setTimeout(() => {
|
||||
this.connect().catch((r) => {
|
||||
this.log("Reconnection failed:", r);
|
||||
this.connect().catch((n) => {
|
||||
this.log("Reconnection failed:", n);
|
||||
});
|
||||
}, this.config.reconnectInterval));
|
||||
};
|
||||
@@ -129,8 +147,8 @@ class p {
|
||||
}
|
||||
async request(e, t, s) {
|
||||
this.ensureConnected();
|
||||
const r = l(), i = {
|
||||
id: r,
|
||||
const n = l(), i = {
|
||||
id: n,
|
||||
type: "request",
|
||||
operation: e,
|
||||
entity: t,
|
||||
@@ -140,10 +158,10 @@ class p {
|
||||
options: s?.options
|
||||
};
|
||||
return new Promise((a, c) => {
|
||||
this.messageHandlers.set(r, (o) => {
|
||||
this.messageHandlers.set(n, (o) => {
|
||||
o.success ? a(o.data) : c(new Error(o.error?.message || "Request failed"));
|
||||
}), this.send(i), setTimeout(() => {
|
||||
this.messageHandlers.has(r) && (this.messageHandlers.delete(r), c(new Error("Request timeout")));
|
||||
this.messageHandlers.has(n) && (this.messageHandlers.delete(n), c(new Error("Request timeout")));
|
||||
}, 3e4);
|
||||
});
|
||||
}
|
||||
@@ -167,9 +185,9 @@ class p {
|
||||
data: t
|
||||
});
|
||||
}
|
||||
async update(e, t, s, r) {
|
||||
async update(e, t, s, n) {
|
||||
return this.request("update", e, {
|
||||
schema: r?.schema,
|
||||
schema: n?.schema,
|
||||
record_id: t,
|
||||
data: s
|
||||
});
|
||||
@@ -187,8 +205,8 @@ class p {
|
||||
}
|
||||
async subscribe(e, t, s) {
|
||||
this.ensureConnected();
|
||||
const r = l(), i = {
|
||||
id: r,
|
||||
const n = l(), i = {
|
||||
id: n,
|
||||
type: "subscription",
|
||||
operation: "subscribe",
|
||||
entity: e,
|
||||
@@ -198,7 +216,7 @@ class p {
|
||||
}
|
||||
};
|
||||
return new Promise((a, c) => {
|
||||
this.messageHandlers.set(r, (o) => {
|
||||
this.messageHandlers.set(n, (o) => {
|
||||
if (o.success && o.data?.subscription_id) {
|
||||
const h = o.data.subscription_id;
|
||||
this.subscriptions.set(h, {
|
||||
@@ -211,7 +229,7 @@ class p {
|
||||
} else
|
||||
c(new Error(o.error?.message || "Subscription failed"));
|
||||
}), this.send(i), setTimeout(() => {
|
||||
this.messageHandlers.has(r) && (this.messageHandlers.delete(r), c(new Error("Subscription timeout")));
|
||||
this.messageHandlers.has(n) && (this.messageHandlers.delete(n), c(new Error("Subscription timeout")));
|
||||
}, 1e4);
|
||||
});
|
||||
}
|
||||
@@ -223,9 +241,9 @@ class p {
|
||||
operation: "unsubscribe",
|
||||
subscription_id: e
|
||||
};
|
||||
return new Promise((r, i) => {
|
||||
return new Promise((n, i) => {
|
||||
this.messageHandlers.set(t, (a) => {
|
||||
a.success ? (this.subscriptions.delete(e), this.log(`Unsubscribed from ${e}`), r()) : i(new Error(a.error?.message || "Unsubscribe failed"));
|
||||
a.success ? (this.subscriptions.delete(e), this.log(`Unsubscribed from ${e}`), n()) : i(new Error(a.error?.message || "Unsubscribe failed"));
|
||||
}), this.send(s), setTimeout(() => {
|
||||
this.messageHandlers.has(t) && (this.messageHandlers.delete(t), i(new Error("Unsubscribe timeout")));
|
||||
}, 1e4);
|
||||
@@ -309,44 +327,44 @@ class p {
|
||||
this.config.debug && console.log("[WebSocketClient]", ...e);
|
||||
}
|
||||
}
|
||||
function v(n) {
|
||||
return typeof btoa == "function" ? "ZIP_" + btoa(n) : "ZIP_" + Buffer.from(n, "utf-8").toString("base64");
|
||||
function W(r) {
|
||||
return typeof btoa == "function" ? "ZIP_" + btoa(r) : "ZIP_" + Buffer.from(r, "utf-8").toString("base64");
|
||||
}
|
||||
function w(n) {
|
||||
let e = n;
|
||||
return e.startsWith("ZIP_") ? (e = e.slice(4).replace(/[\n\r ]/g, ""), e = m(e)) : e.startsWith("__") && (e = e.slice(2).replace(/[\n\r ]/g, ""), e = m(e)), (e.startsWith("ZIP_") || e.startsWith("__")) && (e = w(e)), e;
|
||||
function H(r) {
|
||||
let e = r;
|
||||
return e.startsWith("ZIP_") ? (e = e.slice(4).replace(/[\n\r ]/g, ""), e = g(e)) : e.startsWith("__") && (e = e.slice(2).replace(/[\n\r ]/g, ""), e = g(e)), (e.startsWith("ZIP_") || e.startsWith("__")) && (e = H(e)), e;
|
||||
}
|
||||
function m(n) {
|
||||
return typeof atob == "function" ? atob(n) : Buffer.from(n, "base64").toString("utf-8");
|
||||
function g(r) {
|
||||
return typeof atob == "function" ? atob(r) : Buffer.from(r, "base64").toString("utf-8");
|
||||
}
|
||||
function u(n) {
|
||||
function d(r) {
|
||||
const e = {};
|
||||
if (n.columns?.length && (e["X-Select-Fields"] = n.columns.join(",")), n.omit_columns?.length && (e["X-Not-Select-Fields"] = n.omit_columns.join(",")), n.filters?.length)
|
||||
for (const t of n.filters) {
|
||||
const s = t.logic_operator ?? "AND", r = y(t.operator), i = S(t);
|
||||
t.operator === "eq" && s === "AND" ? e[`X-FieldFilter-${t.column}`] = i : s === "OR" ? e[`X-SearchOr-${r}-${t.column}`] = i : e[`X-SearchOp-${r}-${t.column}`] = i;
|
||||
if (r.columns?.length && (e["X-Select-Fields"] = r.columns.join(",")), r.omit_columns?.length && (e["X-Not-Select-Fields"] = r.omit_columns.join(",")), r.filters?.length)
|
||||
for (const t of r.filters) {
|
||||
const s = t.logic_operator ?? "AND", n = C(t.operator), i = E(t);
|
||||
t.operator === "eq" && s === "AND" ? e[`X-FieldFilter-${t.column}`] = i : s === "OR" ? e[`X-SearchOr-${n}-${t.column}`] = i : e[`X-SearchOp-${n}-${t.column}`] = i;
|
||||
}
|
||||
if (n.sort?.length) {
|
||||
const t = n.sort.map((s) => s.direction.toUpperCase() === "DESC" ? `-${s.column}` : `+${s.column}`);
|
||||
if (r.sort?.length) {
|
||||
const t = r.sort.map((s) => s.direction.toUpperCase() === "DESC" ? `-${s.column}` : `+${s.column}`);
|
||||
e["X-Sort"] = t.join(",");
|
||||
}
|
||||
if (n.limit !== void 0 && (e["X-Limit"] = String(n.limit)), n.offset !== void 0 && (e["X-Offset"] = String(n.offset)), n.cursor_forward && (e["X-Cursor-Forward"] = n.cursor_forward), n.cursor_backward && (e["X-Cursor-Backward"] = n.cursor_backward), n.preload?.length) {
|
||||
const t = n.preload.map((s) => s.columns?.length ? `${s.relation}:${s.columns.join(",")}` : s.relation);
|
||||
if (r.limit !== void 0 && (e["X-Limit"] = String(r.limit)), r.offset !== void 0 && (e["X-Offset"] = String(r.offset)), r.cursor_forward && (e["X-Cursor-Forward"] = r.cursor_forward), r.cursor_backward && (e["X-Cursor-Backward"] = r.cursor_backward), r.preload?.length) {
|
||||
const t = r.preload.map((s) => s.columns?.length ? `${s.relation}:${s.columns.join(",")}` : s.relation);
|
||||
e["X-Preload"] = t.join("|");
|
||||
}
|
||||
if (n.fetch_row_number && (e["X-Fetch-RowNumber"] = n.fetch_row_number), n.computedColumns?.length)
|
||||
for (const t of n.computedColumns)
|
||||
if (r.fetch_row_number && (e["X-Fetch-RowNumber"] = r.fetch_row_number), r.computedColumns?.length)
|
||||
for (const t of r.computedColumns)
|
||||
e[`X-CQL-SEL-${t.name}`] = t.expression;
|
||||
if (n.customOperators?.length) {
|
||||
const t = n.customOperators.map(
|
||||
if (r.customOperators?.length) {
|
||||
const t = r.customOperators.map(
|
||||
(s) => s.sql
|
||||
);
|
||||
e["X-Custom-SQL-W"] = t.join(" AND ");
|
||||
}
|
||||
return e;
|
||||
}
|
||||
function y(n) {
|
||||
switch (n) {
|
||||
function C(r) {
|
||||
switch (r) {
|
||||
case "eq":
|
||||
return "equals";
|
||||
case "neq":
|
||||
@@ -378,42 +396,39 @@ function y(n) {
|
||||
case "is_not_null":
|
||||
return "notempty";
|
||||
default:
|
||||
return n;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
function S(n) {
|
||||
return n.value === null || n.value === void 0 ? "" : Array.isArray(n.value) ? n.value.join(",") : String(n.value);
|
||||
function E(r) {
|
||||
return r.value === null || r.value === void 0 ? "" : Array.isArray(r.value) ? r.value.join(",") : String(r.value);
|
||||
}
|
||||
const b = /* @__PURE__ */ new Map();
|
||||
function C(n) {
|
||||
const e = n.baseUrl;
|
||||
let t = b.get(e);
|
||||
return t || (t = new H(n), b.set(e, t)), t;
|
||||
const w = /* @__PURE__ */ new Map();
|
||||
function T(r) {
|
||||
const e = p(r);
|
||||
let t = w.get(e);
|
||||
return t || (t = new _(r), w.set(e, t)), t;
|
||||
}
|
||||
class H {
|
||||
class _ {
|
||||
constructor(e) {
|
||||
this.config = e;
|
||||
this.config = { ...e, headers: { ...e.headers } };
|
||||
}
|
||||
buildUrl(e, t, s) {
|
||||
let r = `${this.config.baseUrl}/${e}/${t}`;
|
||||
return s && (r += `/${s}`), r;
|
||||
let n = `${this.config.baseUrl}/${e}/${t}`;
|
||||
return s && (n += `/${s}`), n;
|
||||
}
|
||||
baseHeaders() {
|
||||
const e = {
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
return this.config.token && (e.Authorization = `Bearer ${this.config.token}`), e;
|
||||
return m(this.config);
|
||||
}
|
||||
async fetchWithError(e, t) {
|
||||
const s = await fetch(e, t), r = await s.json();
|
||||
const s = await fetch(e, t), n = await s.json();
|
||||
if (!s.ok)
|
||||
throw new Error(
|
||||
r.error?.message || `${s.statusText} (${s.status})`
|
||||
n.error?.message || `${s.statusText} (${s.status})`
|
||||
);
|
||||
return {
|
||||
data: r,
|
||||
data: n,
|
||||
success: !0,
|
||||
error: r.error ? r.error : void 0,
|
||||
error: n.error ? n.error : void 0,
|
||||
metadata: {
|
||||
count: s.headers.get("content-range") ? Number(s.headers.get("content-range")?.split("/")[1]) : 0,
|
||||
total: s.headers.get("content-range") ? Number(s.headers.get("content-range")?.split("/")[1]) : 0,
|
||||
@@ -425,45 +440,45 @@ class H {
|
||||
}
|
||||
};
|
||||
}
|
||||
async read(e, t, s, r) {
|
||||
const i = this.buildUrl(e, t, s), a = r ? u(r) : {};
|
||||
async read(e, t, s, n) {
|
||||
const i = this.buildUrl(e, t, s), a = n ? d(n) : {};
|
||||
return this.fetchWithError(i, {
|
||||
method: "GET",
|
||||
headers: { ...this.baseHeaders(), ...a }
|
||||
headers: u(this.baseHeaders(), a)
|
||||
});
|
||||
}
|
||||
async create(e, t, s, r) {
|
||||
const i = this.buildUrl(e, t), a = r ? u(r) : {};
|
||||
async create(e, t, s, n) {
|
||||
const i = this.buildUrl(e, t), a = n ? d(n) : {};
|
||||
return this.fetchWithError(i, {
|
||||
method: "POST",
|
||||
headers: { ...this.baseHeaders(), ...a },
|
||||
headers: u(this.baseHeaders(), a),
|
||||
body: JSON.stringify(s)
|
||||
});
|
||||
}
|
||||
async update(e, t, s, r, i) {
|
||||
const a = this.buildUrl(e, t, s), c = i ? u(i) : {};
|
||||
async update(e, t, s, n, i) {
|
||||
const a = this.buildUrl(e, t, s), c = i ? d(i) : {};
|
||||
return this.fetchWithError(a, {
|
||||
method: "PUT",
|
||||
headers: { ...this.baseHeaders(), ...c },
|
||||
body: JSON.stringify(r)
|
||||
headers: u(this.baseHeaders(), c),
|
||||
body: JSON.stringify(n)
|
||||
});
|
||||
}
|
||||
async delete(e, t, s) {
|
||||
const r = this.buildUrl(e, t, s);
|
||||
return this.fetchWithError(r, {
|
||||
const n = this.buildUrl(e, t, s);
|
||||
return this.fetchWithError(n, {
|
||||
method: "DELETE",
|
||||
headers: this.baseHeaders()
|
||||
});
|
||||
}
|
||||
}
|
||||
export {
|
||||
H as HeaderSpecClient,
|
||||
g as ResolveSpecClient,
|
||||
p as WebSocketClient,
|
||||
u as buildHeaders,
|
||||
w as decodeHeaderValue,
|
||||
v as encodeHeaderValue,
|
||||
C as getHeaderSpecClient,
|
||||
E as getResolveSpecClient,
|
||||
_ as getWebSocketClient
|
||||
_ as HeaderSpecClient,
|
||||
y as ResolveSpecClient,
|
||||
S as WebSocketClient,
|
||||
d as buildHeaders,
|
||||
H as decodeHeaderValue,
|
||||
W as encodeHeaderValue,
|
||||
T as getHeaderSpecClient,
|
||||
v as getResolveSpecClient,
|
||||
O as getWebSocketClient
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user