UNPKG

drizzle-cube

Version:

Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.

309 lines (308 loc) 8.14 kB
import { jsx as R } from "react/jsx-runtime"; import { useState as w, useCallback as m, useEffect as j, useContext as T, createContext as U, useMemo as E } from "react"; class D { apiUrl; headers; constructor(t, r = {}) { this.apiUrl = r.apiUrl || "/cubejs-api/v1", this.headers = { "Content-Type": "application/json", ...r.headers }, t && (this.headers.Authorization = t); } async load(t) { const r = JSON.stringify(t), e = encodeURIComponent(r), a = `${this.apiUrl}/load?query=${e}`, s = await fetch(a, { method: "GET", headers: { // Remove Content-Type for GET request ...Object.fromEntries( Object.entries(this.headers).filter(([c]) => c !== "Content-Type") ) }, credentials: "include" // Include cookies for session auth }); if (!s.ok) { let c = `Cube query failed: ${s.status}`; try { const u = await s.text(); try { const l = JSON.parse(u); l.error ? c = l.error : c += ` ${u}`; } catch { c += ` ${u}`; } } catch { } throw new Error(c); } const o = await s.json(); return new C(o); } async meta() { const t = `${this.apiUrl}/meta`, r = await fetch(t, { method: "GET", headers: this.headers, credentials: "include" }); if (!r.ok) throw new Error(`Failed to fetch meta: ${r.status}`); return r.json(); } async sql(t) { const r = encodeURIComponent(JSON.stringify(t)), e = `${this.apiUrl}/sql?query=${r}`, a = await fetch(e, { method: "GET", headers: { // Remove Content-Type for GET request ...Object.fromEntries( Object.entries(this.headers).filter(([s]) => s !== "Content-Type") ) }, credentials: "include" }); if (!a.ok) throw new Error(`SQL generation failed: ${a.status}`); return a.json(); } async dryRun(t) { const r = `${this.apiUrl}/dry-run`, e = await fetch(r, { method: "POST", headers: this.headers, credentials: "include", body: JSON.stringify({ query: t }) }); if (!e.ok) { let a = `Dry run failed: ${e.status}`; try { const s = await e.text(); try { const o = JSON.parse(s); o.error ? a = o.error : a += ` ${s}`; } catch { a += ` ${s}`; } } catch { } throw new Error(a); } return e.json(); } /** * Execute multiple queries in a single batch request * Used by BatchCoordinator to optimize network requests */ async batchLoad(t) { const r = `${this.apiUrl}/batch`, e = await fetch(r, { method: "POST", headers: this.headers, credentials: "include", body: JSON.stringify({ queries: t }) }); if (!e.ok) { let s = `Batch query failed: ${e.status}`; try { const o = await e.text(); try { const c = JSON.parse(o); c.error ? s = c.error : s += ` ${o}`; } catch { s += ` ${o}`; } } catch { } throw new Error(s); } return (await e.json()).results.map((s) => !s.success && s.error ? { ...new C({ data: [], annotation: {} }), error: s.error } : new C(s)); } } class C { loadResponse; constructor(t) { this.loadResponse = t; } rawData() { return this.loadResponse.results && this.loadResponse.results[0] ? this.loadResponse.results[0].data || [] : this.loadResponse.data || []; } tablePivot() { return this.rawData(); } series() { return this.rawData(); } annotation() { return this.loadResponse.results && this.loadResponse.results[0] ? this.loadResponse.results[0].annotation || {} : this.loadResponse.annotation || {}; } } function L(n, t = {}) { return new D(n, t); } const P = 900 * 1e3; let h = null; function k(n) { const t = {}; return n.cubes.forEach((r) => { r.measures.forEach((e) => { t[e.name] = e.title || e.shortTitle || e.name; }), r.dimensions.forEach((e) => { t[e.name] = e.title || e.shortTitle || e.name; }), r.segments.forEach((e) => { t[e.name] = e.title || e.shortTitle || e.name; }); }), t; } function v() { return h ? Date.now() - h.timestamp < P : !1; } function F(n) { const [t, r] = w(null), [e, a] = w({}), [s, o] = w(!0), [c, u] = w(null), l = m(async () => { if (v() && h) { r(h.data), a(h.labelMap), o(!1), u(null); return; } try { o(!0), u(null); const i = await n.meta(), p = k(i); h = { data: i, labelMap: p, timestamp: Date.now() }, r(i), a(p); } catch (i) { const p = i instanceof Error ? i.message : "Failed to fetch metadata"; u(p), console.error("Failed to fetch cube metadata:", i); } finally { o(!1); } }, [n]); j(() => { l(); }, [l]); const d = m((i) => e[i] || i, [e]), f = m(() => { h = null, l(); }, [l]); return { meta: t, labelMap: e, loading: s, error: c, refetch: f, getFieldLabel: d }; } class J { queue = []; flushScheduled = !1; batchExecutor; delayMs; constructor(t, r = 100) { this.batchExecutor = t, this.delayMs = r; } /** * Register a query to be batched. Returns a promise that resolves * when the batch is executed and this specific query's result is available. */ register(t) { return new Promise((r, e) => { this.queue.push({ query: t, resolve: r, reject: e }), this.flushScheduled || this.scheduleFlush(); }); } /** * Schedule a flush after a short delay to collect multiple queries. * The delay allows queries from lazy-loaded portlets that become visible * during the same scroll action to be batched together. */ scheduleFlush() { this.flushScheduled = !0, setTimeout(() => { this.flush(); }, this.delayMs); } /** * Execute all queued queries as a batch and resolve individual promises */ async flush() { this.flushScheduled = !1; const t = this.queue.slice(); if (this.queue = [], t.length !== 0) try { const r = t.map((a) => a.query), e = await this.batchExecutor(r); t.forEach((a, s) => { const o = e[s]; o && "error" in o && o.error ? a.reject(new Error(o.error)) : a.resolve(o); }); } catch (r) { t.forEach((e) => { e.reject(r instanceof Error ? r : new Error(String(r))); }); } } /** * Get current queue size (useful for debugging) */ getQueueSize() { return this.queue.length; } /** * Clear the queue (useful for testing/cleanup) */ clear() { this.queue = [], this.flushScheduled = !1; } } const g = U(null); function G({ cubeApi: n, apiOptions: t, token: r, options: e = {}, features: a = { enableAI: !0, aiEndpoint: "/api/ai/generate" }, // Default to AI enabled for backward compatibility enableBatching: s = !0, // Default to batching enabled batchDelayMs: o = 100, // Default 100ms batch window children: c }) { const [u, l] = w(null), d = u || { apiOptions: t || { apiUrl: "/cubejs-api/v1" }, token: r }, f = E(() => n && !t && !r ? n : L(d.token, d.apiOptions), [n, t, r, d.apiOptions, d.token]), i = E(() => { if (!s) return null; const y = (b) => f.batchLoad(b); return new J(y, o); }, [s, f, o]), { meta: p, labelMap: M, loading: S, error: x, getFieldLabel: $, refetch: q } = F(f), O = { cubeApi: f, options: e, meta: p, labelMap: M, metaLoading: S, metaError: x, getFieldLabel: $, refetchMeta: q, updateApiConfig: (y, b) => { l({ apiOptions: y, token: b }); }, features: a, batchCoordinator: i, enableBatching: s }; return /* @__PURE__ */ R(g.Provider, { value: O, children: c }); } function Q() { const n = T(g); if (!n) throw new Error("useCubeContext must be used within a CubeProvider"); return n; } export { G as C, F as a, L as c, Q as u }; //# sourceMappingURL=providers-DONuYrGH.js.map