@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
package/dist/types.d.ts
CHANGED
|
@@ -1,118 +1,223 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* @fileoverview Core Type Definitions for the Stellar Sync Engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Defines the foundational TypeScript types used throughout the engine:
|
|
5
|
+
* - Intent-based sync operation types (preserving operation semantics)
|
|
6
|
+
* - Offline authentication types (credential caching, session tokens)
|
|
7
|
+
* - Conflict resolution types (field-level history tracking)
|
|
8
|
+
* - Single-user authentication types (PIN/password gate)
|
|
9
|
+
* - Device trust types (multi-device verification)
|
|
6
10
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Operations are converted to final values before pushing to Supabase, so conflicts
|
|
13
|
-
* use last-write-wins. Full numeric merge would require an operation inbox system.
|
|
11
|
+
* Architecture note:
|
|
12
|
+
* Operations use an intent-based model (e.g., "increment by 1") rather than
|
|
13
|
+
* final-state snapshots (e.g., "current_value: 50"). This enables local
|
|
14
|
+
* coalescing (50 rapid +1s become a single +50) and smarter conflict
|
|
15
|
+
* resolution. See {@link queue.ts} for the coalescing implementation.
|
|
14
16
|
*/
|
|
15
17
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* - '
|
|
20
|
-
* - '
|
|
18
|
+
* The four supported operation intents for the sync queue.
|
|
19
|
+
*
|
|
20
|
+
* Each intent carries different semantics during coalescing and push:
|
|
21
|
+
* - `'increment'` — Add a numeric delta to a field (coalesceable: multiple deltas sum)
|
|
22
|
+
* - `'set'` — Overwrite field(s) with new value(s) (coalesceable: later sets win)
|
|
23
|
+
* - `'create'` — Insert a new entity (coalesceable: subsequent sets merge into the create payload)
|
|
24
|
+
* - `'delete'` — Soft-delete an entity (a create + delete pair cancels both out entirely)
|
|
21
25
|
*/
|
|
22
26
|
export type OperationType = 'increment' | 'set' | 'create' | 'delete';
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
28
|
+
* A single intent-based sync operation stored in the IndexedDB `syncQueue` table.
|
|
29
|
+
*
|
|
30
|
+
* Design decisions:
|
|
31
|
+
* - `operationType` preserves the *intent* so the coalescer can intelligently merge
|
|
32
|
+
* (e.g., 50 increment ops → one +50 instead of 50 separate server requests).
|
|
33
|
+
* - `field` is optional: increment/single-field set use it; create and multi-field
|
|
34
|
+
* set store data in `value` instead.
|
|
35
|
+
* - `retries` and `lastRetryAt` power exponential backoff for failed pushes.
|
|
25
36
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* - `value` is the delta (for increment) or new value (for set/create)
|
|
37
|
+
* @example
|
|
38
|
+
* // Increment operation: add 1 to `current_value`
|
|
39
|
+
* { table: "goals", entityId: "abc", operationType: "increment", field: "current_value", value: 1 }
|
|
30
40
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
41
|
+
* // Multi-field set operation: update title and description
|
|
42
|
+
* { table: "goals", entityId: "abc", operationType: "set", value: { title: "New", description: "..." } }
|
|
43
|
+
*
|
|
44
|
+
* // Create operation: full entity payload in `value`
|
|
45
|
+
* { table: "goals", entityId: "abc", operationType: "create", value: { title: "Goal", target: 10 } }
|
|
35
46
|
*/
|
|
36
47
|
export interface SyncOperationItem {
|
|
48
|
+
/** Auto-increment primary key (assigned by IndexedDB). */
|
|
37
49
|
id?: number;
|
|
50
|
+
/** Supabase table name (e.g., `"goals"`, `"goal_lists"`). */
|
|
38
51
|
table: string;
|
|
52
|
+
/** UUID of the entity being operated on. */
|
|
39
53
|
entityId: string;
|
|
54
|
+
/** The operation intent: `'increment'`, `'set'`, `'create'`, or `'delete'`. */
|
|
40
55
|
operationType: OperationType;
|
|
56
|
+
/** Target field name — used by increment and single-field set operations. */
|
|
41
57
|
field?: string;
|
|
58
|
+
/** Payload — delta (increment), new value (set), full entity (create), or unused (delete). */
|
|
42
59
|
value?: unknown;
|
|
60
|
+
/** ISO 8601 timestamp of when the operation was enqueued locally. */
|
|
43
61
|
timestamp: string;
|
|
62
|
+
/** Number of failed push attempts (drives exponential backoff). */
|
|
44
63
|
retries: number;
|
|
64
|
+
/** ISO 8601 timestamp of the last retry attempt (used for backoff calculation). */
|
|
45
65
|
lastRetryAt?: string;
|
|
46
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Cached credentials stored in IndexedDB for offline sign-in.
|
|
69
|
+
*
|
|
70
|
+
* Uses a singleton pattern (`id: 'current_user'`) so only one set of
|
|
71
|
+
* credentials is cached at a time. The password is stored as a SHA-256
|
|
72
|
+
* hash — legacy records may contain plaintext from before hashing was added.
|
|
73
|
+
*
|
|
74
|
+
* @see {@link auth/offlineCredentials.ts} for caching/verification logic
|
|
75
|
+
*/
|
|
47
76
|
export interface OfflineCredentials {
|
|
77
|
+
/** Singleton key — always `'current_user'`. */
|
|
48
78
|
id: string;
|
|
79
|
+
/** Supabase user UUID. */
|
|
49
80
|
userId: string;
|
|
81
|
+
/** User's email address. */
|
|
50
82
|
email: string;
|
|
83
|
+
/** SHA-256 hash of the user's password (legacy records may be plaintext). */
|
|
51
84
|
password: string;
|
|
85
|
+
/** App-specific profile data (e.g., `{ firstName, lastName }`). */
|
|
52
86
|
profile: Record<string, unknown>;
|
|
87
|
+
/** ISO 8601 timestamp of when credentials were cached. */
|
|
53
88
|
cachedAt: string;
|
|
54
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Offline session token stored in IndexedDB.
|
|
92
|
+
*
|
|
93
|
+
* Created when the device goes offline (if credentials are cached) and
|
|
94
|
+
* consumed during offline sign-in to verify the user's identity without
|
|
95
|
+
* a network call.
|
|
96
|
+
*
|
|
97
|
+
* Sessions have no expiry — they are revoked only on:
|
|
98
|
+
* 1. Successful online re-authentication
|
|
99
|
+
* 2. Explicit logout
|
|
100
|
+
*
|
|
101
|
+
* @see {@link auth/offlineSession.ts} for session management
|
|
102
|
+
*/
|
|
55
103
|
export interface OfflineSession {
|
|
104
|
+
/** Singleton key — always `'current_session'`. */
|
|
56
105
|
id: string;
|
|
106
|
+
/** Supabase user UUID. */
|
|
57
107
|
userId: string;
|
|
108
|
+
/** Random UUID used as the offline session token. */
|
|
58
109
|
offlineToken: string;
|
|
110
|
+
/** ISO 8601 timestamp of session creation. */
|
|
59
111
|
createdAt: string;
|
|
60
112
|
}
|
|
61
113
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
114
|
+
* A single field-level conflict resolution record stored in IndexedDB.
|
|
115
|
+
*
|
|
116
|
+
* Recorded whenever the conflict resolution engine detects divergent values
|
|
117
|
+
* for the same field across devices. Entries are retained for 30 days to
|
|
118
|
+
* allow review and potential manual override.
|
|
119
|
+
*
|
|
120
|
+
* @see {@link conflicts.ts} for the three-tier resolution algorithm
|
|
64
121
|
*/
|
|
65
122
|
export interface ConflictHistoryEntry {
|
|
123
|
+
/** Auto-increment primary key (assigned by IndexedDB). */
|
|
66
124
|
id?: number;
|
|
125
|
+
/** UUID of the conflicting entity. */
|
|
67
126
|
entityId: string;
|
|
127
|
+
/** Supabase table name (e.g., `"goals"`). */
|
|
68
128
|
entityType: string;
|
|
129
|
+
/** Field that had conflicting values. */
|
|
69
130
|
field: string;
|
|
131
|
+
/** Value from the local device. */
|
|
70
132
|
localValue: unknown;
|
|
133
|
+
/** Value from the remote server. */
|
|
71
134
|
remoteValue: unknown;
|
|
135
|
+
/** Final merged value written to IndexedDB. */
|
|
72
136
|
resolvedValue: unknown;
|
|
137
|
+
/** Which side's value was chosen (or `'merged'` for numeric merges). */
|
|
73
138
|
winner: 'local' | 'remote' | 'merged';
|
|
139
|
+
/** The strategy that resolved this conflict (e.g., `'last_write'`, `'delete_wins'`). */
|
|
74
140
|
strategy: string;
|
|
141
|
+
/** ISO 8601 timestamp of when the conflict was resolved. */
|
|
75
142
|
timestamp: string;
|
|
76
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Current state of the sync engine's background loop.
|
|
146
|
+
*
|
|
147
|
+
* - `'idle'` — No active sync; everything is up to date
|
|
148
|
+
* - `'syncing'` — Push or pull currently in progress
|
|
149
|
+
* - `'error'` — Last sync attempt failed (will retry)
|
|
150
|
+
* - `'offline'` — Device has no network connectivity
|
|
151
|
+
*/
|
|
77
152
|
export type SyncStatus = 'idle' | 'syncing' | 'error' | 'offline';
|
|
153
|
+
/**
|
|
154
|
+
* Authentication mode for the engine.
|
|
155
|
+
*
|
|
156
|
+
* - `'supabase'` — Standard Supabase email/password or OAuth auth
|
|
157
|
+
* - `'offline'` — Using cached credentials (device is offline)
|
|
158
|
+
* - `'none'` — No active authentication
|
|
159
|
+
*/
|
|
78
160
|
export type AuthMode = 'supabase' | 'offline' | 'none';
|
|
161
|
+
/**
|
|
162
|
+
* The type of gate protecting single-user mode.
|
|
163
|
+
*
|
|
164
|
+
* - `'code'` — Numeric PIN (4 or 6 digits)
|
|
165
|
+
* - `'password'` — Freeform password string
|
|
166
|
+
*/
|
|
79
167
|
export type SingleUserGateType = 'code' | 'password';
|
|
168
|
+
/**
|
|
169
|
+
* Persistent configuration for single-user mode, stored in IndexedDB.
|
|
170
|
+
*
|
|
171
|
+
* Single-user mode replaces traditional email/password sign-in with a
|
|
172
|
+
* simplified local gate (PIN or password). Under the hood it still uses
|
|
173
|
+
* a real Supabase account — the PIN is padded to meet Supabase's minimum
|
|
174
|
+
* password length and used as the account password.
|
|
175
|
+
*
|
|
176
|
+
* Uses a singleton pattern (`id: 'config'`).
|
|
177
|
+
*
|
|
178
|
+
* @see {@link auth/singleUser.ts} for setup, unlock, and change flows
|
|
179
|
+
*/
|
|
80
180
|
export interface SingleUserConfig {
|
|
181
|
+
/** Singleton key — always `'config'`. */
|
|
81
182
|
id: string;
|
|
183
|
+
/** Whether the gate is a numeric code or a freeform password. */
|
|
82
184
|
gateType: SingleUserGateType;
|
|
185
|
+
/** Digit count for code gates (4 or 6). Only set when `gateType === 'code'`. */
|
|
83
186
|
codeLength?: 4 | 6;
|
|
187
|
+
/** SHA-256 hash of the code/password (deprecated — kept for offline fallback verification). */
|
|
84
188
|
gateHash?: string;
|
|
189
|
+
/** Email address used for the underlying Supabase account. */
|
|
85
190
|
email?: string;
|
|
191
|
+
/** App-specific profile data (e.g., `{ firstName, lastName }`). */
|
|
86
192
|
profile: Record<string, unknown>;
|
|
193
|
+
/** Supabase user UUID (set after first successful online setup). */
|
|
87
194
|
supabaseUserId?: string;
|
|
195
|
+
/** ISO 8601 timestamp of initial setup. */
|
|
88
196
|
setupAt: string;
|
|
197
|
+
/** ISO 8601 timestamp of last configuration change. */
|
|
89
198
|
updatedAt: string;
|
|
90
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* A trusted device record stored in the Supabase `trusted_devices` table.
|
|
202
|
+
*
|
|
203
|
+
* When device verification is enabled, untrusted devices must complete an
|
|
204
|
+
* email OTP challenge before they can access data. Once verified, the device
|
|
205
|
+
* is trusted for a configurable duration (default: 90 days).
|
|
206
|
+
*
|
|
207
|
+
* @see {@link auth/deviceVerification.ts} for the trust/verify flow
|
|
208
|
+
*/
|
|
91
209
|
export interface TrustedDevice {
|
|
210
|
+
/** Row UUID (primary key in Supabase). */
|
|
92
211
|
id: string;
|
|
212
|
+
/** Supabase user UUID who owns this device. */
|
|
93
213
|
userId: string;
|
|
214
|
+
/** Stable device identifier from localStorage. */
|
|
94
215
|
deviceId: string;
|
|
216
|
+
/** Human-readable device label (e.g., browser + OS). */
|
|
95
217
|
deviceLabel?: string;
|
|
218
|
+
/** ISO 8601 timestamp of when the device was first trusted. */
|
|
96
219
|
trustedAt: string;
|
|
220
|
+
/** ISO 8601 timestamp of the device's most recent use. */
|
|
97
221
|
lastUsedAt: string;
|
|
98
222
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Base interface for all syncable entities.
|
|
101
|
-
* Every entity managed by the sync engine has these fields.
|
|
102
|
-
*/
|
|
103
|
-
export interface SyncableEntity {
|
|
104
|
-
id: string;
|
|
105
|
-
created_at: string;
|
|
106
|
-
updated_at: string;
|
|
107
|
-
deleted?: boolean;
|
|
108
|
-
_version?: number;
|
|
109
|
-
device_id?: string | null;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Base interface for entities owned by a user.
|
|
113
|
-
* Extends SyncableEntity with a user_id field.
|
|
114
|
-
*/
|
|
115
|
-
export interface UserOwnedEntity extends SyncableEntity {
|
|
116
|
-
user_id: string;
|
|
117
|
-
}
|
|
118
223
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* @fileoverview Core Type Definitions for the Stellar Sync Engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Defines the foundational TypeScript types used throughout the engine:
|
|
5
|
+
* - Intent-based sync operation types (preserving operation semantics)
|
|
6
|
+
* - Offline authentication types (credential caching, session tokens)
|
|
7
|
+
* - Conflict resolution types (field-level history tracking)
|
|
8
|
+
* - Single-user authentication types (PIN/password gate)
|
|
9
|
+
* - Device trust types (multi-device verification)
|
|
6
10
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Operations are converted to final values before pushing to Supabase, so conflicts
|
|
13
|
-
* use last-write-wins. Full numeric merge would require an operation inbox system.
|
|
11
|
+
* Architecture note:
|
|
12
|
+
* Operations use an intent-based model (e.g., "increment by 1") rather than
|
|
13
|
+
* final-state snapshots (e.g., "current_value: 50"). This enables local
|
|
14
|
+
* coalescing (50 rapid +1s become a single +50) and smarter conflict
|
|
15
|
+
* resolution. See {@link queue.ts} for the coalescing implementation.
|
|
14
16
|
*/
|
|
15
17
|
export {};
|
|
16
18
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,29 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Common
|
|
2
|
+
* @fileoverview Common Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* Small, pure helper functions used across the sync engine and exposed
|
|
5
|
+
* to consumers via the `@prabhask5/stellar-engine/utils` subpath export.
|
|
6
|
+
*
|
|
7
|
+
* All functions in this module are side-effect-free and safe to call in
|
|
8
|
+
* both browser and SSR contexts.
|
|
3
9
|
*/
|
|
4
10
|
/**
|
|
5
|
-
* Convert a snake_case string to a safe camelCase identifier.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
11
|
+
* Convert a `snake_case` string to a safe `camelCase` identifier.
|
|
12
|
+
*
|
|
13
|
+
* First strips any characters that are not alphanumeric or underscores,
|
|
14
|
+
* then converts underscore-separated words to camelCase.
|
|
15
|
+
*
|
|
16
|
+
* Used internally to derive Dexie (IndexedDB) table names from Supabase
|
|
17
|
+
* snake_case table names.
|
|
18
|
+
*
|
|
19
|
+
* @param s - The snake_case string to convert.
|
|
20
|
+
* @returns The camelCase equivalent.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* snakeToCamel('goal_lists'); // → 'goalLists'
|
|
24
|
+
* snakeToCamel('goals'); // → 'goals'
|
|
25
|
+
* snakeToCamel('my-table!'); // → 'mytable' (invalid chars stripped first)
|
|
9
26
|
*/
|
|
10
27
|
export declare function snakeToCamel(s: string): string;
|
|
11
28
|
/**
|
|
12
|
-
* Generate a UUID v4 (random UUID).
|
|
29
|
+
* Generate a UUID v4 (random UUID) using the native Web Crypto API.
|
|
30
|
+
*
|
|
31
|
+
* Suitable for entity primary keys, device IDs, and any context where
|
|
32
|
+
* a universally unique identifier is needed.
|
|
33
|
+
*
|
|
34
|
+
* @returns A lowercase UUID v4 string (e.g., `"550e8400-e29b-41d4-a716-446655440000"`).
|
|
13
35
|
*/
|
|
14
36
|
export declare function generateId(): string;
|
|
15
37
|
/**
|
|
16
|
-
* Get the current timestamp as an ISO string.
|
|
38
|
+
* Get the current timestamp as an ISO 8601 string.
|
|
39
|
+
*
|
|
40
|
+
* Convenience wrapper around `new Date().toISOString()` used as the
|
|
41
|
+
* default value for `created_at`, `updated_at`, and sync operation
|
|
42
|
+
* timestamps throughout the engine.
|
|
43
|
+
*
|
|
44
|
+
* @returns An ISO 8601 timestamp string (e.g., `"2025-01-15T12:30:00.000Z"`).
|
|
17
45
|
*/
|
|
18
46
|
export declare function now(): string;
|
|
19
47
|
/**
|
|
20
|
-
* Calculate new order value when moving an item to a
|
|
21
|
-
*
|
|
48
|
+
* Calculate a new `order` value when moving an item to a different position.
|
|
49
|
+
*
|
|
50
|
+
* Uses **fractional ordering** so that only the moved item's `order` value
|
|
51
|
+
* changes — no need to re-index the entire list. The new value is placed
|
|
52
|
+
* at the midpoint between its new neighbours.
|
|
53
|
+
*
|
|
54
|
+
* Includes a guard against floating-point precision exhaustion: if the
|
|
55
|
+
* midpoint collapses to either bound, a small epsilon nudge is applied
|
|
56
|
+
* instead. In practice, precision issues only arise after ~50 consecutive
|
|
57
|
+
* moves between the same two items.
|
|
58
|
+
*
|
|
59
|
+
* @typeParam T - Any object with a numeric `order` property.
|
|
60
|
+
* @param items - The sorted array of items (by `order`, ascending).
|
|
61
|
+
* @param fromIndex - Current index of the item being moved.
|
|
62
|
+
* @param toIndex - Target index where the item should be placed.
|
|
63
|
+
* @returns The new `order` value for the moved item.
|
|
22
64
|
*
|
|
23
|
-
* @
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
65
|
+
* @example
|
|
66
|
+
* const items = [{ order: 1 }, { order: 2 }, { order: 3 }];
|
|
67
|
+
* calculateNewOrder(items, 2, 0); // → 0 (before the first item)
|
|
68
|
+
* calculateNewOrder(items, 0, 1); // → 2.5 (between items[1] and items[2])
|
|
27
69
|
*/
|
|
28
70
|
export declare function calculateNewOrder<T extends {
|
|
29
71
|
order: number;
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAMD;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAMD;;;;;;;;GAQG;AACH,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAC3D,KAAK,EAAE,CAAC,EAAE,EACV,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAkDR"}
|
package/dist/utils.js
CHANGED
|
@@ -1,68 +1,129 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Common
|
|
2
|
+
* @fileoverview Common Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* Small, pure helper functions used across the sync engine and exposed
|
|
5
|
+
* to consumers via the `@prabhask5/stellar-engine/utils` subpath export.
|
|
6
|
+
*
|
|
7
|
+
* All functions in this module are side-effect-free and safe to call in
|
|
8
|
+
* both browser and SSR contexts.
|
|
3
9
|
*/
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// String Utilities
|
|
12
|
+
// =============================================================================
|
|
4
13
|
/**
|
|
5
|
-
* Convert a snake_case string to a safe camelCase identifier.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
14
|
+
* Convert a `snake_case` string to a safe `camelCase` identifier.
|
|
15
|
+
*
|
|
16
|
+
* First strips any characters that are not alphanumeric or underscores,
|
|
17
|
+
* then converts underscore-separated words to camelCase.
|
|
18
|
+
*
|
|
19
|
+
* Used internally to derive Dexie (IndexedDB) table names from Supabase
|
|
20
|
+
* snake_case table names.
|
|
21
|
+
*
|
|
22
|
+
* @param s - The snake_case string to convert.
|
|
23
|
+
* @returns The camelCase equivalent.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* snakeToCamel('goal_lists'); // → 'goalLists'
|
|
27
|
+
* snakeToCamel('goals'); // → 'goals'
|
|
28
|
+
* snakeToCamel('my-table!'); // → 'mytable' (invalid chars stripped first)
|
|
9
29
|
*/
|
|
10
30
|
export function snakeToCamel(s) {
|
|
11
31
|
return s.replace(/[^a-zA-Z0-9_]/g, '').replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
12
32
|
}
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// ID Generation
|
|
35
|
+
// =============================================================================
|
|
13
36
|
/**
|
|
14
|
-
* Generate a UUID v4 (random UUID).
|
|
37
|
+
* Generate a UUID v4 (random UUID) using the native Web Crypto API.
|
|
38
|
+
*
|
|
39
|
+
* Suitable for entity primary keys, device IDs, and any context where
|
|
40
|
+
* a universally unique identifier is needed.
|
|
41
|
+
*
|
|
42
|
+
* @returns A lowercase UUID v4 string (e.g., `"550e8400-e29b-41d4-a716-446655440000"`).
|
|
15
43
|
*/
|
|
16
44
|
export function generateId() {
|
|
17
45
|
return crypto.randomUUID();
|
|
18
46
|
}
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// Timestamp Utilities
|
|
49
|
+
// =============================================================================
|
|
19
50
|
/**
|
|
20
|
-
* Get the current timestamp as an ISO string.
|
|
51
|
+
* Get the current timestamp as an ISO 8601 string.
|
|
52
|
+
*
|
|
53
|
+
* Convenience wrapper around `new Date().toISOString()` used as the
|
|
54
|
+
* default value for `created_at`, `updated_at`, and sync operation
|
|
55
|
+
* timestamps throughout the engine.
|
|
56
|
+
*
|
|
57
|
+
* @returns An ISO 8601 timestamp string (e.g., `"2025-01-15T12:30:00.000Z"`).
|
|
21
58
|
*/
|
|
22
59
|
export function now() {
|
|
23
60
|
return new Date().toISOString();
|
|
24
61
|
}
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// Ordering Utilities
|
|
64
|
+
// =============================================================================
|
|
25
65
|
/**
|
|
26
|
-
* Calculate new order value when moving an item to a
|
|
27
|
-
*
|
|
66
|
+
* Calculate a new `order` value when moving an item to a different position.
|
|
67
|
+
*
|
|
68
|
+
* Uses **fractional ordering** so that only the moved item's `order` value
|
|
69
|
+
* changes — no need to re-index the entire list. The new value is placed
|
|
70
|
+
* at the midpoint between its new neighbours.
|
|
71
|
+
*
|
|
72
|
+
* Includes a guard against floating-point precision exhaustion: if the
|
|
73
|
+
* midpoint collapses to either bound, a small epsilon nudge is applied
|
|
74
|
+
* instead. In practice, precision issues only arise after ~50 consecutive
|
|
75
|
+
* moves between the same two items.
|
|
76
|
+
*
|
|
77
|
+
* @typeParam T - Any object with a numeric `order` property.
|
|
78
|
+
* @param items - The sorted array of items (by `order`, ascending).
|
|
79
|
+
* @param fromIndex - Current index of the item being moved.
|
|
80
|
+
* @param toIndex - Target index where the item should be placed.
|
|
81
|
+
* @returns The new `order` value for the moved item.
|
|
28
82
|
*
|
|
29
|
-
* @
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
83
|
+
* @example
|
|
84
|
+
* const items = [{ order: 1 }, { order: 2 }, { order: 3 }];
|
|
85
|
+
* calculateNewOrder(items, 2, 0); // → 0 (before the first item)
|
|
86
|
+
* calculateNewOrder(items, 0, 1); // → 2.5 (between items[1] and items[2])
|
|
33
87
|
*/
|
|
34
88
|
export function calculateNewOrder(items, fromIndex, toIndex) {
|
|
35
|
-
|
|
89
|
+
/* No movement — return the item's existing order. */
|
|
36
90
|
if (fromIndex === toIndex) {
|
|
37
91
|
return items[fromIndex].order;
|
|
38
92
|
}
|
|
39
|
-
|
|
93
|
+
/* Moving to the beginning — place 1 unit before the current first item. */
|
|
40
94
|
if (toIndex === 0) {
|
|
41
95
|
return items[0].order - 1;
|
|
42
96
|
}
|
|
43
|
-
|
|
97
|
+
/* Moving to the end — place 1 unit after the current last item. */
|
|
44
98
|
if (toIndex === items.length - 1) {
|
|
45
99
|
return items[items.length - 1].order + 1;
|
|
46
100
|
}
|
|
47
|
-
|
|
48
|
-
|
|
101
|
+
/*
|
|
102
|
+
* Moving between two existing items.
|
|
103
|
+
* Account for the index shift that occurs when the item is removed
|
|
104
|
+
* from its original position before being re-inserted.
|
|
105
|
+
*/
|
|
49
106
|
let prevIndex;
|
|
50
107
|
let nextIndex;
|
|
51
108
|
if (fromIndex < toIndex) {
|
|
52
|
-
|
|
109
|
+
/* Moving down: the item lands between toIndex and toIndex + 1. */
|
|
53
110
|
prevIndex = toIndex;
|
|
54
111
|
nextIndex = toIndex + 1;
|
|
55
112
|
}
|
|
56
113
|
else {
|
|
57
|
-
|
|
114
|
+
/* Moving up: the item lands between toIndex - 1 and toIndex. */
|
|
58
115
|
prevIndex = toIndex - 1;
|
|
59
116
|
nextIndex = toIndex;
|
|
60
117
|
}
|
|
61
118
|
const prevOrder = items[prevIndex].order;
|
|
62
119
|
const nextOrder = items[nextIndex].order;
|
|
63
120
|
const midpoint = (prevOrder + nextOrder) / 2;
|
|
64
|
-
|
|
65
|
-
|
|
121
|
+
/*
|
|
122
|
+
* Guard against floating-point precision exhaustion:
|
|
123
|
+
* If the midpoint collapses to either bound (i.e., they're so close
|
|
124
|
+
* that IEEE 754 can't represent a value between them), nudge by a
|
|
125
|
+
* small epsilon instead.
|
|
126
|
+
*/
|
|
66
127
|
if (midpoint === prevOrder || midpoint === nextOrder) {
|
|
67
128
|
return prevOrder + Number.EPSILON * 100;
|
|
68
129
|
}
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC;AAED,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAG;IACjB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAU,EACV,SAAiB,EACjB,OAAe;IAEf,qDAAqD;IACrD,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC;IAChC,CAAC;IAED,2EAA2E;IAC3E,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,mEAAmE;IACnE,IAAI,OAAO,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,IAAI,SAAiB,CAAC;IACtB,IAAI,SAAiB,CAAC;IAEtB,IAAI,SAAS,GAAG,OAAO,EAAE,CAAC;QACxB,kEAAkE;QAClE,SAAS,GAAG,OAAO,CAAC;QACpB,SAAS,GAAG,OAAO,GAAG,CAAC,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,gEAAgE;QAChE,SAAS,GAAG,OAAO,GAAG,CAAC,CAAC;QACxB,SAAS,GAAG,OAAO,CAAC;IACtB,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC;IACzC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC;IAEzC,MAAM,QAAQ,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE7C;;;;;OAKG;IACH,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACrD,OAAO,SAAS,GAAG,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC;IAC1C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|