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
JavaScript
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