UNPKG

drizzle-cube

Version:

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

885 lines (884 loc) 24.1 kB
import { useState as D, useRef as v, useMemo as g, useEffect as M, useCallback as T } from "react"; import { a as w, _ as O, $ as E, a0 as j } from "./charts-DzHumCCp.js"; const N = { // String operators equals: { label: "equals", description: "Exact match", requiresValues: !0, supportsMultipleValues: !0, valueType: "any", fieldTypes: ["string", "number", "boolean", "time"] }, notEquals: { label: "not equals", description: "Does not match", requiresValues: !0, supportsMultipleValues: !0, valueType: "any", fieldTypes: ["string", "number", "boolean", "time"] }, contains: { label: "contains", description: "Contains text (case insensitive)", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, notContains: { label: "not contains", description: "Does not contain text", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, startsWith: { label: "starts with", description: "Starts with text", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, notStartsWith: { label: "not starts with", description: "Does not start with text", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, endsWith: { label: "ends with", description: "Ends with text", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, notEndsWith: { label: "not ends with", description: "Does not end with text", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, like: { label: "like", description: "SQL LIKE pattern matching (case sensitive)", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, notLike: { label: "not like", description: "SQL NOT LIKE pattern matching (case sensitive)", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, ilike: { label: "ilike", description: "SQL ILIKE pattern matching (case insensitive)", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, // Numeric operators gt: { label: "greater than", description: "Greater than value", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, gte: { label: "greater than or equal", description: "Greater than or equal to value", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, lt: { label: "less than", description: "Less than value", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, lte: { label: "less than or equal", description: "Less than or equal to value", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, between: { label: "between", description: "Between two values (inclusive)", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, notBetween: { label: "not between", description: "Not between two values", requiresValues: !0, supportsMultipleValues: !1, valueType: "number", fieldTypes: ["number", "count", "sum", "avg", "min", "max"] }, // Array operators in: { label: "in", description: "Matches any of the provided values", requiresValues: !0, supportsMultipleValues: !0, valueType: "any", fieldTypes: ["string", "number", "boolean"] }, notIn: { label: "not in", description: "Does not match any of the provided values", requiresValues: !0, supportsMultipleValues: !0, valueType: "any", fieldTypes: ["string", "number", "boolean"] }, // Null/Empty operators set: { label: "is set", description: "Is not null/empty", requiresValues: !1, supportsMultipleValues: !1, valueType: "any", fieldTypes: ["string", "number", "time", "boolean"] }, notSet: { label: "is not set", description: "Is null/empty", requiresValues: !1, supportsMultipleValues: !1, valueType: "any", fieldTypes: ["string", "number", "time", "boolean"] }, isEmpty: { label: "is empty", description: "Is empty string or null", requiresValues: !1, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, isNotEmpty: { label: "is not empty", description: "Is not empty string and not null", requiresValues: !1, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, // Date operators inDateRange: { label: "in date range", description: "Between two dates", requiresValues: !0, supportsMultipleValues: !1, valueType: "date", fieldTypes: ["time"] }, beforeDate: { label: "before date", description: "Before specified date", requiresValues: !0, supportsMultipleValues: !1, valueType: "date", fieldTypes: ["time"] }, afterDate: { label: "after date", description: "After specified date", requiresValues: !0, supportsMultipleValues: !1, valueType: "date", fieldTypes: ["time"] }, // Regex operators regex: { label: "matches regex", description: "Matches regular expression pattern", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, notRegex: { label: "not matches regex", description: "Does not match regular expression pattern", requiresValues: !0, supportsMultipleValues: !1, valueType: "string", fieldTypes: ["string"] }, // PostgreSQL array operators arrayContains: { label: "array contains all", description: "Array field contains all specified values (PostgreSQL only)", requiresValues: !0, supportsMultipleValues: !0, valueType: "string", fieldTypes: ["string"] }, arrayOverlaps: { label: "array contains any", description: "Array field contains any of the specified values (PostgreSQL only)", requiresValues: !0, supportsMultipleValues: !0, valueType: "string", fieldTypes: ["string"] }, arrayContained: { label: "array values in", description: "All array field values are within specified values (PostgreSQL only)", requiresValues: !0, supportsMultipleValues: !0, valueType: "string", fieldTypes: ["string"] } }, oe = [ { value: "custom", label: "Custom" }, { value: "today", label: "Today" }, { value: "yesterday", label: "Yesterday" }, { value: "this_week", label: "This week" }, { value: "this_month", label: "This month" }, { value: "this_quarter", label: "This quarter" }, { value: "this_year", label: "This year" }, { value: "last_7_days", label: "Last 7 days" }, { value: "last_30_days", label: "Last 30 days" }, { value: "last_n_days", label: "Last N days" }, { value: "last_week", label: "Last week" }, { value: "last_n_weeks", label: "Last N weeks" }, { value: "last_month", label: "Last month" }, { value: "last_12_months", label: "Last 12 months" }, { value: "last_n_months", label: "Last N months" }, { value: "last_quarter", label: "Last quarter" }, { value: "last_n_quarters", label: "Last N quarters" }, { value: "last_year", label: "Last year" }, { value: "last_n_years", label: "Last N years" } ]; function z(e) { return "member" in e && "operator" in e && "values" in e; } function G(e) { return "type" in e && "filters" in e; } function U(e) { const r = (t) => { if (z(t)) return t; if (G(t)) { const s = t.filters.map(r); return t.type === "and" ? { and: s } : { or: s }; } return t; }; return e.map(r); } function J(e) { const r = {}; return e.measures && e.measures.length > 0 && (r.measures = e.measures), e.dimensions && e.dimensions.length > 0 && (r.dimensions = e.dimensions), e.timeDimensions && e.timeDimensions.length > 0 && (r.timeDimensions = e.timeDimensions), e.filters && e.filters.length > 0 && (r.filters = e.filters), e.order && (r.order = e.order), e.limit && (r.limit = e.limit), e.offset && (r.offset = e.offset), e.segments && e.segments.length > 0 && (r.segments = e.segments), r; } function R(e) { const r = J(e); return r.filters && r.filters.length > 0 && (r.filters = U(r.filters)), r; } function ce(e) { const r = []; for (const [t, s] of Object.entries(N)) s.fieldTypes.includes(e) && r.push({ operator: t, label: s.label }); return r; } function de(e, r) { const t = { today: "today", yesterday: "yesterday", this_week: "this week", this_month: "this month", this_quarter: "this quarter", this_year: "this year", last_7_days: "last 7 days", last_30_days: "last 30 days", last_week: "last week", last_month: "last month", last_quarter: "last quarter", last_year: "last year", last_12_months: "last 12 months" }; if (e.startsWith("last_n_") && r !== void 0 && r > 0) { const s = e.replace("last_n_", ""), a = s.slice(0, -1); return r === 1 ? `last ${a}` : `last ${r} ${s}`; } return t[e] || e; } function fe(e) { return e.startsWith("last_n_"); } function pe(e) { return e.toISOString().split("T")[0]; } function V(e) { const r = /* @__PURE__ */ new WeakSet(), t = (s) => { if (s === null || typeof s != "object") return JSON.stringify(s); if (r.has(s)) return '"[Circular]"'; if (r.add(s), Array.isArray(s)) return `[${s.map((o) => t(o)).join(",")}]`; const a = s; return `{${Object.keys(a).sort().map((o) => `${JSON.stringify(o)}:${t(a[o])}`).join(",")}}`; }; return t(e); } function W(e, r) { const { isValid: t, skip: s = !1, debounceMs: a = 300 } = r, [n, i] = D(null), [o, d] = D(!1), p = v(null), l = v(""), u = v(s), c = g(() => e ? V(e) : "", [e]); return M(() => { const y = u.current && !s; if (u.current = s, !(c === l.current && !y)) return p.current && clearTimeout(p.current), t && !s ? (d(!0), p.current = setTimeout(() => { l.current = c, i(e), d(!1); }, a)) : (l.current = c, i(null), d(!1)), () => { p.current && clearTimeout(p.current); }; }, [c, t, s, a, e]), { debouncedValue: n, isDebouncing: o }; } const H = 300; function x(e) { return e ? ["cube", "load", V(e)] : ["cube", "load", null]; } function Y(e) { if (!e) return !1; const r = !!(e.measures && e.measures.length > 0), t = !!(e.dimensions && e.dimensions.length > 0), s = !!(e.timeDimensions && e.timeDimensions.length > 0); return r || t || s; } function X(e, r = {}) { const { skip: t = !1, debounceMs: s = H, resetResultSetOnChange: a = !0, staleTime: n = 60 * 1e3, keepPreviousData: i = !0 } = r, { cubeApi: o, batchCoordinator: d, enableBatching: p } = w(), l = O(), u = Y(e), { debouncedValue: c, isDebouncing: f } = W(e, { isValid: u, skip: t, debounceMs: s }), y = g(() => c ? R(c) : null, [c]), b = E({ queryKey: x(y), queryFn: async () => { if (!y) throw new Error("No query provided"); return p && d ? d.register(y) : o.load(y); }, enabled: !!y && !t, staleTime: n, placeholderData: i ? (A) => A : void 0 }), S = g(() => { if (!b.data) return null; try { return b.data.rawData(); } catch { return null; } }, [b.data]), Q = () => { y && l.refetchQueries({ queryKey: x(y) }); }, k = () => { l.removeQueries({ queryKey: ["cube", "load"] }); }; return { resultSet: g(() => b.data ?? null, [b.data, f, a]), rawData: S, isLoading: b.isLoading || f, isFetching: b.isFetching, isDebouncing: f, error: b.error, debouncedQuery: c, isValidQuery: u, refetch: Q, clearCache: k }; } function B(e) { return e.length > 0 && typeof e[0] == "object" && e[0] !== null && "__queryIndex" in e[0]; } function ye(e) { if (!B(e)) return []; const r = /* @__PURE__ */ new Set(); for (const t of e) { const s = t.__queryLabel; typeof s == "string" && r.add(s); } return Array.from(r); } function me(e) { if (!B(e)) return []; const r = /* @__PURE__ */ new Set(); for (const t of e) { const s = t.__queryIndex; typeof s == "number" && r.add(s); } return Array.from(r).sort((t, s) => t - s); } function Z(e, r, t) { const s = []; return e.forEach((a, n) => { const i = a.rawData(), o = t?.[n] || `Query ${n + 1}`; i.forEach((d) => { s.push({ ...d, __queryIndex: n, __queryLabel: o }); }); }), s; } function ee(e, r, t, s) { const a = /* @__PURE__ */ new Map(); return e.forEach((n, i) => { const o = n.rawData(), d = r[i].measures || []; o.forEach((p) => { const l = t.map((c) => String(p[c] ?? "")).join("|"); if (!a.has(l)) { const c = {}; t.forEach((f) => { c[f] = p[f]; }), a.set(l, c); } const u = a.get(l); d.forEach((c) => { c in u || (u[c] = p[c]); }), i === 0 && Object.keys(p).forEach((c) => { !t.includes(c) && !d.includes(c) && (c in u || (u[c] = p[c])); }); }); }), Array.from(a.values()).sort((n, i) => { const o = String(n[t[0]] ?? ""), d = String(i[t[0]] ?? ""); return o.localeCompare(d); }); } function te(e, r, t, s, a) { return e.length === 0 ? [] : e.length === 1 ? e[0].rawData() : t === "merge" && s && s.length > 0 ? ee(e, r, s) : Z(e, r, a); } function be(e, r) { const t = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(); return e.forEach((n) => { n.measures?.forEach((i) => t.add(i)), n.dimensions?.forEach((i) => s.add(i)), n.timeDimensions?.forEach((i) => a.add(i.dimension)); }), { measures: Array.from(t), dimensions: Array.from(s), timeDimensions: Array.from(a) }; } function he(e, r) { if (e.measures && e.measures.length > 0) { const t = e.measures[0], s = t.split("."); return s.length > 1 ? s[s.length - 1] : t; } return `Query ${r + 1}`; } function ge(e, r) { const t = []; return e.forEach((s, a) => { [ ...s.dimensions || [], ...s.timeDimensions?.map((i) => i.dimension) || [] ].includes(r) || t.push(a); }), { isValid: t.length === 0, missingInQueries: t }; } const se = 300; function I(e) { return e ? ["cube", "multiLoad", V(e)] : ["cube", "multiLoad", null]; } function re(e) { return !e || !e.queries || e.queries.length < 2 ? !1 : e.queries.filter( (t) => t.measures && t.measures.length > 0 || t.dimensions && t.dimensions.length > 0 || t.timeDimensions && t.timeDimensions.length > 0 ).length >= 2; } function ve(e, r = {}) { const { skip: t = !1, debounceMs: s = se, staleTime: a = 60 * 1e3, keepPreviousData: n = !0 } = r, { cubeApi: i, batchCoordinator: o, enableBatching: d } = w(), p = O(), l = re(e), { debouncedValue: u, isDebouncing: c } = W(e, { isValid: l, skip: t, debounceMs: s }), f = g(() => u ? { ...u, queries: u.queries.map((h) => R(h)) } : null, [u]), y = E({ queryKey: I(f), queryFn: async () => { if (!f) throw new Error("No config provided"); let h; d && o ? h = await Promise.all( f.queries.map((m) => o.register(m)) ) : h = await i.batchLoad(f.queries); const q = h.map((m) => m && "error" in m && m.error ? new Error(m.error) : null), P = h.map((m, _) => { if (q[_]) return null; try { return m.rawData(); } catch { return null; } }), C = h.filter((m, _) => !q[_]), $ = f.queries.filter((m, _) => !q[_]); return { data: C.length > 0 ? te( C, $, f.mergeStrategy, f.mergeKeys, f.queryLabels ) : [], resultSets: h, perQueryData: P, errors: q, firstError: q.find((m) => m !== null) || null }; }, enabled: !!f && !t, staleTime: a, placeholderData: n ? (h) => h : void 0 }), b = () => { f && p.refetchQueries({ queryKey: I(f) }); }, S = y.data?.data ?? null, Q = y.data?.resultSets ?? null, k = y.data?.perQueryData ?? null, F = y.data?.errors ?? [], A = y.data?.firstError ?? y.error; return { data: S, resultSets: Q, perQueryData: k, isLoading: y.isLoading || c, isFetching: y.isFetching, isDebouncing: c, error: A, errors: F, debouncedConfig: u, isValidConfig: l, refetch: b }; } function K(e) { return e ? ["cube", "dryRun", V(e)] : ["cube", "dryRun", null]; } function Te(e, r = {}) { const { skip: t = !1, staleTime: s = 300 * 1e3 } = r, { cubeApi: a } = w(), n = g(() => e ? R(e) : null, [e]), i = E({ queryKey: K(n), queryFn: async () => { if (!n) throw new Error("No query provided"); const d = await a.dryRun(n); return { sql: d.sql, analysis: d.analysis }; }, enabled: !!n && !t, staleTime: s }); return { debugData: { sql: i.data?.sql ?? null, analysis: i.data?.analysis ?? null, loading: i.isLoading, error: i.error ?? null }, refetch: () => i.refetch() }; } function ne(e, r = {}) { const { skip: t = !1, staleTime: s = 300 * 1e3 } = r, { cubeApi: a } = w(), n = g(() => e.map((l) => R(l)), [e]), i = j({ queries: n.map((l) => ({ queryKey: K(l), queryFn: async () => { const u = await a.dryRun(l); return { sql: u.sql, analysis: u.analysis }; }, enabled: !t, staleTime: s })) }), o = i.map((l) => ({ sql: l.data?.sql ?? null, analysis: l.data?.analysis ?? null, loading: l.isLoading, error: l.error ?? null })), d = i.some((l) => l.isLoading); return { debugDataPerQuery: o, isLoading: d, refetchAll: () => { i.forEach((l) => l.refetch()); } }; } function we(e) { const { queries: r, isMultiQueryMode: t, skip: s = !1, staleTime: a } = e, n = t ? r : r.slice(0, 1); return ne(n, { skip: s, staleTime: a }); } function Ve(e, r = {}) { const { skip: t = !1, staleTime: s = 300 * 1e3 } = r, { cubeApi: a } = w(), n = E({ queryKey: ["cube", "dryRun", "funnel", e ? V(e) : null], queryFn: async () => { if (!e) throw new Error("No funnel query provided"); const o = await a.dryRun(e); return { sql: o.sql, analysis: o.analysis, funnelMetadata: o.funnel }; }, enabled: !!e && !t, staleTime: s }); return { debugData: { sql: n.data?.sql ?? null, analysis: n.data?.analysis ?? null, loading: n.isLoading, error: n.error ?? null, funnelMetadata: n.data?.funnelMetadata }, refetch: () => n.refetch() }; } function qe(e, r = {}) { const { skip: t = !1, staleTime: s = 300 * 1e3 } = r, { cubeApi: a } = w(), n = E({ queryKey: ["cube", "dryRun", "flow", e ? V(e) : null], queryFn: async () => { if (!e) throw new Error("No flow query provided"); const o = await a.dryRun(e); return { sql: o.sql, analysis: o.analysis, flowMetadata: o.flow }; }, enabled: !!e && !t, staleTime: s }); return { debugData: { sql: n.data?.sql ?? null, analysis: n.data?.analysis ?? null, loading: n.isLoading, error: n.error ?? null, flowMetadata: n.data?.flowMetadata }, refetch: () => n.refetch() }; } function _e(e, r = !0) { const [t, s] = D(null), a = v(""), { resultSet: n, isLoading: i, error: o } = X(t, { skip: !t || !r || !e, debounceMs: 150, // Quick debounce for filter searches keepPreviousData: !0 }), d = g(() => { if (!n || i || o || !e) return []; try { const u = n.tablePivot(), c = /* @__PURE__ */ new Set(); return u.forEach((f) => { const y = f[e]; y != null && y !== "" && c.add(y); }), Array.from(c); } catch (u) { return console.error("Error extracting values from result set:", u), []; } }, [n, i, o, e]); M(() => { (!e || !r) && (s(null), a.current = ""); }, [e, r]); const p = T(() => { if (e) { a.current = ""; try { const u = { dimensions: [e], limit: 25, order: { [e]: "asc" } }; s(u); } catch (u) { console.error("Error creating query:", u); } } }, [e]), l = T((u, c = !1) => { if (e && !(!c && u === a.current)) { a.current = u; try { const f = { dimensions: [e], limit: 25, order: { [e]: "asc" } }; u && u.trim() && (f.filters = [{ member: e, operator: "contains", values: [u.trim()] }]), s(f); } catch (f) { console.error("Error creating search query:", f); } } }, [e]); return { values: d, loading: i, error: o ? o instanceof Error ? o.message : String(o) : null, refetch: p, searchValues: l }; } function De(e, r) { const [t, s] = D(e); return M(() => { const a = setTimeout(() => { s(e); }, r); return () => { clearTimeout(a); }; }, [e, r]), t; } const L = 1200, ae = 768; function Me() { const [e, r] = D( () => typeof window < "u" ? window.innerWidth : L ), t = v(null), s = v(null), a = T((d) => { if (t.current && (t.current.disconnect(), t.current = null), s.current = d, d) { const p = d.offsetWidth; p > 0 && r(p), t.current = new ResizeObserver((l) => { const u = l[0]?.contentRect.width; u && u > 0 && r(u); }), t.current.observe(d); } }, []); M(() => () => { t.current && t.current.disconnect(); }, []), M(() => { const d = () => { if (s.current) { const l = s.current.offsetWidth; l > 0 && r(l); } }; window.addEventListener("resize", d); const p = setTimeout(d, 100); return () => { window.removeEventListener("resize", d), clearTimeout(p); }; }, []); const n = g(() => e >= L ? "desktop" : e >= ae ? "scaled" : "mobile", [e]), i = g(() => n !== "scaled" ? 1 : e / L, [e, n]); return { containerRef: a, containerWidth: e, displayMode: n, scaleFactor: i, isEditable: n === "desktop", designWidth: L }; } function Ee({ initialConfig: e, onConfigChange: r, onSave: t, onDirtyStateChange: s }) { const a = v(e), n = v(!1), i = T( async (l) => { if (n.current) { s && s(!0); try { t && await t(l), a.current = l, s && s(!1); } catch (u) { throw console.error("Save failed:", u), u; } } }, [t, s] ), o = T( (l) => { r && r(l); const u = JSON.stringify(l), c = JSON.stringify(a.current); u !== c && (n.current = !0, s && s(!0)); }, [r, s] ), d = T(() => n.current, []), p = T((l) => { a.current = l, n.current = !1; }, []); return { handleConfigChange: o, handleSave: i, hasChanged: d, resetInitialConfig: p }; } export { pe as A, Me as B, Ee as C, oe as D, N as F, ve as a, Te as b, ne as c, we as d, Z as e, ee as f, be as g, ye as h, B as i, me as j, he as k, W as l, te as m, R as n, I as o, x as p, de as q, Ve as r, V as s, qe as t, X as u, ge as v, De as w, ce as x, _e as y, fe as z }; //# sourceMappingURL=hooks-BJbBseyx.js.map