stellar-drive 1.0.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 (246) hide show
  1. package/README.md +607 -0
  2. package/dist/actions/remoteChange.d.ts +204 -0
  3. package/dist/actions/remoteChange.d.ts.map +1 -0
  4. package/dist/actions/remoteChange.js +424 -0
  5. package/dist/actions/remoteChange.js.map +1 -0
  6. package/dist/actions/truncateTooltip.d.ts +56 -0
  7. package/dist/actions/truncateTooltip.d.ts.map +1 -0
  8. package/dist/actions/truncateTooltip.js +312 -0
  9. package/dist/actions/truncateTooltip.js.map +1 -0
  10. package/dist/auth/crypto.d.ts +41 -0
  11. package/dist/auth/crypto.d.ts.map +1 -0
  12. package/dist/auth/crypto.js +50 -0
  13. package/dist/auth/crypto.js.map +1 -0
  14. package/dist/auth/deviceVerification.d.ts +283 -0
  15. package/dist/auth/deviceVerification.d.ts.map +1 -0
  16. package/dist/auth/deviceVerification.js +575 -0
  17. package/dist/auth/deviceVerification.js.map +1 -0
  18. package/dist/auth/displayUtils.d.ts +98 -0
  19. package/dist/auth/displayUtils.d.ts.map +1 -0
  20. package/dist/auth/displayUtils.js +145 -0
  21. package/dist/auth/displayUtils.js.map +1 -0
  22. package/dist/auth/loginGuard.d.ts +134 -0
  23. package/dist/auth/loginGuard.d.ts.map +1 -0
  24. package/dist/auth/loginGuard.js +276 -0
  25. package/dist/auth/loginGuard.js.map +1 -0
  26. package/dist/auth/offlineCredentials.d.ts +105 -0
  27. package/dist/auth/offlineCredentials.d.ts.map +1 -0
  28. package/dist/auth/offlineCredentials.js +176 -0
  29. package/dist/auth/offlineCredentials.js.map +1 -0
  30. package/dist/auth/offlineSession.d.ts +96 -0
  31. package/dist/auth/offlineSession.d.ts.map +1 -0
  32. package/dist/auth/offlineSession.js +145 -0
  33. package/dist/auth/offlineSession.js.map +1 -0
  34. package/dist/auth/resolveAuthState.d.ts +85 -0
  35. package/dist/auth/resolveAuthState.d.ts.map +1 -0
  36. package/dist/auth/resolveAuthState.js +249 -0
  37. package/dist/auth/resolveAuthState.js.map +1 -0
  38. package/dist/auth/singleUser.d.ts +498 -0
  39. package/dist/auth/singleUser.d.ts.map +1 -0
  40. package/dist/auth/singleUser.js +1282 -0
  41. package/dist/auth/singleUser.js.map +1 -0
  42. package/dist/bin/commands.d.ts +14 -0
  43. package/dist/bin/commands.d.ts.map +1 -0
  44. package/dist/bin/commands.js +68 -0
  45. package/dist/bin/commands.js.map +1 -0
  46. package/dist/bin/install-pwa.d.ts +41 -0
  47. package/dist/bin/install-pwa.d.ts.map +1 -0
  48. package/dist/bin/install-pwa.js +4594 -0
  49. package/dist/bin/install-pwa.js.map +1 -0
  50. package/dist/config.d.ts +249 -0
  51. package/dist/config.d.ts.map +1 -0
  52. package/dist/config.js +395 -0
  53. package/dist/config.js.map +1 -0
  54. package/dist/conflicts.d.ts +306 -0
  55. package/dist/conflicts.d.ts.map +1 -0
  56. package/dist/conflicts.js +807 -0
  57. package/dist/conflicts.js.map +1 -0
  58. package/dist/crdt/awareness.d.ts +128 -0
  59. package/dist/crdt/awareness.d.ts.map +1 -0
  60. package/dist/crdt/awareness.js +284 -0
  61. package/dist/crdt/awareness.js.map +1 -0
  62. package/dist/crdt/channel.d.ts +165 -0
  63. package/dist/crdt/channel.d.ts.map +1 -0
  64. package/dist/crdt/channel.js +522 -0
  65. package/dist/crdt/channel.js.map +1 -0
  66. package/dist/crdt/config.d.ts +58 -0
  67. package/dist/crdt/config.d.ts.map +1 -0
  68. package/dist/crdt/config.js +123 -0
  69. package/dist/crdt/config.js.map +1 -0
  70. package/dist/crdt/helpers.d.ts +104 -0
  71. package/dist/crdt/helpers.d.ts.map +1 -0
  72. package/dist/crdt/helpers.js +116 -0
  73. package/dist/crdt/helpers.js.map +1 -0
  74. package/dist/crdt/offline.d.ts +58 -0
  75. package/dist/crdt/offline.d.ts.map +1 -0
  76. package/dist/crdt/offline.js +130 -0
  77. package/dist/crdt/offline.js.map +1 -0
  78. package/dist/crdt/persistence.d.ts +65 -0
  79. package/dist/crdt/persistence.d.ts.map +1 -0
  80. package/dist/crdt/persistence.js +171 -0
  81. package/dist/crdt/persistence.js.map +1 -0
  82. package/dist/crdt/provider.d.ts +109 -0
  83. package/dist/crdt/provider.d.ts.map +1 -0
  84. package/dist/crdt/provider.js +543 -0
  85. package/dist/crdt/provider.js.map +1 -0
  86. package/dist/crdt/store.d.ts +111 -0
  87. package/dist/crdt/store.d.ts.map +1 -0
  88. package/dist/crdt/store.js +158 -0
  89. package/dist/crdt/store.js.map +1 -0
  90. package/dist/crdt/types.d.ts +281 -0
  91. package/dist/crdt/types.d.ts.map +1 -0
  92. package/dist/crdt/types.js +26 -0
  93. package/dist/crdt/types.js.map +1 -0
  94. package/dist/data.d.ts +502 -0
  95. package/dist/data.d.ts.map +1 -0
  96. package/dist/data.js +862 -0
  97. package/dist/data.js.map +1 -0
  98. package/dist/database.d.ts +153 -0
  99. package/dist/database.d.ts.map +1 -0
  100. package/dist/database.js +325 -0
  101. package/dist/database.js.map +1 -0
  102. package/dist/debug.d.ts +87 -0
  103. package/dist/debug.d.ts.map +1 -0
  104. package/dist/debug.js +135 -0
  105. package/dist/debug.js.map +1 -0
  106. package/dist/demo.d.ts +131 -0
  107. package/dist/demo.d.ts.map +1 -0
  108. package/dist/demo.js +168 -0
  109. package/dist/demo.js.map +1 -0
  110. package/dist/deviceId.d.ts +47 -0
  111. package/dist/deviceId.d.ts.map +1 -0
  112. package/dist/deviceId.js +106 -0
  113. package/dist/deviceId.js.map +1 -0
  114. package/dist/diagnostics.d.ts +292 -0
  115. package/dist/diagnostics.d.ts.map +1 -0
  116. package/dist/diagnostics.js +378 -0
  117. package/dist/diagnostics.js.map +1 -0
  118. package/dist/engine.d.ts +230 -0
  119. package/dist/engine.d.ts.map +1 -0
  120. package/dist/engine.js +2636 -0
  121. package/dist/engine.js.map +1 -0
  122. package/dist/entries/actions.d.ts +16 -0
  123. package/dist/entries/actions.d.ts.map +1 -0
  124. package/dist/entries/actions.js +29 -0
  125. package/dist/entries/actions.js.map +1 -0
  126. package/dist/entries/auth.d.ts +19 -0
  127. package/dist/entries/auth.d.ts.map +1 -0
  128. package/dist/entries/auth.js +50 -0
  129. package/dist/entries/auth.js.map +1 -0
  130. package/dist/entries/config.d.ts +15 -0
  131. package/dist/entries/config.d.ts.map +1 -0
  132. package/dist/entries/config.js +20 -0
  133. package/dist/entries/config.js.map +1 -0
  134. package/dist/entries/crdt.d.ts +32 -0
  135. package/dist/entries/crdt.d.ts.map +1 -0
  136. package/dist/entries/crdt.js +52 -0
  137. package/dist/entries/crdt.js.map +1 -0
  138. package/dist/entries/kit.d.ts +22 -0
  139. package/dist/entries/kit.d.ts.map +1 -0
  140. package/dist/entries/kit.js +58 -0
  141. package/dist/entries/kit.js.map +1 -0
  142. package/dist/entries/stores.d.ts +22 -0
  143. package/dist/entries/stores.d.ts.map +1 -0
  144. package/dist/entries/stores.js +57 -0
  145. package/dist/entries/stores.js.map +1 -0
  146. package/dist/entries/types.d.ts +23 -0
  147. package/dist/entries/types.d.ts.map +1 -0
  148. package/dist/entries/types.js +12 -0
  149. package/dist/entries/types.js.map +1 -0
  150. package/dist/entries/utils.d.ts +12 -0
  151. package/dist/entries/utils.d.ts.map +1 -0
  152. package/dist/entries/utils.js +42 -0
  153. package/dist/entries/utils.js.map +1 -0
  154. package/dist/entries/vite.d.ts +20 -0
  155. package/dist/entries/vite.d.ts.map +1 -0
  156. package/dist/entries/vite.js +26 -0
  157. package/dist/entries/vite.js.map +1 -0
  158. package/dist/index.d.ts +77 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +234 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/kit/auth.d.ts +80 -0
  163. package/dist/kit/auth.d.ts.map +1 -0
  164. package/dist/kit/auth.js +75 -0
  165. package/dist/kit/auth.js.map +1 -0
  166. package/dist/kit/confirm.d.ts +111 -0
  167. package/dist/kit/confirm.d.ts.map +1 -0
  168. package/dist/kit/confirm.js +169 -0
  169. package/dist/kit/confirm.js.map +1 -0
  170. package/dist/kit/loads.d.ts +187 -0
  171. package/dist/kit/loads.d.ts.map +1 -0
  172. package/dist/kit/loads.js +208 -0
  173. package/dist/kit/loads.js.map +1 -0
  174. package/dist/kit/server.d.ts +175 -0
  175. package/dist/kit/server.d.ts.map +1 -0
  176. package/dist/kit/server.js +297 -0
  177. package/dist/kit/server.js.map +1 -0
  178. package/dist/kit/sw.d.ts +176 -0
  179. package/dist/kit/sw.d.ts.map +1 -0
  180. package/dist/kit/sw.js +320 -0
  181. package/dist/kit/sw.js.map +1 -0
  182. package/dist/queue.d.ts +306 -0
  183. package/dist/queue.d.ts.map +1 -0
  184. package/dist/queue.js +925 -0
  185. package/dist/queue.js.map +1 -0
  186. package/dist/realtime.d.ts +280 -0
  187. package/dist/realtime.d.ts.map +1 -0
  188. package/dist/realtime.js +1031 -0
  189. package/dist/realtime.js.map +1 -0
  190. package/dist/runtime/runtimeConfig.d.ts +110 -0
  191. package/dist/runtime/runtimeConfig.d.ts.map +1 -0
  192. package/dist/runtime/runtimeConfig.js +260 -0
  193. package/dist/runtime/runtimeConfig.js.map +1 -0
  194. package/dist/schema.d.ts +150 -0
  195. package/dist/schema.d.ts.map +1 -0
  196. package/dist/schema.js +891 -0
  197. package/dist/schema.js.map +1 -0
  198. package/dist/stores/authState.d.ts +204 -0
  199. package/dist/stores/authState.d.ts.map +1 -0
  200. package/dist/stores/authState.js +336 -0
  201. package/dist/stores/authState.js.map +1 -0
  202. package/dist/stores/factories.d.ts +140 -0
  203. package/dist/stores/factories.d.ts.map +1 -0
  204. package/dist/stores/factories.js +157 -0
  205. package/dist/stores/factories.js.map +1 -0
  206. package/dist/stores/network.d.ts +48 -0
  207. package/dist/stores/network.d.ts.map +1 -0
  208. package/dist/stores/network.js +261 -0
  209. package/dist/stores/network.js.map +1 -0
  210. package/dist/stores/remoteChanges.d.ts +417 -0
  211. package/dist/stores/remoteChanges.d.ts.map +1 -0
  212. package/dist/stores/remoteChanges.js +626 -0
  213. package/dist/stores/remoteChanges.js.map +1 -0
  214. package/dist/stores/sync.d.ts +165 -0
  215. package/dist/stores/sync.d.ts.map +1 -0
  216. package/dist/stores/sync.js +275 -0
  217. package/dist/stores/sync.js.map +1 -0
  218. package/dist/supabase/auth.d.ts +219 -0
  219. package/dist/supabase/auth.d.ts.map +1 -0
  220. package/dist/supabase/auth.js +459 -0
  221. package/dist/supabase/auth.js.map +1 -0
  222. package/dist/supabase/client.d.ts +88 -0
  223. package/dist/supabase/client.d.ts.map +1 -0
  224. package/dist/supabase/client.js +313 -0
  225. package/dist/supabase/client.js.map +1 -0
  226. package/dist/supabase/validate.d.ts +118 -0
  227. package/dist/supabase/validate.d.ts.map +1 -0
  228. package/dist/supabase/validate.js +208 -0
  229. package/dist/supabase/validate.js.map +1 -0
  230. package/dist/sw/build/vite-plugin.d.ts +149 -0
  231. package/dist/sw/build/vite-plugin.d.ts.map +1 -0
  232. package/dist/sw/build/vite-plugin.js +517 -0
  233. package/dist/sw/build/vite-plugin.js.map +1 -0
  234. package/dist/sw/sw.js +664 -0
  235. package/dist/types.d.ts +363 -0
  236. package/dist/types.d.ts.map +1 -0
  237. package/dist/types.js +18 -0
  238. package/dist/types.js.map +1 -0
  239. package/dist/utils.d.ts +85 -0
  240. package/dist/utils.d.ts.map +1 -0
  241. package/dist/utils.js +156 -0
  242. package/dist/utils.js.map +1 -0
  243. package/package.json +117 -0
  244. package/src/components/DeferredChangesBanner.svelte +477 -0
  245. package/src/components/DemoBanner.svelte +110 -0
  246. package/src/components/SyncStatus.svelte +1732 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * @fileoverview Supabase Authentication Module
3
+ *
4
+ * Provides core authentication utilities on top of Supabase Auth for the
5
+ * single-user PIN/password gate system:
6
+ *
7
+ * - **Sign-out & teardown**: Full 10-step teardown sequence to ensure no stale
8
+ * data leaks across sessions.
9
+ *
10
+ * - **Session management**: `getSession()` falls back to localStorage when the
11
+ * device is offline, ensuring the app can still render authenticated views
12
+ * with stale-but-usable session data.
13
+ *
14
+ * - **Profile CRUD**: Read and update user profile metadata on Supabase and
15
+ * in the offline credential cache.
16
+ *
17
+ * - **Email confirmation & OTP**: Resend confirmation emails and verify OTP
18
+ * token hashes from confirmation links.
19
+ *
20
+ * Security considerations:
21
+ * - Corrupted sessions are detected and automatically cleared to prevent
22
+ * infinite error loops.
23
+ * - Sign-out follows a strict 10-step teardown sequence to ensure no stale
24
+ * data leaks across user boundaries.
25
+ *
26
+ * Integration patterns:
27
+ * - Used internally by single-user auth (`../auth/singleUser.ts`) and the
28
+ * scaffolded confirm page (`../kit/confirm.ts`).
29
+ * - Works in tandem with `./client.ts` (lazy Supabase singleton) and
30
+ * `../engine.ts` (sync engine lifecycle).
31
+ * - Offline credential helpers live in `../auth/offlineCredentials.ts`.
32
+ *
33
+ * @module supabase/auth
34
+ */
35
+ import { supabase } from './client';
36
+ import { clearOfflineCredentials, updateOfflineCredentialsProfile } from '../auth/offlineCredentials';
37
+ import { clearOfflineSession } from '../auth/offlineSession';
38
+ import { resetLoginGuard } from '../auth/loginGuard';
39
+ import { debugWarn, debugError } from '../debug';
40
+ import { getEngineConfig } from '../config';
41
+ import { syncStatusStore } from '../stores/sync';
42
+ import { authState } from '../stores/authState';
43
+ import { isDemoMode } from '../demo';
44
+ // =============================================================================
45
+ // SECTION: Helpers
46
+ // =============================================================================
47
+ /**
48
+ * Get the email confirmation redirect URL.
49
+ *
50
+ * Points to the `/confirm` page (or the path configured via
51
+ * `auth.confirmRedirectPath`) which handles the token verification flow
52
+ * after a user clicks the link in their confirmation email.
53
+ *
54
+ * @returns The fully-qualified redirect URL, e.g. `https://app.example.com/confirm`.
55
+ * Falls back to a relative `/confirm` path in SSR environments where
56
+ * `window` is unavailable.
57
+ *
58
+ * @see {@link resendConfirmationEmail} — uses this URL
59
+ */
60
+ function getConfirmRedirectUrl() {
61
+ if (typeof window !== 'undefined') {
62
+ const path = getEngineConfig().auth?.confirmRedirectPath || '/confirm';
63
+ return `${window.location.origin}${path}`;
64
+ }
65
+ // Fallback for SSR (shouldn't be called, but just in case)
66
+ return '/confirm';
67
+ }
68
+ // =============================================================================
69
+ // SECTION: Sign Out
70
+ // =============================================================================
71
+ /**
72
+ * Sign the current user out and perform a full teardown of local state.
73
+ *
74
+ * The teardown follows a strict **10-step sequence** to ensure no stale data
75
+ * leaks between user sessions:
76
+ *
77
+ * 1. Stop the sync engine (dynamic import avoids circular deps).
78
+ * 2. Clear the pending sync queue (unless `preserveLocalData` is set).
79
+ * 3. Clear the local cache (unless `preserveLocalData` is set).
80
+ * 4. Clear the offline session token.
81
+ * 5. Clear offline credentials (only when online, to preserve offline re-login).
82
+ * 6. Call `supabase.auth.signOut()`.
83
+ * 7. Remove all `sb-*` keys from localStorage (Supabase internal storage).
84
+ * 8. Reset the login guard (brute-force counters).
85
+ * 9. Reset the sync status store.
86
+ * 10. Reset the auth state store.
87
+ *
88
+ * Each step is wrapped in its own try/catch so that a failure in one step
89
+ * does not prevent subsequent cleanup from running.
90
+ *
91
+ * @param options - Optional flags to control teardown behavior.
92
+ * @param options.preserveOfflineCredentials - When `true`, offline credentials
93
+ * are kept so the user can re-authenticate without network access.
94
+ * @param options.preserveLocalData - When `true`, pending sync queue and local
95
+ * cache are retained.
96
+ * @returns An object with an `error` field (`null` on success).
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * await signOut(); // full teardown
101
+ * await signOut({ preserveLocalData: true }); // keep cached data
102
+ * ```
103
+ */
104
+ export async function signOut(options) {
105
+ if (isDemoMode()) {
106
+ authState.reset();
107
+ syncStatusStore.reset();
108
+ return { error: null };
109
+ }
110
+ // 1. Stop sync engine (import dynamically to avoid circular deps)
111
+ try {
112
+ const { stopSyncEngine, clearLocalCache, clearPendingSyncQueue } = await import('../engine');
113
+ await stopSyncEngine();
114
+ if (!options?.preserveLocalData) {
115
+ // 2. Clear pending sync queue
116
+ await clearPendingSyncQueue();
117
+ // 3. Clear local cache
118
+ await clearLocalCache();
119
+ }
120
+ }
121
+ catch (e) {
122
+ debugError('[Auth] Failed to stop engine/clear data:', e);
123
+ }
124
+ // 4. Clear offline session
125
+ try {
126
+ await clearOfflineSession();
127
+ }
128
+ catch (e) {
129
+ debugError('[Auth] Failed to clear offline session:', e);
130
+ }
131
+ // 5. Clear offline credentials (only if online, for offline re-login preservation)
132
+ try {
133
+ if (!options?.preserveOfflineCredentials) {
134
+ /* Only clear when online — if the user is offline, we want to keep
135
+ the cached credentials so they can still sign back in without
136
+ network access. This is a deliberate security trade-off favouring
137
+ availability over strict credential erasure. */
138
+ const isOnline = typeof navigator !== 'undefined' && navigator.onLine;
139
+ if (isOnline) {
140
+ await clearOfflineCredentials();
141
+ }
142
+ }
143
+ }
144
+ catch (e) {
145
+ debugError('[Auth] Failed to clear offline credentials:', e);
146
+ }
147
+ // 6. Supabase auth signOut
148
+ const { error } = await supabase.auth.signOut();
149
+ // 7. Clear sb-* localStorage keys
150
+ try {
151
+ if (typeof localStorage !== 'undefined') {
152
+ const keys = Object.keys(localStorage).filter((k) => k.startsWith('sb-'));
153
+ keys.forEach((k) => localStorage.removeItem(k));
154
+ }
155
+ }
156
+ catch {
157
+ // Ignore storage errors
158
+ }
159
+ // 8. Reset login guard state
160
+ resetLoginGuard();
161
+ // 9. Reset sync status store
162
+ syncStatusStore.reset();
163
+ // 10. Reset auth state store
164
+ authState.reset();
165
+ return { error: error?.message || null };
166
+ }
167
+ // =============================================================================
168
+ // SECTION: Session Management
169
+ // =============================================================================
170
+ /**
171
+ * Get the current Supabase session.
172
+ *
173
+ * When the device is **online**, this delegates to `supabase.auth.getSession()`
174
+ * which may trigger a token refresh if the access token is close to expiry.
175
+ *
176
+ * When the device is **offline**, or if the Supabase call fails with a
177
+ * corrupted-session error, this falls back to reading the session directly
178
+ * from localStorage via {@link getSessionFromStorage}. The returned session
179
+ * may be expired, but callers can use {@link isSessionExpired} to check and
180
+ * should handle offline mode appropriately (e.g. show cached data, queue
181
+ * mutations for later sync).
182
+ *
183
+ * @returns The current `Session` object, or `null` if no valid session exists.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const session = await getSession();
188
+ * if (session && !isSessionExpired(session)) {
189
+ * // Fully authenticated
190
+ * }
191
+ * ```
192
+ *
193
+ * @see {@link getSessionFromStorage} — direct localStorage fallback
194
+ * @see {@link isSessionExpired} — expiry check helper
195
+ * @see {@link getValidSession} — combined convenience wrapper
196
+ */
197
+ export async function getSession() {
198
+ if (isDemoMode())
199
+ return null;
200
+ const isOffline = typeof navigator !== 'undefined' && !navigator.onLine;
201
+ try {
202
+ const { data, error } = await supabase.auth.getSession();
203
+ if (error) {
204
+ debugError('[Auth] getSession error:', error.message);
205
+ // If offline and we got an error, don't clear session - it might just be a network issue
206
+ if (isOffline) {
207
+ debugWarn('[Auth] Offline - keeping session despite error');
208
+ // Try to get session from localStorage directly
209
+ return getSessionFromStorage();
210
+ }
211
+ /* Detect corrupted session data (e.g. "can't access property 'hash'
212
+ of undefined") which can occur when localStorage is partially written.
213
+ Signing out clears the corrupt state so subsequent calls succeed. */
214
+ if (error.message?.includes('hash') || error.message?.includes('undefined')) {
215
+ debugWarn('[Auth] Detected corrupted session, attempting to clear');
216
+ await supabase.auth.signOut();
217
+ }
218
+ return null;
219
+ }
220
+ return data.session;
221
+ }
222
+ catch (e) {
223
+ debugError('[Auth] Unexpected error getting session:', e);
224
+ // If offline, don't clear anything - try to get from storage
225
+ if (isOffline) {
226
+ debugWarn('[Auth] Offline - attempting to get session from storage');
227
+ return getSessionFromStorage();
228
+ }
229
+ // Attempt to clear any corrupted state when online
230
+ try {
231
+ await supabase.auth.signOut();
232
+ }
233
+ catch {
234
+ // Ignore signOut errors
235
+ }
236
+ return null;
237
+ }
238
+ }
239
+ /**
240
+ * Read the session directly from localStorage, bypassing Supabase's
241
+ * built-in token refresh logic.
242
+ *
243
+ * This is used as a **fallback** when the device is offline and the normal
244
+ * `supabase.auth.getSession()` call fails. The returned session may be
245
+ * expired, but it still contains the user identity, which is sufficient for
246
+ * rendering cached offline views.
247
+ *
248
+ * Supabase stores its auth token in localStorage under a key matching the
249
+ * pattern `sb-{project-ref}-auth-token`. The internal structure has changed
250
+ * between Supabase versions, so we check for both `currentSession` (older)
251
+ * and `session` (newer) shapes.
252
+ *
253
+ * @returns The cached `Session`, or `null` if nothing usable is found.
254
+ */
255
+ function getSessionFromStorage() {
256
+ try {
257
+ // Supabase stores session in localStorage with key pattern: sb-{project-ref}-auth-token
258
+ const keys = Object.keys(localStorage);
259
+ const sessionKey = keys.find((k) => k.includes('-auth-token'));
260
+ if (!sessionKey)
261
+ return null;
262
+ const stored = localStorage.getItem(sessionKey);
263
+ if (!stored)
264
+ return null;
265
+ const parsed = JSON.parse(stored);
266
+ if (parsed?.currentSession) {
267
+ return parsed.currentSession;
268
+ }
269
+ // Newer Supabase versions use different structure
270
+ if (parsed?.session) {
271
+ return parsed.session;
272
+ }
273
+ return null;
274
+ }
275
+ catch (e) {
276
+ debugError('[Auth] Failed to get session from storage:', e);
277
+ return null;
278
+ }
279
+ }
280
+ /**
281
+ * Check whether a session's access token has expired.
282
+ *
283
+ * The `expires_at` field on a Supabase session is a **Unix timestamp in
284
+ * seconds**. We compare it against `Date.now() / 1000` (which is in
285
+ * milliseconds, hence the division).
286
+ *
287
+ * @param session - The session to check, or `null`.
288
+ * @returns `true` if the session is `null`, missing `expires_at`, or past
289
+ * its expiry time; `false` otherwise.
290
+ *
291
+ * @example
292
+ * ```ts
293
+ * if (isSessionExpired(session)) {
294
+ * // Prompt re-authentication or attempt token refresh
295
+ * }
296
+ * ```
297
+ */
298
+ export function isSessionExpired(session) {
299
+ if (!session)
300
+ return true;
301
+ // expires_at is in seconds
302
+ const expiresAt = session.expires_at;
303
+ if (!expiresAt)
304
+ return true;
305
+ return Date.now() / 1000 > expiresAt;
306
+ }
307
+ // =============================================================================
308
+ // SECTION: User Profile
309
+ // =============================================================================
310
+ /**
311
+ * Extract the user's profile from their Supabase `user_metadata`.
312
+ *
313
+ * If the engine config provides a custom `auth.profileExtractor`, it is
314
+ * invoked to transform the raw metadata into the app's profile shape.
315
+ * Otherwise the raw `user_metadata` object is returned as-is.
316
+ *
317
+ * @param user - The Supabase `User` object (may be `null`).
318
+ * @returns A key-value record representing the user's profile fields.
319
+ *
320
+ * @example
321
+ * ```ts
322
+ * const profile = getUserProfile(session.user);
323
+ * console.log(profile.display_name);
324
+ * ```
325
+ */
326
+ export function getUserProfile(user) {
327
+ const config = getEngineConfig();
328
+ if (config.auth?.profileExtractor && user) {
329
+ return config.auth.profileExtractor(user.user_metadata || {});
330
+ }
331
+ return user?.user_metadata || {};
332
+ }
333
+ /**
334
+ * Update the current user's profile metadata on Supabase.
335
+ *
336
+ * The profile data is transformed through `auth.profileToMetadata` (if
337
+ * configured) before being sent. On success the offline credential cache
338
+ * is also updated so that the profile stays consistent across online and
339
+ * offline modes.
340
+ *
341
+ * @param profile - The updated profile fields to persist.
342
+ * @returns An object with an `error` field (`null` on success).
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * const { error } = await updateProfile({ display_name: 'New Name' });
347
+ * ```
348
+ *
349
+ * @see {@link updateOfflineCredentialsProfile} — keeps the offline cache in sync
350
+ */
351
+ export async function updateProfile(profile) {
352
+ if (isDemoMode())
353
+ return { error: null };
354
+ const config = getEngineConfig();
355
+ const metadata = config.auth?.profileToMetadata
356
+ ? config.auth.profileToMetadata(profile)
357
+ : profile;
358
+ const { error } = await supabase.auth.updateUser({
359
+ data: metadata
360
+ });
361
+ if (!error) {
362
+ // Update offline cache
363
+ try {
364
+ await updateOfflineCredentialsProfile(profile);
365
+ }
366
+ catch (e) {
367
+ debugError('[Auth] Failed to update offline profile:', e);
368
+ }
369
+ }
370
+ return { error: error?.message || null };
371
+ }
372
+ // =============================================================================
373
+ // SECTION: Email Confirmation & OTP
374
+ // =============================================================================
375
+ /**
376
+ * Resend the signup confirmation email for a given address.
377
+ *
378
+ * The caller should enforce a client-side cooldown (recommended: 30 seconds)
379
+ * to prevent abuse, since Supabase may not always rate-limit resends on its
380
+ * own.
381
+ *
382
+ * @param email - The email address that needs a new confirmation link.
383
+ * @returns An object with an `error` field (`null` on success).
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * const { error } = await resendConfirmationEmail('user@example.com');
388
+ * ```
389
+ */
390
+ export async function resendConfirmationEmail(email) {
391
+ if (isDemoMode())
392
+ return { error: null };
393
+ const { error } = await supabase.auth.resend({
394
+ type: 'signup',
395
+ email,
396
+ options: {
397
+ emailRedirectTo: getConfirmRedirectUrl()
398
+ }
399
+ });
400
+ return { error: error?.message || null };
401
+ }
402
+ /**
403
+ * Verify an OTP token hash received from a confirmation email link.
404
+ *
405
+ * This absorbs the direct Supabase call that would otherwise live in the
406
+ * confirm page component, keeping all auth logic centralised in this module.
407
+ *
408
+ * @param tokenHash - The `token_hash` query parameter from the confirmation URL.
409
+ * @param type - The type of OTP: `'signup'`, `'email'`, or `'email_change'`.
410
+ * @returns An object with an `error` field (`null` on success).
411
+ *
412
+ * @example
413
+ * ```ts
414
+ * // On the /confirm page:
415
+ * const hash = new URL(location.href).searchParams.get('token_hash');
416
+ * const { error } = await verifyOtp(hash, 'signup');
417
+ * ```
418
+ */
419
+ export async function verifyOtp(tokenHash, type) {
420
+ if (isDemoMode())
421
+ return { error: null };
422
+ const { error } = await supabase.auth.verifyOtp({
423
+ token_hash: tokenHash,
424
+ type
425
+ });
426
+ return { error: error?.message || null };
427
+ }
428
+ // =============================================================================
429
+ // SECTION: Convenience Wrappers
430
+ // =============================================================================
431
+ /**
432
+ * Get a valid (non-expired) session, or `null`.
433
+ *
434
+ * This is a convenience wrapper that combines {@link getSession} and
435
+ * {@link isSessionExpired} into a single call, useful when the caller only
436
+ * cares about sessions that can still be used for API requests.
437
+ *
438
+ * @returns A non-expired `Session`, or `null`.
439
+ *
440
+ * @example
441
+ * ```ts
442
+ * const session = await getValidSession();
443
+ * if (!session) {
444
+ * redirectToLogin();
445
+ * }
446
+ * ```
447
+ *
448
+ * @see {@link getSession}
449
+ * @see {@link isSessionExpired}
450
+ */
451
+ export async function getValidSession() {
452
+ const session = await getSession();
453
+ if (!session)
454
+ return null;
455
+ if (isSessionExpired(session))
456
+ return null;
457
+ return session;
458
+ }
459
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/supabase/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @fileoverview Supabase Client — Lazy Initialization via ES Proxy
3
+ *
4
+ * This module exports a single `supabase` constant that looks and behaves
5
+ * exactly like a `SupabaseClient` instance, but is actually an ES `Proxy`
6
+ * that defers client creation until the **first property access**. This
7
+ * "lazy singleton" pattern solves a critical bootstrapping problem:
8
+ *
9
+ * The Supabase URL and anon key are loaded at **runtime** (via
10
+ * `getConfig()` from `../runtime/runtimeConfig`), not at build time.
11
+ * Modules that `import { supabase }` at the top level would otherwise
12
+ * crash because the config has not been initialized yet when the import
13
+ * executes.
14
+ *
15
+ * How the Proxy pattern works:
16
+ * 1. `supabase` is exported as `new Proxy({} as SupabaseClient, handler)`.
17
+ * 2. The handler's `get` trap intercepts every property access (e.g.
18
+ * `supabase.auth`, `supabase.from(...)`).
19
+ * 3. On first access, `getOrCreateClient()` reads the runtime config and
20
+ * calls `createClient(url, key, options)` to build the real client.
21
+ * 4. The real client is cached in a module-level `realClient` variable;
22
+ * subsequent accesses reuse it (standard singleton).
23
+ * 5. Function values are `.bind(client)` to preserve `this` context.
24
+ *
25
+ * Additional responsibilities:
26
+ * - **Corrupted session cleanup**: Before the client is created, any
27
+ * malformed `sb-*` entries in localStorage are detected and removed to
28
+ * prevent "can't access property 'hash'" runtime errors.
29
+ * - **Unhandled rejection handler**: A global listener catches Supabase
30
+ * auth errors that escape normal error handling, clears storage, and
31
+ * performs a single guarded page reload to recover.
32
+ * - **iOS PWA detection**: The client sends a custom `x-client-info`
33
+ * header indicating whether it is running as a standalone PWA on iOS,
34
+ * which helps with server-side debugging of session eviction issues.
35
+ *
36
+ * Security considerations:
37
+ * - The anon key is a **public** key (safe to include in client bundles).
38
+ * - PKCE flow is used instead of the implicit flow for stronger OAuth
39
+ * security and better compatibility with PWA environments.
40
+ * - Session persistence uses localStorage; the module proactively scrubs
41
+ * corrupted entries to prevent denial-of-service via bad local state.
42
+ *
43
+ * @module supabase/client
44
+ */
45
+ import { type SupabaseClient } from '@supabase/supabase-js';
46
+ /**
47
+ * Override the storage key prefix used by the Supabase client.
48
+ *
49
+ * Must be called **before** the first access to the `supabase` export,
50
+ * since the prefix is baked into the client options at creation time.
51
+ *
52
+ * @param prefix - The new prefix string (e.g. the app's name).
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * _setClientPrefix('myapp');
57
+ * // Later accesses will use storageKey 'myapp-auth'
58
+ * ```
59
+ */
60
+ export declare function _setClientPrefix(prefix: string): void;
61
+ /**
62
+ * The public Supabase client — a Proxy-based lazy singleton.
63
+ *
64
+ * **Why a Proxy?**
65
+ * The Supabase URL and anon key are not available at import time (they come
66
+ * from a runtime config that is loaded asynchronously). A Proxy lets every
67
+ * module `import { supabase }` at the top level without worrying about
68
+ * initialization order. The real client is created transparently on first
69
+ * property access.
70
+ *
71
+ * **How it works:**
72
+ * - The `get` trap intercepts every property read (e.g. `supabase.auth`,
73
+ * `supabase.from`).
74
+ * - It calls `getOrCreateClient()` to ensure the real client exists.
75
+ * - It forwards the property access via `Reflect.get`.
76
+ * - Function values are `.bind(client)` to keep `this` correct when the
77
+ * caller destructures methods (e.g. `const { from } = supabase`).
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { supabase } from './client';
82
+ *
83
+ * // Works immediately — the Proxy defers creation until this line runs:
84
+ * const { data } = await supabase.from('users').select('*');
85
+ * ```
86
+ */
87
+ export declare const supabase: SupabaseClient;
88
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/supabase/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAgB1E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,QAE9C;AAqOD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,EAAE,cASrB,CAAC"}