@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,353 @@
|
|
|
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
|
+
import { writable, derived } from 'svelte/store';
|
|
26
|
+
// ============================================================
|
|
27
|
+
// STORE
|
|
28
|
+
// ============================================================
|
|
29
|
+
const ANIMATION_DURATION = 2000; // How long to keep change in "recent" for animation
|
|
30
|
+
const CLEANUP_INTERVAL = 5000; // How often to clean up old changes
|
|
31
|
+
// How long to keep items in pending delete state for animation
|
|
32
|
+
const DELETE_ANIMATION_DURATION = 500;
|
|
33
|
+
function createRemoteChangesStore() {
|
|
34
|
+
const { subscribe, update } = writable({
|
|
35
|
+
recentChanges: new Map(),
|
|
36
|
+
activeEdits: new Map(),
|
|
37
|
+
deferredChanges: new Map(),
|
|
38
|
+
pendingDeletes: new Map()
|
|
39
|
+
});
|
|
40
|
+
// Cleanup old changes periodically
|
|
41
|
+
let cleanupInterval = null;
|
|
42
|
+
function startCleanup() {
|
|
43
|
+
if (cleanupInterval)
|
|
44
|
+
return;
|
|
45
|
+
if (typeof window === 'undefined')
|
|
46
|
+
return;
|
|
47
|
+
cleanupInterval = setInterval(() => {
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
update((state) => {
|
|
50
|
+
// Remove old recent changes
|
|
51
|
+
for (const [key, change] of state.recentChanges) {
|
|
52
|
+
if (now - change.timestamp > ANIMATION_DURATION) {
|
|
53
|
+
state.recentChanges.delete(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return state;
|
|
57
|
+
});
|
|
58
|
+
}, CLEANUP_INTERVAL);
|
|
59
|
+
}
|
|
60
|
+
function stopCleanup() {
|
|
61
|
+
if (cleanupInterval) {
|
|
62
|
+
clearInterval(cleanupInterval);
|
|
63
|
+
cleanupInterval = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
subscribe,
|
|
68
|
+
/**
|
|
69
|
+
* Detect action type from event type and changed fields.
|
|
70
|
+
* This is how we know what animation to play even though Supabase
|
|
71
|
+
* doesn't store the "action" - we infer it from what changed.
|
|
72
|
+
*/
|
|
73
|
+
detectActionType(eventType, fields, valueDelta) {
|
|
74
|
+
if (eventType === 'INSERT')
|
|
75
|
+
return 'create';
|
|
76
|
+
if (eventType === 'DELETE')
|
|
77
|
+
return 'delete';
|
|
78
|
+
// For UPDATE, determine action from which fields changed
|
|
79
|
+
// Priority order matters - more specific actions first
|
|
80
|
+
// Toggle actions (completed, is_enabled)
|
|
81
|
+
if (fields.includes('completed') || fields.includes('is_enabled')) {
|
|
82
|
+
return 'toggle';
|
|
83
|
+
}
|
|
84
|
+
// Increment/decrement (current_value changed)
|
|
85
|
+
if (fields.includes('current_value')) {
|
|
86
|
+
if (valueDelta !== undefined) {
|
|
87
|
+
return valueDelta > 0 ? 'increment' : 'decrement';
|
|
88
|
+
}
|
|
89
|
+
return 'increment'; // Default to increment if delta unknown
|
|
90
|
+
}
|
|
91
|
+
// Reorder (order changed)
|
|
92
|
+
if (fields.includes('order') && fields.length === 1) {
|
|
93
|
+
return 'reorder';
|
|
94
|
+
}
|
|
95
|
+
// Rename or visual property change (name, color)
|
|
96
|
+
if ((fields.includes('name') || fields.includes('color')) && fields.length <= 2) {
|
|
97
|
+
return 'rename';
|
|
98
|
+
}
|
|
99
|
+
// Generic update for other changes
|
|
100
|
+
return 'update';
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* Record a remote change that just arrived.
|
|
104
|
+
* If the entity is being edited with a manual-save form, defer the change.
|
|
105
|
+
* Otherwise, mark it as a recent change for animation.
|
|
106
|
+
*
|
|
107
|
+
* @param eventType - Supabase event type (INSERT/UPDATE/DELETE)
|
|
108
|
+
* @param valueDelta - For increment/decrement, the change in value
|
|
109
|
+
*/
|
|
110
|
+
recordRemoteChange(entityId, entityType, fields, applied, eventType = 'UPDATE', valueDelta) {
|
|
111
|
+
startCleanup();
|
|
112
|
+
const actionType = this.detectActionType(eventType, fields, valueDelta);
|
|
113
|
+
const change = {
|
|
114
|
+
entityId,
|
|
115
|
+
entityType,
|
|
116
|
+
fields,
|
|
117
|
+
actionType,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
applied,
|
|
120
|
+
valueDelta
|
|
121
|
+
};
|
|
122
|
+
let deferred = false;
|
|
123
|
+
update((state) => {
|
|
124
|
+
const key = `${entityType}:${entityId}`;
|
|
125
|
+
const activeEdit = state.activeEdits.get(key);
|
|
126
|
+
if (activeEdit && activeEdit.formType === 'manual-save') {
|
|
127
|
+
// Entity is being edited in a form with Save button - defer the change
|
|
128
|
+
const existing = state.deferredChanges.get(key) || [];
|
|
129
|
+
existing.push(change);
|
|
130
|
+
state.deferredChanges.set(key, existing);
|
|
131
|
+
deferred = true;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Apply immediately (or already applied) - record for animation
|
|
135
|
+
state.recentChanges.set(key, change);
|
|
136
|
+
}
|
|
137
|
+
return state;
|
|
138
|
+
});
|
|
139
|
+
return { deferred, actionType };
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Record a local change for animation purposes.
|
|
143
|
+
* Call this BEFORE the component mounts (e.g., right before adding to database)
|
|
144
|
+
* so that when the component mounts with remoteChangeAnimation, the create animation triggers.
|
|
145
|
+
*
|
|
146
|
+
* This is useful for local creates to animate the same way as remote creates.
|
|
147
|
+
*/
|
|
148
|
+
recordLocalChange(entityId, entityType, actionType, fields = ['*']) {
|
|
149
|
+
startCleanup();
|
|
150
|
+
const change = {
|
|
151
|
+
entityId,
|
|
152
|
+
entityType,
|
|
153
|
+
fields,
|
|
154
|
+
actionType,
|
|
155
|
+
timestamp: Date.now(),
|
|
156
|
+
applied: true
|
|
157
|
+
};
|
|
158
|
+
update((state) => {
|
|
159
|
+
const key = `${entityType}:${entityId}`;
|
|
160
|
+
state.recentChanges.set(key, change);
|
|
161
|
+
return state;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
/**
|
|
165
|
+
* Mark an entity as being actively edited.
|
|
166
|
+
* @param formType 'auto-save' for inline edits, 'manual-save' for modals with Save button
|
|
167
|
+
*/
|
|
168
|
+
startEditing(entityId, entityType, formType, fields) {
|
|
169
|
+
update((state) => {
|
|
170
|
+
const key = `${entityType}:${entityId}`;
|
|
171
|
+
state.activeEdits.set(key, {
|
|
172
|
+
entityId,
|
|
173
|
+
entityType,
|
|
174
|
+
formType,
|
|
175
|
+
startedAt: Date.now(),
|
|
176
|
+
fields
|
|
177
|
+
});
|
|
178
|
+
return state;
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* Mark editing as complete. Returns any deferred changes that need to be processed.
|
|
183
|
+
*/
|
|
184
|
+
stopEditing(entityId, entityType) {
|
|
185
|
+
let deferredChanges = [];
|
|
186
|
+
update((state) => {
|
|
187
|
+
const key = `${entityType}:${entityId}`;
|
|
188
|
+
state.activeEdits.delete(key);
|
|
189
|
+
// Return deferred changes for processing
|
|
190
|
+
if (state.deferredChanges.has(key)) {
|
|
191
|
+
deferredChanges = state.deferredChanges.get(key) || [];
|
|
192
|
+
state.deferredChanges.delete(key);
|
|
193
|
+
}
|
|
194
|
+
return state;
|
|
195
|
+
});
|
|
196
|
+
return deferredChanges;
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* Check if an entity is currently being edited.
|
|
200
|
+
*/
|
|
201
|
+
isEditing(entityId, entityType) {
|
|
202
|
+
let editing = false;
|
|
203
|
+
const unsubscribe = subscribe((state) => {
|
|
204
|
+
editing = state.activeEdits.has(`${entityType}:${entityId}`);
|
|
205
|
+
});
|
|
206
|
+
unsubscribe();
|
|
207
|
+
return editing;
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Clear deferred changes for an entity without stopping editing.
|
|
211
|
+
* Used when user dismisses or loads remote changes in the banner.
|
|
212
|
+
*/
|
|
213
|
+
clearDeferredChanges(entityId, entityType) {
|
|
214
|
+
update((state) => {
|
|
215
|
+
const key = `${entityType}:${entityId}`;
|
|
216
|
+
state.deferredChanges.delete(key);
|
|
217
|
+
return state;
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
/**
|
|
221
|
+
* Check if an entity has deferred changes waiting.
|
|
222
|
+
*/
|
|
223
|
+
hasDeferredChanges(entityId, entityType) {
|
|
224
|
+
let hasChanges = false;
|
|
225
|
+
const unsubscribe = subscribe((state) => {
|
|
226
|
+
const key = `${entityType}:${entityId}`;
|
|
227
|
+
const changes = state.deferredChanges.get(key);
|
|
228
|
+
hasChanges = !!changes && changes.length > 0;
|
|
229
|
+
});
|
|
230
|
+
unsubscribe();
|
|
231
|
+
return hasChanges;
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Check if an entity was recently changed (for animation).
|
|
235
|
+
*/
|
|
236
|
+
wasRecentlyChanged(entityId, entityType) {
|
|
237
|
+
let recent = false;
|
|
238
|
+
const unsubscribe = subscribe((state) => {
|
|
239
|
+
const key = `${entityType}:${entityId}`;
|
|
240
|
+
const change = state.recentChanges.get(key);
|
|
241
|
+
if (change && Date.now() - change.timestamp < ANIMATION_DURATION) {
|
|
242
|
+
recent = true;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
unsubscribe();
|
|
246
|
+
return recent;
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Get recent change details for an entity (for field-level animation).
|
|
250
|
+
*/
|
|
251
|
+
getRecentChange(entityId, entityType) {
|
|
252
|
+
let change = null;
|
|
253
|
+
const unsubscribe = subscribe((state) => {
|
|
254
|
+
const key = `${entityType}:${entityId}`;
|
|
255
|
+
const c = state.recentChanges.get(key);
|
|
256
|
+
if (c && Date.now() - c.timestamp < ANIMATION_DURATION) {
|
|
257
|
+
change = c;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
unsubscribe();
|
|
261
|
+
return change;
|
|
262
|
+
},
|
|
263
|
+
/**
|
|
264
|
+
* Mark an entity as pending delete (for delete animation).
|
|
265
|
+
* Returns a promise that resolves after the animation duration.
|
|
266
|
+
* The pending delete is cleared AFTER resolve so the caller can
|
|
267
|
+
* delete from DB first — this prevents a reactive flash where the
|
|
268
|
+
* item reappears between animation end and DOM removal.
|
|
269
|
+
*/
|
|
270
|
+
markPendingDelete(entityId, entityType) {
|
|
271
|
+
update((state) => {
|
|
272
|
+
const key = `${entityType}:${entityId}`;
|
|
273
|
+
state.pendingDeletes.set(key, Date.now());
|
|
274
|
+
return state;
|
|
275
|
+
});
|
|
276
|
+
// Return promise that resolves after animation duration
|
|
277
|
+
return new Promise((resolve) => {
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
resolve();
|
|
280
|
+
// Clean up after a short delay — the caller deletes from DB on
|
|
281
|
+
// resolve which triggers DOM removal, so this is just housekeeping
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
update((state) => {
|
|
284
|
+
const key = `${entityType}:${entityId}`;
|
|
285
|
+
state.pendingDeletes.delete(key);
|
|
286
|
+
return state;
|
|
287
|
+
});
|
|
288
|
+
}, 100);
|
|
289
|
+
}, DELETE_ANIMATION_DURATION);
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
/**
|
|
293
|
+
* Check if an entity is pending deletion (for animation).
|
|
294
|
+
*/
|
|
295
|
+
isPendingDelete(entityId, entityType) {
|
|
296
|
+
let pending = false;
|
|
297
|
+
const unsubscribe = subscribe((state) => {
|
|
298
|
+
const key = `${entityType}:${entityId}`;
|
|
299
|
+
pending = state.pendingDeletes.has(key);
|
|
300
|
+
});
|
|
301
|
+
unsubscribe();
|
|
302
|
+
return pending;
|
|
303
|
+
},
|
|
304
|
+
/**
|
|
305
|
+
* Clear all tracking (for logout).
|
|
306
|
+
*/
|
|
307
|
+
clear() {
|
|
308
|
+
stopCleanup();
|
|
309
|
+
update(() => ({
|
|
310
|
+
recentChanges: new Map(),
|
|
311
|
+
activeEdits: new Map(),
|
|
312
|
+
deferredChanges: new Map(),
|
|
313
|
+
pendingDeletes: new Map()
|
|
314
|
+
}));
|
|
315
|
+
},
|
|
316
|
+
/**
|
|
317
|
+
* Stop the cleanup interval (for cleanup on unmount).
|
|
318
|
+
*/
|
|
319
|
+
destroy() {
|
|
320
|
+
stopCleanup();
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
export const remoteChangesStore = createRemoteChangesStore();
|
|
325
|
+
// ============================================================
|
|
326
|
+
// DERIVED STORES FOR UI
|
|
327
|
+
// ============================================================
|
|
328
|
+
/**
|
|
329
|
+
* Derived store for components to check if a specific entity was recently updated remotely.
|
|
330
|
+
* Use this to trigger animations.
|
|
331
|
+
*/
|
|
332
|
+
export function createRecentChangeIndicator(entityId, entityType) {
|
|
333
|
+
return derived(remoteChangesStore, ($state) => {
|
|
334
|
+
const key = `${entityType}:${entityId}`;
|
|
335
|
+
const change = $state.recentChanges.get(key);
|
|
336
|
+
if (!change)
|
|
337
|
+
return null;
|
|
338
|
+
if (Date.now() - change.timestamp > ANIMATION_DURATION)
|
|
339
|
+
return null;
|
|
340
|
+
return change;
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Derived store for components to check if a specific entity is pending deletion.
|
|
345
|
+
* Use this to apply delete animation before removing from DOM.
|
|
346
|
+
*/
|
|
347
|
+
export function createPendingDeleteIndicator(entityId, entityType) {
|
|
348
|
+
return derived(remoteChangesStore, ($state) => {
|
|
349
|
+
const key = `${entityType}:${entityId}`;
|
|
350
|
+
return $state.pendingDeletes.has(key);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=remoteChanges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteChanges.js","sourceRoot":"","sources":["../../src/stores/remoteChanges.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SyncStatus } from '../types';
|
|
2
|
+
export interface SyncError {
|
|
3
|
+
table: string;
|
|
4
|
+
operation: string;
|
|
5
|
+
entityId: string;
|
|
6
|
+
message: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
}
|
|
9
|
+
export type RealtimeState = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
10
|
+
interface SyncState {
|
|
11
|
+
status: SyncStatus;
|
|
12
|
+
pendingCount: number;
|
|
13
|
+
lastError: string | null;
|
|
14
|
+
lastErrorDetails: string | null;
|
|
15
|
+
syncErrors: SyncError[];
|
|
16
|
+
lastSyncTime: string | null;
|
|
17
|
+
syncMessage: string | null;
|
|
18
|
+
isTabVisible: boolean;
|
|
19
|
+
realtimeState: RealtimeState;
|
|
20
|
+
}
|
|
21
|
+
export declare const syncStatusStore: {
|
|
22
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<SyncState>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
23
|
+
setStatus: (status: SyncStatus) => void;
|
|
24
|
+
setPendingCount: (count: number) => void;
|
|
25
|
+
setError: (friendly: string | null, raw?: string | null) => void;
|
|
26
|
+
addSyncError: (error: SyncError) => void;
|
|
27
|
+
clearSyncErrors: () => void;
|
|
28
|
+
setLastSyncTime: (time: string) => void;
|
|
29
|
+
setSyncMessage: (message: string | null) => void;
|
|
30
|
+
setTabVisible: (visible: boolean) => void;
|
|
31
|
+
setRealtimeState: (realtimeState: RealtimeState) => void;
|
|
32
|
+
reset: () => void;
|
|
33
|
+
};
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/stores/sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAElF,UAAU,SAAS;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,aAAa,CAAC;CAC9B;AA4HD,eAAO,MAAM,eAAe;;wBAhGJ,UAAU;6BAuDL,MAAM;yBACV,MAAM,GAAG,IAAI,QAAQ,MAAM,GAAG,IAAI;0BAMjC,SAAS;;4BAMP,MAAM;8BACJ,MAAM,GAAG,IAAI;6BAEd,OAAO;sCACE,aAAa;;CAwBG,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
// Minimum time to show 'syncing' state to prevent flickering (ms)
|
|
3
|
+
const MIN_SYNCING_TIME = 500;
|
|
4
|
+
// Max errors to keep in history
|
|
5
|
+
const MAX_ERROR_HISTORY = 10;
|
|
6
|
+
function createSyncStatusStore() {
|
|
7
|
+
const { subscribe, set, update } = writable({
|
|
8
|
+
status: 'idle',
|
|
9
|
+
pendingCount: 0,
|
|
10
|
+
lastError: null,
|
|
11
|
+
lastErrorDetails: null,
|
|
12
|
+
syncErrors: [],
|
|
13
|
+
lastSyncTime: null,
|
|
14
|
+
syncMessage: null,
|
|
15
|
+
isTabVisible: true,
|
|
16
|
+
realtimeState: 'disconnected'
|
|
17
|
+
});
|
|
18
|
+
let currentStatus = 'idle';
|
|
19
|
+
let syncingStartTime = null;
|
|
20
|
+
let pendingStatusChange = null;
|
|
21
|
+
return {
|
|
22
|
+
subscribe,
|
|
23
|
+
setStatus: (status) => {
|
|
24
|
+
// Ignore redundant status updates to prevent unnecessary re-renders
|
|
25
|
+
if (status === currentStatus && status !== 'syncing') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Clear any pending status change
|
|
29
|
+
if (pendingStatusChange) {
|
|
30
|
+
clearTimeout(pendingStatusChange.timeout);
|
|
31
|
+
pendingStatusChange = null;
|
|
32
|
+
}
|
|
33
|
+
if (status === 'syncing') {
|
|
34
|
+
// Starting sync - record the time and clear previous errors
|
|
35
|
+
syncingStartTime = Date.now();
|
|
36
|
+
currentStatus = status;
|
|
37
|
+
update((state) => ({ ...state, status, lastError: null, syncErrors: [] }));
|
|
38
|
+
}
|
|
39
|
+
else if (syncingStartTime !== null) {
|
|
40
|
+
// Ending sync - ensure minimum display time
|
|
41
|
+
const elapsed = Date.now() - syncingStartTime;
|
|
42
|
+
const remaining = MIN_SYNCING_TIME - elapsed;
|
|
43
|
+
if (remaining > 0) {
|
|
44
|
+
// Delay the status change to prevent flickering
|
|
45
|
+
pendingStatusChange = {
|
|
46
|
+
status,
|
|
47
|
+
timeout: setTimeout(() => {
|
|
48
|
+
syncingStartTime = null;
|
|
49
|
+
pendingStatusChange = null;
|
|
50
|
+
currentStatus = status;
|
|
51
|
+
update((state) => ({
|
|
52
|
+
...state,
|
|
53
|
+
status,
|
|
54
|
+
lastError: status === 'idle' ? null : state.lastError
|
|
55
|
+
}));
|
|
56
|
+
}, remaining)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
syncingStartTime = null;
|
|
61
|
+
currentStatus = status;
|
|
62
|
+
update((state) => ({
|
|
63
|
+
...state,
|
|
64
|
+
status,
|
|
65
|
+
lastError: status === 'idle' ? null : state.lastError
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
currentStatus = status;
|
|
71
|
+
update((state) => ({
|
|
72
|
+
...state,
|
|
73
|
+
status,
|
|
74
|
+
lastError: status === 'idle' ? null : state.lastError
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
setPendingCount: (count) => update((state) => ({ ...state, pendingCount: count })),
|
|
79
|
+
setError: (friendly, raw) => update((state) => ({
|
|
80
|
+
...state,
|
|
81
|
+
lastError: friendly,
|
|
82
|
+
lastErrorDetails: raw ?? null
|
|
83
|
+
})),
|
|
84
|
+
addSyncError: (error) => update((state) => ({
|
|
85
|
+
...state,
|
|
86
|
+
syncErrors: [...state.syncErrors, error].slice(-MAX_ERROR_HISTORY)
|
|
87
|
+
})),
|
|
88
|
+
clearSyncErrors: () => update((state) => ({ ...state, syncErrors: [] })),
|
|
89
|
+
setLastSyncTime: (time) => update((state) => ({ ...state, lastSyncTime: time })),
|
|
90
|
+
setSyncMessage: (message) => update((state) => ({ ...state, syncMessage: message })),
|
|
91
|
+
setTabVisible: (visible) => update((state) => ({ ...state, isTabVisible: visible })),
|
|
92
|
+
setRealtimeState: (realtimeState) => update((state) => ({ ...state, realtimeState })),
|
|
93
|
+
reset: () => {
|
|
94
|
+
if (pendingStatusChange) {
|
|
95
|
+
clearTimeout(pendingStatusChange.timeout);
|
|
96
|
+
pendingStatusChange = null;
|
|
97
|
+
}
|
|
98
|
+
syncingStartTime = null;
|
|
99
|
+
currentStatus = 'idle';
|
|
100
|
+
set({
|
|
101
|
+
status: 'idle',
|
|
102
|
+
pendingCount: 0,
|
|
103
|
+
lastError: null,
|
|
104
|
+
lastErrorDetails: null,
|
|
105
|
+
syncErrors: [],
|
|
106
|
+
lastSyncTime: null,
|
|
107
|
+
syncMessage: null,
|
|
108
|
+
isTabVisible: true,
|
|
109
|
+
realtimeState: 'disconnected'
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export const syncStatusStore = createSyncStatusStore();
|
|
115
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/stores/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { User, Session } from '@supabase/supabase-js';
|
|
2
|
+
export interface AuthResponse {
|
|
3
|
+
user: User | null;
|
|
4
|
+
session: Session | null;
|
|
5
|
+
error: string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function signIn(email: string, password: string): Promise<AuthResponse>;
|
|
8
|
+
export declare function signUp(email: string, password: string, profileData: Record<string, unknown>): Promise<AuthResponse>;
|
|
9
|
+
export declare function signOut(options?: {
|
|
10
|
+
preserveOfflineCredentials?: boolean;
|
|
11
|
+
preserveLocalData?: boolean;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
error: string | null;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Get current Supabase session
|
|
17
|
+
* When offline, returns the cached session from localStorage even if expired
|
|
18
|
+
* (the caller should handle offline mode appropriately)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getSession(): Promise<Session | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a session's access token is expired
|
|
23
|
+
*/
|
|
24
|
+
export declare function isSessionExpired(session: Session | null): boolean;
|
|
25
|
+
export declare function getUserProfile(user: User | null): Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Update user profile
|
|
28
|
+
* Also updates cached offline credentials
|
|
29
|
+
*/
|
|
30
|
+
export declare function updateProfile(profile: Record<string, unknown>): Promise<{
|
|
31
|
+
error: string | null;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Change user password
|
|
35
|
+
* Verifies current password first, then updates
|
|
36
|
+
* Also updates cached offline credentials
|
|
37
|
+
*/
|
|
38
|
+
export declare function changePassword(currentPassword: string, newPassword: string): Promise<{
|
|
39
|
+
error: string | null;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Resend confirmation email for signup
|
|
43
|
+
* Should be rate-limited on the client side (30 second cooldown)
|
|
44
|
+
*/
|
|
45
|
+
export declare function resendConfirmationEmail(email: string): Promise<{
|
|
46
|
+
error: string | null;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Verify OTP token (for email confirmation).
|
|
50
|
+
* Absorbs confirm page's direct Supabase call.
|
|
51
|
+
*/
|
|
52
|
+
export declare function verifyOtp(tokenHash: string, type: 'signup' | 'email'): Promise<{
|
|
53
|
+
error: string | null;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Get a valid (non-expired) session, or null.
|
|
57
|
+
* Merges getSession() + isSessionExpired() into a single call.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getValidSession(): Promise<Session | null>;
|
|
60
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/supabase/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AA0B3D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAqBnF;AAED,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,YAAY,CAAC,CAoBvB;AAED,wBAAsB,OAAO,CAAC,OAAO,CAAC,EAAE;IACtC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAyDpC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA0C1D;AA+BD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAMjE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAoBnC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAmCnC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAU9F;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,GAAG,OAAO,GACvB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAOnC;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAK/D"}
|