@inkindcards/semantic-layer 0.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QT5EEWL4.js → chunk-3OR3QCN6.js} +8 -20
- package/dist/chunk-3OR3QCN6.js.map +1 -0
- package/dist/{chunk-SKDBCU5M.cjs → chunk-T2C43AAL.cjs} +8 -20
- package/dist/chunk-T2C43AAL.cjs.map +1 -0
- package/dist/chunk-W64RGDXH.cjs +225 -0
- package/dist/chunk-W64RGDXH.cjs.map +1 -0
- package/dist/chunk-WR4D3UI6.js +222 -0
- package/dist/chunk-WR4D3UI6.js.map +1 -0
- package/dist/components.cjs +9 -9
- package/dist/components.d.cts +1 -1
- package/dist/components.d.ts +1 -1
- package/dist/components.js +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +36 -32
- package/dist/index.d.ts +36 -32
- package/dist/index.js +1 -1
- package/dist/react.cjs +61 -155
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +10 -20
- package/dist/react.d.ts +10 -20
- package/dist/react.js +52 -146
- package/dist/react.js.map +1 -1
- package/dist/{types-Dc8Zdacw.d.cts → types-DJpYLOyy.d.cts} +0 -1
- package/dist/{types-Dc8Zdacw.d.ts → types-DJpYLOyy.d.ts} +0 -1
- package/package.json +11 -7
- package/dist/chunk-7VEHB5VC.cjs +0 -181
- package/dist/chunk-7VEHB5VC.cjs.map +0 -1
- package/dist/chunk-I7NWJQ63.js +0 -178
- package/dist/chunk-I7NWJQ63.js.map +0 -1
- package/dist/chunk-QT5EEWL4.js.map +0 -1
- package/dist/chunk-SKDBCU5M.cjs.map +0 -1
package/dist/react.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { SemanticLayerClient } from './chunk-
|
|
2
|
-
import { SemanticLayerContext, useAuth } from './chunk-
|
|
3
|
-
export { SemanticLayerContext, useAdminFields, useAdminRoles, useAdminUsers, useAuth, useMetrics, usePivotQuery, useSemanticQuery } from './chunk-
|
|
1
|
+
import { SemanticLayerClient } from './chunk-WR4D3UI6.js';
|
|
2
|
+
import { SemanticLayerContext, useAuth } from './chunk-3OR3QCN6.js';
|
|
3
|
+
export { SemanticLayerContext, useAdminFields, useAdminRoles, useAdminUsers, useAuth, useMetrics, usePivotQuery, useSemanticQuery } from './chunk-3OR3QCN6.js';
|
|
4
4
|
import { useMemo, useState, useEffect, useCallback } from 'react';
|
|
5
5
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
function SemanticLayerProvider({
|
|
8
8
|
gatewayUrl,
|
|
9
|
-
anonKey,
|
|
10
9
|
emailDomain,
|
|
11
10
|
client: externalClient,
|
|
12
11
|
children
|
|
13
12
|
}) {
|
|
14
|
-
const client = useMemo(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const client = useMemo(
|
|
14
|
+
() => externalClient ?? new SemanticLayerClient({ gatewayUrl }),
|
|
15
|
+
[gatewayUrl, externalClient]
|
|
16
|
+
);
|
|
18
17
|
const [auth, setAuth] = useState({
|
|
19
18
|
isAuthenticated: false,
|
|
20
19
|
isLoading: true,
|
|
@@ -24,6 +23,31 @@ function SemanticLayerProvider({
|
|
|
24
23
|
useEffect(() => {
|
|
25
24
|
let mounted = true;
|
|
26
25
|
async function initAuth() {
|
|
26
|
+
try {
|
|
27
|
+
const handledCallback = client.handleAuthCallback();
|
|
28
|
+
if (handledCallback) {
|
|
29
|
+
const session = await client.getSession();
|
|
30
|
+
if (!mounted) return;
|
|
31
|
+
if (session?.user) {
|
|
32
|
+
setAuth({
|
|
33
|
+
isAuthenticated: true,
|
|
34
|
+
isLoading: false,
|
|
35
|
+
user: { id: session.user.id, email: session.user.email },
|
|
36
|
+
error: null
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (!mounted) return;
|
|
43
|
+
setAuth({
|
|
44
|
+
isAuthenticated: false,
|
|
45
|
+
isLoading: false,
|
|
46
|
+
user: null,
|
|
47
|
+
error: err instanceof Error ? err.message : "Auth callback failed"
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
27
51
|
try {
|
|
28
52
|
const session = await client.getSession();
|
|
29
53
|
if (!mounted) return;
|
|
@@ -31,7 +55,7 @@ function SemanticLayerProvider({
|
|
|
31
55
|
setAuth({
|
|
32
56
|
isAuthenticated: true,
|
|
33
57
|
isLoading: false,
|
|
34
|
-
user: { id: session.user.id, email: session.user.email
|
|
58
|
+
user: { id: session.user.id, email: session.user.email },
|
|
35
59
|
error: null
|
|
36
60
|
});
|
|
37
61
|
} else {
|
|
@@ -53,13 +77,15 @@ function SemanticLayerProvider({
|
|
|
53
77
|
}
|
|
54
78
|
}
|
|
55
79
|
initAuth();
|
|
56
|
-
const {
|
|
80
|
+
const {
|
|
81
|
+
data: { subscription }
|
|
82
|
+
} = client.onAuthStateChange((_event, session) => {
|
|
57
83
|
if (!mounted) return;
|
|
58
84
|
if (session?.user) {
|
|
59
85
|
setAuth({
|
|
60
86
|
isAuthenticated: true,
|
|
61
87
|
isLoading: false,
|
|
62
|
-
user: { id: session.user.id, email: session.user.email
|
|
88
|
+
user: { id: session.user.id, email: session.user.email },
|
|
63
89
|
error: null
|
|
64
90
|
});
|
|
65
91
|
} else {
|
|
@@ -76,7 +102,13 @@ function SemanticLayerProvider({
|
|
|
76
102
|
subscription.unsubscribe();
|
|
77
103
|
};
|
|
78
104
|
}, [client]);
|
|
79
|
-
const
|
|
105
|
+
const signIn = useCallback(() => {
|
|
106
|
+
window.location.href = client.getSignInUrl();
|
|
107
|
+
}, [client]);
|
|
108
|
+
const value = useMemo(
|
|
109
|
+
() => ({ client, auth, emailDomain, signIn }),
|
|
110
|
+
[client, auth, emailDomain, signIn]
|
|
111
|
+
);
|
|
80
112
|
return /* @__PURE__ */ jsx(SemanticLayerContext.Provider, { value, children });
|
|
81
113
|
}
|
|
82
114
|
var containerStyle = {
|
|
@@ -109,156 +141,30 @@ var buttonStyle = {
|
|
|
109
141
|
alignItems: "center",
|
|
110
142
|
gap: "8px"
|
|
111
143
|
};
|
|
112
|
-
var inputStyle = {
|
|
113
|
-
padding: "10px 14px",
|
|
114
|
-
fontSize: "16px",
|
|
115
|
-
border: "1px solid #d1d5db",
|
|
116
|
-
borderRadius: "8px",
|
|
117
|
-
width: "280px",
|
|
118
|
-
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
119
|
-
};
|
|
120
144
|
var errorStyle = {
|
|
121
145
|
margin: 0,
|
|
122
146
|
color: "#ef4444",
|
|
123
147
|
fontSize: "14px"
|
|
124
148
|
};
|
|
125
|
-
|
|
126
|
-
background: "none",
|
|
127
|
-
border: "none",
|
|
128
|
-
color: "#4285f4",
|
|
129
|
-
cursor: "pointer",
|
|
130
|
-
fontSize: "14px",
|
|
131
|
-
padding: 0,
|
|
132
|
-
textDecoration: "underline"
|
|
133
|
-
};
|
|
134
|
-
function isInIframe() {
|
|
135
|
-
try {
|
|
136
|
-
return typeof window !== "undefined" && window.self !== window.top;
|
|
137
|
-
} catch {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function OtpSignIn() {
|
|
142
|
-
const { sendOtp, verifyOtp, emailDomain } = useAuth();
|
|
143
|
-
const [email, setEmail] = useState("");
|
|
144
|
-
const [code, setCode] = useState("");
|
|
145
|
-
const [step, setStep] = useState("email");
|
|
146
|
-
const [sending, setSending] = useState(false);
|
|
147
|
-
const [error, setError] = useState(null);
|
|
148
|
-
const handleSendCode = useCallback(async () => {
|
|
149
|
-
setError(null);
|
|
150
|
-
const trimmed = email.trim().toLowerCase();
|
|
151
|
-
if (!trimmed) {
|
|
152
|
-
setError("Please enter your email.");
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (emailDomain && !trimmed.endsWith(`@${emailDomain}`)) {
|
|
156
|
-
setError(`Please use your @${emailDomain} email.`);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
setSending(true);
|
|
160
|
-
try {
|
|
161
|
-
const { error: otpError } = await sendOtp(trimmed);
|
|
162
|
-
if (otpError) {
|
|
163
|
-
setError(otpError.message);
|
|
164
|
-
} else {
|
|
165
|
-
setStep("code");
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
setError(err instanceof Error ? err.message : "Failed to send code");
|
|
169
|
-
} finally {
|
|
170
|
-
setSending(false);
|
|
171
|
-
}
|
|
172
|
-
}, [email, emailDomain, sendOtp]);
|
|
173
|
-
const handleVerifyCode = useCallback(async () => {
|
|
174
|
-
setError(null);
|
|
175
|
-
const trimmedCode = code.trim();
|
|
176
|
-
if (!trimmedCode) {
|
|
177
|
-
setError("Please enter the code from your email.");
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
setSending(true);
|
|
181
|
-
try {
|
|
182
|
-
const { error: verifyError } = await verifyOtp(email.trim().toLowerCase(), trimmedCode);
|
|
183
|
-
if (verifyError) {
|
|
184
|
-
setError(verifyError.message);
|
|
185
|
-
}
|
|
186
|
-
} catch (err) {
|
|
187
|
-
setError(err instanceof Error ? err.message : "Verification failed");
|
|
188
|
-
} finally {
|
|
189
|
-
setSending(false);
|
|
190
|
-
}
|
|
191
|
-
}, [email, code, verifyOtp]);
|
|
192
|
-
if (step === "code") {
|
|
193
|
-
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
194
|
-
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "24px", color: "#111827" }, children: "Check your email" }),
|
|
195
|
-
/* @__PURE__ */ jsxs("p", { style: { margin: 0, color: "#6b7280", textAlign: "center", maxWidth: "320px" }, children: [
|
|
196
|
-
"We sent a sign-in code to ",
|
|
197
|
-
/* @__PURE__ */ jsx("strong", { children: email.trim() })
|
|
198
|
-
] }),
|
|
199
|
-
/* @__PURE__ */ jsx(
|
|
200
|
-
"input",
|
|
201
|
-
{
|
|
202
|
-
style: inputStyle,
|
|
203
|
-
type: "text",
|
|
204
|
-
inputMode: "numeric",
|
|
205
|
-
autoComplete: "one-time-code",
|
|
206
|
-
placeholder: "Enter 6-digit code",
|
|
207
|
-
value: code,
|
|
208
|
-
onChange: (e) => setCode(e.target.value),
|
|
209
|
-
onKeyDown: (e) => e.key === "Enter" && handleVerifyCode()
|
|
210
|
-
}
|
|
211
|
-
),
|
|
212
|
-
error && /* @__PURE__ */ jsx("p", { style: errorStyle, children: error }),
|
|
213
|
-
/* @__PURE__ */ jsx("button", { style: buttonStyle, onClick: handleVerifyCode, disabled: sending, children: sending ? "Verifying..." : "Verify code" }),
|
|
214
|
-
/* @__PURE__ */ jsx("button", { style: linkStyle, onClick: () => {
|
|
215
|
-
setStep("email");
|
|
216
|
-
setCode("");
|
|
217
|
-
setError(null);
|
|
218
|
-
}, children: "Use a different email" })
|
|
219
|
-
] });
|
|
220
|
-
}
|
|
221
|
-
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
222
|
-
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "24px", color: "#111827" }, children: "Sign in to continue" }),
|
|
223
|
-
/* @__PURE__ */ jsxs("p", { style: { margin: 0, color: "#6b7280" }, children: [
|
|
224
|
-
"Enter your ",
|
|
225
|
-
emailDomain ? `@${emailDomain} ` : "",
|
|
226
|
-
"email to receive a sign-in code."
|
|
227
|
-
] }),
|
|
228
|
-
/* @__PURE__ */ jsx(
|
|
229
|
-
"input",
|
|
230
|
-
{
|
|
231
|
-
style: inputStyle,
|
|
232
|
-
type: "email",
|
|
233
|
-
placeholder: emailDomain ? `you@${emailDomain}` : "you@company.com",
|
|
234
|
-
value: email,
|
|
235
|
-
onChange: (e) => setEmail(e.target.value),
|
|
236
|
-
onKeyDown: (e) => e.key === "Enter" && handleSendCode()
|
|
237
|
-
}
|
|
238
|
-
),
|
|
239
|
-
error && /* @__PURE__ */ jsx("p", { style: errorStyle, children: error }),
|
|
240
|
-
/* @__PURE__ */ jsx("button", { style: buttonStyle, onClick: handleSendCode, disabled: sending, children: sending ? "Sending..." : "Send sign-in code" })
|
|
241
|
-
] });
|
|
242
|
-
}
|
|
243
|
-
function AuthGate({ children, loading, signInComponent, useEmailOtp }) {
|
|
149
|
+
function AuthGate({ children, loading, signInComponent }) {
|
|
244
150
|
const { isAuthenticated, isLoading, error, signIn } = useAuth();
|
|
245
|
-
const
|
|
246
|
-
const
|
|
151
|
+
const [signingIn, setSigningIn] = useState(false);
|
|
152
|
+
const handleSignIn = useCallback(() => {
|
|
153
|
+
setSigningIn(true);
|
|
154
|
+
signIn();
|
|
155
|
+
}, [signIn]);
|
|
247
156
|
if (isLoading) {
|
|
248
157
|
return /* @__PURE__ */ jsx(Fragment, { children: loading || /* @__PURE__ */ jsx("div", { style: loadingStyle, children: "Loading..." }) });
|
|
249
158
|
}
|
|
250
159
|
if (!isAuthenticated) {
|
|
251
|
-
if (shouldUseOtp) {
|
|
252
|
-
return /* @__PURE__ */ jsx(OtpSignIn, {});
|
|
253
|
-
}
|
|
254
160
|
if (signInComponent) {
|
|
255
|
-
return /* @__PURE__ */ jsx(Fragment, { children: signInComponent({ signIn:
|
|
161
|
+
return /* @__PURE__ */ jsx(Fragment, { children: signInComponent({ signIn: handleSignIn }) });
|
|
256
162
|
}
|
|
257
163
|
return /* @__PURE__ */ jsxs("div", { style: containerStyle, children: [
|
|
258
164
|
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: "24px", color: "#111827" }, children: "Sign in to continue" }),
|
|
259
165
|
/* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#6b7280" }, children: "Use your Google account to access data." }),
|
|
260
166
|
error && /* @__PURE__ */ jsx("p", { style: errorStyle, children: error }),
|
|
261
|
-
/* @__PURE__ */ jsx("button", { style: buttonStyle, onClick:
|
|
167
|
+
/* @__PURE__ */ jsx("button", { style: buttonStyle, onClick: handleSignIn, disabled: signingIn, children: signingIn ? "Signing in..." : "Sign in with Google" })
|
|
262
168
|
] });
|
|
263
169
|
}
|
|
264
170
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/provider.tsx","../src/react/auth-gate.tsx"],"names":["useState","jsx"],"mappings":"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","file":"react.js","sourcesContent":["import React, { useEffect, useMemo, useState } from \"react\";\nimport { SemanticLayerClient } from \"../client\";\nimport type { SemanticLayerConfig, AuthState } from \"../types\";\nimport { SemanticLayerContext } from \"./context\";\n\nexport interface SemanticLayerProviderProps {\n /** The Supabase URL of the gateway project. */\n gatewayUrl: string;\n /** The Supabase anon/publishable key of the gateway project. */\n anonKey: string;\n /**\n * Optional: restrict sign-in to emails matching this domain (e.g. \"inkind.com\").\n * Used for client-side validation in the OTP sign-in form.\n */\n emailDomain?: string;\n /**\n * Optional: a pre-constructed client instance.\n * If provided, gatewayUrl and anonKey are ignored.\n */\n client?: SemanticLayerClient;\n children: React.ReactNode;\n}\n\nexport function SemanticLayerProvider({\n gatewayUrl,\n anonKey,\n emailDomain,\n client: externalClient,\n children,\n}: SemanticLayerProviderProps) {\n const client = useMemo(() => {\n if (externalClient) return externalClient;\n return new SemanticLayerClient({ gatewayUrl, anonKey });\n }, [gatewayUrl, anonKey, externalClient]);\n\n const [auth, setAuth] = useState<AuthState>({\n isAuthenticated: false,\n isLoading: true,\n user: null,\n error: null,\n });\n\n useEffect(() => {\n let mounted = true;\n\n async function initAuth() {\n try {\n const session = await client.getSession();\n if (!mounted) return;\n\n if (session?.user) {\n setAuth({\n isAuthenticated: true,\n isLoading: false,\n user: { id: session.user.id, email: session.user.email || \"\" },\n error: null,\n });\n } else {\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: null,\n });\n }\n } catch (err) {\n if (!mounted) return;\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: err instanceof Error ? err.message : \"Auth initialization failed\",\n });\n }\n }\n\n initAuth();\n\n const { data: { subscription } } = client.onAuthStateChange((_event, session) => {\n if (!mounted) return;\n if (session?.user) {\n setAuth({\n isAuthenticated: true,\n isLoading: false,\n user: { id: session.user.id, email: session.user.email || \"\" },\n error: null,\n });\n } else {\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: null,\n });\n }\n });\n\n return () => {\n mounted = false;\n subscription.unsubscribe();\n };\n }, [client]);\n\n const value = useMemo(() => ({ client, auth, emailDomain }), [client, auth, emailDomain]);\n\n return (\n <SemanticLayerContext.Provider value={value}>\n {children}\n </SemanticLayerContext.Provider>\n );\n}\n","import React, { useCallback, useState } from \"react\";\nimport { useAuth } from \"./hooks\";\n\nexport interface AuthGateProps {\n children: React.ReactNode;\n /** Custom loading component. */\n loading?: React.ReactNode;\n /** Custom sign-in component. Receives a signIn callback. */\n signInComponent?: (props: { signIn: () => void }) => React.ReactNode;\n /** Force email OTP sign-in instead of Google OAuth (recommended for Lovable editor). */\n useEmailOtp?: boolean;\n}\n\nconst containerStyle: React.CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n gap: \"16px\",\n};\n\nconst loadingStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n color: \"#6b7280\",\n};\n\nconst buttonStyle: React.CSSProperties = {\n padding: \"12px 24px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#4285f4\",\n border: \"none\",\n borderRadius: \"8px\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n};\n\nconst inputStyle: React.CSSProperties = {\n padding: \"10px 14px\",\n fontSize: \"16px\",\n border: \"1px solid #d1d5db\",\n borderRadius: \"8px\",\n width: \"280px\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n};\n\nconst errorStyle: React.CSSProperties = {\n margin: 0,\n color: \"#ef4444\",\n fontSize: \"14px\",\n};\n\nconst linkStyle: React.CSSProperties = {\n background: \"none\",\n border: \"none\",\n color: \"#4285f4\",\n cursor: \"pointer\",\n fontSize: \"14px\",\n padding: 0,\n textDecoration: \"underline\",\n};\n\nfunction isInIframe(): boolean {\n try {\n return typeof window !== \"undefined\" && window.self !== window.top;\n } catch {\n return true;\n }\n}\n\n/**\n * Email OTP sign-in form for use inside iframes (Lovable editor preview).\n * Two-step: enter email → enter code from inbox.\n */\nfunction OtpSignIn() {\n const { sendOtp, verifyOtp, emailDomain } = useAuth();\n const [email, setEmail] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [step, setStep] = useState<\"email\" | \"code\">(\"email\");\n const [sending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleSendCode = useCallback(async () => {\n setError(null);\n const trimmed = email.trim().toLowerCase();\n if (!trimmed) {\n setError(\"Please enter your email.\");\n return;\n }\n if (emailDomain && !trimmed.endsWith(`@${emailDomain}`)) {\n setError(`Please use your @${emailDomain} email.`);\n return;\n }\n setSending(true);\n try {\n const { error: otpError } = await sendOtp(trimmed);\n if (otpError) {\n setError(otpError.message);\n } else {\n setStep(\"code\");\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to send code\");\n } finally {\n setSending(false);\n }\n }, [email, emailDomain, sendOtp]);\n\n const handleVerifyCode = useCallback(async () => {\n setError(null);\n const trimmedCode = code.trim();\n if (!trimmedCode) {\n setError(\"Please enter the code from your email.\");\n return;\n }\n setSending(true);\n try {\n const { error: verifyError } = await verifyOtp(email.trim().toLowerCase(), trimmedCode);\n if (verifyError) {\n setError(verifyError.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Verification failed\");\n } finally {\n setSending(false);\n }\n }, [email, code, verifyOtp]);\n\n if (step === \"code\") {\n return (\n <div style={containerStyle}>\n <h2 style={{ margin: 0, fontSize: \"24px\", color: \"#111827\" }}>Check your email</h2>\n <p style={{ margin: 0, color: \"#6b7280\", textAlign: \"center\", maxWidth: \"320px\" }}>\n We sent a sign-in code to <strong>{email.trim()}</strong>\n </p>\n <input\n style={inputStyle}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n placeholder=\"Enter 6-digit code\"\n value={code}\n onChange={(e) => setCode(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && handleVerifyCode()}\n />\n {error && <p style={errorStyle}>{error}</p>}\n <button style={buttonStyle} onClick={handleVerifyCode} disabled={sending}>\n {sending ? \"Verifying...\" : \"Verify code\"}\n </button>\n <button style={linkStyle} onClick={() => { setStep(\"email\"); setCode(\"\"); setError(null); }}>\n Use a different email\n </button>\n </div>\n );\n }\n\n return (\n <div style={containerStyle}>\n <h2 style={{ margin: 0, fontSize: \"24px\", color: \"#111827\" }}>Sign in to continue</h2>\n <p style={{ margin: 0, color: \"#6b7280\" }}>\n Enter your {emailDomain ? `@${emailDomain} ` : \"\"}email to receive a sign-in code.\n </p>\n <input\n style={inputStyle}\n type=\"email\"\n placeholder={emailDomain ? `you@${emailDomain}` : \"you@company.com\"}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && handleSendCode()}\n />\n {error && <p style={errorStyle}>{error}</p>}\n <button style={buttonStyle} onClick={handleSendCode} disabled={sending}>\n {sending ? \"Sending...\" : \"Send sign-in code\"}\n </button>\n </div>\n );\n}\n\n/**\n * Wraps children and only renders them when the user is authenticated.\n * In iframes (e.g. Lovable editor preview), shows an email OTP form.\n * In standalone tabs, shows the Google OAuth sign-in button.\n */\nexport function AuthGate({ children, loading, signInComponent, useEmailOtp }: AuthGateProps) {\n const { isAuthenticated, isLoading, error, signIn } = useAuth();\n const inIframe = isInIframe();\n const shouldUseOtp = useEmailOtp || inIframe;\n\n if (isLoading) {\n return <>{loading || <div style={loadingStyle}>Loading...</div>}</>;\n }\n\n if (!isAuthenticated) {\n if (shouldUseOtp) {\n return <OtpSignIn />;\n }\n\n if (signInComponent) {\n return <>{signInComponent({ signIn: () => signIn(window.location.origin) })}</>;\n }\n\n return (\n <div style={containerStyle}>\n <h2 style={{ margin: 0, fontSize: \"24px\", color: \"#111827\" }}>Sign in to continue</h2>\n <p style={{ margin: 0, color: \"#6b7280\" }}>\n Use your Google account to access data.\n </p>\n {error && <p style={errorStyle}>{error}</p>}\n <button style={buttonStyle} onClick={() => signIn(window.location.origin)}>\n Sign in with Google\n </button>\n </div>\n );\n }\n\n return <>{children}</>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/react/provider.tsx","../src/react/auth-gate.tsx"],"names":["useState","useCallback","jsx"],"mappings":"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","file":"react.js","sourcesContent":["import React, { useEffect, useMemo, useState, useCallback } from \"react\";\nimport { SemanticLayerClient } from \"../client\";\nimport type { AuthState } from \"../types\";\nimport { SemanticLayerContext } from \"./context\";\n\nexport interface SemanticLayerProviderProps {\n /** The gateway API URL (e.g. https://semantic-layer-gateway.inkind-dev.com). */\n gatewayUrl: string;\n /**\n * Optional: restrict sign-in to emails matching this domain (e.g. \"inkind.com\").\n * This is a client-side hint only; the gateway enforces domain restrictions server-side.\n */\n emailDomain?: string;\n /**\n * Optional: a pre-constructed client instance.\n * If provided, gatewayUrl is ignored.\n */\n client?: SemanticLayerClient;\n children: React.ReactNode;\n}\n\nexport function SemanticLayerProvider({\n gatewayUrl,\n emailDomain,\n client: externalClient,\n children,\n}: SemanticLayerProviderProps) {\n const client = useMemo(\n () => externalClient ?? new SemanticLayerClient({ gatewayUrl }),\n [gatewayUrl, externalClient],\n );\n\n const [auth, setAuth] = useState<AuthState>({\n isAuthenticated: false,\n isLoading: true,\n user: null,\n error: null,\n });\n\n // On mount, check for auth tokens in the URL hash (from gateway redirect)\n // and check for an existing session in sessionStorage.\n useEffect(() => {\n let mounted = true;\n\n async function initAuth() {\n try {\n const handledCallback = client.handleAuthCallback();\n\n if (handledCallback) {\n const session = await client.getSession();\n if (!mounted) return;\n if (session?.user) {\n setAuth({\n isAuthenticated: true,\n isLoading: false,\n user: { id: session.user.id, email: session.user.email },\n error: null,\n });\n return;\n }\n }\n } catch (err) {\n if (!mounted) return;\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: err instanceof Error ? err.message : \"Auth callback failed\",\n });\n return;\n }\n\n try {\n const session = await client.getSession();\n if (!mounted) return;\n if (session?.user) {\n setAuth({\n isAuthenticated: true,\n isLoading: false,\n user: { id: session.user.id, email: session.user.email },\n error: null,\n });\n } else {\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: null,\n });\n }\n } catch (err) {\n if (!mounted) return;\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: err instanceof Error ? err.message : \"Auth initialization failed\",\n });\n }\n }\n\n initAuth();\n\n const {\n data: { subscription },\n } = client.onAuthStateChange((_event, session) => {\n if (!mounted) return;\n if (session?.user) {\n setAuth({\n isAuthenticated: true,\n isLoading: false,\n user: { id: session.user.id, email: session.user.email },\n error: null,\n });\n } else {\n setAuth({\n isAuthenticated: false,\n isLoading: false,\n user: null,\n error: null,\n });\n }\n });\n\n return () => {\n mounted = false;\n subscription.unsubscribe();\n };\n }, [client]);\n\n const signIn = useCallback(() => {\n window.location.href = client.getSignInUrl();\n }, [client]);\n\n const value = useMemo(\n () => ({ client, auth, emailDomain, signIn }),\n [client, auth, emailDomain, signIn],\n );\n\n return (\n <SemanticLayerContext.Provider value={value}>\n {children}\n </SemanticLayerContext.Provider>\n );\n}\n","import React, { useCallback, useState } from \"react\";\nimport { useAuth } from \"./hooks\";\n\nexport interface AuthGateProps {\n children: React.ReactNode;\n /** Custom loading component. */\n loading?: React.ReactNode;\n /** Custom sign-in component. Receives a signIn callback. */\n signInComponent?: (props: { signIn: () => void }) => React.ReactNode;\n}\n\nconst containerStyle: React.CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n gap: \"16px\",\n};\n\nconst loadingStyle: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n color: \"#6b7280\",\n};\n\nconst buttonStyle: React.CSSProperties = {\n padding: \"12px 24px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#4285f4\",\n border: \"none\",\n borderRadius: \"8px\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n};\n\nconst errorStyle: React.CSSProperties = {\n margin: 0,\n color: \"#ef4444\",\n fontSize: \"14px\",\n};\n\n/**\n * Wraps children and only renders them when the user is authenticated.\n * Shows a Google sign-in button when unauthenticated.\n */\nexport function AuthGate({ children, loading, signInComponent }: AuthGateProps) {\n const { isAuthenticated, isLoading, error, signIn } = useAuth();\n const [signingIn, setSigningIn] = useState(false);\n\n const handleSignIn = useCallback(() => {\n setSigningIn(true);\n signIn();\n }, [signIn]);\n\n if (isLoading) {\n return <>{loading || <div style={loadingStyle}>Loading...</div>}</>;\n }\n\n if (!isAuthenticated) {\n if (signInComponent) {\n return <>{signInComponent({ signIn: handleSignIn })}</>;\n }\n\n return (\n <div style={containerStyle}>\n <h2 style={{ margin: 0, fontSize: \"24px\", color: \"#111827\" }}>Sign in to continue</h2>\n <p style={{ margin: 0, color: \"#6b7280\" }}>\n Use your Google account to access data.\n </p>\n {error && <p style={errorStyle}>{error}</p>}\n <button style={buttonStyle} onClick={handleSignIn} disabled={signingIn}>\n {signingIn ? \"Signing in...\" : \"Sign in with Google\"}\n </button>\n </div>\n );\n }\n\n return <>{children}</>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkindcards/semantic-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "SDK for connecting Lovable apps to the dbt Semantic Layer via a shared gateway",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"require": "./dist/components.cjs"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
|
-
"files": [
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
27
29
|
"scripts": {
|
|
28
30
|
"build": "tsup",
|
|
29
31
|
"dev": "tsup --watch",
|
|
@@ -35,12 +37,14 @@
|
|
|
35
37
|
"react-dom": ">=18"
|
|
36
38
|
},
|
|
37
39
|
"peerDependenciesMeta": {
|
|
38
|
-
"react": {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
"react": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"react-dom": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
43
46
|
},
|
|
47
|
+
"dependencies": {},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"@types/react": "^18.3.0",
|
|
46
50
|
"react": "^18.3.0",
|
package/dist/chunk-7VEHB5VC.cjs
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var supabaseJs = require('@supabase/supabase-js');
|
|
4
|
-
|
|
5
|
-
// src/client.ts
|
|
6
|
-
var SemanticLayerClient = class {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.metadataCache = null;
|
|
9
|
-
this.metadataCacheTime = 0;
|
|
10
|
-
this.CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
11
|
-
this.admin = {
|
|
12
|
-
// Fields
|
|
13
|
-
listFields: () => this.adminCall("fields.list"),
|
|
14
|
-
createField: (input) => this.adminCall("fields.create", input),
|
|
15
|
-
updateField: (input) => this.adminCall("fields.update", input),
|
|
16
|
-
deleteField: (id) => this.adminCall("fields.delete", { id }),
|
|
17
|
-
// Roles
|
|
18
|
-
listRoles: () => this.adminCall("roles.list"),
|
|
19
|
-
createRole: (input) => this.adminCall("roles.create", input),
|
|
20
|
-
updateRole: (input) => this.adminCall("roles.update", input),
|
|
21
|
-
deleteRole: (id) => this.adminCall("roles.delete", { id }),
|
|
22
|
-
setRoleFields: (role_id, curated_field_ids) => this.adminCall("roles.setFields", { role_id, curated_field_ids }),
|
|
23
|
-
// Users
|
|
24
|
-
listUsers: () => this.adminCall("users.list"),
|
|
25
|
-
assignUserRole: (input) => this.adminCall("users.assignRole", input),
|
|
26
|
-
removeUserRole: (input) => this.adminCall("users.removeRole", input),
|
|
27
|
-
// Per-user overrides
|
|
28
|
-
listUserOverrides: (user_id) => this.adminCall("users.listOverrides", { user_id }),
|
|
29
|
-
setUserOverride: (input) => this.adminCall("users.setOverride", input),
|
|
30
|
-
removeUserOverride: (input) => this.adminCall("users.removeOverride", input)
|
|
31
|
-
};
|
|
32
|
-
this.gatewayUrl = config.gatewayUrl;
|
|
33
|
-
this.supabase = supabaseJs.createClient(config.gatewayUrl, config.anonKey, {
|
|
34
|
-
auth: {
|
|
35
|
-
persistSession: true,
|
|
36
|
-
autoRefreshToken: true,
|
|
37
|
-
storageKey: "semantic-layer-auth"
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/** Access the underlying Supabase client (for auth operations). */
|
|
42
|
-
getSupabaseClient() {
|
|
43
|
-
return this.supabase;
|
|
44
|
-
}
|
|
45
|
-
// ─── Auth ────────────────────────────────────────────────────────────────────
|
|
46
|
-
async signInWithGoogle(redirectTo) {
|
|
47
|
-
return this.supabase.auth.signInWithOAuth({
|
|
48
|
-
provider: "google",
|
|
49
|
-
options: { redirectTo }
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
/** Get the Google sign-in URL without triggering a redirect (for popup flows). */
|
|
53
|
-
async getSignInUrl(redirectTo) {
|
|
54
|
-
const { data, error } = await this.supabase.auth.signInWithOAuth({
|
|
55
|
-
provider: "google",
|
|
56
|
-
options: {
|
|
57
|
-
redirectTo,
|
|
58
|
-
skipBrowserRedirect: true
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
if (error || !data.url) return null;
|
|
62
|
-
return data.url;
|
|
63
|
-
}
|
|
64
|
-
/** Send a one-time code to the given email (for iframe/editor sign-in). */
|
|
65
|
-
async sendOtp(email) {
|
|
66
|
-
return this.supabase.auth.signInWithOtp({ email });
|
|
67
|
-
}
|
|
68
|
-
/** Verify a one-time code received via email. */
|
|
69
|
-
async verifyOtp(email, token) {
|
|
70
|
-
return this.supabase.auth.verifyOtp({ email, token, type: "email" });
|
|
71
|
-
}
|
|
72
|
-
async signOut() {
|
|
73
|
-
return this.supabase.auth.signOut();
|
|
74
|
-
}
|
|
75
|
-
async getSession() {
|
|
76
|
-
const { data } = await this.supabase.auth.getSession();
|
|
77
|
-
return data.session;
|
|
78
|
-
}
|
|
79
|
-
onAuthStateChange(callback) {
|
|
80
|
-
return this.supabase.auth.onAuthStateChange(callback);
|
|
81
|
-
}
|
|
82
|
-
// ─── Metadata ────────────────────────────────────────────────────────────────
|
|
83
|
-
/**
|
|
84
|
-
* Fetch the curated catalog of metrics and dimensions from the gateway.
|
|
85
|
-
* Results are cached for 5 minutes.
|
|
86
|
-
*/
|
|
87
|
-
async getMetadata(forceRefresh = false) {
|
|
88
|
-
const now = Date.now();
|
|
89
|
-
if (!forceRefresh && this.metadataCache && now - this.metadataCacheTime < this.CACHE_TTL_MS) {
|
|
90
|
-
return this.metadataCache;
|
|
91
|
-
}
|
|
92
|
-
const { data, error } = await this.supabase.functions.invoke("dbt-metadata");
|
|
93
|
-
if (error) {
|
|
94
|
-
throw new SemanticLayerError(`Failed to fetch metadata: ${error.message}`, "METADATA_ERROR");
|
|
95
|
-
}
|
|
96
|
-
if (data?.error) {
|
|
97
|
-
throw new SemanticLayerError(data.error, "METADATA_ERROR");
|
|
98
|
-
}
|
|
99
|
-
this.metadataCache = data;
|
|
100
|
-
this.metadataCacheTime = now;
|
|
101
|
-
return this.metadataCache;
|
|
102
|
-
}
|
|
103
|
-
/** Get only metrics from the catalog. */
|
|
104
|
-
async getMetrics() {
|
|
105
|
-
const { fields } = await this.getMetadata();
|
|
106
|
-
return fields.filter((f) => f.type === "metric");
|
|
107
|
-
}
|
|
108
|
-
/** Get only dimensions (including time dimensions) from the catalog. */
|
|
109
|
-
async getDimensions() {
|
|
110
|
-
const { fields } = await this.getMetadata();
|
|
111
|
-
return fields.filter((f) => f.type === "dimension" || f.type === "time_dimension");
|
|
112
|
-
}
|
|
113
|
-
/** Find a field by name or id. */
|
|
114
|
-
async getField(nameOrId) {
|
|
115
|
-
const { fields } = await this.getMetadata();
|
|
116
|
-
return fields.find((f) => f.name === nameOrId || f.id === nameOrId);
|
|
117
|
-
}
|
|
118
|
-
// ─── Query ───────────────────────────────────────────────────────────────────
|
|
119
|
-
/** Execute a query using the full PivotConfig format. */
|
|
120
|
-
async query(config) {
|
|
121
|
-
if (config.values.length === 0) {
|
|
122
|
-
return { columns: [], rows: [], totalRows: 0, executionTimeMs: 0 };
|
|
123
|
-
}
|
|
124
|
-
const { data, error } = await this.supabase.functions.invoke("dbt-query", {
|
|
125
|
-
body: { config }
|
|
126
|
-
});
|
|
127
|
-
if (error) {
|
|
128
|
-
throw new SemanticLayerError(`Query failed: ${error.message}`, "QUERY_ERROR");
|
|
129
|
-
}
|
|
130
|
-
if (data?.error) {
|
|
131
|
-
throw new SemanticLayerError(data.error, "QUERY_ERROR");
|
|
132
|
-
}
|
|
133
|
-
return data;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Execute a query using the simplified input format.
|
|
137
|
-
* Automatically resolves metric/dimension names to full field objects.
|
|
138
|
-
*/
|
|
139
|
-
async simpleQuery(input) {
|
|
140
|
-
const { fields } = await this.getMetadata();
|
|
141
|
-
const fieldMap = new Map(fields.map((f) => [f.name, f]));
|
|
142
|
-
const values = input.metrics.map((name) => {
|
|
143
|
-
const field = fieldMap.get(name);
|
|
144
|
-
if (!field) throw new SemanticLayerError(`Unknown metric: "${name}"`, "UNKNOWN_FIELD");
|
|
145
|
-
return { fieldId: field.id, field };
|
|
146
|
-
});
|
|
147
|
-
const rows = (input.groupBy || []).map((name) => {
|
|
148
|
-
const field = fieldMap.get(name);
|
|
149
|
-
if (!field) throw new SemanticLayerError(`Unknown dimension: "${name}"`, "UNKNOWN_FIELD");
|
|
150
|
-
const grain = field.type === "time_dimension" ? input.grain : void 0;
|
|
151
|
-
return { fieldId: field.id, field, grain };
|
|
152
|
-
});
|
|
153
|
-
const filters = Object.entries(input.filters || {}).map(([name, filterValues]) => {
|
|
154
|
-
const field = fieldMap.get(name);
|
|
155
|
-
if (!field) throw new SemanticLayerError(`Unknown filter field: "${name}"`, "UNKNOWN_FIELD");
|
|
156
|
-
return { fieldId: field.id, field, filterValues };
|
|
157
|
-
});
|
|
158
|
-
return this.query({ values, rows, columns: [], filters });
|
|
159
|
-
}
|
|
160
|
-
// ─── Admin ──────────────────────────────────────────────────────────────────
|
|
161
|
-
async adminCall(action, body = {}) {
|
|
162
|
-
const { data, error } = await this.supabase.functions.invoke("admin-manage", {
|
|
163
|
-
body: { action, ...body }
|
|
164
|
-
});
|
|
165
|
-
if (error) throw new SemanticLayerError(`Admin call failed: ${error.message}`, "ADMIN_ERROR");
|
|
166
|
-
if (data?.error) throw new SemanticLayerError(data.error, "ADMIN_ERROR");
|
|
167
|
-
return data;
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
var SemanticLayerError = class extends Error {
|
|
171
|
-
constructor(message, code) {
|
|
172
|
-
super(message);
|
|
173
|
-
this.name = "SemanticLayerError";
|
|
174
|
-
this.code = code;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
exports.SemanticLayerClient = SemanticLayerClient;
|
|
179
|
-
exports.SemanticLayerError = SemanticLayerError;
|
|
180
|
-
//# sourceMappingURL=chunk-7VEHB5VC.cjs.map
|
|
181
|
-
//# sourceMappingURL=chunk-7VEHB5VC.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"names":["createClient"],"mappings":"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","file":"chunk-7VEHB5VC.cjs","sourcesContent":["import { createClient, SupabaseClient, Session } from \"@supabase/supabase-js\";\nimport type {\n SemanticLayerConfig,\n MetadataResponse,\n PivotConfig,\n QueryResult,\n SimpleQueryInput,\n SemanticField,\n CuratedField,\n AccessRole,\n UserRoleAssignment,\n UserFieldOverride,\n} from \"./types\";\n\nexport class SemanticLayerClient {\n private supabase: SupabaseClient;\n private metadataCache: MetadataResponse | null = null;\n private metadataCacheTime = 0;\n private readonly CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n readonly gatewayUrl: string;\n\n constructor(config: SemanticLayerConfig) {\n this.gatewayUrl = config.gatewayUrl;\n this.supabase = createClient(config.gatewayUrl, config.anonKey, {\n auth: {\n persistSession: true,\n autoRefreshToken: true,\n storageKey: \"semantic-layer-auth\",\n },\n });\n }\n\n /** Access the underlying Supabase client (for auth operations). */\n getSupabaseClient(): SupabaseClient {\n return this.supabase;\n }\n\n // ─── Auth ────────────────────────────────────────────────────────────────────\n\n async signInWithGoogle(redirectTo?: string) {\n return this.supabase.auth.signInWithOAuth({\n provider: \"google\",\n options: { redirectTo },\n });\n }\n\n /** Get the Google sign-in URL without triggering a redirect (for popup flows). */\n async getSignInUrl(redirectTo?: string): Promise<string | null> {\n const { data, error } = await this.supabase.auth.signInWithOAuth({\n provider: \"google\",\n options: {\n redirectTo,\n skipBrowserRedirect: true,\n },\n });\n if (error || !data.url) return null;\n return data.url;\n }\n\n /** Send a one-time code to the given email (for iframe/editor sign-in). */\n async sendOtp(email: string) {\n return this.supabase.auth.signInWithOtp({ email });\n }\n\n /** Verify a one-time code received via email. */\n async verifyOtp(email: string, token: string) {\n return this.supabase.auth.verifyOtp({ email, token, type: \"email\" });\n }\n\n async signOut() {\n return this.supabase.auth.signOut();\n }\n\n async getSession(): Promise<Session | null> {\n const { data } = await this.supabase.auth.getSession();\n return data.session;\n }\n\n onAuthStateChange(callback: (event: string, session: Session | null) => void) {\n return this.supabase.auth.onAuthStateChange(callback);\n }\n\n // ─── Metadata ────────────────────────────────────────────────────────────────\n\n /**\n * Fetch the curated catalog of metrics and dimensions from the gateway.\n * Results are cached for 5 minutes.\n */\n async getMetadata(forceRefresh = false): Promise<MetadataResponse> {\n const now = Date.now();\n if (!forceRefresh && this.metadataCache && now - this.metadataCacheTime < this.CACHE_TTL_MS) {\n return this.metadataCache;\n }\n\n const { data, error } = await this.supabase.functions.invoke(\"dbt-metadata\");\n\n if (error) {\n throw new SemanticLayerError(`Failed to fetch metadata: ${error.message}`, \"METADATA_ERROR\");\n }\n if (data?.error) {\n throw new SemanticLayerError(data.error, \"METADATA_ERROR\");\n }\n\n this.metadataCache = data as MetadataResponse;\n this.metadataCacheTime = now;\n return this.metadataCache;\n }\n\n /** Get only metrics from the catalog. */\n async getMetrics(): Promise<SemanticField[]> {\n const { fields } = await this.getMetadata();\n return fields.filter((f) => f.type === \"metric\");\n }\n\n /** Get only dimensions (including time dimensions) from the catalog. */\n async getDimensions(): Promise<SemanticField[]> {\n const { fields } = await this.getMetadata();\n return fields.filter((f) => f.type === \"dimension\" || f.type === \"time_dimension\");\n }\n\n /** Find a field by name or id. */\n async getField(nameOrId: string): Promise<SemanticField | undefined> {\n const { fields } = await this.getMetadata();\n return fields.find((f) => f.name === nameOrId || f.id === nameOrId);\n }\n\n // ─── Query ───────────────────────────────────────────────────────────────────\n\n /** Execute a query using the full PivotConfig format. */\n async query(config: PivotConfig): Promise<QueryResult> {\n if (config.values.length === 0) {\n return { columns: [], rows: [], totalRows: 0, executionTimeMs: 0 };\n }\n\n const { data, error } = await this.supabase.functions.invoke(\"dbt-query\", {\n body: { config },\n });\n\n if (error) {\n throw new SemanticLayerError(`Query failed: ${error.message}`, \"QUERY_ERROR\");\n }\n if (data?.error) {\n throw new SemanticLayerError(data.error, \"QUERY_ERROR\");\n }\n\n return data as QueryResult;\n }\n\n /**\n * Execute a query using the simplified input format.\n * Automatically resolves metric/dimension names to full field objects.\n */\n async simpleQuery(input: SimpleQueryInput): Promise<QueryResult> {\n const { fields } = await this.getMetadata();\n const fieldMap = new Map(fields.map((f) => [f.name, f]));\n\n const values = input.metrics.map((name) => {\n const field = fieldMap.get(name);\n if (!field) throw new SemanticLayerError(`Unknown metric: \"${name}\"`, \"UNKNOWN_FIELD\");\n return { fieldId: field.id, field };\n });\n\n const rows = (input.groupBy || []).map((name) => {\n const field = fieldMap.get(name);\n if (!field) throw new SemanticLayerError(`Unknown dimension: \"${name}\"`, \"UNKNOWN_FIELD\");\n const grain = field.type === \"time_dimension\" ? input.grain : undefined;\n return { fieldId: field.id, field, grain };\n });\n\n const filters = Object.entries(input.filters || {}).map(([name, filterValues]) => {\n const field = fieldMap.get(name);\n if (!field) throw new SemanticLayerError(`Unknown filter field: \"${name}\"`, \"UNKNOWN_FIELD\");\n return { fieldId: field.id, field, filterValues };\n });\n\n return this.query({ values, rows, columns: [], filters });\n }\n\n // ─── Admin ──────────────────────────────────────────────────────────────────\n\n private async adminCall<T>(action: string, body: Record<string, unknown> = {}): Promise<T> {\n const { data, error } = await this.supabase.functions.invoke(\"admin-manage\", {\n body: { action, ...body },\n });\n if (error) throw new SemanticLayerError(`Admin call failed: ${error.message}`, \"ADMIN_ERROR\");\n if (data?.error) throw new SemanticLayerError(data.error, \"ADMIN_ERROR\");\n return data as T;\n }\n\n readonly admin = {\n // Fields\n listFields: () => this.adminCall<{ fields: CuratedField[] }>(\"fields.list\"),\n createField: (input: { field_name: string; field_type: string; display_name?: string; description?: string }) =>\n this.adminCall<{ field: CuratedField }>(\"fields.create\", input),\n updateField: (input: { id: number; field_name?: string; display_name?: string; description?: string; is_active?: boolean }) =>\n this.adminCall<{ field: CuratedField }>(\"fields.update\", input),\n deleteField: (id: number) =>\n this.adminCall<{ deleted: boolean }>(\"fields.delete\", { id }),\n\n // Roles\n listRoles: () => this.adminCall<{ roles: AccessRole[] }>(\"roles.list\"),\n createRole: (input: { name: string; description?: string; is_default?: boolean }) =>\n this.adminCall<{ role: AccessRole }>(\"roles.create\", input),\n updateRole: (input: { id: number; name?: string; description?: string; is_default?: boolean }) =>\n this.adminCall<{ role: AccessRole }>(\"roles.update\", input),\n deleteRole: (id: number) =>\n this.adminCall<{ deleted: boolean }>(\"roles.delete\", { id }),\n setRoleFields: (role_id: number, curated_field_ids: number[]) =>\n this.adminCall<{ role_id: number; field_count: number }>(\"roles.setFields\", { role_id, curated_field_ids }),\n\n // Users\n listUsers: () => this.adminCall<{ users: UserRoleAssignment[] }>(\"users.list\"),\n assignUserRole: (input: { email: string; role_id: number; is_admin?: boolean }) =>\n this.adminCall<{ assignment: UserRoleAssignment }>(\"users.assignRole\", input),\n removeUserRole: (input: { email: string; role_id: number }) =>\n this.adminCall<{ deleted: boolean }>(\"users.removeRole\", input),\n\n // Per-user overrides\n listUserOverrides: (user_id: string) =>\n this.adminCall<{ overrides: UserFieldOverride[] }>(\"users.listOverrides\", { user_id }),\n setUserOverride: (input: { user_id: string; curated_field_id: number; access: \"grant\" | \"deny\" }) =>\n this.adminCall<{ override: UserFieldOverride }>(\"users.setOverride\", input),\n removeUserOverride: (input: { user_id: string; curated_field_id: number }) =>\n this.adminCall<{ deleted: boolean }>(\"users.removeOverride\", input),\n };\n}\n\nexport class SemanticLayerError extends Error {\n code: string;\n constructor(message: string, code: string) {\n super(message);\n this.name = \"SemanticLayerError\";\n this.code = code;\n }\n}\n"]}
|