@blinkdotnew/sdk 0.18.2 → 0.18.4

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
@@ -5,7 +5,7 @@
5
5
 
6
6
  **The full-stack TypeScript SDK that powers Blink AI-generated apps**
7
7
 
8
- Blink is an AI App Developer that builds fully functional apps in seconds. This SDK (`@blinkdotnew/sdk`) is the TypeScript foundation that powers every Blink app natively, providing zero-boilerplate authentication, database operations, AI capabilities, and file storage. Works seamlessly on both client-side (React, Vue, etc.) and server-side (Node.js, Deno, Edge functions).
8
+ Blink is an AI App Builder that builds fully functional apps in seconds. This SDK (`@blinkdotnew/sdk`) is the TypeScript foundation that powers every Blink app natively, providing zero-boilerplate authentication, database operations, AI capabilities, and file storage. Works seamlessly on both client-side (React, Vue, etc.) and server-side (Node.js, Deno, Edge functions).
9
9
 
10
10
  ## 🚀 Quick Start
11
11
 
@@ -133,7 +133,7 @@ const { publicUrl } = await blink.storage.upload(
133
133
 
134
134
  ## 🤖 What is Blink?
135
135
 
136
- **Blink is an AI App Developer** that creates fully functional applications in seconds. Simply describe what you want to build, and Blink's AI agent will:
136
+ **Blink is an AI App Builder** that creates fully functional applications in seconds. Simply describe what you want to build, and Blink's AI agent will:
137
137
 
138
138
  - 🏗️ **Generate complete apps** with React + TypeScript + Vite
139
139
  - 🔧 **Auto-install this SDK** with zero configuration
@@ -240,6 +240,9 @@ await blink.auth.sendMagicLink(email)
240
240
 
241
241
  // Password management
242
242
  await blink.auth.sendPasswordResetEmail(email)
243
+ await blink.auth.sendPasswordResetEmail(email, {
244
+ redirectUrl: 'https://myapp.com/reset-password'
245
+ })
243
246
  await blink.auth.changePassword(oldPass, newPass)
244
247
 
245
248
  // Email verification
@@ -434,6 +437,10 @@ blink.auth.setToken(jwt, persist?)
434
437
  const isAuth = blink.auth.isAuthenticated()
435
438
 
436
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
+ })
437
444
  await blink.auth.changePassword('oldPass', 'newPass')
438
445
  await blink.auth.confirmPasswordReset(token, newPassword)
439
446
 
@@ -1841,13 +1848,24 @@ const blink = createClient({
1841
1848
  })
1842
1849
 
1843
1850
  function AuthForm() {
1851
+ const [mode, setMode] = useState('signin') // 'signin' | 'signup' | 'reset'
1844
1852
  const [email, setEmail] = useState('')
1845
1853
  const [password, setPassword] = useState('')
1854
+ const [message, setMessage] = useState('')
1846
1855
 
1847
1856
  const handleEmailAuth = async () => {
1848
1857
  try {
1849
- await blink.auth.signInWithEmail(email, password)
1850
- // User state updates via onAuthStateChanged
1858
+ if (mode === 'signin') {
1859
+ await blink.auth.signInWithEmail(email, password)
1860
+ } else if (mode === 'signup') {
1861
+ await blink.auth.signUp({ email, password })
1862
+ setMessage('Account created! Check your email to verify.')
1863
+ } else if (mode === 'reset') {
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.')
1868
+ }
1851
1869
  } catch (error) {
1852
1870
  console.error('Auth failed:', error.message)
1853
1871
  }
@@ -1862,23 +1880,100 @@ function AuthForm() {
1862
1880
  }
1863
1881
 
1864
1882
  return (
1865
- <form onSubmit={handleEmailAuth}>
1866
- <input
1867
- type="email"
1868
- value={email}
1869
- onChange={(e) => setEmail(e.target.value)}
1870
- placeholder="Email"
1871
- />
1872
- <input
1873
- type="password"
1874
- value={password}
1875
- onChange={(e) => setPassword(e.target.value)}
1876
- placeholder="Password"
1883
+ <div>
1884
+ {message && <p style={{ color: 'green' }}>{message}</p>}
1885
+
1886
+ <form onSubmit={handleEmailAuth}>
1887
+ <input
1888
+ type="email"
1889
+ value={email}
1890
+ onChange={(e) => setEmail(e.target.value)}
1891
+ placeholder="Email"
1892
+ />
1893
+
1894
+ {mode !== 'reset' && (
1895
+ <input
1896
+ type="password"
1897
+ value={password}
1898
+ onChange={(e) => setPassword(e.target.value)}
1899
+ placeholder="Password"
1900
+ />
1901
+ )}
1902
+
1903
+ <button type="submit">
1904
+ {mode === 'signin' ? 'Sign In' : mode === 'signup' ? 'Sign Up' : 'Send Reset Email'}
1905
+ </button>
1906
+ </form>
1907
+
1908
+ {mode !== 'reset' && (
1909
+ <button type="button" onClick={handleSocialAuth}>
1910
+ Continue with Google
1911
+ </button>
1912
+ )}
1913
+
1914
+ <div>
1915
+ {mode === 'signin' && (
1916
+ <>
1917
+ <button onClick={() => setMode('signup')}>Create Account</button>
1918
+ <button onClick={() => setMode('reset')}>Forgot Password?</button>
1919
+ </>
1920
+ )}
1921
+ {mode === 'signup' && (
1922
+ <button onClick={() => setMode('signin')}>Back to Sign In</button>
1923
+ )}
1924
+ {mode === 'reset' && (
1925
+ <button onClick={() => setMode('signin')}>Back to Sign In</button>
1926
+ )}
1927
+ </div>
1928
+ </div>
1929
+ )
1930
+ }
1931
+ ```
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}
1877
1974
  />
1878
- <button type="submit">Sign In</button>
1879
- <button type="button" onClick={handleSocialAuth}>
1880
- Continue with Google
1881
- </button>
1975
+ <button type="submit">Reset Password</button>
1976
+ {message && <p style={{ color: 'green' }}>{message}</p>}
1882
1977
  </form>
1883
1978
  )
1884
1979
  }
package/dist/index.d.mts CHANGED
@@ -826,6 +826,14 @@ declare class BlinkAuth {
826
826
  private initializationPromise;
827
827
  private isInitialized;
828
828
  constructor(config: BlinkClientConfig);
829
+ /**
830
+ * Generate project-scoped storage key
831
+ */
832
+ private getStorageKey;
833
+ /**
834
+ * Migrate existing global tokens to project-scoped storage
835
+ */
836
+ private migrateExistingTokens;
829
837
  /**
830
838
  * Wait for authentication initialization to complete
831
839
  */
@@ -914,7 +922,9 @@ declare class BlinkAuth {
914
922
  /**
915
923
  * Send password reset email (using Blink default email service)
916
924
  */
917
- sendPasswordResetEmail(email: string): Promise<void>;
925
+ sendPasswordResetEmail(email: string, options?: {
926
+ redirectUrl?: string;
927
+ }): Promise<void>;
918
928
  /**
919
929
  * Confirm password reset with token
920
930
  */
@@ -1236,6 +1246,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
1236
1246
  private utmParams;
1237
1247
  private persistedAttribution;
1238
1248
  constructor(httpClient: HttpClient, projectId: string);
1249
+ /**
1250
+ * Generate project-scoped storage key for analytics
1251
+ */
1252
+ private getStorageKey;
1239
1253
  /**
1240
1254
  * Log a custom analytics event
1241
1255
  */
@@ -1475,7 +1489,7 @@ declare class BlinkAIImpl implements BlinkAI {
1475
1489
  * // With options
1476
1490
  * const { text, usage } = await blink.ai.generateText({
1477
1491
  * prompt: "Summarize this article",
1478
- * model: "gpt-4o-mini",
1492
+ * model: "gpt-4.1-mini",
1479
1493
  * maxTokens: 150,
1480
1494
  * temperature: 0.7
1481
1495
  * });
@@ -1483,7 +1497,7 @@ declare class BlinkAIImpl implements BlinkAI {
1483
1497
  * // With web search (OpenAI models only)
1484
1498
  * const { text, sources } = await blink.ai.generateText({
1485
1499
  * prompt: "What are the latest developments in AI?",
1486
- * model: "gpt-4o-mini",
1500
+ * model: "gpt-4.1-mini",
1487
1501
  * search: true // Enables web search
1488
1502
  * });
1489
1503
  *
package/dist/index.d.ts CHANGED
@@ -826,6 +826,14 @@ declare class BlinkAuth {
826
826
  private initializationPromise;
827
827
  private isInitialized;
828
828
  constructor(config: BlinkClientConfig);
829
+ /**
830
+ * Generate project-scoped storage key
831
+ */
832
+ private getStorageKey;
833
+ /**
834
+ * Migrate existing global tokens to project-scoped storage
835
+ */
836
+ private migrateExistingTokens;
829
837
  /**
830
838
  * Wait for authentication initialization to complete
831
839
  */
@@ -914,7 +922,9 @@ declare class BlinkAuth {
914
922
  /**
915
923
  * Send password reset email (using Blink default email service)
916
924
  */
917
- sendPasswordResetEmail(email: string): Promise<void>;
925
+ sendPasswordResetEmail(email: string, options?: {
926
+ redirectUrl?: string;
927
+ }): Promise<void>;
918
928
  /**
919
929
  * Confirm password reset with token
920
930
  */
@@ -1236,6 +1246,10 @@ declare class BlinkAnalyticsImpl implements BlinkAnalytics {
1236
1246
  private utmParams;
1237
1247
  private persistedAttribution;
1238
1248
  constructor(httpClient: HttpClient, projectId: string);
1249
+ /**
1250
+ * Generate project-scoped storage key for analytics
1251
+ */
1252
+ private getStorageKey;
1239
1253
  /**
1240
1254
  * Log a custom analytics event
1241
1255
  */
@@ -1475,7 +1489,7 @@ declare class BlinkAIImpl implements BlinkAI {
1475
1489
  * // With options
1476
1490
  * const { text, usage } = await blink.ai.generateText({
1477
1491
  * prompt: "Summarize this article",
1478
- * model: "gpt-4o-mini",
1492
+ * model: "gpt-4.1-mini",
1479
1493
  * maxTokens: 150,
1480
1494
  * temperature: 0.7
1481
1495
  * });
@@ -1483,7 +1497,7 @@ declare class BlinkAIImpl implements BlinkAI {
1483
1497
  * // With web search (OpenAI models only)
1484
1498
  * const { text, sources } = await blink.ai.generateText({
1485
1499
  * prompt: "What are the latest developments in AI?",
1486
- * model: "gpt-4o-mini",
1500
+ * model: "gpt-4.1-mini",
1487
1501
  * search: true // Enables web search
1488
1502
  * });
1489
1503
  *
package/dist/index.js CHANGED
@@ -916,6 +916,9 @@ var BlinkAuth = class {
916
916
  isInitialized = false;
917
917
  constructor(config) {
918
918
  this.config = config;
919
+ if (!config.projectId) {
920
+ throw new Error("projectId is required for authentication");
921
+ }
919
922
  this.authConfig = {
920
923
  mode: "managed",
921
924
  // Default mode
@@ -946,6 +949,25 @@ var BlinkAuth = class {
946
949
  this.isInitialized = true;
947
950
  }
948
951
  }
952
+ /**
953
+ * Generate project-scoped storage key
954
+ */
955
+ getStorageKey(suffix) {
956
+ return `blink_${suffix}_${this.config.projectId}`;
957
+ }
958
+ /**
959
+ * Migrate existing global tokens to project-scoped storage
960
+ */
961
+ migrateExistingTokens() {
962
+ if (typeof window === "undefined") return;
963
+ const oldTokens = localStorage.getItem("blink_tokens");
964
+ const projectKey = this.getStorageKey("tokens");
965
+ if (oldTokens && !localStorage.getItem(projectKey)) {
966
+ localStorage.setItem(projectKey, oldTokens);
967
+ localStorage.removeItem("blink_tokens");
968
+ console.log(`\u2705 Migrated tokens to project scope: ${this.config.projectId}`);
969
+ }
970
+ }
949
971
  /**
950
972
  * Wait for authentication initialization to complete
951
973
  */
@@ -996,6 +1018,7 @@ var BlinkAuth = class {
996
1018
  console.log("\u{1F680} Initializing Blink Auth...");
997
1019
  this.setLoading(true);
998
1020
  try {
1021
+ this.migrateExistingTokens();
999
1022
  if (this.isIframe) {
1000
1023
  console.log("\u{1F50D} Detected iframe environment, waiting for parent tokens...");
1001
1024
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -1501,7 +1524,7 @@ var BlinkAuth = class {
1501
1524
  /**
1502
1525
  * Send password reset email (using Blink default email service)
1503
1526
  */
1504
- async sendPasswordResetEmail(email) {
1527
+ async sendPasswordResetEmail(email, options) {
1505
1528
  try {
1506
1529
  const response = await fetch(`${this.authUrl}/api/auth/password/reset`, {
1507
1530
  method: "POST",
@@ -1510,7 +1533,8 @@ var BlinkAuth = class {
1510
1533
  },
1511
1534
  body: JSON.stringify({
1512
1535
  email,
1513
- projectId: this.config.projectId
1536
+ projectId: this.config.projectId,
1537
+ redirectUrl: options?.redirectUrl
1514
1538
  })
1515
1539
  });
1516
1540
  if (!response.ok) {
@@ -2067,7 +2091,7 @@ var BlinkAuth = class {
2067
2091
  });
2068
2092
  if (persist && typeof window !== "undefined") {
2069
2093
  try {
2070
- localStorage.setItem("blink_tokens", JSON.stringify(tokensWithTimestamp));
2094
+ localStorage.setItem(this.getStorageKey("tokens"), JSON.stringify(tokensWithTimestamp));
2071
2095
  console.log("\u{1F4BE} Tokens persisted to localStorage");
2072
2096
  } catch (error) {
2073
2097
  console.log("\u{1F4A5} Error persisting tokens to localStorage:", error);
@@ -2118,7 +2142,7 @@ var BlinkAuth = class {
2118
2142
  clearTokens() {
2119
2143
  if (typeof window !== "undefined") {
2120
2144
  try {
2121
- localStorage.removeItem("blink_tokens");
2145
+ localStorage.removeItem(this.getStorageKey("tokens"));
2122
2146
  } catch (error) {
2123
2147
  console.log("\u{1F4A5} Error clearing tokens from localStorage:", error);
2124
2148
  }
@@ -2136,7 +2160,7 @@ var BlinkAuth = class {
2136
2160
  return this.parentWindowTokens;
2137
2161
  }
2138
2162
  try {
2139
- const stored = localStorage.getItem("blink_tokens");
2163
+ const stored = localStorage.getItem(this.getStorageKey("tokens"));
2140
2164
  console.log("\u{1F50D} Checking localStorage for tokens:", {
2141
2165
  hasStoredData: !!stored,
2142
2166
  storedLength: stored?.length || 0,
@@ -2306,7 +2330,7 @@ var BlinkAuth = class {
2306
2330
  setupCrossTabSync() {
2307
2331
  if (typeof window === "undefined") return;
2308
2332
  window.addEventListener("storage", (e) => {
2309
- if (e.key === "blink_tokens") {
2333
+ if (e.key === this.getStorageKey("tokens")) {
2310
2334
  const newTokens = e.newValue ? JSON.parse(e.newValue) : null;
2311
2335
  if (newTokens && newTokens !== this.authState.tokens) {
2312
2336
  this.setTokens(newTokens, false).catch((error) => {
@@ -3053,7 +3077,7 @@ var BlinkAIImpl = class {
3053
3077
  * // With options
3054
3078
  * const { text, usage } = await blink.ai.generateText({
3055
3079
  * prompt: "Summarize this article",
3056
- * model: "gpt-4o-mini",
3080
+ * model: "gpt-4.1-mini",
3057
3081
  * maxTokens: 150,
3058
3082
  * temperature: 0.7
3059
3083
  * });
@@ -3061,7 +3085,7 @@ var BlinkAIImpl = class {
3061
3085
  * // With web search (OpenAI models only)
3062
3086
  * const { text, sources } = await blink.ai.generateText({
3063
3087
  * prompt: "What are the latest developments in AI?",
3064
- * model: "gpt-4o-mini",
3088
+ * model: "gpt-4.1-mini",
3065
3089
  * search: true // Enables web search
3066
3090
  * });
3067
3091
  *
@@ -4455,9 +4479,6 @@ var SESSION_DURATION = 30 * 60 * 1e3;
4455
4479
  var MAX_BATCH_SIZE = 10;
4456
4480
  var BATCH_TIMEOUT = 3e3;
4457
4481
  var MAX_STRING_LENGTH = 256;
4458
- var STORAGE_KEY_QUEUE = "blinkAnalyticsQueue";
4459
- var STORAGE_KEY_SESSION = "blinkAnalyticsSession";
4460
- var STORAGE_KEY_ATTRIBUTION = "blinkAnalyticsAttribution";
4461
4482
  var BlinkAnalyticsImpl = class {
4462
4483
  httpClient;
4463
4484
  projectId;
@@ -4486,6 +4507,12 @@ var BlinkAnalyticsImpl = class {
4486
4507
  this.setupRouteChangeListener();
4487
4508
  this.setupUnloadListener();
4488
4509
  }
4510
+ /**
4511
+ * Generate project-scoped storage key for analytics
4512
+ */
4513
+ getStorageKey(suffix) {
4514
+ return `blinkAnalytics${suffix}_${this.projectId}`;
4515
+ }
4489
4516
  /**
4490
4517
  * Log a custom analytics event
4491
4518
  */
@@ -4542,7 +4569,7 @@ var BlinkAnalyticsImpl = class {
4542
4569
  clearAttribution() {
4543
4570
  this.persistedAttribution = {};
4544
4571
  try {
4545
- localStorage.removeItem(STORAGE_KEY_ATTRIBUTION);
4572
+ localStorage.removeItem(this.getStorageKey("Attribution"));
4546
4573
  } catch {
4547
4574
  }
4548
4575
  }
@@ -4618,7 +4645,7 @@ var BlinkAnalyticsImpl = class {
4618
4645
  }
4619
4646
  getOrCreateSessionId() {
4620
4647
  try {
4621
- const stored = localStorage.getItem(STORAGE_KEY_SESSION);
4648
+ const stored = localStorage.getItem(this.getStorageKey("Session"));
4622
4649
  if (stored) {
4623
4650
  const session = JSON.parse(stored);
4624
4651
  const now = Date.now();
@@ -4626,7 +4653,7 @@ var BlinkAnalyticsImpl = class {
4626
4653
  return this.createNewSession();
4627
4654
  }
4628
4655
  session.lastActivityAt = now;
4629
- localStorage.setItem(STORAGE_KEY_SESSION, JSON.stringify(session));
4656
+ localStorage.setItem(this.getStorageKey("Session"), JSON.stringify(session));
4630
4657
  return session.id;
4631
4658
  }
4632
4659
  return this.createNewSession();
@@ -4643,14 +4670,14 @@ var BlinkAnalyticsImpl = class {
4643
4670
  lastActivityAt: now
4644
4671
  };
4645
4672
  try {
4646
- localStorage.setItem(STORAGE_KEY_SESSION, JSON.stringify(session));
4673
+ localStorage.setItem(this.getStorageKey("Session"), JSON.stringify(session));
4647
4674
  } catch {
4648
4675
  }
4649
4676
  return session.id;
4650
4677
  }
4651
4678
  loadQueue() {
4652
4679
  try {
4653
- const stored = localStorage.getItem(STORAGE_KEY_QUEUE);
4680
+ const stored = localStorage.getItem(this.getStorageKey("Queue"));
4654
4681
  if (stored) {
4655
4682
  this.queue = JSON.parse(stored);
4656
4683
  if (this.queue.length > 0) {
@@ -4664,9 +4691,9 @@ var BlinkAnalyticsImpl = class {
4664
4691
  persistQueue() {
4665
4692
  try {
4666
4693
  if (this.queue.length === 0) {
4667
- localStorage.removeItem(STORAGE_KEY_QUEUE);
4694
+ localStorage.removeItem(this.getStorageKey("Queue"));
4668
4695
  } else {
4669
- localStorage.setItem(STORAGE_KEY_QUEUE, JSON.stringify(this.queue));
4696
+ localStorage.setItem(this.getStorageKey("Queue"), JSON.stringify(this.queue));
4670
4697
  }
4671
4698
  } catch {
4672
4699
  }
@@ -4734,7 +4761,7 @@ var BlinkAnalyticsImpl = class {
4734
4761
  }
4735
4762
  loadPersistedAttribution() {
4736
4763
  try {
4737
- const stored = localStorage.getItem(STORAGE_KEY_ATTRIBUTION);
4764
+ const stored = localStorage.getItem(this.getStorageKey("Attribution"));
4738
4765
  if (stored) {
4739
4766
  this.persistedAttribution = JSON.parse(stored);
4740
4767
  }
@@ -4750,7 +4777,7 @@ var BlinkAnalyticsImpl = class {
4750
4777
  Object.entries(this.utmParams).filter(([_, v]) => v !== null)
4751
4778
  )
4752
4779
  };
4753
- localStorage.setItem(STORAGE_KEY_ATTRIBUTION, JSON.stringify(attribution));
4780
+ localStorage.setItem(this.getStorageKey("Attribution"), JSON.stringify(attribution));
4754
4781
  this.persistedAttribution = attribution;
4755
4782
  } catch {
4756
4783
  }
package/dist/index.mjs CHANGED
@@ -914,6 +914,9 @@ var BlinkAuth = class {
914
914
  isInitialized = false;
915
915
  constructor(config) {
916
916
  this.config = config;
917
+ if (!config.projectId) {
918
+ throw new Error("projectId is required for authentication");
919
+ }
917
920
  this.authConfig = {
918
921
  mode: "managed",
919
922
  // Default mode
@@ -944,6 +947,25 @@ var BlinkAuth = class {
944
947
  this.isInitialized = true;
945
948
  }
946
949
  }
950
+ /**
951
+ * Generate project-scoped storage key
952
+ */
953
+ getStorageKey(suffix) {
954
+ return `blink_${suffix}_${this.config.projectId}`;
955
+ }
956
+ /**
957
+ * Migrate existing global tokens to project-scoped storage
958
+ */
959
+ migrateExistingTokens() {
960
+ if (typeof window === "undefined") return;
961
+ const oldTokens = localStorage.getItem("blink_tokens");
962
+ const projectKey = this.getStorageKey("tokens");
963
+ if (oldTokens && !localStorage.getItem(projectKey)) {
964
+ localStorage.setItem(projectKey, oldTokens);
965
+ localStorage.removeItem("blink_tokens");
966
+ console.log(`\u2705 Migrated tokens to project scope: ${this.config.projectId}`);
967
+ }
968
+ }
947
969
  /**
948
970
  * Wait for authentication initialization to complete
949
971
  */
@@ -994,6 +1016,7 @@ var BlinkAuth = class {
994
1016
  console.log("\u{1F680} Initializing Blink Auth...");
995
1017
  this.setLoading(true);
996
1018
  try {
1019
+ this.migrateExistingTokens();
997
1020
  if (this.isIframe) {
998
1021
  console.log("\u{1F50D} Detected iframe environment, waiting for parent tokens...");
999
1022
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -1499,7 +1522,7 @@ var BlinkAuth = class {
1499
1522
  /**
1500
1523
  * Send password reset email (using Blink default email service)
1501
1524
  */
1502
- async sendPasswordResetEmail(email) {
1525
+ async sendPasswordResetEmail(email, options) {
1503
1526
  try {
1504
1527
  const response = await fetch(`${this.authUrl}/api/auth/password/reset`, {
1505
1528
  method: "POST",
@@ -1508,7 +1531,8 @@ var BlinkAuth = class {
1508
1531
  },
1509
1532
  body: JSON.stringify({
1510
1533
  email,
1511
- projectId: this.config.projectId
1534
+ projectId: this.config.projectId,
1535
+ redirectUrl: options?.redirectUrl
1512
1536
  })
1513
1537
  });
1514
1538
  if (!response.ok) {
@@ -2065,7 +2089,7 @@ var BlinkAuth = class {
2065
2089
  });
2066
2090
  if (persist && typeof window !== "undefined") {
2067
2091
  try {
2068
- localStorage.setItem("blink_tokens", JSON.stringify(tokensWithTimestamp));
2092
+ localStorage.setItem(this.getStorageKey("tokens"), JSON.stringify(tokensWithTimestamp));
2069
2093
  console.log("\u{1F4BE} Tokens persisted to localStorage");
2070
2094
  } catch (error) {
2071
2095
  console.log("\u{1F4A5} Error persisting tokens to localStorage:", error);
@@ -2116,7 +2140,7 @@ var BlinkAuth = class {
2116
2140
  clearTokens() {
2117
2141
  if (typeof window !== "undefined") {
2118
2142
  try {
2119
- localStorage.removeItem("blink_tokens");
2143
+ localStorage.removeItem(this.getStorageKey("tokens"));
2120
2144
  } catch (error) {
2121
2145
  console.log("\u{1F4A5} Error clearing tokens from localStorage:", error);
2122
2146
  }
@@ -2134,7 +2158,7 @@ var BlinkAuth = class {
2134
2158
  return this.parentWindowTokens;
2135
2159
  }
2136
2160
  try {
2137
- const stored = localStorage.getItem("blink_tokens");
2161
+ const stored = localStorage.getItem(this.getStorageKey("tokens"));
2138
2162
  console.log("\u{1F50D} Checking localStorage for tokens:", {
2139
2163
  hasStoredData: !!stored,
2140
2164
  storedLength: stored?.length || 0,
@@ -2304,7 +2328,7 @@ var BlinkAuth = class {
2304
2328
  setupCrossTabSync() {
2305
2329
  if (typeof window === "undefined") return;
2306
2330
  window.addEventListener("storage", (e) => {
2307
- if (e.key === "blink_tokens") {
2331
+ if (e.key === this.getStorageKey("tokens")) {
2308
2332
  const newTokens = e.newValue ? JSON.parse(e.newValue) : null;
2309
2333
  if (newTokens && newTokens !== this.authState.tokens) {
2310
2334
  this.setTokens(newTokens, false).catch((error) => {
@@ -3051,7 +3075,7 @@ var BlinkAIImpl = class {
3051
3075
  * // With options
3052
3076
  * const { text, usage } = await blink.ai.generateText({
3053
3077
  * prompt: "Summarize this article",
3054
- * model: "gpt-4o-mini",
3078
+ * model: "gpt-4.1-mini",
3055
3079
  * maxTokens: 150,
3056
3080
  * temperature: 0.7
3057
3081
  * });
@@ -3059,7 +3083,7 @@ var BlinkAIImpl = class {
3059
3083
  * // With web search (OpenAI models only)
3060
3084
  * const { text, sources } = await blink.ai.generateText({
3061
3085
  * prompt: "What are the latest developments in AI?",
3062
- * model: "gpt-4o-mini",
3086
+ * model: "gpt-4.1-mini",
3063
3087
  * search: true // Enables web search
3064
3088
  * });
3065
3089
  *
@@ -4453,9 +4477,6 @@ var SESSION_DURATION = 30 * 60 * 1e3;
4453
4477
  var MAX_BATCH_SIZE = 10;
4454
4478
  var BATCH_TIMEOUT = 3e3;
4455
4479
  var MAX_STRING_LENGTH = 256;
4456
- var STORAGE_KEY_QUEUE = "blinkAnalyticsQueue";
4457
- var STORAGE_KEY_SESSION = "blinkAnalyticsSession";
4458
- var STORAGE_KEY_ATTRIBUTION = "blinkAnalyticsAttribution";
4459
4480
  var BlinkAnalyticsImpl = class {
4460
4481
  httpClient;
4461
4482
  projectId;
@@ -4484,6 +4505,12 @@ var BlinkAnalyticsImpl = class {
4484
4505
  this.setupRouteChangeListener();
4485
4506
  this.setupUnloadListener();
4486
4507
  }
4508
+ /**
4509
+ * Generate project-scoped storage key for analytics
4510
+ */
4511
+ getStorageKey(suffix) {
4512
+ return `blinkAnalytics${suffix}_${this.projectId}`;
4513
+ }
4487
4514
  /**
4488
4515
  * Log a custom analytics event
4489
4516
  */
@@ -4540,7 +4567,7 @@ var BlinkAnalyticsImpl = class {
4540
4567
  clearAttribution() {
4541
4568
  this.persistedAttribution = {};
4542
4569
  try {
4543
- localStorage.removeItem(STORAGE_KEY_ATTRIBUTION);
4570
+ localStorage.removeItem(this.getStorageKey("Attribution"));
4544
4571
  } catch {
4545
4572
  }
4546
4573
  }
@@ -4616,7 +4643,7 @@ var BlinkAnalyticsImpl = class {
4616
4643
  }
4617
4644
  getOrCreateSessionId() {
4618
4645
  try {
4619
- const stored = localStorage.getItem(STORAGE_KEY_SESSION);
4646
+ const stored = localStorage.getItem(this.getStorageKey("Session"));
4620
4647
  if (stored) {
4621
4648
  const session = JSON.parse(stored);
4622
4649
  const now = Date.now();
@@ -4624,7 +4651,7 @@ var BlinkAnalyticsImpl = class {
4624
4651
  return this.createNewSession();
4625
4652
  }
4626
4653
  session.lastActivityAt = now;
4627
- localStorage.setItem(STORAGE_KEY_SESSION, JSON.stringify(session));
4654
+ localStorage.setItem(this.getStorageKey("Session"), JSON.stringify(session));
4628
4655
  return session.id;
4629
4656
  }
4630
4657
  return this.createNewSession();
@@ -4641,14 +4668,14 @@ var BlinkAnalyticsImpl = class {
4641
4668
  lastActivityAt: now
4642
4669
  };
4643
4670
  try {
4644
- localStorage.setItem(STORAGE_KEY_SESSION, JSON.stringify(session));
4671
+ localStorage.setItem(this.getStorageKey("Session"), JSON.stringify(session));
4645
4672
  } catch {
4646
4673
  }
4647
4674
  return session.id;
4648
4675
  }
4649
4676
  loadQueue() {
4650
4677
  try {
4651
- const stored = localStorage.getItem(STORAGE_KEY_QUEUE);
4678
+ const stored = localStorage.getItem(this.getStorageKey("Queue"));
4652
4679
  if (stored) {
4653
4680
  this.queue = JSON.parse(stored);
4654
4681
  if (this.queue.length > 0) {
@@ -4662,9 +4689,9 @@ var BlinkAnalyticsImpl = class {
4662
4689
  persistQueue() {
4663
4690
  try {
4664
4691
  if (this.queue.length === 0) {
4665
- localStorage.removeItem(STORAGE_KEY_QUEUE);
4692
+ localStorage.removeItem(this.getStorageKey("Queue"));
4666
4693
  } else {
4667
- localStorage.setItem(STORAGE_KEY_QUEUE, JSON.stringify(this.queue));
4694
+ localStorage.setItem(this.getStorageKey("Queue"), JSON.stringify(this.queue));
4668
4695
  }
4669
4696
  } catch {
4670
4697
  }
@@ -4732,7 +4759,7 @@ var BlinkAnalyticsImpl = class {
4732
4759
  }
4733
4760
  loadPersistedAttribution() {
4734
4761
  try {
4735
- const stored = localStorage.getItem(STORAGE_KEY_ATTRIBUTION);
4762
+ const stored = localStorage.getItem(this.getStorageKey("Attribution"));
4736
4763
  if (stored) {
4737
4764
  this.persistedAttribution = JSON.parse(stored);
4738
4765
  }
@@ -4748,7 +4775,7 @@ var BlinkAnalyticsImpl = class {
4748
4775
  Object.entries(this.utmParams).filter(([_, v]) => v !== null)
4749
4776
  )
4750
4777
  };
4751
- localStorage.setItem(STORAGE_KEY_ATTRIBUTION, JSON.stringify(attribution));
4778
+ localStorage.setItem(this.getStorageKey("Attribution"), JSON.stringify(attribution));
4752
4779
  this.persistedAttribution = attribution;
4753
4780
  } catch {
4754
4781
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "0.18.2",
3
+ "version": "0.18.4",
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",