@blinkdotnew/sdk 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,17 +30,21 @@ const blink = createClient({
30
30
  authRequired: false // Don't force immediate auth - let users browse first
31
31
  })
32
32
 
33
- // Authentication (flexible) - Two modes available
33
+ // Authentication - Choose your mode:
34
34
 
35
- // For websites: Let users browse, require auth for protected areas
36
- if (!blink.auth.isAuthenticated() && needsAuth) {
37
- blink.auth.login() // Redirects to blink.new auth page when needed
38
- }
35
+ // 🎯 MANAGED MODE: Quick setup with hosted auth page
36
+ const blink = createClient({
37
+ projectId: 'your-project',
38
+ auth: { mode: 'managed' }
39
+ })
40
+ // Use: blink.auth.login() - redirects to blink.new auth
39
41
 
40
- // For apps with custom auth UI (headless mode)
41
- const user = await blink.auth.signUp({ email: 'user@example.com', password: 'secure123' })
42
- const user = await blink.auth.signInWithEmail('user@example.com', 'secure123')
43
- const user = await blink.auth.signInWithGoogle()
42
+ // 🎨 HEADLESS MODE: Custom UI with full control
43
+ const blink = createClient({
44
+ projectId: 'your-project',
45
+ auth: { mode: 'headless' }
46
+ })
47
+ // Use: blink.auth.signInWithEmail(), blink.auth.signInWithGoogle(), etc.
44
48
 
45
49
  // Current user (works in both modes)
46
50
  const user = await blink.auth.me()
@@ -193,65 +197,80 @@ blink.auth.setToken(jwtFromHeader)
193
197
 
194
198
  > **⚠️ Version Requirement**: The flexible authentication system requires SDK version **0.18.0 or higher**. Version 0.17.x and below only support the legacy authentication system.
195
199
 
196
- Blink provides **two authentication modes** for maximum flexibility:
197
-
198
- #### Managed Mode (Default - Redirect-based)
200
+ Blink provides **two authentication modes**:
199
201
 
200
- Perfect for quick setup with Blink's hosted auth page:
202
+ ## 🎯 Managed Mode (Redirect-based)
203
+ **Perfect for:** Quick setup, minimal code
204
+ **Best for:** Websites, simple apps, MVP development
201
205
 
202
206
  ```typescript
203
- // ⚠️ DEPRECATED: authRequired is legacy - use auth.mode instead
204
- const blink = createClient({
205
- projectId: 'your-project',
206
- authRequired: false // Legacy - still works but deprecated
207
- })
208
-
209
- // ✅ RECOMMENDED: Use new auth configuration
210
207
  const blink = createClient({
211
208
  projectId: 'your-project',
212
- auth: {
213
- mode: 'managed', // Explicit mode configuration
214
- redirectUrl: 'https://myapp.com/dashboard'
215
- }
209
+ auth: { mode: 'managed' }
216
210
  })
217
211
 
218
- // Simple redirect authentication
219
- blink.auth.login() // Redirects to blink.new auth page
212
+ // ONE METHOD: Redirect to hosted auth page
213
+ blink.auth.login() // Redirects to blink.new/auth
220
214
  blink.auth.logout() // Clear tokens and redirect
221
- ```
222
215
 
223
- #### Headless Mode (Custom UI Control)
216
+ // User state (automatic after redirect)
217
+ const user = await blink.auth.me()
218
+ ```
224
219
 
225
- Build your own authentication UI with full control:
220
+ ## 🎨 Headless Mode (Custom UI)
221
+ **Perfect for:** Custom branding, advanced UX, mobile apps
222
+ **Best for:** Production apps, branded experiences
226
223
 
227
224
  ```typescript
228
225
  const blink = createClient({
229
226
  projectId: 'your-project',
230
- auth: {
231
- mode: 'headless'
232
- // Providers controlled via project settings
233
- }
227
+ auth: { mode: 'headless' }
234
228
  })
235
229
 
236
- // Email/password authentication
237
- const user = await blink.auth.signUp({
238
- email: 'user@example.com',
239
- password: 'SecurePass123!',
240
- metadata: { displayName: 'John Doe' }
241
- })
242
-
243
- const user = await blink.auth.signInWithEmail('user@example.com', 'password')
244
-
245
- // Social provider authentication
230
+ // MULTIPLE METHODS: Build your own UI
231
+ const user = await blink.auth.signUp({ email, password })
232
+ const user = await blink.auth.signInWithEmail(email, password)
246
233
  const user = await blink.auth.signInWithGoogle()
247
234
  const user = await blink.auth.signInWithGitHub()
248
235
  const user = await blink.auth.signInWithApple()
249
236
  const user = await blink.auth.signInWithMicrosoft()
250
237
 
251
238
  // Magic links (passwordless)
252
- await blink.auth.sendMagicLink('user@example.com', {
253
- redirectUrl: 'https://myapp.com/dashboard'
239
+ await blink.auth.sendMagicLink(email)
240
+
241
+ // Password management
242
+ await blink.auth.sendPasswordResetEmail(email)
243
+ await blink.auth.sendPasswordResetEmail(email, {
244
+ redirectUrl: 'https://myapp.com/reset-password'
245
+ })
246
+ await blink.auth.changePassword(oldPass, newPass)
247
+
248
+ // Email verification
249
+ await blink.auth.sendEmailVerification()
250
+ ```
251
+
252
+ ### ⚡ Quick Mode Comparison
253
+
254
+ | Feature | **Managed Mode** | **Headless Mode** |
255
+ |---------|------------------|-------------------|
256
+ | **Setup** | 1 line of code | Custom UI required |
257
+ | **Methods** | `login()` only | `signInWith*()` methods |
258
+ | **UI** | Hosted auth page | Your custom forms |
259
+ | **Branding** | Blink-branded | Fully customizable |
260
+ | **Mobile** | Web redirects | Native integration |
261
+
262
+ ### 🚨 **Common Mistake**
263
+
264
+ ```typescript
265
+ // ❌ WRONG: Using managed method in headless mode
266
+ const blink = createClient({
267
+ auth: { mode: 'headless' }
254
268
  })
269
+ blink.auth.login() // Still redirects! Wrong method for headless
270
+
271
+ // ✅ CORRECT: Use headless methods
272
+ await blink.auth.signInWithEmail(email, password)
273
+ await blink.auth.signInWithGoogle()
255
274
  ```
256
275
 
257
276
  #### 🔧 Provider Configuration
@@ -418,6 +437,10 @@ blink.auth.setToken(jwt, persist?)
418
437
  const isAuth = blink.auth.isAuthenticated()
419
438
 
420
439
  // Password management
440
+ await blink.auth.sendPasswordResetEmail('user@example.com')
441
+ await blink.auth.sendPasswordResetEmail('user@example.com', {
442
+ redirectUrl: 'https://myapp.com/reset-password' // Custom reset page
443
+ })
421
444
  await blink.auth.changePassword('oldPass', 'newPass')
422
445
  await blink.auth.confirmPasswordReset(token, newPassword)
423
446
 
@@ -1567,8 +1590,8 @@ const blink = createClient({
1567
1590
  baseUrl: 'https://custom-api.example.com',
1568
1591
  auth: {
1569
1592
  mode: 'headless', // 'managed' | 'headless'
1570
- authUrl: 'https://custom-auth.example.com', // Custom auth domain
1571
- coreUrl: 'https://custom-core.example.com', // Custom API domain
1593
+ authUrl: 'https://your-auth-service.com', // Custom auth domain (for all auth endpoints)
1594
+ coreUrl: 'https://custom-core.example.com', // Custom API domain (for db, ai, storage)
1572
1595
  // Providers controlled via project settings
1573
1596
  redirectUrl: 'https://myapp.com/dashboard',
1574
1597
  roles: {
@@ -1777,146 +1800,126 @@ function MyRealtimeComponent() {
1777
1800
 
1778
1801
  ### React
1779
1802
 
1780
- #### Authentication Integration
1803
+ #### 🔑 Complete Examples by Mode
1781
1804
 
1782
- **Complete authentication example with custom UI:**
1805
+ **🎯 Managed Mode Example:**
1783
1806
 
1784
1807
  ```typescript
1785
1808
  import { createClient } from '@blinkdotnew/sdk'
1786
- import { useState, useEffect } from 'react'
1787
1809
 
1788
- // ✅ RECOMMENDED: Don't force immediate auth - let users browse first
1789
1810
  const blink = createClient({
1790
1811
  projectId: 'your-project',
1791
- authRequired: false, // Let users browse public areas
1792
- auth: {
1793
- mode: 'headless' // Use headless for custom UI
1794
- // Providers controlled via project settings
1795
- }
1812
+ auth: { mode: 'managed' }
1796
1813
  })
1797
1814
 
1798
1815
  function App() {
1799
1816
  const [user, setUser] = useState(null)
1800
- const [loading, setLoading] = useState(true)
1801
- const [currentPage, setCurrentPage] = useState('home')
1802
1817
 
1803
- // ✅ CRITICAL: Always use onAuthStateChanged
1804
1818
  useEffect(() => {
1805
1819
  const unsubscribe = blink.auth.onAuthStateChanged((state) => {
1806
1820
  setUser(state.user)
1807
- setLoading(state.isLoading)
1808
1821
  })
1809
1822
  return unsubscribe
1810
1823
  }, [])
1811
1824
 
1812
- // Protected route check - only require auth for dashboard/account areas
1813
- const requiresAuth = ['dashboard', 'account', 'settings'].includes(currentPage)
1814
-
1815
- if (loading) return <div>Loading...</div>
1816
- if (requiresAuth && !user) return <AuthForm />
1825
+ if (!user) {
1826
+ return (
1827
+ <div>
1828
+ <h1>Welcome to My App</h1>
1829
+ <button onClick={() => blink.auth.login()}>
1830
+ Sign In
1831
+ </button>
1832
+ </div>
1833
+ )
1834
+ }
1817
1835
 
1818
- // Public pages (home, pricing, about) don't require auth
1819
- if (currentPage === 'home') return <HomePage />
1820
- if (currentPage === 'pricing') return <PricingPage />
1821
-
1822
- // Protected pages require authentication
1823
1836
  return <Dashboard user={user} />
1824
1837
  }
1838
+ ```
1839
+
1840
+ **🎨 Headless Mode Example:**
1841
+
1842
+ ```typescript
1843
+ import { createClient } from '@blinkdotnew/sdk'
1844
+
1845
+ const blink = createClient({
1846
+ projectId: 'your-project',
1847
+ auth: { mode: 'headless' }
1848
+ })
1825
1849
 
1826
1850
  function AuthForm() {
1827
1851
  const [mode, setMode] = useState('signin') // 'signin' | 'signup' | 'reset'
1828
1852
  const [email, setEmail] = useState('')
1829
1853
  const [password, setPassword] = useState('')
1830
- const [error, setError] = useState('')
1831
1854
  const [message, setMessage] = useState('')
1832
1855
 
1833
- const handleEmailAuth = async (e) => {
1834
- e.preventDefault()
1835
- setError('')
1836
-
1856
+ const handleEmailAuth = async () => {
1837
1857
  try {
1838
- if (mode === 'signup') {
1839
- const user = await blink.auth.signUp({ email, password })
1840
- setMessage('Account created! Please check your email to verify.')
1841
- await blink.auth.sendEmailVerification()
1842
- } else if (mode === 'signin') {
1858
+ if (mode === 'signin') {
1843
1859
  await blink.auth.signInWithEmail(email, password)
1844
- // User state updates automatically via onAuthStateChanged
1860
+ } else if (mode === 'signup') {
1861
+ await blink.auth.signUp({ email, password })
1862
+ setMessage('Account created! Check your email to verify.')
1845
1863
  } else if (mode === 'reset') {
1846
- await blink.auth.sendPasswordResetEmail(email)
1847
- setMessage('Password reset email sent!')
1848
- }
1849
- } catch (err) {
1850
- if (err.code === 'EMAIL_NOT_VERIFIED') {
1851
- setError('Please verify your email first')
1852
- await blink.auth.sendEmailVerification()
1853
- } else {
1854
- setError(err.message)
1864
+ await blink.auth.sendPasswordResetEmail(email, {
1865
+ redirectUrl: 'https://myapp.com/reset-password' // Your custom reset page
1866
+ })
1867
+ setMessage('Password reset email sent! Check your inbox.')
1855
1868
  }
1869
+ } catch (error) {
1870
+ console.error('Auth failed:', error.message)
1856
1871
  }
1857
1872
  }
1858
1873
 
1859
- const handleSocialAuth = async (provider) => {
1874
+ const handleSocialAuth = async () => {
1860
1875
  try {
1861
- await blink.auth.signInWithProvider(provider)
1862
- } catch (err) {
1863
- setError(err.message)
1876
+ await blink.auth.signInWithGoogle()
1877
+ } catch (error) {
1878
+ console.error('Social auth failed:', error.message)
1864
1879
  }
1865
1880
  }
1866
1881
 
1867
1882
  return (
1868
- <div style={{ maxWidth: '400px', margin: '0 auto', padding: '2rem' }}>
1869
- <h1>{mode === 'signin' ? 'Sign In' : mode === 'signup' ? 'Sign Up' : 'Reset Password'}</h1>
1870
-
1871
- {error && <div style={{ color: 'red', marginBottom: '1rem' }}>{error}</div>}
1872
- {message && <div style={{ color: 'green', marginBottom: '1rem' }}>{message}</div>}
1883
+ <div>
1884
+ {message && <p style={{ color: 'green' }}>{message}</p>}
1873
1885
 
1874
- <form onSubmit={handleEmailAuth} style={{ marginBottom: '1rem' }}>
1875
- <input
1876
- type="email"
1877
- placeholder="Email"
1886
+ <form onSubmit={handleEmailAuth}>
1887
+ <input
1888
+ type="email"
1878
1889
  value={email}
1879
1890
  onChange={(e) => setEmail(e.target.value)}
1880
- style={{ width: '100%', padding: '0.5rem', marginBottom: '0.5rem' }}
1891
+ placeholder="Email"
1881
1892
  />
1882
1893
 
1883
1894
  {mode !== 'reset' && (
1884
- <input
1885
- type="password"
1886
- placeholder="Password"
1895
+ <input
1896
+ type="password"
1887
1897
  value={password}
1888
1898
  onChange={(e) => setPassword(e.target.value)}
1889
- style={{ width: '100%', padding: '0.5rem', marginBottom: '1rem' }}
1899
+ placeholder="Password"
1890
1900
  />
1891
1901
  )}
1892
1902
 
1893
- <button type="submit" style={{ width: '100%', padding: '0.75rem', marginBottom: '1rem' }}>
1894
- {mode === 'signin' ? 'Sign In' : mode === 'signup' ? 'Create Account' : 'Send Reset Email'}
1903
+ <button type="submit">
1904
+ {mode === 'signin' ? 'Sign In' : mode === 'signup' ? 'Sign Up' : 'Send Reset Email'}
1895
1905
  </button>
1896
1906
  </form>
1897
1907
 
1898
1908
  {mode !== 'reset' && (
1899
- <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '1rem' }}>
1900
- <button onClick={() => handleSocialAuth('google')} style={{ flex: 1, padding: '0.5rem' }}>
1901
- Google
1902
- </button>
1903
- <button onClick={() => handleSocialAuth('github')} style={{ flex: 1, padding: '0.5rem' }}>
1904
- GitHub
1905
- </button>
1906
- </div>
1909
+ <button type="button" onClick={handleSocialAuth}>
1910
+ Continue with Google
1911
+ </button>
1907
1912
  )}
1908
1913
 
1909
- <div style={{ textAlign: 'center', fontSize: '0.875rem' }}>
1914
+ <div>
1910
1915
  {mode === 'signin' && (
1911
1916
  <>
1912
- <button onClick={() => setMode('signup')} style={{ marginRight: '1rem' }}>
1913
- Create Account
1914
- </button>
1917
+ <button onClick={() => setMode('signup')}>Create Account</button>
1915
1918
  <button onClick={() => setMode('reset')}>Forgot Password?</button>
1916
1919
  </>
1917
1920
  )}
1918
1921
  {mode === 'signup' && (
1919
- <button onClick={() => setMode('signin')}>Already have an account?</button>
1922
+ <button onClick={() => setMode('signin')}>Back to Sign In</button>
1920
1923
  )}
1921
1924
  {mode === 'reset' && (
1922
1925
  <button onClick={() => setMode('signin')}>Back to Sign In</button>
@@ -1927,6 +1930,55 @@ function AuthForm() {
1927
1930
  }
1928
1931
  ```
1929
1932
 
1933
+ #### 🔄 Custom Reset Page Handling
1934
+
1935
+ **When users click the reset link, handle it in your app:**
1936
+
1937
+ ```typescript
1938
+ // /reset-password page component
1939
+ function ResetPasswordPage() {
1940
+ const [token, setToken] = useState('')
1941
+ const [projectId, setProjectId] = useState('')
1942
+ const [newPassword, setNewPassword] = useState('')
1943
+ const [message, setMessage] = useState('')
1944
+
1945
+ useEffect(() => {
1946
+ // Extract token and projectId from URL params
1947
+ const params = new URLSearchParams(window.location.search)
1948
+ setToken(params.get('token') || '')
1949
+ setProjectId(params.get('projectId') || '')
1950
+ }, [])
1951
+
1952
+ const handleReset = async (e) => {
1953
+ e.preventDefault()
1954
+
1955
+ try {
1956
+ await blink.auth.confirmPasswordReset(token, newPassword)
1957
+ setMessage('Password reset successfully! You can now sign in.')
1958
+ } catch (error) {
1959
+ console.error('Reset failed:', error.message)
1960
+ }
1961
+ }
1962
+
1963
+ if (!token) return <div>Invalid reset link</div>
1964
+
1965
+ return (
1966
+ <form onSubmit={handleReset}>
1967
+ <h1>Set New Password</h1>
1968
+ <input
1969
+ type="password"
1970
+ value={newPassword}
1971
+ onChange={(e) => setNewPassword(e.target.value)}
1972
+ placeholder="Enter new password"
1973
+ minLength={8}
1974
+ />
1975
+ <button type="submit">Reset Password</button>
1976
+ {message && <p style={{ color: 'green' }}>{message}</p>}
1977
+ </form>
1978
+ )
1979
+ }
1980
+ ```
1981
+
1930
1982
  #### Custom Email Branding Example
1931
1983
 
1932
1984
  **Send password reset with your own email service:**
package/dist/index.d.mts CHANGED
@@ -914,7 +914,9 @@ declare class BlinkAuth {
914
914
  /**
915
915
  * Send password reset email (using Blink default email service)
916
916
  */
917
- sendPasswordResetEmail(email: string): Promise<void>;
917
+ sendPasswordResetEmail(email: string, options?: {
918
+ redirectUrl?: string;
919
+ }): Promise<void>;
918
920
  /**
919
921
  * Confirm password reset with token
920
922
  */
package/dist/index.d.ts CHANGED
@@ -914,7 +914,9 @@ declare class BlinkAuth {
914
914
  /**
915
915
  * Send password reset email (using Blink default email service)
916
916
  */
917
- sendPasswordResetEmail(email: string): Promise<void>;
917
+ sendPasswordResetEmail(email: string, options?: {
918
+ redirectUrl?: string;
919
+ }): Promise<void>;
918
920
  /**
919
921
  * Confirm password reset with token
920
922
  */
package/dist/index.js CHANGED
@@ -925,8 +925,8 @@ var BlinkAuth = class {
925
925
  };
926
926
  this.authUrl = this.authConfig.authUrl || "https://blink.new";
927
927
  this.coreUrl = this.authConfig.coreUrl || "https://core.blink.new";
928
- if (typeof window !== "undefined" && this.coreUrl === "https://core.blink.new" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
929
- console.warn("\u26A0\uFE0F Using default coreUrl in development. Set auth.coreUrl to your app origin for headless auth endpoints to work.");
928
+ if (typeof window !== "undefined" && this.authUrl === "https://blink.new" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
929
+ console.warn("\u26A0\uFE0F Using default authUrl in development. Set auth.authUrl to your app origin for headless auth endpoints to work.");
930
930
  }
931
931
  if (config.authRequired !== void 0 && !config.auth?.mode) {
932
932
  this.authConfig.mode = config.authRequired ? "managed" : "headless";
@@ -1258,7 +1258,7 @@ var BlinkAuth = class {
1258
1258
  throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signUp is only available in headless mode");
1259
1259
  }
1260
1260
  try {
1261
- const response = await fetch(`${this.coreUrl}/api/auth/signup`, {
1261
+ const response = await fetch(`${this.authUrl}/api/auth/signup`, {
1262
1262
  method: "POST",
1263
1263
  headers: {
1264
1264
  "Content-Type": "application/json"
@@ -1297,7 +1297,7 @@ var BlinkAuth = class {
1297
1297
  throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithEmail is only available in headless mode");
1298
1298
  }
1299
1299
  try {
1300
- const response = await fetch(`${this.coreUrl}/api/auth/signin/email`, {
1300
+ const response = await fetch(`${this.authUrl}/api/auth/signin/email`, {
1301
1301
  method: "POST",
1302
1302
  headers: {
1303
1303
  "Content-Type": "application/json"
@@ -1466,7 +1466,7 @@ var BlinkAuth = class {
1466
1466
  */
1467
1467
  async generatePasswordResetToken(email) {
1468
1468
  try {
1469
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset/generate`, {
1469
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset/generate`, {
1470
1470
  method: "POST",
1471
1471
  headers: {
1472
1472
  "Content-Type": "application/json"
@@ -1501,16 +1501,17 @@ var BlinkAuth = class {
1501
1501
  /**
1502
1502
  * Send password reset email (using Blink default email service)
1503
1503
  */
1504
- async sendPasswordResetEmail(email) {
1504
+ async sendPasswordResetEmail(email, options) {
1505
1505
  try {
1506
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset`, {
1506
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset`, {
1507
1507
  method: "POST",
1508
1508
  headers: {
1509
1509
  "Content-Type": "application/json"
1510
1510
  },
1511
1511
  body: JSON.stringify({
1512
1512
  email,
1513
- projectId: this.config.projectId
1513
+ projectId: this.config.projectId,
1514
+ redirectUrl: options?.redirectUrl
1514
1515
  })
1515
1516
  });
1516
1517
  if (!response.ok) {
@@ -1530,7 +1531,7 @@ var BlinkAuth = class {
1530
1531
  */
1531
1532
  async confirmPasswordReset(token, newPassword) {
1532
1533
  try {
1533
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset/confirm`, {
1534
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset/confirm`, {
1534
1535
  method: "POST",
1535
1536
  headers: {
1536
1537
  "Content-Type": "application/json"
@@ -1562,7 +1563,7 @@ var BlinkAuth = class {
1562
1563
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1563
1564
  }
1564
1565
  try {
1565
- const response = await fetch(`${this.coreUrl}/api/auth/password/change`, {
1566
+ const response = await fetch(`${this.authUrl}/api/auth/password/change`, {
1566
1567
  method: "POST",
1567
1568
  headers: {
1568
1569
  "Authorization": `Bearer ${token}`,
@@ -1594,7 +1595,7 @@ var BlinkAuth = class {
1594
1595
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1595
1596
  }
1596
1597
  try {
1597
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify/generate`, {
1598
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify/generate`, {
1598
1599
  method: "POST",
1599
1600
  headers: {
1600
1601
  "Authorization": `Bearer ${token}`,
@@ -1632,7 +1633,7 @@ var BlinkAuth = class {
1632
1633
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1633
1634
  }
1634
1635
  try {
1635
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify/send`, {
1636
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify/send`, {
1636
1637
  method: "POST",
1637
1638
  headers: {
1638
1639
  "Authorization": `Bearer ${token}`,
@@ -1656,7 +1657,7 @@ var BlinkAuth = class {
1656
1657
  */
1657
1658
  async verifyEmail(token) {
1658
1659
  try {
1659
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify`, {
1660
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify`, {
1660
1661
  method: "POST",
1661
1662
  headers: {
1662
1663
  "Content-Type": "application/json"
@@ -1683,7 +1684,7 @@ var BlinkAuth = class {
1683
1684
  */
1684
1685
  async generateMagicLinkToken(email, options) {
1685
1686
  try {
1686
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic/generate`, {
1687
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic/generate`, {
1687
1688
  method: "POST",
1688
1689
  headers: {
1689
1690
  "Content-Type": "application/json"
@@ -1721,7 +1722,7 @@ var BlinkAuth = class {
1721
1722
  */
1722
1723
  async sendMagicLink(email, options) {
1723
1724
  try {
1724
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic`, {
1725
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic`, {
1725
1726
  method: "POST",
1726
1727
  headers: {
1727
1728
  "Content-Type": "application/json"
@@ -1753,7 +1754,7 @@ var BlinkAuth = class {
1753
1754
  throw new BlinkAuthError("VERIFICATION_FAILED" /* VERIFICATION_FAILED */, "No magic link token found");
1754
1755
  }
1755
1756
  try {
1756
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic/verify`, {
1757
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic/verify`, {
1757
1758
  method: "POST",
1758
1759
  headers: {
1759
1760
  "Content-Type": "application/json"
@@ -1789,7 +1790,7 @@ var BlinkAuth = class {
1789
1790
  */
1790
1791
  async getAvailableProviders() {
1791
1792
  try {
1792
- const response = await fetch(`${this.coreUrl}/api/auth/providers?projectId=${encodeURIComponent(this.config.projectId)}`);
1793
+ const response = await fetch(`${this.authUrl}/api/auth/providers?projectId=${encodeURIComponent(this.config.projectId)}`);
1793
1794
  if (!response.ok) {
1794
1795
  return ["email", "google"];
1795
1796
  }
package/dist/index.mjs CHANGED
@@ -923,8 +923,8 @@ var BlinkAuth = class {
923
923
  };
924
924
  this.authUrl = this.authConfig.authUrl || "https://blink.new";
925
925
  this.coreUrl = this.authConfig.coreUrl || "https://core.blink.new";
926
- if (typeof window !== "undefined" && this.coreUrl === "https://core.blink.new" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
927
- console.warn("\u26A0\uFE0F Using default coreUrl in development. Set auth.coreUrl to your app origin for headless auth endpoints to work.");
926
+ if (typeof window !== "undefined" && this.authUrl === "https://blink.new" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")) {
927
+ console.warn("\u26A0\uFE0F Using default authUrl in development. Set auth.authUrl to your app origin for headless auth endpoints to work.");
928
928
  }
929
929
  if (config.authRequired !== void 0 && !config.auth?.mode) {
930
930
  this.authConfig.mode = config.authRequired ? "managed" : "headless";
@@ -1256,7 +1256,7 @@ var BlinkAuth = class {
1256
1256
  throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signUp is only available in headless mode");
1257
1257
  }
1258
1258
  try {
1259
- const response = await fetch(`${this.coreUrl}/api/auth/signup`, {
1259
+ const response = await fetch(`${this.authUrl}/api/auth/signup`, {
1260
1260
  method: "POST",
1261
1261
  headers: {
1262
1262
  "Content-Type": "application/json"
@@ -1295,7 +1295,7 @@ var BlinkAuth = class {
1295
1295
  throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithEmail is only available in headless mode");
1296
1296
  }
1297
1297
  try {
1298
- const response = await fetch(`${this.coreUrl}/api/auth/signin/email`, {
1298
+ const response = await fetch(`${this.authUrl}/api/auth/signin/email`, {
1299
1299
  method: "POST",
1300
1300
  headers: {
1301
1301
  "Content-Type": "application/json"
@@ -1464,7 +1464,7 @@ var BlinkAuth = class {
1464
1464
  */
1465
1465
  async generatePasswordResetToken(email) {
1466
1466
  try {
1467
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset/generate`, {
1467
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset/generate`, {
1468
1468
  method: "POST",
1469
1469
  headers: {
1470
1470
  "Content-Type": "application/json"
@@ -1499,16 +1499,17 @@ var BlinkAuth = class {
1499
1499
  /**
1500
1500
  * Send password reset email (using Blink default email service)
1501
1501
  */
1502
- async sendPasswordResetEmail(email) {
1502
+ async sendPasswordResetEmail(email, options) {
1503
1503
  try {
1504
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset`, {
1504
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset`, {
1505
1505
  method: "POST",
1506
1506
  headers: {
1507
1507
  "Content-Type": "application/json"
1508
1508
  },
1509
1509
  body: JSON.stringify({
1510
1510
  email,
1511
- projectId: this.config.projectId
1511
+ projectId: this.config.projectId,
1512
+ redirectUrl: options?.redirectUrl
1512
1513
  })
1513
1514
  });
1514
1515
  if (!response.ok) {
@@ -1528,7 +1529,7 @@ var BlinkAuth = class {
1528
1529
  */
1529
1530
  async confirmPasswordReset(token, newPassword) {
1530
1531
  try {
1531
- const response = await fetch(`${this.coreUrl}/api/auth/password/reset/confirm`, {
1532
+ const response = await fetch(`${this.authUrl}/api/auth/password/reset/confirm`, {
1532
1533
  method: "POST",
1533
1534
  headers: {
1534
1535
  "Content-Type": "application/json"
@@ -1560,7 +1561,7 @@ var BlinkAuth = class {
1560
1561
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1561
1562
  }
1562
1563
  try {
1563
- const response = await fetch(`${this.coreUrl}/api/auth/password/change`, {
1564
+ const response = await fetch(`${this.authUrl}/api/auth/password/change`, {
1564
1565
  method: "POST",
1565
1566
  headers: {
1566
1567
  "Authorization": `Bearer ${token}`,
@@ -1592,7 +1593,7 @@ var BlinkAuth = class {
1592
1593
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1593
1594
  }
1594
1595
  try {
1595
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify/generate`, {
1596
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify/generate`, {
1596
1597
  method: "POST",
1597
1598
  headers: {
1598
1599
  "Authorization": `Bearer ${token}`,
@@ -1630,7 +1631,7 @@ var BlinkAuth = class {
1630
1631
  throw new BlinkAuthError("TOKEN_EXPIRED" /* TOKEN_EXPIRED */, "No access token available");
1631
1632
  }
1632
1633
  try {
1633
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify/send`, {
1634
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify/send`, {
1634
1635
  method: "POST",
1635
1636
  headers: {
1636
1637
  "Authorization": `Bearer ${token}`,
@@ -1654,7 +1655,7 @@ var BlinkAuth = class {
1654
1655
  */
1655
1656
  async verifyEmail(token) {
1656
1657
  try {
1657
- const response = await fetch(`${this.coreUrl}/api/auth/email/verify`, {
1658
+ const response = await fetch(`${this.authUrl}/api/auth/email/verify`, {
1658
1659
  method: "POST",
1659
1660
  headers: {
1660
1661
  "Content-Type": "application/json"
@@ -1681,7 +1682,7 @@ var BlinkAuth = class {
1681
1682
  */
1682
1683
  async generateMagicLinkToken(email, options) {
1683
1684
  try {
1684
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic/generate`, {
1685
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic/generate`, {
1685
1686
  method: "POST",
1686
1687
  headers: {
1687
1688
  "Content-Type": "application/json"
@@ -1719,7 +1720,7 @@ var BlinkAuth = class {
1719
1720
  */
1720
1721
  async sendMagicLink(email, options) {
1721
1722
  try {
1722
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic`, {
1723
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic`, {
1723
1724
  method: "POST",
1724
1725
  headers: {
1725
1726
  "Content-Type": "application/json"
@@ -1751,7 +1752,7 @@ var BlinkAuth = class {
1751
1752
  throw new BlinkAuthError("VERIFICATION_FAILED" /* VERIFICATION_FAILED */, "No magic link token found");
1752
1753
  }
1753
1754
  try {
1754
- const response = await fetch(`${this.coreUrl}/api/auth/signin/magic/verify`, {
1755
+ const response = await fetch(`${this.authUrl}/api/auth/signin/magic/verify`, {
1755
1756
  method: "POST",
1756
1757
  headers: {
1757
1758
  "Content-Type": "application/json"
@@ -1787,7 +1788,7 @@ var BlinkAuth = class {
1787
1788
  */
1788
1789
  async getAvailableProviders() {
1789
1790
  try {
1790
- const response = await fetch(`${this.coreUrl}/api/auth/providers?projectId=${encodeURIComponent(this.config.projectId)}`);
1791
+ const response = await fetch(`${this.authUrl}/api/auth/providers?projectId=${encodeURIComponent(this.config.projectId)}`);
1791
1792
  if (!response.ok) {
1792
1793
  return ["email", "google"];
1793
1794
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth + AI + analytics + notifications for modern SaaS/AI apps",
5
5
  "keywords": [
6
6
  "blink",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {},
52
52
  "devDependencies": {
53
- "@blink/core": "0.4.0",
53
+ "@blink/core": "0.4.1",
54
54
  "tsup": "^8.0.0",
55
55
  "typescript": "^5.0.0"
56
56
  },