@prabhask5/stellar-engine 1.1.6 → 1.1.8
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 +68 -25
- package/dist/actions/remoteChange.d.ts +143 -18
- package/dist/actions/remoteChange.d.ts.map +1 -1
- package/dist/actions/remoteChange.js +182 -58
- package/dist/actions/remoteChange.js.map +1 -1
- package/dist/actions/truncateTooltip.d.ts +56 -0
- package/dist/actions/truncateTooltip.d.ts.map +1 -0
- package/dist/actions/truncateTooltip.js +312 -0
- package/dist/actions/truncateTooltip.js.map +1 -0
- package/dist/auth/admin.d.ts +40 -3
- package/dist/auth/admin.d.ts.map +1 -1
- package/dist/auth/admin.js +45 -5
- package/dist/auth/admin.js.map +1 -1
- package/dist/auth/crypto.d.ts +55 -5
- package/dist/auth/crypto.d.ts.map +1 -1
- package/dist/auth/crypto.js +58 -5
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/deviceVerification.d.ts +236 -20
- package/dist/auth/deviceVerification.d.ts.map +1 -1
- package/dist/auth/deviceVerification.js +293 -40
- package/dist/auth/deviceVerification.js.map +1 -1
- package/dist/auth/displayUtils.d.ts +98 -0
- package/dist/auth/displayUtils.d.ts.map +1 -0
- package/dist/auth/displayUtils.js +133 -0
- package/dist/auth/displayUtils.js.map +1 -0
- package/dist/auth/loginGuard.d.ts +108 -14
- package/dist/auth/loginGuard.d.ts.map +1 -1
- package/dist/auth/loginGuard.js +153 -31
- package/dist/auth/loginGuard.js.map +1 -1
- package/dist/auth/offlineCredentials.d.ts +132 -15
- package/dist/auth/offlineCredentials.d.ts.map +1 -1
- package/dist/auth/offlineCredentials.js +167 -23
- package/dist/auth/offlineCredentials.js.map +1 -1
- package/dist/auth/offlineLogin.d.ts +96 -10
- package/dist/auth/offlineLogin.d.ts.map +1 -1
- package/dist/auth/offlineLogin.js +82 -15
- package/dist/auth/offlineLogin.js.map +1 -1
- package/dist/auth/offlineSession.d.ts +83 -9
- package/dist/auth/offlineSession.d.ts.map +1 -1
- package/dist/auth/offlineSession.js +104 -13
- package/dist/auth/offlineSession.js.map +1 -1
- package/dist/auth/resolveAuthState.d.ts +70 -8
- package/dist/auth/resolveAuthState.d.ts.map +1 -1
- package/dist/auth/resolveAuthState.js +142 -46
- package/dist/auth/resolveAuthState.js.map +1 -1
- package/dist/auth/singleUser.d.ts +390 -37
- package/dist/auth/singleUser.d.ts.map +1 -1
- package/dist/auth/singleUser.js +505 -133
- package/dist/auth/singleUser.js.map +1 -1
- package/dist/bin/install-pwa.d.ts +25 -0
- package/dist/bin/install-pwa.d.ts.map +1 -0
- package/dist/bin/install-pwa.js +2197 -0
- package/dist/bin/install-pwa.js.map +1 -0
- package/dist/config.d.ts +132 -12
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +87 -9
- package/dist/config.js.map +1 -1
- package/dist/conflicts.d.ts +246 -23
- package/dist/conflicts.d.ts.map +1 -1
- package/dist/conflicts.js +495 -46
- package/dist/conflicts.js.map +1 -1
- package/dist/data.d.ts +338 -18
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +385 -34
- package/dist/data.js.map +1 -1
- package/dist/database.d.ts +72 -14
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +120 -29
- package/dist/database.js.map +1 -1
- package/dist/debug.d.ts +77 -1
- package/dist/debug.d.ts.map +1 -1
- package/dist/debug.js +88 -1
- package/dist/debug.js.map +1 -1
- package/dist/deviceId.d.ts +38 -7
- package/dist/deviceId.d.ts.map +1 -1
- package/dist/deviceId.js +68 -10
- package/dist/deviceId.js.map +1 -1
- package/dist/engine.d.ts +175 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +831 -110
- package/dist/engine.js.map +1 -1
- package/dist/entries/actions.d.ts +14 -0
- package/dist/entries/actions.d.ts.map +1 -1
- package/dist/entries/actions.js +27 -1
- package/dist/entries/actions.js.map +1 -1
- package/dist/entries/auth.d.ts +16 -0
- package/dist/entries/auth.d.ts.map +1 -1
- package/dist/entries/auth.js +73 -1
- package/dist/entries/auth.js.map +1 -1
- package/dist/entries/config.d.ts +12 -0
- package/dist/entries/config.d.ts.map +1 -1
- package/dist/entries/config.js +18 -1
- package/dist/entries/config.js.map +1 -1
- package/dist/entries/kit.d.ts +21 -9
- package/dist/entries/kit.d.ts.map +1 -1
- package/dist/entries/kit.js +57 -8
- package/dist/entries/kit.js.map +1 -1
- package/dist/entries/stores.d.ts +11 -0
- package/dist/entries/stores.d.ts.map +1 -1
- package/dist/entries/stores.js +43 -2
- package/dist/entries/stores.js.map +1 -1
- package/dist/entries/types.d.ts +11 -1
- package/dist/entries/types.d.ts.map +1 -1
- package/dist/entries/types.js +10 -0
- package/dist/entries/types.js.map +1 -1
- package/dist/entries/utils.d.ts +7 -1
- package/dist/entries/utils.d.ts.map +1 -1
- package/dist/entries/utils.js +23 -2
- package/dist/entries/utils.js.map +1 -1
- package/dist/entries/vite.d.ts +20 -0
- package/dist/entries/vite.d.ts.map +1 -0
- package/dist/entries/vite.js +26 -0
- package/dist/entries/vite.js.map +1 -0
- package/dist/index.d.ts +33 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -21
- package/dist/index.js.map +1 -1
- package/dist/kit/auth.d.ts +80 -0
- package/dist/kit/auth.d.ts.map +1 -0
- package/dist/kit/auth.js +72 -0
- package/dist/kit/auth.js.map +1 -0
- package/dist/kit/confirm.d.ts +111 -0
- package/dist/kit/confirm.d.ts.map +1 -0
- package/dist/kit/confirm.js +169 -0
- package/dist/kit/confirm.js.map +1 -0
- package/dist/kit/loads.d.ts +189 -0
- package/dist/kit/loads.d.ts.map +1 -0
- package/dist/kit/loads.js +205 -0
- package/dist/kit/loads.js.map +1 -0
- package/dist/kit/server.d.ts +175 -0
- package/dist/kit/server.d.ts.map +1 -0
- package/dist/kit/server.js +297 -0
- package/dist/kit/server.js.map +1 -0
- package/dist/kit/sw.d.ts +176 -0
- package/dist/kit/sw.d.ts.map +1 -0
- package/dist/kit/sw.js +320 -0
- package/dist/kit/sw.js.map +1 -0
- package/dist/queue.d.ts +274 -0
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +556 -38
- package/dist/queue.js.map +1 -1
- package/dist/realtime.d.ts +241 -27
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +633 -109
- package/dist/realtime.js.map +1 -1
- package/dist/runtime/runtimeConfig.d.ts +91 -16
- package/dist/runtime/runtimeConfig.d.ts.map +1 -1
- package/dist/runtime/runtimeConfig.js +146 -19
- package/dist/runtime/runtimeConfig.js.map +1 -1
- package/dist/stores/authState.d.ts +150 -11
- package/dist/stores/authState.d.ts.map +1 -1
- package/dist/stores/authState.js +169 -17
- package/dist/stores/authState.js.map +1 -1
- package/dist/stores/network.d.ts +39 -0
- package/dist/stores/network.d.ts.map +1 -1
- package/dist/stores/network.js +169 -16
- package/dist/stores/network.js.map +1 -1
- package/dist/stores/remoteChanges.d.ts +327 -52
- package/dist/stores/remoteChanges.d.ts.map +1 -1
- package/dist/stores/remoteChanges.js +337 -75
- package/dist/stores/remoteChanges.js.map +1 -1
- package/dist/stores/sync.d.ts +130 -0
- package/dist/stores/sync.d.ts.map +1 -1
- package/dist/stores/sync.js +167 -7
- package/dist/stores/sync.js.map +1 -1
- package/dist/supabase/auth.d.ts +326 -19
- package/dist/supabase/auth.d.ts.map +1 -1
- package/dist/supabase/auth.js +374 -26
- package/dist/supabase/auth.js.map +1 -1
- package/dist/supabase/client.d.ts +79 -6
- package/dist/supabase/client.d.ts.map +1 -1
- package/dist/supabase/client.js +158 -15
- package/dist/supabase/client.js.map +1 -1
- package/dist/supabase/validate.d.ts +101 -7
- package/dist/supabase/validate.d.ts.map +1 -1
- package/dist/supabase/validate.js +117 -8
- package/dist/supabase/validate.js.map +1 -1
- package/dist/sw/build/vite-plugin.d.ts +74 -0
- package/dist/sw/build/vite-plugin.d.ts.map +1 -0
- package/dist/sw/build/vite-plugin.js +183 -0
- package/dist/sw/build/vite-plugin.js.map +1 -0
- package/dist/sw/sw.js +669 -0
- package/dist/types.d.ts +150 -45
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +12 -10
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +55 -13
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +83 -22
- package/dist/utils.js.map +1 -1
- package/package.json +20 -22
- package/src/components/DeferredChangesBanner.svelte +477 -0
- package/src/components/SyncStatus.svelte +1732 -0
- package/dist/crdt/awareness.d.ts +0 -54
- package/dist/crdt/awareness.d.ts.map +0 -1
- package/dist/crdt/awareness.js +0 -219
- package/dist/crdt/awareness.js.map +0 -1
- package/dist/crdt/doc.d.ts +0 -56
- package/dist/crdt/doc.d.ts.map +0 -1
- package/dist/crdt/doc.js +0 -130
- package/dist/crdt/doc.js.map +0 -1
- package/dist/crdt/index.d.ts +0 -15
- package/dist/crdt/index.d.ts.map +0 -1
- package/dist/crdt/index.js +0 -20
- package/dist/crdt/index.js.map +0 -1
- package/dist/crdt/offline.d.ts +0 -91
- package/dist/crdt/offline.d.ts.map +0 -1
- package/dist/crdt/offline.js +0 -353
- package/dist/crdt/offline.js.map +0 -1
- package/dist/crdt/sync.d.ts +0 -58
- package/dist/crdt/sync.d.ts.map +0 -1
- package/dist/crdt/sync.js +0 -399
- package/dist/crdt/sync.js.map +0 -1
- package/dist/crdt/types.d.ts +0 -62
- package/dist/crdt/types.d.ts.map +0 -1
- package/dist/crdt/types.js +0 -7
- package/dist/crdt/types.js.map +0 -1
- package/dist/email/sendEmail.d.ts +0 -31
- package/dist/email/sendEmail.d.ts.map +0 -1
- package/dist/email/sendEmail.js +0 -39
- package/dist/email/sendEmail.js.map +0 -1
- package/dist/email/validateSmtp.d.ts +0 -18
- package/dist/email/validateSmtp.d.ts.map +0 -1
- package/dist/email/validateSmtp.js +0 -33
- package/dist/email/validateSmtp.js.map +0 -1
- package/dist/entries/crdt.d.ts +0 -3
- package/dist/entries/crdt.d.ts.map +0 -1
- package/dist/entries/crdt.js +0 -13
- package/dist/entries/crdt.js.map +0 -1
- package/dist/entries/email.d.ts +0 -4
- package/dist/entries/email.d.ts.map +0 -1
- package/dist/entries/email.js +0 -4
- package/dist/entries/email.js.map +0 -1
- package/dist/kit/authPresets.d.ts +0 -28
- package/dist/kit/authPresets.d.ts.map +0 -1
- package/dist/kit/authPresets.js +0 -23
- package/dist/kit/authPresets.js.map +0 -1
- package/dist/kit/configEndpoint.d.ts +0 -18
- package/dist/kit/configEndpoint.d.ts.map +0 -1
- package/dist/kit/configEndpoint.js +0 -27
- package/dist/kit/configEndpoint.js.map +0 -1
- package/dist/kit/deployEndpoint.d.ts +0 -22
- package/dist/kit/deployEndpoint.d.ts.map +0 -1
- package/dist/kit/deployEndpoint.js +0 -79
- package/dist/kit/deployEndpoint.js.map +0 -1
- package/dist/kit/layoutLoad.d.ts +0 -23
- package/dist/kit/layoutLoad.d.ts.map +0 -1
- package/dist/kit/layoutLoad.js +0 -41
- package/dist/kit/layoutLoad.js.map +0 -1
- package/dist/kit/protectedLoad.d.ts +0 -16
- package/dist/kit/protectedLoad.d.ts.map +0 -1
- package/dist/kit/protectedLoad.js +0 -28
- package/dist/kit/protectedLoad.js.map +0 -1
- package/dist/kit/setupLoad.d.ts +0 -11
- package/dist/kit/setupLoad.d.ts.map +0 -1
- package/dist/kit/setupLoad.js +0 -28
- package/dist/kit/setupLoad.js.map +0 -1
- package/dist/kit/validateEndpoint.d.ts +0 -9
- package/dist/kit/validateEndpoint.d.ts.map +0 -1
- package/dist/kit/validateEndpoint.js +0 -25
- package/dist/kit/validateEndpoint.js.map +0 -1
- package/dist/kit/vercelApi.d.ts +0 -6
- package/dist/kit/vercelApi.d.ts.map +0 -1
- package/dist/kit/vercelApi.js +0 -48
- package/dist/kit/vercelApi.js.map +0 -1
|
@@ -1,15 +1,88 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Supabase Client
|
|
2
|
+
* @fileoverview Supabase Client — Lazy Initialization via ES Proxy
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
6
44
|
*/
|
|
7
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
|
+
*/
|
|
8
60
|
export declare function _setClientPrefix(prefix: string): void;
|
|
9
61
|
/**
|
|
10
|
-
* Proxy-based lazy singleton.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
+
* ```
|
|
13
86
|
*/
|
|
14
87
|
export declare const supabase: SupabaseClient;
|
|
15
88
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/supabase/client.ts"],"names":[],"mappings":"AAAA
|
|
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;AAe1E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,QAE9C;AAwND;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,EAAE,cASrB,CAAC"}
|
package/dist/supabase/client.js
CHANGED
|
@@ -1,18 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Supabase Client
|
|
2
|
+
* @fileoverview Supabase Client — Lazy Initialization via ES Proxy
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
6
44
|
*/
|
|
7
45
|
import { createClient } from '@supabase/supabase-js';
|
|
8
46
|
import { getConfig } from '../runtime/runtimeConfig';
|
|
9
47
|
import { debugLog, debugWarn, debugError } from '../debug';
|
|
48
|
+
// =============================================================================
|
|
49
|
+
// SECTION: Client Prefix Configuration
|
|
50
|
+
// =============================================================================
|
|
51
|
+
/**
|
|
52
|
+
* Prefix used for the Supabase `storageKey` and custom headers.
|
|
53
|
+
* Defaults to `'stellar'`; can be overridden by the host application
|
|
54
|
+
* via {@link _setClientPrefix} before the client is first accessed.
|
|
55
|
+
*/
|
|
10
56
|
let _prefix = 'stellar';
|
|
57
|
+
/**
|
|
58
|
+
* Override the storage key prefix used by the Supabase client.
|
|
59
|
+
*
|
|
60
|
+
* Must be called **before** the first access to the `supabase` export,
|
|
61
|
+
* since the prefix is baked into the client options at creation time.
|
|
62
|
+
*
|
|
63
|
+
* @param prefix - The new prefix string (e.g. the app's name).
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* _setClientPrefix('myapp');
|
|
68
|
+
* // Later accesses will use storageKey 'myapp-auth'
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
11
71
|
export function _setClientPrefix(prefix) {
|
|
12
72
|
_prefix = prefix;
|
|
13
73
|
}
|
|
14
|
-
//
|
|
15
|
-
//
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// SECTION: Corrupted Session Cleanup
|
|
76
|
+
// =============================================================================
|
|
77
|
+
/**
|
|
78
|
+
* Scan localStorage for corrupted Supabase auth entries and remove them.
|
|
79
|
+
*
|
|
80
|
+
* Supabase stores session data under keys prefixed with `sb-`. If the
|
|
81
|
+
* browser was closed mid-write, or if a bug produced malformed JSON, these
|
|
82
|
+
* entries can cause runtime errors like "can't access property 'hash' of
|
|
83
|
+
* undefined" on the next page load.
|
|
84
|
+
*
|
|
85
|
+
* This function runs **once** at module evaluation time (before the client
|
|
86
|
+
* is created) and acts as a defensive self-healing mechanism.
|
|
87
|
+
*
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
16
90
|
function clearCorruptedAuthData() {
|
|
17
91
|
if (typeof localStorage === 'undefined')
|
|
18
92
|
return;
|
|
@@ -41,7 +115,9 @@ function clearCorruptedAuthData() {
|
|
|
41
115
|
}
|
|
42
116
|
}
|
|
43
117
|
catch {
|
|
44
|
-
|
|
118
|
+
/* JSON.parse failed — the stored value is not valid JSON, which
|
|
119
|
+
means the data was partially written or otherwise corrupted.
|
|
120
|
+
Removing it is the safest recovery action. */
|
|
45
121
|
debugWarn('[Auth] Clearing malformed session data:', key);
|
|
46
122
|
localStorage.removeItem(key);
|
|
47
123
|
}
|
|
@@ -52,7 +128,13 @@ function clearCorruptedAuthData() {
|
|
|
52
128
|
debugError('[Auth] Error checking localStorage:', e);
|
|
53
129
|
}
|
|
54
130
|
}
|
|
55
|
-
//
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// SECTION: Global Error Recovery
|
|
133
|
+
// =============================================================================
|
|
134
|
+
/* Register a global unhandled-rejection handler that catches Supabase auth
|
|
135
|
+
errors which escape normal try/catch boundaries. This is a last-resort
|
|
136
|
+
recovery mechanism: clear the corrupted storage and reload once. A
|
|
137
|
+
sessionStorage flag (`__stellar_auth_reload`) guards against reload loops. */
|
|
56
138
|
if (typeof window !== 'undefined') {
|
|
57
139
|
// Clear reload guard on successful startup (app loaded without crashing)
|
|
58
140
|
try {
|
|
@@ -73,7 +155,8 @@ if (typeof window !== 'undefined') {
|
|
|
73
155
|
try {
|
|
74
156
|
const keys = Object.keys(localStorage).filter((k) => k.startsWith('sb-'));
|
|
75
157
|
keys.forEach((k) => localStorage.removeItem(k));
|
|
76
|
-
|
|
158
|
+
/* Guard against reload loop: only reload once per browser session.
|
|
159
|
+
Without this guard, a persistent error could cause infinite reloads. */
|
|
77
160
|
if (!sessionStorage.getItem('__stellar_auth_reload')) {
|
|
78
161
|
sessionStorage.setItem('__stellar_auth_reload', '1');
|
|
79
162
|
window.location.reload();
|
|
@@ -86,17 +169,51 @@ if (typeof window !== 'undefined') {
|
|
|
86
169
|
}
|
|
87
170
|
});
|
|
88
171
|
}
|
|
89
|
-
//
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// SECTION: Module-Level Initialization
|
|
174
|
+
// =============================================================================
|
|
175
|
+
/* Run the corruption cleanup synchronously at module load time, before any
|
|
176
|
+
code can attempt to read the (potentially corrupted) session data. */
|
|
90
177
|
clearCorruptedAuthData();
|
|
91
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Detect if the app is running as an iOS PWA (standalone mode).
|
|
180
|
+
*
|
|
181
|
+
* iOS PWAs have unique session-persistence challenges: Safari's
|
|
182
|
+
* Intelligent Tracking Prevention (ITP) and aggressive localStorage
|
|
183
|
+
* eviction can cause sessions to disappear unexpectedly. Knowing we are
|
|
184
|
+
* in this environment lets us log more aggressively and send a custom
|
|
185
|
+
* header for server-side debugging.
|
|
186
|
+
*/
|
|
92
187
|
const isIOSPWA = typeof window !== 'undefined' &&
|
|
93
188
|
// @ts-expect-error - navigator.standalone is iOS-specific
|
|
94
189
|
(window.navigator.standalone === true || window.matchMedia('(display-mode: standalone)').matches);
|
|
95
190
|
if (isIOSPWA) {
|
|
96
191
|
debugLog('[Auth] Running as iOS PWA - using enhanced auth persistence');
|
|
97
192
|
}
|
|
98
|
-
//
|
|
193
|
+
// =============================================================================
|
|
194
|
+
// SECTION: Lazy Singleton Client
|
|
195
|
+
// =============================================================================
|
|
196
|
+
/** The cached SupabaseClient instance, created on first access. */
|
|
99
197
|
let realClient = null;
|
|
198
|
+
/**
|
|
199
|
+
* Create (or return the cached) SupabaseClient instance.
|
|
200
|
+
*
|
|
201
|
+
* On first invocation this reads the runtime config, constructs the client
|
|
202
|
+
* with appropriate auth options, and wires up an `onAuthStateChange`
|
|
203
|
+
* listener for debug logging. Subsequent calls return the cached instance.
|
|
204
|
+
*
|
|
205
|
+
* Client configuration highlights:
|
|
206
|
+
* - `persistSession: true` — sessions survive page reloads via localStorage.
|
|
207
|
+
* - `autoRefreshToken: true` — the SDK refreshes tokens before they expire.
|
|
208
|
+
* - `flowType: 'pkce'` — PKCE is more secure than the implicit flow and
|
|
209
|
+
* works better with PWA and mobile browser environments.
|
|
210
|
+
* - `storageKey: '{prefix}-auth'` — namespaced to avoid collisions when
|
|
211
|
+
* multiple Supabase-backed apps share the same origin.
|
|
212
|
+
*
|
|
213
|
+
* @returns The fully-initialized `SupabaseClient`.
|
|
214
|
+
*
|
|
215
|
+
* @internal Called exclusively by the Proxy `get` trap below.
|
|
216
|
+
*/
|
|
100
217
|
function getOrCreateClient() {
|
|
101
218
|
if (realClient)
|
|
102
219
|
return realClient;
|
|
@@ -130,7 +247,9 @@ function getOrCreateClient() {
|
|
|
130
247
|
if (typeof window !== 'undefined') {
|
|
131
248
|
realClient.auth.onAuthStateChange((event, session) => {
|
|
132
249
|
debugLog(`[Auth] State change: ${event}`, session ? `User: ${session.user?.id}` : 'No session');
|
|
133
|
-
|
|
250
|
+
/* iOS PWAs can lose sessions silently when Safari evicts localStorage.
|
|
251
|
+
Logging SIGNED_OUT events specifically for PWAs makes this visible
|
|
252
|
+
in remote debugging tools. */
|
|
134
253
|
if (event === 'SIGNED_OUT' && isIOSPWA) {
|
|
135
254
|
debugWarn('[Auth] Signed out on iOS PWA - session may have been evicted');
|
|
136
255
|
}
|
|
@@ -141,10 +260,34 @@ function getOrCreateClient() {
|
|
|
141
260
|
}
|
|
142
261
|
return realClient;
|
|
143
262
|
}
|
|
263
|
+
// =============================================================================
|
|
264
|
+
// SECTION: Proxy Export
|
|
265
|
+
// =============================================================================
|
|
144
266
|
/**
|
|
145
|
-
* Proxy-based lazy singleton.
|
|
146
|
-
*
|
|
147
|
-
*
|
|
267
|
+
* The public Supabase client — a Proxy-based lazy singleton.
|
|
268
|
+
*
|
|
269
|
+
* **Why a Proxy?**
|
|
270
|
+
* The Supabase URL and anon key are not available at import time (they come
|
|
271
|
+
* from a runtime config that is loaded asynchronously). A Proxy lets every
|
|
272
|
+
* module `import { supabase }` at the top level without worrying about
|
|
273
|
+
* initialization order. The real client is created transparently on first
|
|
274
|
+
* property access.
|
|
275
|
+
*
|
|
276
|
+
* **How it works:**
|
|
277
|
+
* - The `get` trap intercepts every property read (e.g. `supabase.auth`,
|
|
278
|
+
* `supabase.from`).
|
|
279
|
+
* - It calls `getOrCreateClient()` to ensure the real client exists.
|
|
280
|
+
* - It forwards the property access via `Reflect.get`.
|
|
281
|
+
* - Function values are `.bind(client)` to keep `this` correct when the
|
|
282
|
+
* caller destructures methods (e.g. `const { from } = supabase`).
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* import { supabase } from './client';
|
|
287
|
+
*
|
|
288
|
+
* // Works immediately — the Proxy defers creation until this line runs:
|
|
289
|
+
* const { data } = await supabase.from('users').select('*');
|
|
290
|
+
* ```
|
|
148
291
|
*/
|
|
149
292
|
export const supabase = new Proxy({}, {
|
|
150
293
|
get(_target, prop, receiver) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/supabase/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/supabase/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Supabase Validation
|
|
2
|
+
* @fileoverview Supabase Validation Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Provides two complementary validation capabilities:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Credential validation** (`validateSupabaseCredentials`):
|
|
7
|
+
* Tests that a given Supabase URL and anon key are well-formed and can
|
|
8
|
+
* reach the Supabase REST API. Used during setup / onboarding flows
|
|
9
|
+
* where the user manually enters their project credentials.
|
|
10
|
+
*
|
|
11
|
+
* 2. **Schema validation** (`validateSchema`):
|
|
12
|
+
* Verifies that every table declared in the engine config actually exists
|
|
13
|
+
* in the Supabase project and is accessible under the current RLS policies.
|
|
14
|
+
* This is a "smoke test" that catches misconfiguration early, before the
|
|
15
|
+
* sync engine attempts real reads/writes and produces cryptic errors.
|
|
16
|
+
*
|
|
17
|
+
* Security considerations:
|
|
18
|
+
* - Credential validation creates a **temporary** `SupabaseClient` scoped
|
|
19
|
+
* to the function call; it does not leak into the module-level singleton.
|
|
20
|
+
* - Schema validation queries use `LIMIT 0`, so no user data is fetched —
|
|
21
|
+
* the query only confirms that the table exists and RLS allows access.
|
|
22
|
+
* - Error messages are deliberately generic to avoid leaking internal
|
|
23
|
+
* database structure to end users; detailed errors go to `debugError`.
|
|
24
|
+
*
|
|
25
|
+
* Integration patterns:
|
|
26
|
+
* - Called from setup wizards, admin panels, and health-check utilities.
|
|
27
|
+
* - `validateSchema` uses the shared `supabase` proxy from `./client.ts`,
|
|
28
|
+
* so it benefits from the same lazy-init and session management.
|
|
29
|
+
*
|
|
30
|
+
* @module supabase/validate
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Validate Supabase credentials by attempting a lightweight API call.
|
|
34
|
+
*
|
|
35
|
+
* This function is designed for **setup flows** where a user provides their
|
|
36
|
+
* Supabase URL and anon key and the app needs to verify them before saving.
|
|
37
|
+
*
|
|
38
|
+
* Validation steps:
|
|
39
|
+
* 1. Parse the URL to ensure it is syntactically valid.
|
|
40
|
+
* 2. Create a disposable `SupabaseClient` with the provided credentials.
|
|
41
|
+
* 3. Issue a `SELECT id FROM <testTable> LIMIT 1` query.
|
|
42
|
+
* 4. Interpret the response:
|
|
43
|
+
* - Success or "relation does not exist" => credentials are valid (the
|
|
44
|
+
* API responded, so URL + key are correct; the table simply may not
|
|
45
|
+
* have been created yet).
|
|
46
|
+
* - "Invalid API key" / PGRST301 => credentials are wrong.
|
|
47
|
+
* - Network error => Supabase is unreachable.
|
|
48
|
+
*
|
|
49
|
+
* @param url - The Supabase project URL (e.g. `https://xyz.supabase.co`).
|
|
50
|
+
* @param anonKey - The project's anonymous (public) API key.
|
|
51
|
+
* @param testTable - Optional table name to query. Defaults to `'_health_check'`.
|
|
52
|
+
* Using a table that does not exist is fine — we only care
|
|
53
|
+
* whether the API responds, not whether the table is present.
|
|
54
|
+
* @returns An object with `valid: true` on success, or `valid: false` plus an
|
|
55
|
+
* `error` message describing what went wrong.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* const result = await validateSupabaseCredentials(
|
|
60
|
+
* 'https://abc.supabase.co',
|
|
61
|
+
* 'eyJhbGci...',
|
|
62
|
+
* 'profiles'
|
|
63
|
+
* );
|
|
64
|
+
* if (!result.valid) {
|
|
65
|
+
* showError(result.error);
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @see {@link validateSchema} — for post-setup table existence checks
|
|
6
70
|
*/
|
|
7
71
|
export declare function validateSupabaseCredentials(url: string, anonKey: string, testTable?: string): Promise<{
|
|
8
72
|
valid: boolean;
|
|
9
73
|
error?: string;
|
|
10
74
|
}>;
|
|
11
75
|
/**
|
|
12
|
-
*
|
|
76
|
+
* Validate that all tables declared in the engine configuration exist in
|
|
77
|
+
* Supabase and are accessible under the current RLS policies.
|
|
78
|
+
*
|
|
79
|
+
* For each table the function executes:
|
|
80
|
+
* ```sql
|
|
81
|
+
* SELECT id FROM <table> LIMIT 0
|
|
82
|
+
* ```
|
|
83
|
+
* This returns **zero rows** (no data egress, minimal latency) but still
|
|
84
|
+
* exercises the full PostgREST pipeline — table lookup, RLS policy
|
|
85
|
+
* evaluation, and column resolution. If any of these steps fail, the table
|
|
86
|
+
* is flagged.
|
|
87
|
+
*
|
|
88
|
+
* When device verification is enabled in the engine config, the
|
|
89
|
+
* `trusted_devices` table is automatically appended to the check list.
|
|
90
|
+
*
|
|
91
|
+
* Error categorization:
|
|
92
|
+
* - **Missing table**: The relation does not exist in the database.
|
|
93
|
+
* - **Permission denied (42501)**: The table exists but RLS or grants
|
|
94
|
+
* prevent the anon role from reading it.
|
|
95
|
+
* - **Other**: Any unexpected PostgREST or network error.
|
|
96
|
+
*
|
|
97
|
+
* @returns An object containing:
|
|
98
|
+
* - `valid` — `true` when all tables pass, `false` otherwise.
|
|
99
|
+
* - `missingTables` — names of tables that do not exist at all.
|
|
100
|
+
* - `errors` — human-readable descriptions of every issue found.
|
|
13
101
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const { valid, missingTables, errors } = await validateSchema();
|
|
105
|
+
* if (!valid) {
|
|
106
|
+
* console.error('Schema issues:', errors);
|
|
107
|
+
* // Prompt user to run migrations or fix RLS policies
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
16
110
|
*
|
|
17
|
-
*
|
|
111
|
+
* @see {@link validateSupabaseCredentials} — for pre-setup credential checks
|
|
18
112
|
*/
|
|
19
113
|
export declare function validateSchema(): Promise<{
|
|
20
114
|
valid: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/supabase/validate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/supabase/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAWH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0C7C;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC;IAC9C,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAiDD"}
|
|
@@ -1,13 +1,80 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Supabase Validation
|
|
2
|
+
* @fileoverview Supabase Validation Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Provides two complementary validation capabilities:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Credential validation** (`validateSupabaseCredentials`):
|
|
7
|
+
* Tests that a given Supabase URL and anon key are well-formed and can
|
|
8
|
+
* reach the Supabase REST API. Used during setup / onboarding flows
|
|
9
|
+
* where the user manually enters their project credentials.
|
|
10
|
+
*
|
|
11
|
+
* 2. **Schema validation** (`validateSchema`):
|
|
12
|
+
* Verifies that every table declared in the engine config actually exists
|
|
13
|
+
* in the Supabase project and is accessible under the current RLS policies.
|
|
14
|
+
* This is a "smoke test" that catches misconfiguration early, before the
|
|
15
|
+
* sync engine attempts real reads/writes and produces cryptic errors.
|
|
16
|
+
*
|
|
17
|
+
* Security considerations:
|
|
18
|
+
* - Credential validation creates a **temporary** `SupabaseClient` scoped
|
|
19
|
+
* to the function call; it does not leak into the module-level singleton.
|
|
20
|
+
* - Schema validation queries use `LIMIT 0`, so no user data is fetched —
|
|
21
|
+
* the query only confirms that the table exists and RLS allows access.
|
|
22
|
+
* - Error messages are deliberately generic to avoid leaking internal
|
|
23
|
+
* database structure to end users; detailed errors go to `debugError`.
|
|
24
|
+
*
|
|
25
|
+
* Integration patterns:
|
|
26
|
+
* - Called from setup wizards, admin panels, and health-check utilities.
|
|
27
|
+
* - `validateSchema` uses the shared `supabase` proxy from `./client.ts`,
|
|
28
|
+
* so it benefits from the same lazy-init and session management.
|
|
29
|
+
*
|
|
30
|
+
* @module supabase/validate
|
|
6
31
|
*/
|
|
7
32
|
import { createClient } from '@supabase/supabase-js';
|
|
8
33
|
import { getEngineConfig } from '../config';
|
|
9
34
|
import { supabase } from './client';
|
|
10
35
|
import { debugError, debugLog } from '../debug';
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// SECTION: Credential Validation
|
|
38
|
+
// =============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Validate Supabase credentials by attempting a lightweight API call.
|
|
41
|
+
*
|
|
42
|
+
* This function is designed for **setup flows** where a user provides their
|
|
43
|
+
* Supabase URL and anon key and the app needs to verify them before saving.
|
|
44
|
+
*
|
|
45
|
+
* Validation steps:
|
|
46
|
+
* 1. Parse the URL to ensure it is syntactically valid.
|
|
47
|
+
* 2. Create a disposable `SupabaseClient` with the provided credentials.
|
|
48
|
+
* 3. Issue a `SELECT id FROM <testTable> LIMIT 1` query.
|
|
49
|
+
* 4. Interpret the response:
|
|
50
|
+
* - Success or "relation does not exist" => credentials are valid (the
|
|
51
|
+
* API responded, so URL + key are correct; the table simply may not
|
|
52
|
+
* have been created yet).
|
|
53
|
+
* - "Invalid API key" / PGRST301 => credentials are wrong.
|
|
54
|
+
* - Network error => Supabase is unreachable.
|
|
55
|
+
*
|
|
56
|
+
* @param url - The Supabase project URL (e.g. `https://xyz.supabase.co`).
|
|
57
|
+
* @param anonKey - The project's anonymous (public) API key.
|
|
58
|
+
* @param testTable - Optional table name to query. Defaults to `'_health_check'`.
|
|
59
|
+
* Using a table that does not exist is fine — we only care
|
|
60
|
+
* whether the API responds, not whether the table is present.
|
|
61
|
+
* @returns An object with `valid: true` on success, or `valid: false` plus an
|
|
62
|
+
* `error` message describing what went wrong.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const result = await validateSupabaseCredentials(
|
|
67
|
+
* 'https://abc.supabase.co',
|
|
68
|
+
* 'eyJhbGci...',
|
|
69
|
+
* 'profiles'
|
|
70
|
+
* );
|
|
71
|
+
* if (!result.valid) {
|
|
72
|
+
* showError(result.error);
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @see {@link validateSchema} — for post-setup table existence checks
|
|
77
|
+
*/
|
|
11
78
|
export async function validateSupabaseCredentials(url, anonKey, testTable) {
|
|
12
79
|
try {
|
|
13
80
|
new URL(url);
|
|
@@ -16,6 +83,9 @@ export async function validateSupabaseCredentials(url, anonKey, testTable) {
|
|
|
16
83
|
return { valid: false, error: 'Invalid Supabase URL format' };
|
|
17
84
|
}
|
|
18
85
|
try {
|
|
86
|
+
/* Create a throwaway client scoped to this validation call. We
|
|
87
|
+
intentionally do NOT reuse the module-level singleton because the
|
|
88
|
+
credentials being tested may differ from the app's active config. */
|
|
19
89
|
const tempClient = createClient(url, anonKey);
|
|
20
90
|
// Test REST API reachability by attempting a simple query
|
|
21
91
|
const { error } = await tempClient
|
|
@@ -30,7 +100,9 @@ export async function validateSupabaseCredentials(url, anonKey, testTable) {
|
|
|
30
100
|
error: 'Invalid Supabase credentials. Check your URL and Anon Key.'
|
|
31
101
|
};
|
|
32
102
|
}
|
|
33
|
-
|
|
103
|
+
/* Table doesn't exist but the API responded — this means the URL and
|
|
104
|
+
anon key are correct; the schema just hasn't been set up yet. We
|
|
105
|
+
treat this as a successful credential validation. */
|
|
34
106
|
if (error.message?.includes('relation') && error.message?.includes('does not exist')) {
|
|
35
107
|
return { valid: true };
|
|
36
108
|
}
|
|
@@ -44,13 +116,46 @@ export async function validateSupabaseCredentials(url, anonKey, testTable) {
|
|
|
44
116
|
return { valid: false, error: `Could not connect to Supabase: ${message}` };
|
|
45
117
|
}
|
|
46
118
|
}
|
|
119
|
+
// =============================================================================
|
|
120
|
+
// SECTION: Schema Validation
|
|
121
|
+
// =============================================================================
|
|
47
122
|
/**
|
|
48
|
-
*
|
|
123
|
+
* Validate that all tables declared in the engine configuration exist in
|
|
124
|
+
* Supabase and are accessible under the current RLS policies.
|
|
125
|
+
*
|
|
126
|
+
* For each table the function executes:
|
|
127
|
+
* ```sql
|
|
128
|
+
* SELECT id FROM <table> LIMIT 0
|
|
129
|
+
* ```
|
|
130
|
+
* This returns **zero rows** (no data egress, minimal latency) but still
|
|
131
|
+
* exercises the full PostgREST pipeline — table lookup, RLS policy
|
|
132
|
+
* evaluation, and column resolution. If any of these steps fail, the table
|
|
133
|
+
* is flagged.
|
|
134
|
+
*
|
|
135
|
+
* When device verification is enabled in the engine config, the
|
|
136
|
+
* `trusted_devices` table is automatically appended to the check list.
|
|
137
|
+
*
|
|
138
|
+
* Error categorization:
|
|
139
|
+
* - **Missing table**: The relation does not exist in the database.
|
|
140
|
+
* - **Permission denied (42501)**: The table exists but RLS or grants
|
|
141
|
+
* prevent the anon role from reading it.
|
|
142
|
+
* - **Other**: Any unexpected PostgREST or network error.
|
|
143
|
+
*
|
|
144
|
+
* @returns An object containing:
|
|
145
|
+
* - `valid` — `true` when all tables pass, `false` otherwise.
|
|
146
|
+
* - `missingTables` — names of tables that do not exist at all.
|
|
147
|
+
* - `errors` — human-readable descriptions of every issue found.
|
|
49
148
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* const { valid, missingTables, errors } = await validateSchema();
|
|
152
|
+
* if (!valid) {
|
|
153
|
+
* console.error('Schema issues:', errors);
|
|
154
|
+
* // Prompt user to run migrations or fix RLS policies
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
52
157
|
*
|
|
53
|
-
*
|
|
158
|
+
* @see {@link validateSupabaseCredentials} — for pre-setup credential checks
|
|
54
159
|
*/
|
|
55
160
|
export async function validateSchema() {
|
|
56
161
|
const config = getEngineConfig();
|
|
@@ -59,10 +164,14 @@ export async function validateSchema() {
|
|
|
59
164
|
if (config.auth?.deviceVerification?.enabled) {
|
|
60
165
|
tableNames.push('trusted_devices');
|
|
61
166
|
}
|
|
167
|
+
/** Tables whose relation does not exist in the database at all. */
|
|
62
168
|
const missingTables = [];
|
|
169
|
+
/** Human-readable error descriptions for all issues encountered. */
|
|
63
170
|
const errors = [];
|
|
64
171
|
for (const tableName of tableNames) {
|
|
65
172
|
try {
|
|
173
|
+
/* SELECT id LIMIT 0 — validates table existence and RLS access
|
|
174
|
+
without fetching any actual data rows. */
|
|
66
175
|
const { error } = await supabase.from(tableName).select('id').limit(0);
|
|
67
176
|
if (error) {
|
|
68
177
|
if (error.message?.includes('relation') && error.message?.includes('does not exist')) {
|