@ezcoder.dev/sdk 1.1.0 → 1.3.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.
Files changed (44) hide show
  1. package/dist/{DatabaseProvider-DalP-KHC.d.ts → DatabaseProvider-DaBP5XUs.d.ts} +16 -2
  2. package/dist/analytics/index.js +7 -5
  3. package/dist/analytics/index.js.map +1 -1
  4. package/dist/auth/index.d.ts +1 -0
  5. package/dist/auth/index.js +25 -6
  6. package/dist/auth/index.js.map +1 -1
  7. package/dist/chunk-CQKYANAW.js +44 -0
  8. package/dist/chunk-CQKYANAW.js.map +1 -0
  9. package/dist/{chunk-GPF4AYNG.js → chunk-HJ2EIZ4S.js} +2 -2
  10. package/dist/{chunk-2WG4O4J2.js → chunk-I2YGB7Z6.js} +14 -50
  11. package/dist/chunk-I2YGB7Z6.js.map +1 -0
  12. package/dist/chunk-LIUE7M7K.js +72 -0
  13. package/dist/chunk-LIUE7M7K.js.map +1 -0
  14. package/dist/{chunk-AWU47M6N.js → chunk-QHB7LGCA.js} +91 -8
  15. package/dist/chunk-QHB7LGCA.js.map +1 -0
  16. package/dist/{chunk-7VGYFCQC.js → chunk-TQC4ROTL.js} +110 -7
  17. package/dist/chunk-TQC4ROTL.js.map +1 -0
  18. package/dist/cms/index.js +4 -2
  19. package/dist/cms/index.js.map +1 -1
  20. package/dist/cron/index.d.ts +17 -0
  21. package/dist/cron/index.js +26 -12
  22. package/dist/cron/index.js.map +1 -1
  23. package/dist/database/index.d.ts +1 -1
  24. package/dist/database/index.js +4 -3
  25. package/dist/email/index.d.ts +18 -0
  26. package/dist/email/index.js +27 -12
  27. package/dist/email/index.js.map +1 -1
  28. package/dist/index.d.ts +168 -2
  29. package/dist/index.js +263 -7
  30. package/dist/index.js.map +1 -1
  31. package/dist/notifications/index.d.ts +27 -2
  32. package/dist/notifications/index.js +53 -18
  33. package/dist/notifications/index.js.map +1 -1
  34. package/dist/payments/index.js +6 -4
  35. package/dist/payments/index.js.map +1 -1
  36. package/dist/roles/index.js +6 -4
  37. package/dist/roles/index.js.map +1 -1
  38. package/dist/storage/index.js +7 -5
  39. package/dist/storage/index.js.map +1 -1
  40. package/package.json +148 -120
  41. package/dist/chunk-2WG4O4J2.js.map +0 -1
  42. package/dist/chunk-7VGYFCQC.js.map +0 -1
  43. package/dist/chunk-AWU47M6N.js.map +0 -1
  44. /package/dist/{chunk-GPF4AYNG.js.map → chunk-HJ2EIZ4S.js.map} +0 -0
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  ezcoder,
3
3
  ezcoderAuthIntegration
4
- } from "./chunk-GPF4AYNG.js";
4
+ } from "./chunk-HJ2EIZ4S.js";
5
5
  import {
6
- env,
7
6
  isSupabaseConfigured,
8
7
  supabase
9
- } from "./chunk-2WG4O4J2.js";
8
+ } from "./chunk-I2YGB7Z6.js";
9
+ import {
10
+ env
11
+ } from "./chunk-LIUE7M7K.js";
10
12
 
11
13
  // src/auth/AuthProvider.tsx
12
14
  import { createContext, useContext, useState, useEffect, useCallback, useRef } from "react";
@@ -26,6 +28,7 @@ var AuthContext = createContext({
26
28
  signInWithProvider: async () => ({ data: null, error: null }),
27
29
  signOut: async () => ({ error: null }),
28
30
  resetPassword: async () => ({ data: null, error: null }),
31
+ updatePassword: async () => ({ data: null, error: null }),
29
32
  updateProfile: async () => ({ data: null, error: null }),
30
33
  refetchProfile: async () => null
31
34
  });
@@ -53,12 +56,37 @@ function AuthProvider({ children }) {
53
56
  return null;
54
57
  }
55
58
  }, []);
59
+ const bindProjectIfNeeded = useCallback(async (currentUser) => {
60
+ if (!currentUser || !env.EZC_PROJECT_ID || !env.EZCODER_API_URL) return;
61
+ const appMeta = currentUser.app_metadata;
62
+ if (appMeta?.bound_project_id) return;
63
+ try {
64
+ const { data } = await supabase.auth.getSession();
65
+ const token = data?.session?.access_token;
66
+ if (!token) return;
67
+ const res = await fetch(`${env.EZCODER_API_URL.replace(/\/$/, "")}/api/managed-auth/bind`, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ ...env.EZC_PROJECT_TOKEN_PUBLIC ? { Authorization: `Bearer ${env.EZC_PROJECT_TOKEN_PUBLIC}` } : {},
72
+ "X-EZC-User-Token": token
73
+ },
74
+ body: JSON.stringify({ projectId: env.EZC_PROJECT_ID })
75
+ });
76
+ const j = await res.json().catch(() => ({}));
77
+ if (j?.changed) {
78
+ await supabase.auth.refreshSession();
79
+ }
80
+ } catch {
81
+ }
82
+ }, []);
56
83
  useEffect(() => {
57
84
  supabase.auth.getSession().then(async ({ data: { session: initialSession } }) => {
58
85
  setSession(initialSession);
59
86
  setUser(initialSession?.user ?? null);
60
87
  if (initialSession?.user) {
61
88
  await fetchProfile(initialSession.user.id);
89
+ bindProjectIfNeeded(initialSession.user);
62
90
  }
63
91
  setLoading(false);
64
92
  });
@@ -69,6 +97,7 @@ function AuthProvider({ children }) {
69
97
  setUser(currentSession?.user ?? null);
70
98
  if (event === "SIGNED_IN" && currentSession?.user) {
71
99
  await fetchProfile(currentSession.user.id);
100
+ bindProjectIfNeeded(currentSession.user);
72
101
  ezcoderAuthIntegration.onLogin(currentSession.user);
73
102
  previousUserIdRef.current = currentSession.user.id;
74
103
  } else if (event === "SIGNED_OUT") {
@@ -84,8 +113,35 @@ function AuthProvider({ children }) {
84
113
  });
85
114
  }
86
115
  });
87
- return () => subscription.unsubscribe();
88
- }, [fetchProfile]);
116
+ const refreshFromExternal = () => {
117
+ supabase.auth.getSession().then(({ data }) => {
118
+ const s = data?.session ?? null;
119
+ setSession(s);
120
+ setUser(s?.user ?? null);
121
+ if (s?.user) {
122
+ fetchProfile(s.user.id);
123
+ bindProjectIfNeeded(s.user);
124
+ }
125
+ });
126
+ };
127
+ const onMsg = (e) => {
128
+ if (e?.data?.type === "ezc-auth-complete") refreshFromExternal();
129
+ };
130
+ const onStorage = (e) => {
131
+ if (e.key && /auth-token/.test(e.key)) refreshFromExternal();
132
+ };
133
+ if (typeof window !== "undefined") {
134
+ window.addEventListener("message", onMsg);
135
+ window.addEventListener("storage", onStorage);
136
+ }
137
+ return () => {
138
+ subscription.unsubscribe();
139
+ if (typeof window !== "undefined") {
140
+ window.removeEventListener("message", onMsg);
141
+ window.removeEventListener("storage", onStorage);
142
+ }
143
+ };
144
+ }, [fetchProfile, bindProjectIfNeeded]);
89
145
  const signUp = useCallback(async (email, password, options = {}) => {
90
146
  if (!isSupabaseConfigured) {
91
147
  return { data: null, error: notConfiguredError() };
@@ -148,10 +204,24 @@ function AuthProvider({ children }) {
148
204
  return { data: null, error: notConfiguredError() };
149
205
  }
150
206
  try {
207
+ const provider2 = provider;
208
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
209
+ const framed = typeof window !== "undefined" && window.self !== window.top;
210
+ if (framed && typeof window !== "undefined") {
211
+ const redirectTo = options.redirectTo || (origin ? `${origin}/auth/callback` : "");
212
+ const result2 = await supabase.auth.signInWithOAuth({
213
+ provider: provider2,
214
+ options: { redirectTo, skipBrowserRedirect: true, ...options }
215
+ });
216
+ if (result2?.error) return { data: null, error: result2.error };
217
+ const url = result2?.data?.url;
218
+ if (url) window.open(url, "ezc-auth", "popup,width=500,height=680");
219
+ return { data: result2?.data ?? null, error: null };
220
+ }
151
221
  const result = await supabase.auth.signInWithOAuth({
152
- provider,
222
+ provider: provider2,
153
223
  options: {
154
- redirectTo: options.redirectTo || (typeof window !== "undefined" ? window.location.origin : ""),
224
+ redirectTo: options.redirectTo || origin,
155
225
  ...options
156
226
  }
157
227
  });
@@ -185,6 +255,18 @@ function AuthProvider({ children }) {
185
255
  return { data: null, error: err instanceof Error ? err : new Error("Password reset failed") };
186
256
  }
187
257
  }, []);
258
+ const updatePassword = useCallback(async (newPassword) => {
259
+ if (!isSupabaseConfigured) {
260
+ return { data: null, error: notConfiguredError() };
261
+ }
262
+ try {
263
+ const result = await supabase.auth.updateUser({ password: newPassword });
264
+ const { data, error } = result || { data: null, error: new Error("Password update failed") };
265
+ return { data, error };
266
+ } catch (err) {
267
+ return { data: null, error: err instanceof Error ? err : new Error("Password update failed") };
268
+ }
269
+ }, []);
188
270
  const updateProfile = useCallback(
189
271
  async (updates) => {
190
272
  if (!user) {
@@ -215,6 +297,7 @@ function AuthProvider({ children }) {
215
297
  signInWithProvider,
216
298
  signOut,
217
299
  resetPassword,
300
+ updatePassword,
218
301
  updateProfile,
219
302
  refetchProfile: fetchProfile
220
303
  };
@@ -233,4 +316,4 @@ export {
233
316
  AuthProvider,
234
317
  useAuth
235
318
  };
236
- //# sourceMappingURL=chunk-AWU47M6N.js.map
319
+ //# sourceMappingURL=chunk-QHB7LGCA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/AuthProvider.tsx"],"sourcesContent":["import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';\r\nimport type { User, Session } from '@supabase/supabase-js';\r\nimport { supabase, isSupabaseConfigured } from '../core/supabase';\r\nimport { env } from '../core/config';\r\nimport { ezcoder, ezcoderAuthIntegration } from '../core/platform';\r\nimport type { UserProfile } from '../core/types';\r\n\r\ninterface AuthResult<T = unknown> {\r\n data: T | null;\r\n error: Error | null;\r\n}\r\n\r\ninterface SignUpOptions {\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\ninterface SignInWithProviderOptions {\r\n redirectTo?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface AuthContextType {\r\n user: User | null;\r\n profile: UserProfile | null;\r\n session: Session | null;\r\n loading: boolean;\r\n isConfigured: boolean;\r\n signUp: (email: string, password: string, options?: SignUpOptions) => Promise<AuthResult>;\r\n signIn: (email: string, password: string) => Promise<AuthResult>;\r\n signInWithProvider: (provider: string, options?: SignInWithProviderOptions) => Promise<AuthResult>;\r\n signOut: () => Promise<{ error: Error | null }>;\r\n resetPassword: (email: string) => Promise<AuthResult>;\r\n updatePassword: (newPassword: string) => Promise<AuthResult>;\r\n updateProfile: (updates: Partial<UserProfile>) => Promise<AuthResult>;\r\n refetchProfile: (userId: string) => Promise<UserProfile | null>;\r\n}\r\n\r\nconst NOT_CONFIGURED_MSG = 'Authentication is not configured. Ensure EzCoder platform credentials are injected, or connect your own Supabase database in Settings → Databases.';\r\nfunction notConfiguredError() {\r\n return new Error(NOT_CONFIGURED_MSG);\r\n}\r\n\r\nexport const AuthContext = createContext<AuthContextType>({\r\n user: null,\r\n profile: null,\r\n session: null,\r\n loading: true,\r\n isConfigured: false,\r\n signUp: async () => ({ data: null, error: null }),\r\n signIn: async () => ({ data: null, error: null }),\r\n signInWithProvider: async () => ({ data: null, error: null }),\r\n signOut: async () => ({ error: null }),\r\n resetPassword: async () => ({ data: null, error: null }),\r\n updatePassword: async () => ({ data: null, error: null }),\r\n updateProfile: async () => ({ data: null, error: null }),\r\n refetchProfile: async () => null,\r\n});\r\n\r\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\r\n const [user, setUser] = useState<User | null>(null);\r\n const [profile, setProfile] = useState<UserProfile | null>(null);\r\n const [session, setSession] = useState<Session | null>(null);\r\n const [loading, setLoading] = useState<boolean>(true);\r\n const previousUserIdRef = useRef<string | null>(null);\r\n\r\n const fetchProfile = useCallback(async (userId: string): Promise<UserProfile | null> => {\r\n if (!userId) return null;\r\n\r\n try {\r\n let query = supabase\r\n .from('user_profiles')\r\n .select('*')\r\n .eq('id', userId);\r\n\r\n if (env.EZC_PROJECT_ID) {\r\n query = query.eq('project_id', env.EZC_PROJECT_ID);\r\n }\r\n\r\n const result = await query.single();\r\n\r\n const { data, error } = result || { data: null, error: null };\r\n\r\n if (error) {\r\n return null;\r\n }\r\n\r\n setProfile(data as UserProfile);\r\n return data as UserProfile;\r\n } catch {\r\n return null;\r\n }\r\n }, []);\r\n\r\n // SEC-1: bind the end-user to THIS project server-side (authoritative; app_metadata\r\n // is service-role-only). OAuth users have no signup metadata so the migration-226\r\n // trigger can't bind them — this is what stops migration 227 from locking them out.\r\n // No-ops if already bound. Non-blocking.\r\n const bindProjectIfNeeded = useCallback(async (currentUser: User | null): Promise<void> => {\r\n if (!currentUser || !env.EZC_PROJECT_ID || !env.EZCODER_API_URL) return;\r\n const appMeta = currentUser.app_metadata as Record<string, unknown> | undefined;\r\n if (appMeta?.bound_project_id) return;\r\n try {\r\n const { data } = await supabase.auth.getSession();\r\n const token = data?.session?.access_token;\r\n if (!token) return;\r\n const res = await fetch(`${env.EZCODER_API_URL.replace(/\\/$/, '')}/api/managed-auth/bind`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...(env.EZC_PROJECT_TOKEN_PUBLIC ? { Authorization: `Bearer ${env.EZC_PROJECT_TOKEN_PUBLIC}` } : {}),\r\n 'X-EZC-User-Token': token,\r\n },\r\n body: JSON.stringify({ projectId: env.EZC_PROJECT_ID }),\r\n });\r\n const j = await res.json().catch(() => ({}));\r\n if (j?.changed) {\r\n // The new bound_project_id claim only appears after a token refresh.\r\n await supabase.auth.refreshSession();\r\n }\r\n } catch {\r\n // non-blocking — login still works; binding reconciles on next load / via backfill\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n supabase.auth.getSession().then(async ({ data: { session: initialSession } }) => {\r\n setSession(initialSession);\r\n setUser(initialSession?.user ?? null);\r\n\r\n if (initialSession?.user) {\r\n await fetchProfile(initialSession.user.id);\r\n bindProjectIfNeeded(initialSession.user);\r\n }\r\n\r\n setLoading(false);\r\n });\r\n\r\n const {\r\n data: { subscription },\r\n } = supabase.auth.onAuthStateChange(async (event, currentSession) => {\r\n setSession(currentSession);\r\n setUser(currentSession?.user ?? null);\r\n\r\n if (event === 'SIGNED_IN' && currentSession?.user) {\r\n await fetchProfile(currentSession.user.id);\r\n bindProjectIfNeeded(currentSession.user);\r\n ezcoderAuthIntegration.onLogin(currentSession.user);\r\n previousUserIdRef.current = currentSession.user.id;\r\n } else if (event === 'SIGNED_OUT') {\r\n setProfile(null);\r\n if (previousUserIdRef.current) {\r\n ezcoderAuthIntegration.onLogout(previousUserIdRef.current);\r\n previousUserIdRef.current = null;\r\n }\r\n } else if (event === 'USER_UPDATED' && currentSession?.user) {\r\n ezcoder.analytics.identify(currentSession.user.id, {\r\n email: currentSession.user.email,\r\n name: currentSession.user.user_metadata?.full_name,\r\n });\r\n }\r\n });\r\n\r\n // In-editor popup sign-in: the /auth/callback popup postMessages on completion;\r\n // the storage event is a same-origin fallback for the popup's localStorage write.\r\n const refreshFromExternal = () => {\r\n supabase.auth.getSession().then(({ data }) => {\r\n const s = data?.session ?? null;\r\n setSession(s);\r\n setUser(s?.user ?? null);\r\n if (s?.user) { fetchProfile(s.user.id); bindProjectIfNeeded(s.user); }\r\n });\r\n };\r\n const onMsg = (e: MessageEvent) => { if (e?.data?.type === 'ezc-auth-complete') refreshFromExternal(); };\r\n const onStorage = (e: StorageEvent) => { if (e.key && /auth-token/.test(e.key)) refreshFromExternal(); };\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('message', onMsg);\r\n window.addEventListener('storage', onStorage);\r\n }\r\n\r\n return () => {\r\n subscription.unsubscribe();\r\n if (typeof window !== 'undefined') {\r\n window.removeEventListener('message', onMsg);\r\n window.removeEventListener('storage', onStorage);\r\n }\r\n };\r\n }, [fetchProfile, bindProjectIfNeeded]);\r\n\r\n const signUp = useCallback(async (email: string, password: string, options: SignUpOptions = {}): Promise<AuthResult> => {\r\n if (!isSupabaseConfigured) {\r\n return { data: null, error: notConfiguredError() };\r\n }\r\n const { metadata = {} } = options;\r\n\r\n try {\r\n const signUpMetadata = { ...metadata };\r\n if (env.EZC_PROJECT_ID) {\r\n signUpMetadata.project_id = env.EZC_PROJECT_ID;\r\n signUpMetadata.bound_project_id = env.EZC_PROJECT_ID;\r\n }\r\n\r\n const result = await supabase.auth.signUp({\r\n email,\r\n password,\r\n options: { data: signUpMetadata },\r\n });\r\n\r\n const { data, error } = result || { data: null, error: new Error('Sign up failed') };\r\n\r\n if (error) {\r\n return { data: null, error };\r\n }\r\n\r\n if (data?.user) {\r\n const profileData: Record<string, unknown> = {\r\n id: data.user.id,\r\n email,\r\n };\r\n if (metadata.display_name) {\r\n profileData.display_name = metadata.display_name;\r\n }\r\n if (env.EZC_PROJECT_ID) {\r\n profileData.project_id = env.EZC_PROJECT_ID;\r\n }\r\n if (metadata.display_name || env.EZC_PROJECT_ID) {\r\n await supabase.from('user_profiles').upsert(profileData);\r\n }\r\n try {\r\n await supabase.rpc('assign_default_role', { user_uuid: data.user.id });\r\n } catch {\r\n // Non-blocking — role tables may not exist in all projects\r\n }\r\n ezcoderAuthIntegration.onSignup(data.user);\r\n }\r\n\r\n return { data, error: null };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('Sign up failed') };\r\n }\r\n }, []);\r\n\r\n const signIn = useCallback(async (email: string, password: string): Promise<AuthResult> => {\r\n if (!isSupabaseConfigured) {\r\n return { data: null, error: notConfiguredError() };\r\n }\r\n try {\r\n const result = await supabase.auth.signInWithPassword({ email, password });\r\n const { data, error } = result || { data: null, error: new Error('Sign in failed') };\r\n return { data, error };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('Sign in failed') };\r\n }\r\n }, []);\r\n\r\n const signInWithProvider = useCallback(async (provider: string, options: SignInWithProviderOptions = {}): Promise<AuthResult> => {\r\n if (!isSupabaseConfigured) {\r\n return { data: null, error: notConfiguredError() };\r\n }\r\n try {\r\n const provider2 = provider as 'google' | 'github' | 'facebook' | 'apple' | 'twitter';\r\n const origin = typeof window !== 'undefined' ? window.location.origin : '';\r\n const framed = typeof window !== 'undefined' && window.self !== window.top;\r\n\r\n if (framed && typeof window !== 'undefined') {\r\n // Provider login pages refuse to render inside an iframe (X-Frame-Options /\r\n // frame-ancestors), so we cannot do a full-page redirect in the editor preview.\r\n // Open the flow in a popup; the /auth/callback page completes PKCE and\r\n // postMessages back (storage event is the fallback). Land on the callback route.\r\n const redirectTo = options.redirectTo || (origin ? `${origin}/auth/callback` : '');\r\n const result = await supabase.auth.signInWithOAuth({\r\n provider: provider2,\r\n options: { redirectTo, skipBrowserRedirect: true, ...options },\r\n });\r\n if (result?.error) return { data: null, error: result.error };\r\n const url = (result?.data as { url?: string } | null)?.url;\r\n if (url) window.open(url, 'ezc-auth', 'popup,width=500,height=680');\r\n return { data: result?.data ?? null, error: null };\r\n }\r\n\r\n // Standalone (deployed app / own tab): full-page redirect. Default to the app\r\n // origin (root) so apps without a scaffolded callback route still complete via\r\n // detectSessionInUrl; new apps pass /auth/callback explicitly.\r\n const result = await supabase.auth.signInWithOAuth({\r\n provider: provider2,\r\n options: {\r\n redirectTo: options.redirectTo || origin,\r\n ...options,\r\n },\r\n });\r\n const { data, error } = result || { data: null, error: new Error('OAuth sign in failed') };\r\n return { data, error };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('OAuth sign in failed') };\r\n }\r\n }, []);\r\n\r\n const signOut = useCallback(async (): Promise<{ error: Error | null }> => {\r\n const userId = user?.id;\r\n try {\r\n const result = await supabase.auth.signOut();\r\n const { error } = result || { error: null };\r\n if (!error && userId) {\r\n ezcoder.users.trackLogout(userId);\r\n }\r\n return { error };\r\n } catch (err: unknown) {\r\n return { error: err instanceof Error ? err : new Error('Sign out failed') };\r\n }\r\n }, [user]);\r\n\r\n const resetPassword = useCallback(async (email: string): Promise<AuthResult> => {\r\n try {\r\n const result = await supabase.auth.resetPasswordForEmail(email, {\r\n redirectTo: `${typeof window !== 'undefined' ? window.location.origin : ''}/auth/reset-password`,\r\n });\r\n const { data, error } = result || { data: null, error: new Error('Password reset failed') };\r\n return { data, error };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('Password reset failed') };\r\n }\r\n }, []);\r\n\r\n // Set a new password. Used on the /auth/reset-password landing page, where the\r\n // recovery link has already established a (recovery) session via detectSessionInUrl.\r\n const updatePassword = useCallback(async (newPassword: string): Promise<AuthResult> => {\r\n if (!isSupabaseConfigured) {\r\n return { data: null, error: notConfiguredError() };\r\n }\r\n try {\r\n const result = await supabase.auth.updateUser({ password: newPassword });\r\n const { data, error } = result || { data: null, error: new Error('Password update failed') };\r\n return { data, error };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('Password update failed') };\r\n }\r\n }, []);\r\n\r\n const updateProfile = useCallback(\r\n async (updates: Partial<UserProfile>): Promise<AuthResult> => {\r\n if (!user) {\r\n return { data: null, error: new Error('Not authenticated') };\r\n }\r\n try {\r\n let query = supabase\r\n .from('user_profiles')\r\n .update(updates)\r\n .eq('id', user.id);\r\n\r\n if (env.EZC_PROJECT_ID) {\r\n query = query.eq('project_id', env.EZC_PROJECT_ID);\r\n }\r\n\r\n const result = await query.select().single();\r\n const { data, error } = result || { data: null, error: null };\r\n return { data, error };\r\n } catch (err: unknown) {\r\n return { data: null, error: err instanceof Error ? err : new Error('Update failed') };\r\n }\r\n },\r\n [user]\r\n );\r\n\r\n const value: AuthContextType = {\r\n user,\r\n profile,\r\n session,\r\n loading,\r\n isConfigured: isSupabaseConfigured,\r\n signUp,\r\n signIn,\r\n signInWithProvider,\r\n signOut,\r\n resetPassword,\r\n updatePassword,\r\n updateProfile,\r\n refetchProfile: fetchProfile,\r\n };\r\n\r\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\r\n}\r\n\r\nexport function useAuth(): AuthContextType {\r\n const context = useContext(AuthContext);\r\n if (context === undefined) {\r\n throw new Error('useAuth must be used within an AuthProvider');\r\n }\r\n return context;\r\n}\r\n\r\nexport type { AuthResult, SignUpOptions, SignInWithProviderOptions };\r\n"],"mappings":"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","names":["result"]}
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  ezcoder
3
- } from "./chunk-GPF4AYNG.js";
3
+ } from "./chunk-HJ2EIZ4S.js";
4
4
  import {
5
- env,
6
- features,
7
5
  isSupabaseConfigured,
8
6
  supabase
9
- } from "./chunk-2WG4O4J2.js";
7
+ } from "./chunk-I2YGB7Z6.js";
8
+ import {
9
+ env,
10
+ features
11
+ } from "./chunk-LIUE7M7K.js";
10
12
 
11
13
  // src/database/DatabaseProvider.tsx
12
14
  import { createContext, useContext, useMemo } from "react";
@@ -54,6 +56,55 @@ function mapRpcError(rpcResult) {
54
56
  return new QueryError(msg);
55
57
  }
56
58
 
59
+ // src/database/publicReadProxy.ts
60
+ var PROXY_TIMEOUT_MS = 1e4;
61
+ var AUTH_FAILURE_PATTERN = /authentication required|permission denied|project binding|unauthorized|jwt|42501/i;
62
+ function isAuthShapedFailure(message) {
63
+ return typeof message === "string" && AUTH_FAILURE_PATTERN.test(message);
64
+ }
65
+ function publicReadAvailable() {
66
+ return Boolean(env.EZC_PROJECT_TOKEN_PUBLIC && env.EZCODER_API_URL);
67
+ }
68
+ var stickyPublicMode = false;
69
+ function shouldSkipRpc() {
70
+ return stickyPublicMode && publicReadAvailable();
71
+ }
72
+ async function hasActiveSession(supabase2) {
73
+ try {
74
+ if (!supabase2?.auth?.getSession) return false;
75
+ const { data } = await supabase2.auth.getSession();
76
+ return Boolean(data?.session);
77
+ } catch {
78
+ return true;
79
+ }
80
+ }
81
+ async function publicReadQuery(projectId, sql) {
82
+ if (!publicReadAvailable()) {
83
+ return { success: false, error: "Public read proxy not configured (missing public token or API URL)" };
84
+ }
85
+ try {
86
+ const base = env.EZCODER_API_URL.replace(/\/$/, "");
87
+ const res = await fetch(`${base}/api/platform/db/${encodeURIComponent(projectId)}/query`, {
88
+ method: "POST",
89
+ headers: {
90
+ "Content-Type": "application/json",
91
+ "X-EzCoder-Public-Token": env.EZC_PROJECT_TOKEN_PUBLIC
92
+ },
93
+ body: JSON.stringify({ sql }),
94
+ signal: typeof AbortSignal !== "undefined" && AbortSignal.timeout ? AbortSignal.timeout(PROXY_TIMEOUT_MS) : void 0
95
+ });
96
+ const payload = await res.json().catch(() => null);
97
+ if (!res.ok || !payload) {
98
+ return { success: false, error: payload?.error || `Public read proxy HTTP ${res.status}` };
99
+ }
100
+ if (payload.success) stickyPublicMode = true;
101
+ return payload;
102
+ } catch (err) {
103
+ const message = err instanceof Error ? err.message : "Public read proxy request failed";
104
+ return { success: false, error: message };
105
+ }
106
+ }
107
+
57
108
  // src/database/QueryBuilder.ts
58
109
  function escapeSqlValue(value) {
59
110
  if (value === null) return "NULL";
@@ -276,7 +327,7 @@ var QueryBuilder = class {
276
327
  default:
277
328
  throw new QueryError(`Unknown operation: ${this.operation}`);
278
329
  }
279
- if (this.serverProxy) {
330
+ if (this.serverProxy?.serverQuery && this.serverProxy?.serverExec) {
280
331
  const fn = isRead ? this.serverProxy.serverQuery : this.serverProxy.serverExec;
281
332
  const data2 = await fn(sql);
282
333
  if (!data2.success) {
@@ -291,15 +342,25 @@ var QueryBuilder = class {
291
342
  schema: data2.schema
292
343
  };
293
344
  }
345
+ const publicQuery = this.serverProxy?.publicQuery;
346
+ if (isRead && publicQuery && shouldSkipRpc() && !await hasActiveSession(this.supabase)) {
347
+ return this.runPublicQuery(publicQuery, sql);
348
+ }
294
349
  const rpcName = isRead ? "sdk_query_project" : "sdk_exec_project";
295
350
  const { data, error } = await this.supabase.rpc(rpcName, {
296
351
  p_project_id: this.projectId,
297
352
  p_sql: sql
298
353
  });
299
354
  if (error) {
355
+ if (isRead && publicQuery && isAuthShapedFailure(error.message) && !await hasActiveSession(this.supabase)) {
356
+ return this.runPublicQuery(publicQuery, sql, error.message);
357
+ }
300
358
  throw new QueryError(error.message, sql);
301
359
  }
302
360
  if (!data.success) {
361
+ if (isRead && publicQuery && isAuthShapedFailure(data.error) && !await hasActiveSession(this.supabase)) {
362
+ return this.runPublicQuery(publicQuery, sql, data.error);
363
+ }
303
364
  const dbError = mapRpcError(data);
304
365
  if (dbError) throw dbError;
305
366
  throw new QueryError(data.error || "Query failed", sql);
@@ -319,6 +380,21 @@ var QueryBuilder = class {
319
380
  schema: data.schema
320
381
  };
321
382
  }
383
+ /** Execute a read through the platform public proxy (anonymous-visitor path). */
384
+ async runPublicQuery(publicQuery, sql, rpcError) {
385
+ const data = await publicQuery(sql);
386
+ if (!data.success) {
387
+ const dbError = mapRpcError(data);
388
+ if (dbError) throw dbError;
389
+ throw new QueryError(data.error || rpcError || "Query failed", sql);
390
+ }
391
+ return {
392
+ success: true,
393
+ data: data.data || [],
394
+ rowCount: data.rowCount ?? (data.data?.length || 0),
395
+ schema: data.schema
396
+ };
397
+ }
322
398
  then(onfulfilled, onrejected) {
323
399
  const promise = this.executeQuery().then((result) => {
324
400
  if (this.singleMode) {
@@ -370,7 +446,9 @@ var DatabaseClient = class _DatabaseClient {
370
446
  serverExec: (sql) => this._serverRequest("/execute", sql)
371
447
  });
372
448
  }
373
- return new QueryBuilder(this.supabase, this.projectId, table);
449
+ return new QueryBuilder(this.supabase, this.projectId, table, {
450
+ publicQuery: (sql) => publicReadQuery(this.projectId, sql)
451
+ });
374
452
  }
375
453
  async sql(query) {
376
454
  const trimmed = query.trim().toLowerCase();
@@ -381,16 +459,25 @@ var DatabaseClient = class _DatabaseClient {
381
459
  return this._serverQuery(query);
382
460
  }
383
461
  const start = Date.now();
462
+ if (shouldSkipRpc() && !await hasActiveSession(this.supabase)) {
463
+ return this._publicFallback(query, start, null);
464
+ }
384
465
  const { data, error } = await this.supabase.rpc("sdk_query_project", {
385
466
  p_project_id: this.projectId,
386
467
  p_sql: query
387
468
  });
388
469
  const latencyMs = Date.now() - start;
389
470
  if (error) {
471
+ if (publicReadAvailable() && isAuthShapedFailure(error.message) && !await hasActiveSession(this.supabase)) {
472
+ return this._publicFallback(query, start, error.message);
473
+ }
390
474
  trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: error.message });
391
475
  throw new QueryError(error.message, query);
392
476
  }
393
477
  if (!data.success) {
478
+ if (publicReadAvailable() && isAuthShapedFailure(data.error) && !await hasActiveSession(this.supabase)) {
479
+ return this._publicFallback(query, start, data.error);
480
+ }
394
481
  trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: data.error });
395
482
  throw new QueryError(data.error || "Query failed", query);
396
483
  }
@@ -402,6 +489,22 @@ var DatabaseClient = class _DatabaseClient {
402
489
  schema: data.schema
403
490
  };
404
491
  }
492
+ /** Read via the platform public proxy (anonymous-visitor path). */
493
+ async _publicFallback(query, start, rpcError) {
494
+ const data = await publicReadQuery(this.projectId, query);
495
+ const latencyMs = Date.now() - start;
496
+ if (!data.success) {
497
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: false, error: data.error, publicProxy: true });
498
+ throw new QueryError(data.error || rpcError || "Query failed", query);
499
+ }
500
+ trackDbEvent("db_query", { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0, publicProxy: true });
501
+ return {
502
+ success: true,
503
+ data: data.data || [],
504
+ rowCount: data.rowCount ?? (data.data?.length || 0),
505
+ schema: data.schema
506
+ };
507
+ }
405
508
  async execute(sql) {
406
509
  if (this.serverMode) {
407
510
  return this._serverExecute(sql);
@@ -637,4 +740,4 @@ export {
637
740
  useIsDatabaseConfigured,
638
741
  useRealtime
639
742
  };
640
- //# sourceMappingURL=chunk-7VGYFCQC.js.map
743
+ //# sourceMappingURL=chunk-TQC4ROTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/database/DatabaseProvider.tsx","../src/database/errors.ts","../src/database/publicReadProxy.ts","../src/database/QueryBuilder.ts","../src/database/DatabaseClient.ts","../src/database/useRealtime.ts"],"sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react';\r\nimport { supabase, isSupabaseConfigured } from '../core/supabase';\r\nimport { env } from '../core/config';\r\nimport { DatabaseClient } from './DatabaseClient';\r\n\r\ninterface DatabaseContextValue {\r\n db: DatabaseClient | null;\r\n isConfigured: boolean;\r\n}\r\n\r\nconst DatabaseContext = createContext<DatabaseContextValue>({\r\n db: null,\r\n isConfigured: false,\r\n});\r\n\r\ninterface DatabaseProviderProps {\r\n children: ReactNode;\r\n projectId?: string;\r\n}\r\n\r\nexport function DatabaseProvider({ children, projectId }: DatabaseProviderProps) {\r\n const resolvedProjectId = projectId || env.EZC_PROJECT_ID;\r\n const configured = isSupabaseConfigured && Boolean(resolvedProjectId);\r\n\r\n const db = useMemo(\r\n () => (configured ? new DatabaseClient(supabase, resolvedProjectId) : null),\r\n [configured, resolvedProjectId],\r\n );\r\n\r\n const value = useMemo(() => ({ db, isConfigured: configured }), [db, configured]);\r\n\r\n return <DatabaseContext.Provider value={value}>{children}</DatabaseContext.Provider>;\r\n}\r\n\r\nexport function useDatabase(): DatabaseClient {\r\n const { db } = useContext(DatabaseContext);\r\n if (!db) {\r\n throw new Error(\r\n 'useDatabase() must be used within <DatabaseProvider>. ' +\r\n 'Ensure EZC_PROJECT_ID and Supabase credentials are configured.',\r\n );\r\n }\r\n return db;\r\n}\r\n\r\nexport function useDatabaseOptional(): DatabaseClient | null {\r\n const { db } = useContext(DatabaseContext);\r\n return db;\r\n}\r\n\r\nexport function useIsDatabaseConfigured(): boolean {\r\n const { isConfigured } = useContext(DatabaseContext);\r\n return isConfigured;\r\n}\r\n","export class DatabaseError extends Error {\r\n readonly code: string;\r\n readonly statusCode: number;\r\n\r\n constructor(message: string, code = 'DATABASE_ERROR', statusCode = 500) {\r\n super(message);\r\n this.name = 'DatabaseError';\r\n this.code = code;\r\n this.statusCode = statusCode;\r\n }\r\n}\r\n\r\nexport class QueryError extends DatabaseError {\r\n readonly sql?: string;\r\n\r\n constructor(message: string, sql?: string) {\r\n super(message, 'QUERY_ERROR', 400);\r\n this.name = 'QueryError';\r\n this.sql = sql;\r\n }\r\n}\r\n\r\nexport class ValidationError extends DatabaseError {\r\n constructor(message: string) {\r\n super(message, 'VALIDATION_ERROR', 422);\r\n this.name = 'ValidationError';\r\n }\r\n}\r\n\r\nexport class ConnectionError extends DatabaseError {\r\n constructor(message: string) {\r\n super(message, 'CONNECTION_ERROR', 503);\r\n this.name = 'ConnectionError';\r\n }\r\n}\r\n\r\nexport class NotFoundError extends DatabaseError {\r\n constructor(message = 'Record not found') {\r\n super(message, 'NOT_FOUND', 404);\r\n this.name = 'NotFoundError';\r\n }\r\n}\r\n\r\nexport function mapRpcError(rpcResult: { success: boolean; error?: string; code?: string }): DatabaseError | null {\r\n if (rpcResult.success) return null;\r\n const msg = rpcResult.error || 'Unknown database error';\r\n if (msg.includes('No database schema exists')) return new NotFoundError(msg);\r\n if (msg.includes('Authentication required')) return new ConnectionError(msg);\r\n if (msg.includes('blocked') || msg.includes('forbidden')) return new ValidationError(msg);\r\n return new QueryError(msg);\r\n}\r\n","/**\n * Public read proxy — anonymous-visitor data reads for deployed sites.\n *\n * The direct browser path (supabase.rpc('sdk_query_project')) requires an\n * authenticated, project-bound session by design (tenant migrations 200/201/\n * 227: the RPC executes raw SELECTs, so anon was deliberately revoked). That\n * left every data-driven section of a deployed site empty for visitors.\n *\n * This module routes READ-ONLY queries through the platform's DB proxy\n * (`POST {EZCODER_API_URL}/api/platform/db/{projectId}/query`) authenticated\n * with the browser-safe PUBLIC-class project token (EZC_PROJECT_TOKEN_PUBLIC,\n * baked into the bundle at build time). Server-side that proxy runs the same\n * hardened tenant function (SELECT-only, project-schema-scoped, system/auth\n * schemas blocked, LIMIT-capped, rate-limited) plus a credential-table guard\n * for public-class tokens. Writes never use this path.\n */\n\nimport { env } from '../core/config';\n\nexport interface PublicProxyResult {\n success: boolean;\n data?: unknown[];\n rowCount?: number;\n error?: string;\n schema?: string;\n}\n\nconst PROXY_TIMEOUT_MS = 10000;\n\n/** Failures that mean \"the RPC path needs an authenticated session\" — the\n * signal to fall back to the public proxy rather than surface the error. */\nconst AUTH_FAILURE_PATTERN =\n /authentication required|permission denied|project binding|unauthorized|jwt|42501/i;\n\nexport function isAuthShapedFailure(message: string | null | undefined): boolean {\n return typeof message === 'string' && AUTH_FAILURE_PATTERN.test(message);\n}\n\nexport function publicReadAvailable(): boolean {\n return Boolean(env.EZC_PROJECT_TOKEN_PUBLIC && env.EZCODER_API_URL);\n}\n\n// Once a query has fallen back because the visitor has no session, later\n// reads skip the doomed RPC round-trip. Never set unless the proxy SUCCEEDED,\n// so a transient RPC failure can't permanently divert an authenticated user.\nlet stickyPublicMode = false;\n\nexport function shouldSkipRpc(): boolean {\n return stickyPublicMode && publicReadAvailable();\n}\n\n/** Test seam. */\nexport function _resetStickyPublicMode(): void {\n stickyPublicMode = false;\n}\n\n/**\n * True when the supabase client holds an active auth session. The public\n * proxy is for ANONYMOUS visitors only — an authenticated user's reads must\n * always go through the RPC (their session is the authority; security review\n * finding 2026-06-11: an anonymous page load must not divert a subsequently\n * signed-in user to the session-less proxy). Fail-safe: on any error, report\n * a session as PRESENT so the caller refuses the fallback.\n */\nexport async function hasActiveSession(supabase: { auth?: { getSession?: () => Promise<{ data?: { session?: unknown } }> } } | null | undefined): Promise<boolean> {\n try {\n if (!supabase?.auth?.getSession) return false;\n const { data } = await supabase.auth.getSession();\n return Boolean(data?.session);\n } catch {\n return true;\n }\n}\n\nexport async function publicReadQuery(projectId: string, sql: string): Promise<PublicProxyResult> {\n if (!publicReadAvailable()) {\n return { success: false, error: 'Public read proxy not configured (missing public token or API URL)' };\n }\n try {\n const base = env.EZCODER_API_URL.replace(/\\/$/, '');\n const res = await fetch(`${base}/api/platform/db/${encodeURIComponent(projectId)}/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-EzCoder-Public-Token': env.EZC_PROJECT_TOKEN_PUBLIC,\n },\n body: JSON.stringify({ sql }),\n signal: typeof AbortSignal !== 'undefined' && AbortSignal.timeout\n ? AbortSignal.timeout(PROXY_TIMEOUT_MS)\n : undefined,\n });\n const payload = (await res.json().catch(() => null)) as PublicProxyResult | null;\n if (!res.ok || !payload) {\n return { success: false, error: payload?.error || `Public read proxy HTTP ${res.status}` };\n }\n if (payload.success) stickyPublicMode = true;\n return payload;\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Public read proxy request failed';\n return { success: false, error: message };\n }\n}\n","import type { SupabaseClient } from '@supabase/supabase-js';\r\nimport type { DatabaseResult, SingleResult, MutationResult, FilterValue, OrderOption } from './types';\r\nimport { QueryError, ValidationError, mapRpcError } from './errors';\r\nimport { isAuthShapedFailure, shouldSkipRpc, hasActiveSession } from './publicReadProxy';\r\n\r\nfunction escapeSqlValue(value: FilterValue): string {\r\n if (value === null) return 'NULL';\r\n if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE';\r\n if (typeof value === 'number') {\r\n if (!Number.isFinite(value)) throw new ValidationError('Non-finite numbers are not allowed');\r\n return String(value);\r\n }\r\n return `'${String(value).replace(/'/g, \"''\")}'`;\r\n}\r\n\r\nfunction escapeIdentifier(name: string): string {\r\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\r\n throw new ValidationError(`Invalid identifier: ${name}`);\r\n }\r\n return `\"${name}\"`;\r\n}\r\n\r\ntype Operation = 'select' | 'insert' | 'update' | 'delete' | 'count' | 'upsert';\r\n\r\ninterface Filter {\r\n column: string;\r\n op: string;\r\n value: FilterValue | FilterValue[];\r\n}\r\n\r\nexport interface ServerProxy {\r\n serverQuery?: (sql: string) => Promise<{ success: boolean; data?: unknown[]; rowCount?: number; error?: string; schema?: string }>;\r\n serverExec?: (sql: string) => Promise<{ success: boolean; data?: unknown[]; rowCount?: number; error?: string; schema?: string }>;\r\n /** Browser-mode READ fallback: routes through the platform public proxy\r\n * (public-class token) when the direct RPC needs an authenticated session\r\n * the visitor doesn't have. Reads only — writes never fall back. */\r\n publicQuery?: (sql: string) => Promise<{ success: boolean; data?: unknown[]; rowCount?: number; error?: string; schema?: string }>;\r\n}\r\n\r\nexport class QueryBuilder<T = Record<string, unknown>> {\r\n private readonly supabase: SupabaseClient;\r\n private readonly projectId: string;\r\n private readonly table: string;\r\n private readonly serverProxy?: ServerProxy;\r\n\r\n private operation: Operation = 'select';\r\n private selectColumns = '*';\r\n private filters: Filter[] = [];\r\n private orderClauses: string[] = [];\r\n private limitValue: number | null = null;\r\n private offsetValue: number | null = null;\r\n private insertData: Record<string, FilterValue>[] | null = null;\r\n private updateData: Record<string, FilterValue> | null = null;\r\n private upsertConflict: string | null = null;\r\n private singleMode = false;\r\n private maybeSingleMode = false;\r\n\r\n constructor(supabase: SupabaseClient, projectId: string, table: string, serverProxy?: ServerProxy) {\r\n this.supabase = supabase;\r\n this.projectId = projectId;\r\n this.table = table;\r\n this.serverProxy = serverProxy;\r\n }\r\n\r\n select(columns = '*'): this {\r\n this.operation = 'select';\r\n this.selectColumns = columns;\r\n return this;\r\n }\r\n\r\n // --- Filters ---\r\n\r\n eq(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '=', value });\r\n return this;\r\n }\r\n\r\n neq(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '!=', value });\r\n return this;\r\n }\r\n\r\n gt(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '>', value });\r\n return this;\r\n }\r\n\r\n lt(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '<', value });\r\n return this;\r\n }\r\n\r\n gte(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '>=', value });\r\n return this;\r\n }\r\n\r\n lte(column: string, value: FilterValue): this {\r\n this.filters.push({ column, op: '<=', value });\r\n return this;\r\n }\r\n\r\n like(column: string, pattern: string): this {\r\n this.filters.push({ column, op: 'LIKE', value: pattern });\r\n return this;\r\n }\r\n\r\n ilike(column: string, pattern: string): this {\r\n this.filters.push({ column, op: 'ILIKE', value: pattern });\r\n return this;\r\n }\r\n\r\n in(column: string, values: FilterValue[]): this {\r\n this.filters.push({ column, op: 'IN', value: values });\r\n return this;\r\n }\r\n\r\n is(column: string, value: null | boolean): this {\r\n this.filters.push({ column, op: 'IS', value });\r\n return this;\r\n }\r\n\r\n // --- Ordering & Pagination ---\r\n\r\n order(column: string, options: OrderOption = {}): this {\r\n const dir = options.ascending === false ? 'DESC' : 'ASC';\r\n const nulls = options.nullsFirst ? 'NULLS FIRST' : 'NULLS LAST';\r\n this.orderClauses.push(`${escapeIdentifier(column)} ${dir} ${nulls}`);\r\n return this;\r\n }\r\n\r\n limit(count: number): this {\r\n this.limitValue = count;\r\n return this;\r\n }\r\n\r\n offset(count: number): this {\r\n this.offsetValue = count;\r\n return this;\r\n }\r\n\r\n // --- Mutations ---\r\n\r\n insert(records: Record<string, FilterValue> | Record<string, FilterValue>[]): this {\r\n this.operation = 'insert';\r\n this.insertData = Array.isArray(records) ? records : [records];\r\n return this;\r\n }\r\n\r\n update(values: Record<string, FilterValue>): this {\r\n this.operation = 'update';\r\n this.updateData = values;\r\n return this;\r\n }\r\n\r\n delete(): this {\r\n this.operation = 'delete';\r\n return this;\r\n }\r\n\r\n upsert(records: Record<string, FilterValue> | Record<string, FilterValue>[], options?: { onConflict?: string }): this {\r\n this.operation = 'upsert';\r\n this.insertData = Array.isArray(records) ? records : [records];\r\n this.upsertConflict = options?.onConflict || null;\r\n return this;\r\n }\r\n\r\n // --- Result Helpers ---\r\n\r\n count(): QueryBuilder<{ count: number }> {\r\n this.operation = 'count';\r\n return this as unknown as QueryBuilder<{ count: number }>;\r\n }\r\n\r\n single(): QueryBuilder<T> {\r\n this.singleMode = true;\r\n this.limitValue = 1;\r\n return this;\r\n }\r\n\r\n maybeSingle(): QueryBuilder<T> {\r\n this.maybeSingleMode = true;\r\n this.limitValue = 1;\r\n return this;\r\n }\r\n\r\n // --- SQL Building ---\r\n\r\n private buildWhereClause(): string {\r\n if (this.filters.length === 0) return '';\r\n const conditions = this.filters.map((f) => {\r\n const col = escapeIdentifier(f.column);\r\n if (f.op === 'IS') {\r\n const val = f.value === null ? 'NULL' : f.value ? 'TRUE' : 'FALSE';\r\n return `${col} IS ${val}`;\r\n }\r\n if (f.op === 'IN') {\r\n const vals = (f.value as FilterValue[]).map(escapeSqlValue).join(', ');\r\n return `${col} IN (${vals})`;\r\n }\r\n return `${col} ${f.op} ${escapeSqlValue(f.value as FilterValue)}`;\r\n });\r\n return ` WHERE ${conditions.join(' AND ')}`;\r\n }\r\n\r\n private buildSelectSql(): string {\r\n let sql = `SELECT ${this.selectColumns} FROM \"${this.table}\"`;\r\n sql += this.buildWhereClause();\r\n if (this.orderClauses.length > 0) sql += ` ORDER BY ${this.orderClauses.join(', ')}`;\r\n if (this.limitValue !== null) sql += ` LIMIT ${this.limitValue}`;\r\n if (this.offsetValue !== null && this.offsetValue > 0) sql += ` OFFSET ${this.offsetValue}`;\r\n return sql;\r\n }\r\n\r\n private buildCountSql(): string {\r\n let sql = `SELECT COUNT(*) as count FROM \"${this.table}\"`;\r\n sql += this.buildWhereClause();\r\n return sql;\r\n }\r\n\r\n private buildInsertSql(): string {\r\n if (!this.insertData || this.insertData.length === 0) {\r\n throw new ValidationError('No data provided for insert');\r\n }\r\n const columns = Object.keys(this.insertData[0]);\r\n const colList = columns.map(escapeIdentifier).join(', ');\r\n const rows = this.insertData.map(\r\n (row) => `(${columns.map((c) => escapeSqlValue(row[c] ?? null)).join(', ')})`\r\n );\r\n return `INSERT INTO \"${this.table}\" (${colList}) VALUES ${rows.join(', ')}`;\r\n }\r\n\r\n private buildUpsertSql(): string {\r\n let sql = this.buildInsertSql();\r\n const conflict = this.upsertConflict || 'id';\r\n const columns = Object.keys(this.insertData![0]);\r\n const updateCols = columns\r\n .filter((c) => c !== conflict)\r\n .map((c) => `${escapeIdentifier(c)} = EXCLUDED.${escapeIdentifier(c)}`)\r\n .join(', ');\r\n sql += ` ON CONFLICT (${escapeIdentifier(conflict)}) DO UPDATE SET ${updateCols}`;\r\n return sql;\r\n }\r\n\r\n private buildUpdateSql(): string {\r\n if (!this.updateData || Object.keys(this.updateData).length === 0) {\r\n throw new ValidationError('No data provided for update');\r\n }\r\n if (this.filters.length === 0) {\r\n throw new ValidationError('UPDATE without filters is not allowed — add .eq() or other filters');\r\n }\r\n const setClauses = Object.entries(this.updateData)\r\n .map(([col, val]) => `${escapeIdentifier(col)} = ${escapeSqlValue(val)}`)\r\n .join(', ');\r\n return `UPDATE \"${this.table}\" SET ${setClauses}${this.buildWhereClause()}`;\r\n }\r\n\r\n private buildDeleteSql(): string {\r\n if (this.filters.length === 0) {\r\n throw new ValidationError('DELETE without filters is not allowed — add .eq() or other filters');\r\n }\r\n return `DELETE FROM \"${this.table}\"${this.buildWhereClause()}`;\r\n }\r\n\r\n // --- Execution ---\r\n\r\n private async executeQuery(): Promise<DatabaseResult<T>> {\r\n const isRead = this.operation === 'select' || this.operation === 'count';\r\n\r\n let sql: string;\r\n switch (this.operation) {\r\n case 'select': sql = this.buildSelectSql(); break;\r\n case 'count': sql = this.buildCountSql(); break;\r\n case 'insert': sql = this.buildInsertSql(); break;\r\n case 'upsert': sql = this.buildUpsertSql(); break;\r\n case 'update': sql = this.buildUpdateSql(); break;\r\n case 'delete': sql = this.buildDeleteSql(); break;\r\n default: throw new QueryError(`Unknown operation: ${this.operation}`);\r\n }\r\n\r\n // Server mode (secret-key proxy): full read/write via the platform.\r\n if (this.serverProxy?.serverQuery && this.serverProxy?.serverExec) {\r\n const fn = isRead ? this.serverProxy.serverQuery : this.serverProxy.serverExec;\r\n const data = await fn(sql);\r\n if (!data.success) {\r\n const dbError = mapRpcError(data);\r\n if (dbError) throw dbError;\r\n throw new QueryError(data.error || 'Query failed', sql);\r\n }\r\n return {\r\n success: true,\r\n data: (isRead ? data.data || [] : []) as T[],\r\n rowCount: data.rowCount ?? (isRead ? (data.data?.length || 0) : 0),\r\n schema: data.schema,\r\n };\r\n }\r\n\r\n // Browser mode, anonymous visitors ONLY: skip the doomed RPC once a read\r\n // has already fallen back to the public proxy. A signed-in user always\r\n // goes through the RPC — their session is the authority.\r\n const publicQuery = this.serverProxy?.publicQuery;\r\n if (isRead && publicQuery && shouldSkipRpc() && !(await hasActiveSession(this.supabase))) {\r\n return this.runPublicQuery(publicQuery, sql);\r\n }\r\n\r\n const rpcName = isRead ? 'sdk_query_project' : 'sdk_exec_project';\r\n const { data, error } = await this.supabase.rpc(rpcName, {\r\n p_project_id: this.projectId,\r\n p_sql: sql,\r\n });\r\n\r\n if (error) {\r\n if (isRead && publicQuery && isAuthShapedFailure(error.message) && !(await hasActiveSession(this.supabase))) {\r\n return this.runPublicQuery(publicQuery, sql, error.message);\r\n }\r\n throw new QueryError(error.message, sql);\r\n }\r\n\r\n if (!data.success) {\r\n if (isRead && publicQuery && isAuthShapedFailure(data.error) && !(await hasActiveSession(this.supabase))) {\r\n return this.runPublicQuery(publicQuery, sql, data.error);\r\n }\r\n const dbError = mapRpcError(data);\r\n if (dbError) throw dbError;\r\n throw new QueryError(data.error || 'Query failed', sql);\r\n }\r\n\r\n if (isRead) {\r\n return {\r\n success: true,\r\n data: data.data || [],\r\n rowCount: data.rowCount ?? (data.data?.length || 0),\r\n schema: data.schema,\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n data: [],\r\n rowCount: data.rowCount ?? 0,\r\n schema: data.schema,\r\n };\r\n }\r\n\r\n /** Execute a read through the platform public proxy (anonymous-visitor path). */\r\n private async runPublicQuery(\r\n publicQuery: NonNullable<ServerProxy['publicQuery']>,\r\n sql: string,\r\n rpcError?: string,\r\n ): Promise<DatabaseResult<T>> {\r\n const data = await publicQuery(sql);\r\n if (!data.success) {\r\n const dbError = mapRpcError(data);\r\n if (dbError) throw dbError;\r\n throw new QueryError(data.error || rpcError || 'Query failed', sql);\r\n }\r\n return {\r\n success: true,\r\n data: (data.data || []) as T[],\r\n rowCount: data.rowCount ?? (data.data?.length || 0),\r\n schema: data.schema,\r\n };\r\n }\r\n\r\n then<TResult1 = DatabaseResult<T> | SingleResult<T>, TResult2 = never>(\r\n onfulfilled?: ((value: DatabaseResult<T> | SingleResult<T>) => TResult1 | PromiseLike<TResult1>) | null,\r\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\r\n ): Promise<TResult1 | TResult2> {\r\n const promise = this.executeQuery().then((result) => {\r\n if (this.singleMode) {\r\n if (result.data.length === 0) {\r\n throw new QueryError('Expected exactly one row, got 0');\r\n }\r\n return { success: true, data: result.data[0], error: undefined } as SingleResult<T>;\r\n }\r\n if (this.maybeSingleMode) {\r\n return {\r\n success: true,\r\n data: result.data.length > 0 ? result.data[0] : null,\r\n error: undefined,\r\n } as SingleResult<T>;\r\n }\r\n return result;\r\n });\r\n return promise.then(onfulfilled, onrejected);\r\n }\r\n}\r\n","import type { SupabaseClient } from '@supabase/supabase-js';\nimport type { DatabaseResult, MutationResult, SchemaInfo } from './types';\nimport { QueryBuilder, type ServerProxy } from './QueryBuilder';\nimport { QueryError, ConnectionError } from './errors';\nimport { ezcoder } from '../core/platform';\nimport { features } from '../core/config';\nimport { publicReadQuery, publicReadAvailable, isAuthShapedFailure, shouldSkipRpc, hasActiveSession } from './publicReadProxy';\n\nfunction trackDbEvent(event: string, props: Record<string, unknown>) {\n if (!features.analytics) return;\n try { ezcoder.analytics.track(event, props); } catch { /* non-critical */ }\n}\n\ninterface ServerProxyResult {\n success: boolean;\n data?: unknown[];\n rowCount?: number;\n error?: string;\n schema?: string;\n exists?: boolean;\n tables?: unknown[];\n tablesCount?: number;\n createdAt?: string;\n lastAccessedAt?: string;\n executed_sql?: string;\n}\n\nconst MAX_RETRIES = 3;\nconst SERVER_TIMEOUT_MS = 5000;\n\nexport class DatabaseClient {\n private readonly supabase: SupabaseClient | null;\n private readonly projectId: string;\n private apiKey?: string;\n private platformUrl?: string;\n private serverMode: boolean = false;\n\n constructor(supabase: SupabaseClient | null, projectId: string) {\n this.supabase = supabase;\n this.projectId = projectId;\n }\n\n static createServerClient(\n projectId: string,\n apiKey: string,\n platformUrl?: string\n ): DatabaseClient {\n const client = new DatabaseClient(null, projectId);\n client.apiKey = apiKey;\n client.platformUrl = platformUrl\n || (typeof process !== 'undefined' && process.env?.EZCODER_API_URL)\n || 'https://ezcoder.app';\n client.serverMode = true;\n return client;\n }\n\n from<T = Record<string, unknown>>(table: string): QueryBuilder<T> {\n if (this.serverMode) {\n return new QueryBuilder<T>(null as unknown as SupabaseClient, this.projectId, table, {\n serverQuery: (sql: string) => this._serverRequest('/query', sql),\n serverExec: (sql: string) => this._serverRequest('/execute', sql),\n });\n }\n // Browser mode: reads get the public-proxy fallback so anonymous visitors\n // on deployed sites see data (the direct RPC requires a signed-in,\n // project-bound session). Writes intentionally have no fallback.\n return new QueryBuilder<T>(this.supabase!, this.projectId, table, {\n publicQuery: (sql: string) => publicReadQuery(this.projectId, sql),\n });\n }\n\n async sql<T = Record<string, unknown>>(query: string): Promise<DatabaseResult<T>> {\n const trimmed = query.trim().toLowerCase();\n if (!trimmed.startsWith('select')) {\n throw new QueryError('sql() is for read-only queries. Use execute() for mutations.');\n }\n\n if (this.serverMode) {\n return this._serverQuery<T>(query);\n }\n\n const start = Date.now();\n\n // Anonymous visitors ONLY: once a read has fallen back to the public\n // proxy, skip the doomed RPC round-trip on subsequent reads. A signed-in\n // user always goes through the RPC — their session is the authority.\n if (shouldSkipRpc() && !(await hasActiveSession(this.supabase))) {\n return this._publicFallback<T>(query, start, null);\n }\n\n const { data, error } = await this.supabase!.rpc('sdk_query_project', {\n p_project_id: this.projectId,\n p_sql: query,\n });\n const latencyMs = Date.now() - start;\n\n if (error) {\n if (publicReadAvailable() && isAuthShapedFailure(error.message) && !(await hasActiveSession(this.supabase))) {\n return this._publicFallback<T>(query, start, error.message);\n }\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: false, error: error.message });\n throw new QueryError(error.message, query);\n }\n if (!data.success) {\n if (publicReadAvailable() && isAuthShapedFailure(data.error) && !(await hasActiveSession(this.supabase))) {\n return this._publicFallback<T>(query, start, data.error);\n }\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: false, error: data.error });\n throw new QueryError(data.error || 'Query failed', query);\n }\n\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0 });\n\n return {\n success: true,\n data: data.data || [],\n rowCount: data.rowCount ?? (data.data?.length || 0),\n schema: data.schema,\n };\n }\n\n /** Read via the platform public proxy (anonymous-visitor path). */\n private async _publicFallback<T>(query: string, start: number, rpcError: string | null): Promise<DatabaseResult<T>> {\n const data = await publicReadQuery(this.projectId, query);\n const latencyMs = Date.now() - start;\n if (!data.success) {\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: false, error: data.error, publicProxy: true });\n // Surface the proxy's error (it is the actionable one); note the RPC error for diagnosis.\n throw new QueryError(data.error || rpcError || 'Query failed', query);\n }\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0, publicProxy: true });\n return {\n success: true,\n data: (data.data || []) as T[],\n rowCount: data.rowCount ?? (data.data?.length || 0),\n schema: data.schema,\n };\n }\n\n async execute(sql: string): Promise<MutationResult> {\n if (this.serverMode) {\n return this._serverExecute(sql);\n }\n\n const start = Date.now();\n const { data, error } = await this.supabase!.rpc('sdk_exec_project', {\n p_project_id: this.projectId,\n p_sql: sql,\n });\n const latencyMs = Date.now() - start;\n\n if (error) {\n trackDbEvent('db_mutation', { projectId: this.projectId, latencyMs, success: false, error: error.message });\n throw new QueryError(error.message, sql);\n }\n if (!data.success) {\n trackDbEvent('db_mutation', { projectId: this.projectId, latencyMs, success: false, error: data.error });\n throw new QueryError(data.error || 'Execution failed', sql);\n }\n\n trackDbEvent('db_mutation', { projectId: this.projectId, latencyMs, success: true, rowCount: data.rowCount ?? 0 });\n\n return {\n success: true,\n rowCount: data.rowCount ?? 0,\n schema: data.schema,\n executed_sql: data.executed_sql,\n };\n }\n\n async getSchema(): Promise<SchemaInfo> {\n if (this.serverMode) {\n return this._serverGetSchema();\n }\n\n const { data, error } = await this.supabase!.rpc('sdk_get_schema_info', {\n p_project_id: this.projectId,\n });\n\n if (error) throw new ConnectionError(error.message);\n\n return {\n exists: data.exists ?? false,\n schema: data.schema,\n tables: data.tables || [],\n tablesCount: data.tablesCount ?? 0,\n createdAt: data.createdAt,\n lastAccessedAt: data.lastAccessedAt,\n };\n }\n\n private async _serverRequest(endpoint: string, sql: string): Promise<ServerProxyResult> {\n const url = `${this.platformUrl}/api/platform/db/${this.projectId}${endpoint}`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ sql }),\n signal: AbortSignal.timeout(SERVER_TIMEOUT_MS),\n });\n\n if (res.status === 429) {\n const retryAfter = parseInt(res.headers.get('retry-after') || '60', 10);\n await new Promise(r => setTimeout(r, retryAfter * 1000));\n continue;\n }\n if (res.status === 503) {\n await new Promise(r => setTimeout(r, (attempt + 1) * 2000));\n continue;\n }\n\n return await res.json() as ServerProxyResult;\n } catch (e) {\n lastError = e instanceof Error ? e : new Error(String(e));\n }\n }\n\n return { success: false, error: lastError?.message || 'Request failed after retries' };\n }\n\n private async _serverQuery<T>(query: string): Promise<DatabaseResult<T>> {\n const start = Date.now();\n const result = await this._serverRequest('/query', query);\n const latencyMs = Date.now() - start;\n\n if (!result.success) {\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: false, error: result.error, serverMode: true });\n throw new QueryError(result.error || 'Query failed', query);\n }\n\n trackDbEvent('db_query', { projectId: this.projectId, latencyMs, success: true, rowCount: result.rowCount ?? 0, serverMode: true });\n\n return {\n success: true,\n data: (result.data || []) as T[],\n rowCount: result.rowCount ?? ((result.data as unknown[])?.length || 0),\n schema: result.schema,\n };\n }\n\n private async _serverExecute(sql: string): Promise<MutationResult> {\n const start = Date.now();\n const result = await this._serverRequest('/execute', sql);\n const latencyMs = Date.now() - start;\n\n if (!result.success) {\n trackDbEvent('db_mutation', { projectId: this.projectId, latencyMs, success: false, error: result.error, serverMode: true });\n throw new QueryError(result.error || 'Execution failed', sql);\n }\n\n trackDbEvent('db_mutation', { projectId: this.projectId, latencyMs, success: true, rowCount: result.rowCount ?? 0, serverMode: true });\n\n return {\n success: true,\n rowCount: result.rowCount ?? 0,\n schema: result.schema,\n executed_sql: result.executed_sql,\n };\n }\n\n private async _serverGetSchema(): Promise<SchemaInfo> {\n const url = `${this.platformUrl}/api/platform/db/${this.projectId}/schema`;\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'GET',\n headers: { 'Authorization': `Bearer ${this.apiKey}` },\n signal: AbortSignal.timeout(SERVER_TIMEOUT_MS),\n });\n\n if (res.status === 503) {\n await new Promise(r => setTimeout(r, (attempt + 1) * 2000));\n continue;\n }\n\n const data = await res.json() as ServerProxyResult;\n if (!data.success) throw new ConnectionError(data.error || 'Schema fetch failed');\n\n return {\n exists: data.exists ?? false,\n schema: data.schema,\n tables: (data.tables || []) as SchemaInfo['tables'],\n tablesCount: data.tablesCount ?? 0,\n createdAt: data.createdAt,\n lastAccessedAt: data.lastAccessedAt,\n };\n } catch (e) {\n lastError = e instanceof Error ? e : new Error(String(e));\n }\n }\n\n throw new ConnectionError(lastError?.message || 'Schema request failed after retries');\n }\n}\n","import { useEffect, useState, useRef } from 'react';\r\nimport { supabase, isSupabaseConfigured } from '../core/supabase';\r\nimport { env } from '../core/config';\r\n\r\ntype RealtimeEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*';\r\n\r\ninterface RealtimeOptions {\r\n event?: RealtimeEvent;\r\n filter?: string;\r\n}\r\n\r\ninterface UseRealtimeReturn<T> {\r\n data: T[];\r\n status: 'connecting' | 'connected' | 'error' | 'disabled';\r\n setData: React.Dispatch<React.SetStateAction<T[]>>;\r\n}\r\n\r\nexport function useRealtime<T extends Record<string, unknown> = Record<string, unknown>>(\r\n table: string,\r\n options: RealtimeOptions = {},\r\n): UseRealtimeReturn<T> {\r\n const [data, setData] = useState<T[]>([]);\r\n const [status, setStatus] = useState<UseRealtimeReturn<T>['status']>('connecting');\r\n const optionsRef = useRef(options);\r\n optionsRef.current = options;\r\n\r\n const projectId = env.EZC_PROJECT_ID;\r\n\r\n useEffect(() => {\r\n if (!isSupabaseConfigured || !projectId) {\r\n setStatus('disabled');\r\n return;\r\n }\r\n\r\n const schemaName = `proj_${projectId.replace(/-/g, '_')}`;\r\n const channelName = `${schemaName}_${table}_${optionsRef.current.event || 'all'}`;\r\n\r\n const channel = supabase\r\n .channel(channelName)\r\n .on(\r\n 'postgres_changes' as never,\r\n {\r\n event: optionsRef.current.event || '*',\r\n schema: schemaName,\r\n table,\r\n filter: optionsRef.current.filter,\r\n } as never,\r\n (payload: { eventType: string; new: Record<string, unknown>; old: Record<string, unknown> }) => {\r\n if (payload.eventType === 'INSERT') {\r\n setData((prev) => [...prev, payload.new as T]);\r\n } else if (payload.eventType === 'UPDATE') {\r\n setData((prev) =>\r\n prev.map((item) =>\r\n (item as Record<string, unknown>).id === (payload.new as Record<string, unknown>).id\r\n ? (payload.new as T)\r\n : item,\r\n ),\r\n );\r\n } else if (payload.eventType === 'DELETE') {\r\n setData((prev) =>\r\n prev.filter(\r\n (item) =>\r\n (item as Record<string, unknown>).id !== (payload.old as Record<string, unknown>).id,\r\n ),\r\n );\r\n }\r\n },\r\n )\r\n .subscribe((subStatus: string) => {\r\n setStatus(subStatus === 'SUBSCRIBED' ? 'connected' : 'connecting');\r\n });\r\n\r\n return () => {\r\n supabase.removeChannel(channel);\r\n };\r\n }, [projectId, table]);\r\n\r\n return { data, status, setData };\r\n}\r\n"],"mappings":"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","names":["supabase","supabase","data","supabase"]}
package/dist/cms/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
- features,
3
2
  supabase
4
- } from "../chunk-2WG4O4J2.js";
3
+ } from "../chunk-I2YGB7Z6.js";
4
+ import {
5
+ features
6
+ } from "../chunk-LIUE7M7K.js";
5
7
 
6
8
  // src/cms/cmsClient.ts
7
9
  function slugify(text) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cms/cmsClient.ts","../../src/cms/useContent.ts"],"sourcesContent":["import { supabase } from '../core/supabase';\r\nimport { features } from '../core/config';\r\n\r\ninterface ContentItem {\r\n id: string;\r\n title: string;\r\n slug: string;\r\n content: string;\r\n type: string;\r\n status: string;\r\n author_id?: string;\r\n metadata?: Record<string, unknown>;\r\n published_at?: string;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\ninterface ContentListOptions {\r\n type?: string;\r\n status?: string;\r\n limit?: number;\r\n}\r\n\r\nfunction slugify(text: string): string {\r\n return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\r\n}\r\n\r\nexport const cmsClient = {\r\n async list(options: ContentListOptions = {}): Promise<ContentItem[]> {\r\n if (!features.database) return [];\r\n\r\n let query = supabase.from('cms_content').select('*');\r\n if (options.type) query = query.eq('type', options.type);\r\n if (options.status) query = query.eq('status', options.status);\r\n query = query.order('created_at', { ascending: false });\r\n if (options.limit) query = query.limit(options.limit);\r\n\r\n const { data, error } = await query;\r\n if (error) throw error;\r\n return (data as ContentItem[]) || [];\r\n },\r\n\r\n async getBySlug(slug: string): Promise<ContentItem | null> {\r\n if (!features.database) return null;\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .select('*')\r\n .eq('slug', slug)\r\n .single();\r\n\r\n if (error) return null;\r\n return data as ContentItem;\r\n },\r\n\r\n async getById(id: string): Promise<ContentItem | null> {\r\n if (!features.database) return null;\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .select('*')\r\n .eq('id', id)\r\n .single();\r\n\r\n if (error) return null;\r\n return data as ContentItem;\r\n },\r\n\r\n async create(item: Partial<ContentItem>): Promise<ContentItem> {\r\n if (!features.database) throw new Error('Database not configured');\r\n\r\n const slug = item.slug || slugify(item.title || '');\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .insert({ ...item, slug, status: item.status || 'draft' })\r\n .select()\r\n .single();\r\n\r\n if (error) throw error;\r\n return data as ContentItem;\r\n },\r\n\r\n async update(id: string, updates: Partial<ContentItem>): Promise<ContentItem> {\r\n if (!features.database) throw new Error('Database not configured');\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .update({ ...updates, updated_at: new Date().toISOString() })\r\n .eq('id', id)\r\n .select()\r\n .single();\r\n\r\n if (error) throw error;\r\n return data as ContentItem;\r\n },\r\n\r\n async delete(id: string): Promise<void> {\r\n if (!features.database) throw new Error('Database not configured');\r\n const { error } = await supabase.from('cms_content').delete().eq('id', id);\r\n if (error) throw error;\r\n },\r\n\r\n async publish(id: string): Promise<ContentItem> {\r\n return cmsClient.update(id, { status: 'published', published_at: new Date().toISOString() });\r\n },\r\n\r\n async unpublish(id: string): Promise<ContentItem> {\r\n return cmsClient.update(id, { status: 'draft' });\r\n },\r\n};\r\n\r\nexport type { ContentItem, ContentListOptions };\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { cmsClient } from './cmsClient';\r\nimport type { ContentItem, ContentListOptions } from './cmsClient';\r\n\r\ninterface UseContentListReturn {\r\n content: ContentItem[];\r\n loading: boolean;\r\n error: string | null;\r\n refetch: () => Promise<void>;\r\n}\r\n\r\ninterface UseContentReturn {\r\n content: ContentItem | null;\r\n loading: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport function useContentList(options: ContentListOptions = {}): UseContentListReturn {\r\n const [content, setContent] = useState<ContentItem[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const fetch = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n const data = await cmsClient.list(options);\r\n setContent(data);\r\n setError(null);\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch content');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [options.type, options.status, options.limit]);\r\n\r\n useEffect(() => {\r\n fetch();\r\n }, [fetch]);\r\n\r\n return { content, loading, error, refetch: fetch };\r\n}\r\n\r\nexport function useContent(slug: string): UseContentReturn {\r\n const [content, setContent] = useState<ContentItem | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) {\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n cmsClient.getBySlug(slug)\r\n .then((data) => {\r\n setContent(data);\r\n setError(null);\r\n })\r\n .catch((err: unknown) => {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch content');\r\n })\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { content, loading, error };\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cms/cmsClient.ts","../../src/cms/useContent.ts"],"sourcesContent":["import { supabase } from '../core/supabase';\r\nimport { features } from '../core/config';\r\n\r\ninterface ContentItem {\r\n id: string;\r\n title: string;\r\n slug: string;\r\n content: string;\r\n type: string;\r\n status: string;\r\n author_id?: string;\r\n metadata?: Record<string, unknown>;\r\n published_at?: string;\r\n created_at: string;\r\n updated_at: string;\r\n}\r\n\r\ninterface ContentListOptions {\r\n type?: string;\r\n status?: string;\r\n limit?: number;\r\n}\r\n\r\nfunction slugify(text: string): string {\r\n return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');\r\n}\r\n\r\nexport const cmsClient = {\r\n async list(options: ContentListOptions = {}): Promise<ContentItem[]> {\r\n if (!features.database) return [];\r\n\r\n let query = supabase.from('cms_content').select('*');\r\n if (options.type) query = query.eq('type', options.type);\r\n if (options.status) query = query.eq('status', options.status);\r\n query = query.order('created_at', { ascending: false });\r\n if (options.limit) query = query.limit(options.limit);\r\n\r\n const { data, error } = await query;\r\n if (error) throw error;\r\n return (data as ContentItem[]) || [];\r\n },\r\n\r\n async getBySlug(slug: string): Promise<ContentItem | null> {\r\n if (!features.database) return null;\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .select('*')\r\n .eq('slug', slug)\r\n .single();\r\n\r\n if (error) return null;\r\n return data as ContentItem;\r\n },\r\n\r\n async getById(id: string): Promise<ContentItem | null> {\r\n if (!features.database) return null;\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .select('*')\r\n .eq('id', id)\r\n .single();\r\n\r\n if (error) return null;\r\n return data as ContentItem;\r\n },\r\n\r\n async create(item: Partial<ContentItem>): Promise<ContentItem> {\r\n if (!features.database) throw new Error('Database not configured');\r\n\r\n const slug = item.slug || slugify(item.title || '');\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .insert({ ...item, slug, status: item.status || 'draft' })\r\n .select()\r\n .single();\r\n\r\n if (error) throw error;\r\n return data as ContentItem;\r\n },\r\n\r\n async update(id: string, updates: Partial<ContentItem>): Promise<ContentItem> {\r\n if (!features.database) throw new Error('Database not configured');\r\n\r\n const { data, error } = await supabase\r\n .from('cms_content')\r\n .update({ ...updates, updated_at: new Date().toISOString() })\r\n .eq('id', id)\r\n .select()\r\n .single();\r\n\r\n if (error) throw error;\r\n return data as ContentItem;\r\n },\r\n\r\n async delete(id: string): Promise<void> {\r\n if (!features.database) throw new Error('Database not configured');\r\n const { error } = await supabase.from('cms_content').delete().eq('id', id);\r\n if (error) throw error;\r\n },\r\n\r\n async publish(id: string): Promise<ContentItem> {\r\n return cmsClient.update(id, { status: 'published', published_at: new Date().toISOString() });\r\n },\r\n\r\n async unpublish(id: string): Promise<ContentItem> {\r\n return cmsClient.update(id, { status: 'draft' });\r\n },\r\n};\r\n\r\nexport type { ContentItem, ContentListOptions };\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { cmsClient } from './cmsClient';\r\nimport type { ContentItem, ContentListOptions } from './cmsClient';\r\n\r\ninterface UseContentListReturn {\r\n content: ContentItem[];\r\n loading: boolean;\r\n error: string | null;\r\n refetch: () => Promise<void>;\r\n}\r\n\r\ninterface UseContentReturn {\r\n content: ContentItem | null;\r\n loading: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport function useContentList(options: ContentListOptions = {}): UseContentListReturn {\r\n const [content, setContent] = useState<ContentItem[]>([]);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const fetch = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n const data = await cmsClient.list(options);\r\n setContent(data);\r\n setError(null);\r\n } catch (err: unknown) {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch content');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [options.type, options.status, options.limit]);\r\n\r\n useEffect(() => {\r\n fetch();\r\n }, [fetch]);\r\n\r\n return { content, loading, error, refetch: fetch };\r\n}\r\n\r\nexport function useContent(slug: string): UseContentReturn {\r\n const [content, setContent] = useState<ContentItem | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) {\r\n setLoading(false);\r\n return;\r\n }\r\n\r\n cmsClient.getBySlug(slug)\r\n .then((data) => {\r\n setContent(data);\r\n setError(null);\r\n })\r\n .catch((err: unknown) => {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch content');\r\n })\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { content, loading, error };\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1,3 +1,19 @@
1
+ /**
2
+ * SDK registerCronJob (B1).
3
+ *
4
+ * Routes to /api/project-cron/register using the SERVER-class project token.
5
+ *
6
+ * BEFORE B1, this function read the Supabase auth session and sent the
7
+ * resulting JWT as `Authorization: Bearer`. The server endpoint expected
8
+ * a NextAuth session — they never matched, so every register returned 401.
9
+ *
10
+ * AFTER B1:
11
+ * - Reads EZC_PROJECT_TOKEN_SERVER from server-side env.
12
+ * - Sends via `X-EzCoder-Server-Token` header.
13
+ * - Refuses to run from browser bundles with a clear, actionable error.
14
+ * - The server endpoint now uses verifyProjectServerToken (project-scoped
15
+ * auth) instead of NextAuth getServerSession (user-scoped auth).
16
+ */
1
17
  interface RegisterJobOptions {
2
18
  httpMethod?: string;
3
19
  headers?: Record<string, string>;
@@ -9,6 +25,7 @@ interface RegisterJobResult {
9
25
  success: boolean;
10
26
  job?: Record<string, unknown>;
11
27
  error?: string;
28
+ code?: string;
12
29
  }
13
30
  declare function registerCronJob(name: string, cronExpression: string, endpointUrl: string, options?: RegisterJobOptions): Promise<RegisterJobResult>;
14
31