@@ -12,22 +12,28 @@ module Supabase.Supabase
1212 , ResultError
1313 , Session
1414 , SessionData
15+ , SignInOptions
1516 , StatusR
17+ , class Or
1618 , User
1719 , class Equals
1820 , class Select
1921 , delete
2022 , equals
23+ , channel
2124 , from
2225 , getSession
2326 , invoke
27+ , maybeSingle
2428 , onAuthStateChange
29+ , or
2530 , range
2631 , select
32+ , getUser
2733 , signInWithOtp
34+ , signInWithOtpOptions
2835 , signOut
2936 , single
30− , maybeSingle
3137 , update
3238 , upsert
3339 ) where
@@ -35,25 +41,28 @@ module Supabase.Supabase
3541import Prelude
3642
3743import Control.Promise (Promise)
38−import Control.Promise as Promise
44+import Data.Either (Either(..))
3945import Data.Function.Uncurried (Fn2, Fn3, runFn2, runFn3)
40−import Data.Maybe (Maybe)
46+import Data.Maybe (Maybe(..))
4147import Data.Nullable (Nullable)
42−import Data.Nullable as Nullable
4348import Data.Tuple (Tuple(..))
4449import Effect (Effect)
4550import 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
51+import Effect.Exception (error)
52+import Effect.Uncurried (EffectFn1, EffectFn3, mkEffectFn1, runEffectFn3)
5053import Foreign (Foreign)
51−import Supabase.Types (Client)
54+import Prim.Row (class Union)
55+import Supabase.Types (Channel, ChannelName, Client)
5256import Type.Function (type ($))
5357import Type.Row (type (+))
58+import Yoga.JSON (class ReadForeign, class WriteForeign, writeImpl)
59+import Fetch as Fetch
60+import Fetch.Internal.Response as FetchInternalResponse
61+import Data.Nullable as Nullable
62+import Control.Promise as Promise
5463import Supabase.Util as Util
5564import Yoga.JSON (class ReadForeign, class WriteForeign, write) as YogaJson
56−import Yoga.JSON (class ReadForeign, class WriteForeign, writeImpl)
65+import Fetch.Core.Response as YogaJson.Core
5766
5867foreign import data QueryBuilder :: Type
5968foreign import data FilterBuilder :: Type
@@ -91,7 +100,7 @@ foreign import selectQueryImpl :: QueryBuilder -> String -> FilterBuilder
91100
92101foreign import selectQueryWithCountImpl :: QueryBuilder -> String -> String -> FilterBuilder
93102
94−class Select input builder output where
103+class Select input builder output | output -> builder where
95104 select :: input -> builder -> output
96105
97106instance (YogaJson.ReadForeign output) => Select Unit FilterBuilder (Aff output) where
@@ -120,6 +129,18 @@ else instance (YogaJson.ReadForeign output) => Equals FilterBuilder (Aff output)
120129 equals :: String -> String -> FilterBuilder -> Aff output
121130 equals l r builder = runFn2 (eqRunImpl builder) l r # Promise.toAffE >>= Util.fromJSON
122131
132+foreign import orImpl :: forall builderIn builderOut. Array String -> builderIn -> builderOut
133+
134+class Or builder output | output -> builder where
135+ or :: Array String -> builder -> output
136+
137+instance Or FilterBuilder FilterBuilder where
138+ or :: Array String -> FilterBuilder -> FilterBuilder
139+ or keys fb = orImpl keys fb
140+else instance Or QueryBuilder FilterBuilder where
141+ or :: Array String -> QueryBuilder -> FilterBuilder
142+ or keys fb = orImpl keys fb
143+
123144foreign import singleImpl :: FilterBuilder -> Effect (Promise Foreign)
124145
125146single :: forall t. YogaJson.ReadForeign t => FilterBuilder -> Aff (Response t)
@@ -139,24 +160,34 @@ range { from: f, to } = rangeImpl f to >>> Promise.toAffE >=> Util.fromJSON
139160
140161type InternalAuthResponse = { error :: Nullable Error }
141162
142−foreign import signInWithOtpImpl :: Client -> String -> Effect (Promise InternalAuthResponse)
143−
144163type AuthResponse = { error :: Maybe Error }
145164
165+foreign import signInWithOtpImpl :: Client -> String -> Effect (Promise InternalAuthResponse)
166+
146167signInWithOtp :: Client -> String -> Aff AuthResponse
147168signInWithOtp client email = signInWithOtpImpl client email # Promise.toAffE <#> \{ error } -> { error: Nullable.toMaybe error }
148169
170+type SignInOptions =
171+ ( shouldCreateUser :: Boolean
172+ , captchaToken :: String
173+ )
174+
175+foreign import signInWithOtpOptionsImpl :: forall options. EffectFn3 Client String options (Promise InternalAuthResponse)
176+
177+signInWithOtpOptions :: forall options thru. Union options thru SignInOptions => Client -> String -> { | options } -> Aff AuthResponse
178+signInWithOtpOptions client email options = runEffectFn3 signInWithOtpOptionsImpl client email options # Promise.toAffE <#> \{ error } -> { error: Nullable.toMaybe error }
179+
149180foreign import signOutImpl :: Client -> Effect (Promise Unit)
150181
151182signOut ∷ Client → Aff Unit
152183signOut client = signOutImpl client # Promise.toAffE
153184
154−foreign import onAuthStateChangeImpl :: Client -> (EffectFn1 (Nullable Session) Unit) -> Effect Unit
185+foreign import onAuthStateChangeImpl :: Client -> (EffectFn1 (Nullable Session) Unit) -> Effect { data :: { id :: String, unsubscribe :: Effect Unit } }
155186
156−onAuthStateChange ∷ Client → (Maybe Session → Effect Unit) → Effect Unit
187+onAuthStateChange ∷ Client → (Maybe Session → Effect Unit) → Effect { data :: { id :: String, unsubscribe :: Effect Unit } }
157188onAuthStateChange client handler = onAuthStateChangeImpl client $ mkEffectFn1 (Nullable.toMaybe >>> handler)
158189
159−type User = { id :: String, email :: String }
190+type User = { id :: String, email :: String, user_metadata :: Foreign }
160191type Session = { user :: User }
161192type SessionData = { session :: Maybe Session }
162193
@@ -165,7 +196,10 @@ type ResultError = { code :: Maybe String, details :: Maybe String, message :: S
165196type DataR d r = (data :: Maybe d | r)
166197type ErrorR r = (error ∷ Maybe ResultError | r)
167198type CountR r = (count :: Int | r)
199+
200+type StatusR :: forall k. k -> Row Type
168201type StatusR r = (status ∷ Maybe Int)
202+
169203type Response d = { data :: Maybe d, error ∷ Maybe ResultError, status ∷ Maybe Int }
170204
171205foreign import getSessionImpl :: Client -> Effect (Promise Foreign)
@@ -177,10 +211,22 @@ type InternalFunctionResponse d = { "data" :: Nullable d, error :: Nullable { me
177211
178212foreign import invokeImpl :: forall body headers t. Client -> Fn3 String body headers (Effect $ Promise $ InternalFunctionResponse t)
179213
180−type FunctionResponse d = { "data" :: Maybe d, error :: Maybe { message :: String, context :: Fetch.Response }}
214+type FunctionResponse d = { "data" :: Maybe d, error :: Maybe { message :: String, context :: Fetch.Response } }
181215
182216invoke ∷ forall t body headers. ReadForeign t ⇒ Client → String → body → headers → Aff (FunctionResponse t)
183217invoke client fn body headers = runFn3 (invokeImpl client) fn body headers # Promise.toAffE <#> convert
184218 where
185219 convertError { message, context } = { message, context: FetchInternalResponse.convert context }
186220 convert { "data": d, error } = { "data": Nullable.toMaybe d, error: Nullable.toMaybe error <#> convertError }
221+
222+foreign import channel :: ChannelName -> Client -> Effect Channel
223+
224+foreign import getUserImpl :: Client -> Effect (Promise { data :: Nullable User, error :: Nullable Error })
225+
226+getUser :: Client -> Aff (Either Error User)
227+getUser client = do
228+ res <- getUserImpl client # Promise.toAffE
229+ case Nullable.toMaybe res.data, Nullable.toMaybe res.error of
230+ Just user, _ -> pure $ Right user
231+ _, Just error -> pure $ Left error
232+ _, _ -> pure $ Left (error "Unexpected response error")