@@ -0,0 +1,183 @@
1+module Supabase.Supabase
2+ ( AuthResponse
3+ , Count(..)
4+ , CountR
5+ , CountResponse
6+ , DataR
7+ , ErrorR
8+ , FilterBuilder
9+ , FunctionResponse
10+ , QueryBuilder
11+ , Response
12+ , ResultError
13+ , Session
14+ , SessionData
15+ , StatusR
16+ , User
17+ , class Equals
18+ , class Select
19+ , delete
20+ , equals
21+ , from
22+ , getSession
23+ , invoke
24+ , onAuthStateChange
25+ , range
26+ , select
27+ , signInWithOtp
28+ , signOut
29+ , single
30+ , update
31+ , upsert
32+ )
33+ where
34+
35+import Prelude
36+
37+import Control.Promise (Promise)
38+import Control.Promise as Promise
39+import Data.Function.Uncurried (Fn2, Fn3, runFn2, runFn3)
40+import Data.Maybe (Maybe)
41+import Data.Nullable (Nullable)
42+import Data.Nullable as Nullable
43+import Data.Tuple (Tuple(..))
44+import Effect (Effect)
45+import Effect.Aff (Aff, Error)
46+import Effect.Uncurried (EffectFn1, mkEffectFn1)
47+import Fetch as Fetch
48+import Fetch.Core.Response as YogaJson.Core
49+import Fetch.Internal.Response as FetchInternalResponse
50+import Foreign (Foreign)
51+import Supabase.Types (Client)
52+import Type.Function (type ($))
53+import Type.Row (type (+))
54+import Supabase.Util as Util
55+import Yoga.JSON (class ReadForeign, class WriteForeign, write) as YogaJson
56+import Yoga.JSON (class ReadForeign, class WriteForeign, writeImpl)
57+
58+foreign import data QueryBuilder :: Type
59+foreign import data FilterBuilder :: Type
60+
61+data Count = Exact | Planned | Estimated
62+
63+countToString ∷ Count → String
64+countToString Exact = "exact"
65+countToString Planned = "planned"
66+countToString Estimated = "estimated"
67+
68+foreign import deleteImpl :: QueryBuilder -> FilterBuilder
69+
70+delete ∷ QueryBuilder -> FilterBuilder
71+delete = deleteImpl
72+
73+foreign import updateImpl :: QueryBuilder -> Foreign -> FilterBuilder
74+
75+update ∷ forall d. YogaJson.WriteForeign d => d -> QueryBuilder -> FilterBuilder
76+update d builder = updateImpl builder $ YogaJson.write d
77+
78+foreign import fromImpl :: Client -> String -> QueryBuilder
79+
80+from ∷ String → Client -> QueryBuilder
81+from s c = fromImpl c s
82+
83+foreign import upsertImpl :: QueryBuilder -> Foreign -> FilterBuilder
84+
85+upsert ∷ ∀ (values ∷ Type). WriteForeign values => values → QueryBuilder → FilterBuilder
86+upsert v q = upsertImpl q $ writeImpl v
87+
88+foreign import selectRunImpl :: forall input. FilterBuilder -> input -> Effect (Promise Foreign)
89+
90+foreign import selectQueryImpl :: QueryBuilder -> String -> FilterBuilder
91+
92+foreign import selectQueryWithCountImpl :: QueryBuilder -> String -> String -> FilterBuilder
93+
94+class Select input builder output where
95+ select :: input -> builder -> output
96+
97+instance (YogaJson.ReadForeign output) => Select Unit FilterBuilder (Aff output) where
98+ select input builder = selectRunImpl builder (input) # Promise.toAffE >>= Util.fromJSON
99+else instance (YogaJson.ReadForeign output) => Select String FilterBuilder (Aff output) where
100+ select input builder = selectRunImpl builder input # Promise.toAffE >>= Util.fromJSON
101+else instance Select (Tuple String Count) QueryBuilder FilterBuilder where
102+ select (Tuple input count) builder = selectQueryWithCountImpl builder input $ countToString count
103+else instance Select String QueryBuilder FilterBuilder where
104+ select = flip selectQueryImpl
105+
106+foreign import eqRunImpl :: FilterBuilder -> Fn2 String String $ Effect (Promise Foreign)
107+
108+foreign import eqImpl :: forall builderIn builderOut. builderIn -> Fn2 String String builderOut
109+
110+class Equals builder output | output -> builder where
111+ equals :: String -> String -> builder -> output
112+
113+instance Equals FilterBuilder FilterBuilder where
114+ equals :: String -> String -> FilterBuilder -> FilterBuilder
115+ equals k v fb = runFn2 (eqImpl fb) k v
116+else instance Equals QueryBuilder FilterBuilder where
117+ equals :: String -> String -> QueryBuilder -> FilterBuilder
118+ equals k v fb = runFn2 (eqImpl fb) k v
119+else instance (YogaJson.ReadForeign output) => Equals FilterBuilder (Aff output) where
120+ equals :: String -> String -> FilterBuilder -> Aff output
121+ equals l r builder = runFn2 (eqRunImpl builder) l r # Promise.toAffE >>= Util.fromJSON
122+
123+foreign import singleImpl :: FilterBuilder -> Effect (Promise Foreign)
124+
125+--type ResultResponse d = { "data" :: Maybe d, status :: Int, error :: Maybe ResultError }
126+
127+single :: forall t. YogaJson.ReadForeign t => FilterBuilder -> Aff (Response t)
128+single = singleImpl >>> Promise.toAffE >=> Util.fromJSON
129+
130+foreign import rangeImpl :: forall i. i -> i -> FilterBuilder -> Effect (Promise Foreign)
131+
132+type CountResponse t = { | DataR t + ErrorR + CountR + StatusR + () }
133+
134+range :: forall response. YogaJson.ReadForeign response => { from :: Int, to :: Int } -> FilterBuilder -> Aff response
135+range { from: f, to } = rangeImpl f to >>> Promise.toAffE >=> Util.fromJSON
136+
137+type InternalAuthResponse = { error :: Nullable Error }
138+
139+foreign import signInWithOtpImpl :: Client -> String -> Effect (Promise InternalAuthResponse)
140+
141+type AuthResponse = { error :: Maybe Error }
142+
143+signInWithOtp :: Client -> String -> Aff AuthResponse
144+signInWithOtp client email = signInWithOtpImpl client email # Promise.toAffE <#> \{ error } -> { error: Nullable.toMaybe error }
145+
146+foreign import signOutImpl :: Client -> Effect (Promise Unit)
147+
148+signOut ∷ Client → Aff Unit
149+signOut client = signOutImpl client # Promise.toAffE
150+
151+foreign import onAuthStateChangeImpl :: Client -> (EffectFn1 (Nullable Session) Unit) -> Effect Unit
152+
153+onAuthStateChange ∷ Client → (Maybe Session → Effect Unit) → Effect Unit
154+onAuthStateChange client handler = onAuthStateChangeImpl client $ mkEffectFn1 (Nullable.toMaybe >>> handler)
155+
156+type User = { id :: String, email :: String }
157+type Session = { user :: User }
158+type SessionData = { session :: Maybe Session }
159+
160+type ResultError = { code :: Maybe String, details :: Maybe String, message :: String }
161+
162+type DataR d r = (data :: Maybe d | r)
163+type ErrorR r = (error ∷ Maybe ResultError | r)
164+type CountR r = (count :: Int | r)
165+type StatusR r = (status ∷ Maybe Int)
166+type Response d = { data :: Maybe d, error ∷ Maybe ResultError, status ∷ Maybe Int }
167+
168+foreign import getSessionImpl :: Client -> Effect (Promise Foreign)
169+
170+getSession :: Client -> Aff (Response SessionData)
171+getSession client = getSessionImpl client # Promise.toAffE >>= Util.fromJSON
172+
173+type InternalFunctionResponse d = { "data" :: Nullable d, error :: Nullable { message :: String, context :: YogaJson.Core.Response}}
174+foreign import invokeImpl :: forall body headers t. Client -> Fn3 String body headers (Effect $ Promise $ InternalFunctionResponse t)
175+
176+type FunctionResponse d = { "data" :: Maybe d, error :: Maybe { message :: String, context :: Fetch.Response}}
177+
178+
179+invoke ∷ forall t body headers. ReadForeign t ⇒ Client → String → body → headers → Aff (FunctionResponse t)
180+invoke client fn body headers = runFn3 (invokeImpl client) fn body headers # Promise.toAffE <#> convert
181+ where
182+ convertError { message, context } = { message, context: FetchInternalResponse.convert context}
183+ convert { "data": d, error } = { "data": Nullable.toMaybe d, error: Nullable.toMaybe error <#> convertError}