@prabhask5/stellar-engine 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -0
- package/dist/actions/remoteChange.d.ts +79 -0
- package/dist/actions/remoteChange.d.ts.map +1 -0
- package/dist/actions/remoteChange.js +300 -0
- package/dist/actions/remoteChange.js.map +1 -0
- package/dist/auth/admin.d.ts +12 -0
- package/dist/auth/admin.d.ts.map +1 -0
- package/dist/auth/admin.js +23 -0
- package/dist/auth/admin.js.map +1 -0
- package/dist/auth/offlineCredentials.d.ts +41 -0
- package/dist/auth/offlineCredentials.d.ts.map +1 -0
- package/dist/auth/offlineCredentials.js +121 -0
- package/dist/auth/offlineCredentials.js.map +1 -0
- package/dist/auth/offlineLogin.d.ts +34 -0
- package/dist/auth/offlineLogin.d.ts.map +1 -0
- package/dist/auth/offlineLogin.js +75 -0
- package/dist/auth/offlineLogin.js.map +1 -0
- package/dist/auth/offlineSession.d.ts +22 -0
- package/dist/auth/offlineSession.d.ts.map +1 -0
- package/dist/auth/offlineSession.js +54 -0
- package/dist/auth/offlineSession.js.map +1 -0
- package/dist/auth/resolveAuthState.d.ts +24 -0
- package/dist/auth/resolveAuthState.d.ts.map +1 -0
- package/dist/auth/resolveAuthState.js +69 -0
- package/dist/auth/resolveAuthState.js.map +1 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/conflicts.d.ts +70 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +321 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/data.d.ts +77 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +360 -0
- package/dist/data.js.map +1 -0
- package/dist/database.d.ts +31 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +51 -0
- package/dist/database.js.map +1 -0
- package/dist/debug.d.ts +11 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +48 -0
- package/dist/debug.js.map +1 -0
- package/dist/deviceId.d.ts +16 -0
- package/dist/deviceId.d.ts.map +1 -0
- package/dist/deviceId.js +48 -0
- package/dist/deviceId.js.map +1 -0
- package/dist/engine.d.ts +14 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1903 -0
- package/dist/engine.js.map +1 -0
- package/dist/entries/actions.d.ts +2 -0
- package/dist/entries/actions.d.ts.map +1 -0
- package/dist/entries/actions.js +3 -0
- package/dist/entries/actions.js.map +1 -0
- package/dist/entries/auth.d.ts +7 -0
- package/dist/entries/auth.d.ts.map +1 -0
- package/dist/entries/auth.js +6 -0
- package/dist/entries/auth.js.map +1 -0
- package/dist/entries/config.d.ts +3 -0
- package/dist/entries/config.d.ts.map +1 -0
- package/dist/entries/config.js +3 -0
- package/dist/entries/config.js.map +1 -0
- package/dist/entries/stores.d.ts +9 -0
- package/dist/entries/stores.d.ts.map +1 -0
- package/dist/entries/stores.js +9 -0
- package/dist/entries/stores.js.map +1 -0
- package/dist/entries/types.d.ts +11 -0
- package/dist/entries/types.d.ts.map +1 -0
- package/dist/entries/types.js +2 -0
- package/dist/entries/types.js.map +1 -0
- package/dist/entries/utils.d.ts +3 -0
- package/dist/entries/utils.d.ts.map +1 -0
- package/dist/entries/utils.js +4 -0
- package/dist/entries/utils.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/operations.d.ts +73 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +227 -0
- package/dist/operations.js.map +1 -0
- package/dist/queue.d.ts +32 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +377 -0
- package/dist/queue.js.map +1 -0
- package/dist/realtime.d.ts +57 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +491 -0
- package/dist/realtime.js.map +1 -0
- package/dist/reconnectHandler.d.ts +16 -0
- package/dist/reconnectHandler.d.ts.map +1 -0
- package/dist/reconnectHandler.js +21 -0
- package/dist/reconnectHandler.js.map +1 -0
- package/dist/runtime/runtimeConfig.d.ts +27 -0
- package/dist/runtime/runtimeConfig.d.ts.map +1 -0
- package/dist/runtime/runtimeConfig.js +133 -0
- package/dist/runtime/runtimeConfig.js.map +1 -0
- package/dist/stores/authState.d.ts +57 -0
- package/dist/stores/authState.d.ts.map +1 -0
- package/dist/stores/authState.js +154 -0
- package/dist/stores/authState.js.map +1 -0
- package/dist/stores/network.d.ts +9 -0
- package/dist/stores/network.d.ts.map +1 -0
- package/dist/stores/network.js +97 -0
- package/dist/stores/network.js.map +1 -0
- package/dist/stores/remoteChanges.d.ts +142 -0
- package/dist/stores/remoteChanges.d.ts.map +1 -0
- package/dist/stores/remoteChanges.js +353 -0
- package/dist/stores/remoteChanges.js.map +1 -0
- package/dist/stores/sync.d.ts +35 -0
- package/dist/stores/sync.d.ts.map +1 -0
- package/dist/stores/sync.js +115 -0
- package/dist/stores/sync.js.map +1 -0
- package/dist/supabase/auth.d.ts +60 -0
- package/dist/supabase/auth.d.ts.map +1 -0
- package/dist/supabase/auth.js +298 -0
- package/dist/supabase/auth.js.map +1 -0
- package/dist/supabase/client.d.ts +15 -0
- package/dist/supabase/client.d.ts.map +1 -0
- package/dist/supabase/client.js +149 -0
- package/dist/supabase/client.js.map +1 -0
- package/dist/supabase/validate.d.ts +11 -0
- package/dist/supabase/validate.d.ts.map +1 -0
- package/dist/supabase/validate.js +38 -0
- package/dist/supabase/validate.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +56 -0
- package/dist/utils.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Configuration Module
|
|
3
|
+
*
|
|
4
|
+
* Replaces build-time $env/static/public with runtime config fetched from the server.
|
|
5
|
+
* Config is cached in localStorage for instant subsequent loads and offline PWA support.
|
|
6
|
+
*/
|
|
7
|
+
let _prefix = 'stellar';
|
|
8
|
+
export function _setConfigPrefix(prefix) {
|
|
9
|
+
_prefix = prefix;
|
|
10
|
+
}
|
|
11
|
+
function getCacheKey() {
|
|
12
|
+
return `${_prefix}_config`;
|
|
13
|
+
}
|
|
14
|
+
let configCache = null;
|
|
15
|
+
let configPromise = null;
|
|
16
|
+
/**
|
|
17
|
+
* Get cached config from localStorage (synchronous, instant)
|
|
18
|
+
*/
|
|
19
|
+
function loadFromCache() {
|
|
20
|
+
if (typeof localStorage === 'undefined')
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
const stored = localStorage.getItem(getCacheKey());
|
|
24
|
+
if (!stored)
|
|
25
|
+
return null;
|
|
26
|
+
const parsed = JSON.parse(stored);
|
|
27
|
+
if (parsed.configured && parsed.supabaseUrl && parsed.supabaseAnonKey) {
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Save config to localStorage cache
|
|
38
|
+
*/
|
|
39
|
+
function saveToCache(config) {
|
|
40
|
+
if (typeof localStorage === 'undefined')
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
localStorage.setItem(getCacheKey(), JSON.stringify(config));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Storage full or unavailable
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize config: tries localStorage first (instant), then validates against server.
|
|
51
|
+
* Returns the config if configured, null if not.
|
|
52
|
+
*/
|
|
53
|
+
export async function initConfig() {
|
|
54
|
+
// Return in-flight promise if already initializing
|
|
55
|
+
if (configPromise)
|
|
56
|
+
return configPromise;
|
|
57
|
+
configPromise = (async () => {
|
|
58
|
+
// Try localStorage first for instant load
|
|
59
|
+
const cached = loadFromCache();
|
|
60
|
+
if (cached) {
|
|
61
|
+
configCache = cached;
|
|
62
|
+
}
|
|
63
|
+
// Fetch from server to validate/update
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch('/api/config');
|
|
66
|
+
if (response.ok) {
|
|
67
|
+
const serverConfig = await response.json();
|
|
68
|
+
if (serverConfig.configured) {
|
|
69
|
+
const config = {
|
|
70
|
+
supabaseUrl: serverConfig.supabaseUrl,
|
|
71
|
+
supabaseAnonKey: serverConfig.supabaseAnonKey,
|
|
72
|
+
configured: true
|
|
73
|
+
};
|
|
74
|
+
configCache = config;
|
|
75
|
+
saveToCache(config);
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Server says not configured — clear any stale cache
|
|
80
|
+
configCache = null;
|
|
81
|
+
clearConfigCache();
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Network error — use cached config if available (offline PWA support)
|
|
88
|
+
if (configCache) {
|
|
89
|
+
return configCache;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return configCache;
|
|
93
|
+
})();
|
|
94
|
+
const result = await configPromise;
|
|
95
|
+
configPromise = null;
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get config synchronously. Returns cached config or null.
|
|
100
|
+
* Call initConfig() first to ensure config is loaded.
|
|
101
|
+
*/
|
|
102
|
+
export function getConfig() {
|
|
103
|
+
if (configCache)
|
|
104
|
+
return configCache;
|
|
105
|
+
// Try localStorage as fallback
|
|
106
|
+
const cached = loadFromCache();
|
|
107
|
+
if (cached) {
|
|
108
|
+
configCache = cached;
|
|
109
|
+
}
|
|
110
|
+
return configCache;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Set config directly (used after setup wizard completes)
|
|
114
|
+
*/
|
|
115
|
+
export function setConfig(config) {
|
|
116
|
+
configCache = config;
|
|
117
|
+
saveToCache(config);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Clear cached config from localStorage
|
|
121
|
+
*/
|
|
122
|
+
function clearConfigCache() {
|
|
123
|
+
configCache = null;
|
|
124
|
+
if (typeof localStorage !== 'undefined') {
|
|
125
|
+
try {
|
|
126
|
+
localStorage.removeItem(getCacheKey());
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Ignore
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=runtimeConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeConfig.js","sourceRoot":"","sources":["../../src/runtime/runtimeConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth State Store
|
|
3
|
+
* Tracks the current authentication mode (supabase/offline/none)
|
|
4
|
+
*/
|
|
5
|
+
import { type Readable } from 'svelte/store';
|
|
6
|
+
import type { AuthMode, OfflineCredentials } from '../types';
|
|
7
|
+
import type { Session } from '@supabase/supabase-js';
|
|
8
|
+
interface AuthState {
|
|
9
|
+
mode: AuthMode;
|
|
10
|
+
session: Session | null;
|
|
11
|
+
offlineProfile: OfflineCredentials | null;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
authKickedMessage: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare const authState: {
|
|
16
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<AuthState>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
17
|
+
/**
|
|
18
|
+
* Set auth mode to Supabase with session
|
|
19
|
+
*/
|
|
20
|
+
setSupabaseAuth(session: Session): void;
|
|
21
|
+
/**
|
|
22
|
+
* Set auth mode to offline with cached profile
|
|
23
|
+
*/
|
|
24
|
+
setOfflineAuth(profile: OfflineCredentials): void;
|
|
25
|
+
/**
|
|
26
|
+
* Set auth mode to none (no session)
|
|
27
|
+
*/
|
|
28
|
+
setNoAuth(kickedMessage?: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Set loading state
|
|
31
|
+
*/
|
|
32
|
+
setLoading(isLoading: boolean): void;
|
|
33
|
+
/**
|
|
34
|
+
* Clear the auth kicked message
|
|
35
|
+
*/
|
|
36
|
+
clearKickedMessage(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Update the Supabase session (for token refresh)
|
|
39
|
+
*/
|
|
40
|
+
updateSession(session: Session | null): void;
|
|
41
|
+
/**
|
|
42
|
+
* Update user profile info in the session
|
|
43
|
+
* Used when profile is updated to immediately reflect changes in UI
|
|
44
|
+
*/
|
|
45
|
+
updateUserProfile(profile: Record<string, unknown>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Reset to initial state
|
|
48
|
+
*/
|
|
49
|
+
reset(): void;
|
|
50
|
+
};
|
|
51
|
+
export declare const isAuthenticated: Readable<boolean>;
|
|
52
|
+
export declare const userDisplayInfo: Readable<{
|
|
53
|
+
profile: Record<string, unknown>;
|
|
54
|
+
email: string;
|
|
55
|
+
} | null>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=authState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authState.d.ts","sourceRoot":"","sources":["../../src/stores/authState.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,UAAU,SAAS;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AA4ID,eAAO,MAAM,SAAS;;IA9HlB;;OAEG;6BACsB,OAAO,GAAG,IAAI;IAWvC;;OAEG;4BACqB,kBAAkB,GAAG,IAAI;IAWjD;;OAEG;8BACuB,MAAM,GAAG,IAAI;IAWvC;;OAEG;0BACmB,OAAO,GAAG,IAAI;IAIpC;;OAEG;0BACmB,IAAI;IAI1B;;OAEG;2BACoB,OAAO,GAAG,IAAI,GAAG,IAAI;IAkB5C;;;OAGG;+BACwB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA8BzD;;OAEG;aACM,IAAI;CAY8B,CAAC;AAGhD,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,OAAO,CAG7C,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,IAAI,CAeN,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth State Store
|
|
3
|
+
* Tracks the current authentication mode (supabase/offline/none)
|
|
4
|
+
*/
|
|
5
|
+
import { writable, derived } from 'svelte/store';
|
|
6
|
+
function createAuthStateStore() {
|
|
7
|
+
const { subscribe, set, update } = writable({
|
|
8
|
+
mode: 'none',
|
|
9
|
+
session: null,
|
|
10
|
+
offlineProfile: null,
|
|
11
|
+
isLoading: true,
|
|
12
|
+
authKickedMessage: null
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
subscribe,
|
|
16
|
+
/**
|
|
17
|
+
* Set auth mode to Supabase with session
|
|
18
|
+
*/
|
|
19
|
+
setSupabaseAuth(session) {
|
|
20
|
+
update((state) => ({
|
|
21
|
+
...state,
|
|
22
|
+
mode: 'supabase',
|
|
23
|
+
session,
|
|
24
|
+
offlineProfile: null,
|
|
25
|
+
isLoading: false,
|
|
26
|
+
authKickedMessage: null
|
|
27
|
+
}));
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Set auth mode to offline with cached profile
|
|
31
|
+
*/
|
|
32
|
+
setOfflineAuth(profile) {
|
|
33
|
+
update((state) => ({
|
|
34
|
+
...state,
|
|
35
|
+
mode: 'offline',
|
|
36
|
+
session: null,
|
|
37
|
+
offlineProfile: profile,
|
|
38
|
+
isLoading: false,
|
|
39
|
+
authKickedMessage: null
|
|
40
|
+
}));
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Set auth mode to none (no session)
|
|
44
|
+
*/
|
|
45
|
+
setNoAuth(kickedMessage) {
|
|
46
|
+
update((state) => ({
|
|
47
|
+
...state,
|
|
48
|
+
mode: 'none',
|
|
49
|
+
session: null,
|
|
50
|
+
offlineProfile: null,
|
|
51
|
+
isLoading: false,
|
|
52
|
+
authKickedMessage: kickedMessage || null
|
|
53
|
+
}));
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* Set loading state
|
|
57
|
+
*/
|
|
58
|
+
setLoading(isLoading) {
|
|
59
|
+
update((state) => ({ ...state, isLoading }));
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Clear the auth kicked message
|
|
63
|
+
*/
|
|
64
|
+
clearKickedMessage() {
|
|
65
|
+
update((state) => ({ ...state, authKickedMessage: null }));
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Update the Supabase session (for token refresh)
|
|
69
|
+
*/
|
|
70
|
+
updateSession(session) {
|
|
71
|
+
update((state) => {
|
|
72
|
+
if (!session) {
|
|
73
|
+
// Session was cleared - if online, set no auth
|
|
74
|
+
// If offline and was in supabase mode, we'll check offline session elsewhere
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
session: null
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
...state,
|
|
82
|
+
session,
|
|
83
|
+
mode: 'supabase'
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Update user profile info in the session
|
|
89
|
+
* Used when profile is updated to immediately reflect changes in UI
|
|
90
|
+
*/
|
|
91
|
+
updateUserProfile(profile) {
|
|
92
|
+
update((state) => {
|
|
93
|
+
if (state.mode === 'supabase' && state.session) {
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
session: {
|
|
97
|
+
...state.session,
|
|
98
|
+
user: {
|
|
99
|
+
...state.session.user,
|
|
100
|
+
user_metadata: {
|
|
101
|
+
...state.session.user.user_metadata,
|
|
102
|
+
...profile
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (state.mode === 'offline' && state.offlineProfile) {
|
|
109
|
+
return {
|
|
110
|
+
...state,
|
|
111
|
+
offlineProfile: {
|
|
112
|
+
...state.offlineProfile,
|
|
113
|
+
profile
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return state;
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* Reset to initial state
|
|
122
|
+
*/
|
|
123
|
+
reset() {
|
|
124
|
+
set({
|
|
125
|
+
mode: 'none',
|
|
126
|
+
session: null,
|
|
127
|
+
offlineProfile: null,
|
|
128
|
+
isLoading: true,
|
|
129
|
+
authKickedMessage: null
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export const authState = createAuthStateStore();
|
|
135
|
+
// Derived store for checking if user is authenticated (any mode)
|
|
136
|
+
export const isAuthenticated = derived(authState, ($authState) => $authState.mode !== 'none' && !$authState.isLoading);
|
|
137
|
+
// Derived store for getting user display info
|
|
138
|
+
export const userDisplayInfo = derived(authState, ($authState) => {
|
|
139
|
+
if ($authState.mode === 'supabase' && $authState.session) {
|
|
140
|
+
const user = $authState.session.user;
|
|
141
|
+
return {
|
|
142
|
+
profile: user.user_metadata || {},
|
|
143
|
+
email: user.email || ''
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if ($authState.mode === 'offline' && $authState.offlineProfile) {
|
|
147
|
+
return {
|
|
148
|
+
profile: $authState.offlineProfile.profile || {},
|
|
149
|
+
email: $authState.offlineProfile.email
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=authState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authState.js","sourceRoot":"","sources":["../../src/stores/authState.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
type NetworkCallback = () => void | Promise<void>;
|
|
3
|
+
export declare const isOnline: Readable<boolean> & {
|
|
4
|
+
init: () => void;
|
|
5
|
+
onReconnect: (callback: NetworkCallback) => () => void;
|
|
6
|
+
onDisconnect: (callback: NetworkCallback) => () => void;
|
|
7
|
+
};
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/stores/network.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAKvD,KAAK,eAAe,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA+GlD,eAAO,MAAM,QAAQ;UA5Gb,MAAM,IAAI;iBACH,CAAC,QAAQ,EAAE,eAAe,KAAK,MAAM,IAAI;kBACxC,CAAC,QAAQ,EAAE,eAAe,KAAK,MAAM,IAAI;CA0Gb,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
const browser = typeof window !== 'undefined';
|
|
3
|
+
import { debugError } from '../debug';
|
|
4
|
+
function createNetworkStore() {
|
|
5
|
+
const { subscribe, set } = writable(true);
|
|
6
|
+
const reconnectCallbacks = new Set();
|
|
7
|
+
const disconnectCallbacks = new Set();
|
|
8
|
+
let wasOffline = false;
|
|
9
|
+
let currentValue = true; // Track current value to prevent redundant updates
|
|
10
|
+
let initialized = false; // Prevent double-initialization
|
|
11
|
+
function setIfChanged(value) {
|
|
12
|
+
if (value !== currentValue) {
|
|
13
|
+
currentValue = value;
|
|
14
|
+
set(value);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Run callbacks sequentially, properly awaiting async ones
|
|
18
|
+
// This ensures auth validation completes before sync is triggered
|
|
19
|
+
async function runCallbacksSequentially(callbacks, label) {
|
|
20
|
+
for (const callback of callbacks) {
|
|
21
|
+
try {
|
|
22
|
+
await callback();
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
debugError(`[Network] ${label} callback error:`, e);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function init() {
|
|
30
|
+
if (!browser)
|
|
31
|
+
return;
|
|
32
|
+
if (initialized)
|
|
33
|
+
return; // Idempotent
|
|
34
|
+
initialized = true;
|
|
35
|
+
// Set initial state
|
|
36
|
+
const initiallyOnline = navigator.onLine;
|
|
37
|
+
currentValue = initiallyOnline;
|
|
38
|
+
set(initiallyOnline);
|
|
39
|
+
wasOffline = !initiallyOnline;
|
|
40
|
+
// Listen for going offline
|
|
41
|
+
window.addEventListener('offline', () => {
|
|
42
|
+
const wasOnline = currentValue;
|
|
43
|
+
wasOffline = true;
|
|
44
|
+
setIfChanged(false);
|
|
45
|
+
// If we were online, trigger disconnect callbacks
|
|
46
|
+
if (wasOnline) {
|
|
47
|
+
runCallbacksSequentially(disconnectCallbacks, 'Disconnect');
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// Listen for coming back online
|
|
51
|
+
window.addEventListener('online', () => {
|
|
52
|
+
setIfChanged(true);
|
|
53
|
+
// If we were offline, trigger reconnect callbacks
|
|
54
|
+
if (wasOffline) {
|
|
55
|
+
wasOffline = false;
|
|
56
|
+
// Small delay to ensure network is stable
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
runCallbacksSequentially(reconnectCallbacks, 'Reconnect');
|
|
59
|
+
}, 500);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// Also listen for visibility changes (iOS specific - PWA may not fire online/offline)
|
|
63
|
+
document.addEventListener('visibilitychange', () => {
|
|
64
|
+
if (document.visibilityState === 'visible') {
|
|
65
|
+
const nowOnline = navigator.onLine;
|
|
66
|
+
setIfChanged(nowOnline); // Only update if actually changed
|
|
67
|
+
// If we're coming back online after being hidden
|
|
68
|
+
if (nowOnline && wasOffline) {
|
|
69
|
+
wasOffline = false;
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
runCallbacksSequentially(reconnectCallbacks, 'Reconnect');
|
|
72
|
+
}, 500);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// When going to background, assume we might lose connection
|
|
77
|
+
wasOffline = !navigator.onLine;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function onReconnect(callback) {
|
|
82
|
+
reconnectCallbacks.add(callback);
|
|
83
|
+
return () => reconnectCallbacks.delete(callback);
|
|
84
|
+
}
|
|
85
|
+
function onDisconnect(callback) {
|
|
86
|
+
disconnectCallbacks.add(callback);
|
|
87
|
+
return () => disconnectCallbacks.delete(callback);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
subscribe,
|
|
91
|
+
init,
|
|
92
|
+
onReconnect,
|
|
93
|
+
onDisconnect
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export const isOnline = createNetworkStore();
|
|
97
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/stores/network.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Changes Store
|
|
3
|
+
*
|
|
4
|
+
* Manages incoming realtime changes and active editing state to enable:
|
|
5
|
+
* - Graceful UI animations when remote changes arrive
|
|
6
|
+
* - Protection of user edits from being overwritten
|
|
7
|
+
* - Deferred change application for entities being edited
|
|
8
|
+
*
|
|
9
|
+
* Two types of entities:
|
|
10
|
+
* 1. Auto-save entities (toggles, quick actions) - changes apply immediately with animation
|
|
11
|
+
* 2. Form entities (modals with Save button) - changes are deferred until form closes
|
|
12
|
+
*
|
|
13
|
+
* Action type detection:
|
|
14
|
+
* Since Supabase Realtime only sends INSERT/UPDATE/DELETE events, we detect
|
|
15
|
+
* the specific action type by analyzing which fields changed:
|
|
16
|
+
* - INSERT → 'create' action
|
|
17
|
+
* - DELETE → 'delete' action
|
|
18
|
+
* - UPDATE with 'completed' changed → 'toggle' action
|
|
19
|
+
* - UPDATE with 'current_value' changed → 'increment' or 'decrement' action
|
|
20
|
+
* - UPDATE with 'order' changed → 'reorder' action
|
|
21
|
+
* - UPDATE with 'name' changed → 'rename' action
|
|
22
|
+
* - UPDATE with 'is_enabled' changed → 'toggle' action (for block lists)
|
|
23
|
+
* - UPDATE with other fields → 'update' action
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Action types that can be detected from realtime events.
|
|
27
|
+
* Used to apply appropriate animations.
|
|
28
|
+
*/
|
|
29
|
+
export type RemoteActionType = 'create' | 'delete' | 'toggle' | 'increment' | 'decrement' | 'reorder' | 'rename' | 'update';
|
|
30
|
+
interface RemoteChange {
|
|
31
|
+
entityId: string;
|
|
32
|
+
entityType: string;
|
|
33
|
+
fields: string[];
|
|
34
|
+
actionType: RemoteActionType;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
applied: boolean;
|
|
37
|
+
valueDelta?: number;
|
|
38
|
+
}
|
|
39
|
+
interface ActiveEdit {
|
|
40
|
+
entityId: string;
|
|
41
|
+
entityType: string;
|
|
42
|
+
formType: 'auto-save' | 'manual-save';
|
|
43
|
+
startedAt: number;
|
|
44
|
+
fields?: string[];
|
|
45
|
+
}
|
|
46
|
+
interface RemoteChangesState {
|
|
47
|
+
recentChanges: Map<string, RemoteChange>;
|
|
48
|
+
activeEdits: Map<string, ActiveEdit>;
|
|
49
|
+
deferredChanges: Map<string, RemoteChange[]>;
|
|
50
|
+
pendingDeletes: Map<string, number>;
|
|
51
|
+
}
|
|
52
|
+
export declare const remoteChangesStore: {
|
|
53
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<RemoteChangesState>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
54
|
+
/**
|
|
55
|
+
* Detect action type from event type and changed fields.
|
|
56
|
+
* This is how we know what animation to play even though Supabase
|
|
57
|
+
* doesn't store the "action" - we infer it from what changed.
|
|
58
|
+
*/
|
|
59
|
+
detectActionType(eventType: "INSERT" | "UPDATE" | "DELETE", fields: string[], valueDelta?: number): RemoteActionType;
|
|
60
|
+
/**
|
|
61
|
+
* Record a remote change that just arrived.
|
|
62
|
+
* If the entity is being edited with a manual-save form, defer the change.
|
|
63
|
+
* Otherwise, mark it as a recent change for animation.
|
|
64
|
+
*
|
|
65
|
+
* @param eventType - Supabase event type (INSERT/UPDATE/DELETE)
|
|
66
|
+
* @param valueDelta - For increment/decrement, the change in value
|
|
67
|
+
*/
|
|
68
|
+
recordRemoteChange(entityId: string, entityType: string, fields: string[], applied: boolean, eventType?: "INSERT" | "UPDATE" | "DELETE", valueDelta?: number): {
|
|
69
|
+
deferred: boolean;
|
|
70
|
+
actionType: RemoteActionType;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Record a local change for animation purposes.
|
|
74
|
+
* Call this BEFORE the component mounts (e.g., right before adding to database)
|
|
75
|
+
* so that when the component mounts with remoteChangeAnimation, the create animation triggers.
|
|
76
|
+
*
|
|
77
|
+
* This is useful for local creates to animate the same way as remote creates.
|
|
78
|
+
*/
|
|
79
|
+
recordLocalChange(entityId: string, entityType: string, actionType: RemoteActionType, fields?: string[]): void;
|
|
80
|
+
/**
|
|
81
|
+
* Mark an entity as being actively edited.
|
|
82
|
+
* @param formType 'auto-save' for inline edits, 'manual-save' for modals with Save button
|
|
83
|
+
*/
|
|
84
|
+
startEditing(entityId: string, entityType: string, formType: "auto-save" | "manual-save", fields?: string[]): void;
|
|
85
|
+
/**
|
|
86
|
+
* Mark editing as complete. Returns any deferred changes that need to be processed.
|
|
87
|
+
*/
|
|
88
|
+
stopEditing(entityId: string, entityType: string): RemoteChange[];
|
|
89
|
+
/**
|
|
90
|
+
* Check if an entity is currently being edited.
|
|
91
|
+
*/
|
|
92
|
+
isEditing(entityId: string, entityType: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Clear deferred changes for an entity without stopping editing.
|
|
95
|
+
* Used when user dismisses or loads remote changes in the banner.
|
|
96
|
+
*/
|
|
97
|
+
clearDeferredChanges(entityId: string, entityType: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* Check if an entity has deferred changes waiting.
|
|
100
|
+
*/
|
|
101
|
+
hasDeferredChanges(entityId: string, entityType: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Check if an entity was recently changed (for animation).
|
|
104
|
+
*/
|
|
105
|
+
wasRecentlyChanged(entityId: string, entityType: string): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Get recent change details for an entity (for field-level animation).
|
|
108
|
+
*/
|
|
109
|
+
getRecentChange(entityId: string, entityType: string): RemoteChange | null;
|
|
110
|
+
/**
|
|
111
|
+
* Mark an entity as pending delete (for delete animation).
|
|
112
|
+
* Returns a promise that resolves after the animation duration.
|
|
113
|
+
* The pending delete is cleared AFTER resolve so the caller can
|
|
114
|
+
* delete from DB first — this prevents a reactive flash where the
|
|
115
|
+
* item reappears between animation end and DOM removal.
|
|
116
|
+
*/
|
|
117
|
+
markPendingDelete(entityId: string, entityType: string): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Check if an entity is pending deletion (for animation).
|
|
120
|
+
*/
|
|
121
|
+
isPendingDelete(entityId: string, entityType: string): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Clear all tracking (for logout).
|
|
124
|
+
*/
|
|
125
|
+
clear(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Stop the cleanup interval (for cleanup on unmount).
|
|
128
|
+
*/
|
|
129
|
+
destroy(): void;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Derived store for components to check if a specific entity was recently updated remotely.
|
|
133
|
+
* Use this to trigger animations.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createRecentChangeIndicator(entityId: string, entityType: string): import("svelte/store").Readable<RemoteChange | null>;
|
|
136
|
+
/**
|
|
137
|
+
* Derived store for components to check if a specific entity is pending deletion.
|
|
138
|
+
* Use this to apply delete animation before removing from DOM.
|
|
139
|
+
*/
|
|
140
|
+
export declare function createPendingDeleteIndicator(entityId: string, entityType: string): import("svelte/store").Readable<boolean>;
|
|
141
|
+
export {};
|
|
142
|
+
//# sourceMappingURL=remoteChanges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteChanges.d.ts","sourceRoot":"","sources":["../../src/stores/remoteChanges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,WAAW,GACX,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAEb,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,WAAW,GAAG,aAAa,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,kBAAkB;IAE1B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAEzC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAErC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAG7C,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAuWD,eAAO,MAAM,kBAAkB;;IApT3B;;;;OAIG;gCAEU,QAAQ,GAAG,QAAQ,GAAG,QAAQ,UACjC,MAAM,EAAE,eACH,MAAM,GAClB,gBAAgB;IAkCnB;;;;;;;OAOG;iCAES,MAAM,cACJ,MAAM,UACV,MAAM,EAAE,WACP,OAAO,cACL,QAAQ,GAAG,QAAQ,GAAG,QAAQ,eAC5B,MAAM,GAClB;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,gBAAgB,CAAA;KAAE;IAsCtD;;;;;;OAMG;gCAES,MAAM,cACJ,MAAM,cACN,gBAAgB,WACpB,MAAM,EAAE,GACf,IAAI;IAmBP;;;OAGG;2BAES,MAAM,cACJ,MAAM,YACR,WAAW,GAAG,aAAa,WAC5B,MAAM,EAAE,GAChB,IAAI;IAcP;;OAEG;0BACmB,MAAM,cAAc,MAAM,GAAG,YAAY,EAAE;IAmBjE;;OAEG;wBACiB,MAAM,cAAc,MAAM,GAAG,OAAO;IASxD;;;OAGG;mCAC4B,MAAM,cAAc,MAAM,GAAG,IAAI;IAQhE;;OAEG;iCAC0B,MAAM,cAAc,MAAM,GAAG,OAAO;IAWjE;;OAEG;iCAC0B,MAAM,cAAc,MAAM,GAAG,OAAO;IAajE;;OAEG;8BACuB,MAAM,cAAc,MAAM,GAAG,YAAY,GAAG,IAAI;IAa1E;;;;;;OAMG;gCACyB,MAAM,cAAc,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBtE;;OAEG;8BACuB,MAAM,cAAc,MAAM,GAAG,OAAO;IAU9D;;OAEG;aACM,IAAI;IAUb;;OAEG;eACQ,IAAI;CAMyC,CAAC;AAM7D;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wDAQ/E;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,4CAKhF"}
|