@prabhask5/stellar-engine 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +59 -12
  2. package/dist/auth/admin.d.ts.map +1 -1
  3. package/dist/auth/admin.js +3 -0
  4. package/dist/auth/admin.js.map +1 -1
  5. package/dist/auth/crypto.d.ts +14 -0
  6. package/dist/auth/crypto.d.ts.map +1 -0
  7. package/dist/auth/crypto.js +22 -0
  8. package/dist/auth/crypto.js.map +1 -0
  9. package/dist/auth/offlineCredentials.d.ts.map +1 -1
  10. package/dist/auth/offlineCredentials.js +17 -3
  11. package/dist/auth/offlineCredentials.js.map +1 -1
  12. package/dist/auth/resolveAuthState.d.ts +2 -0
  13. package/dist/auth/resolveAuthState.d.ts.map +1 -1
  14. package/dist/auth/resolveAuthState.js +60 -0
  15. package/dist/auth/resolveAuthState.js.map +1 -1
  16. package/dist/auth/singleUser.d.ts +58 -0
  17. package/dist/auth/singleUser.d.ts.map +1 -0
  18. package/dist/auth/singleUser.js +450 -0
  19. package/dist/auth/singleUser.js.map +1 -0
  20. package/dist/config.d.ts +13 -1
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +9 -1
  23. package/dist/config.js.map +1 -1
  24. package/dist/data.js +11 -11
  25. package/dist/data.js.map +1 -1
  26. package/dist/database.d.ts.map +1 -1
  27. package/dist/database.js +2 -1
  28. package/dist/database.js.map +1 -1
  29. package/dist/engine.d.ts.map +1 -1
  30. package/dist/engine.js +45 -35
  31. package/dist/engine.js.map +1 -1
  32. package/dist/entries/auth.d.ts +1 -0
  33. package/dist/entries/auth.d.ts.map +1 -1
  34. package/dist/entries/auth.js +1 -0
  35. package/dist/entries/auth.js.map +1 -1
  36. package/dist/entries/types.d.ts +1 -1
  37. package/dist/entries/types.d.ts.map +1 -1
  38. package/dist/entries/utils.d.ts +1 -1
  39. package/dist/entries/utils.d.ts.map +1 -1
  40. package/dist/entries/utils.js +1 -1
  41. package/dist/entries/utils.js.map +1 -1
  42. package/dist/index.d.ts +3 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/queue.d.ts.map +1 -1
  47. package/dist/queue.js +4 -3
  48. package/dist/queue.js.map +1 -1
  49. package/dist/realtime.d.ts.map +1 -1
  50. package/dist/realtime.js +6 -5
  51. package/dist/realtime.js.map +1 -1
  52. package/dist/stores/network.d.ts.map +1 -1
  53. package/dist/stores/network.js +9 -4
  54. package/dist/stores/network.js.map +1 -1
  55. package/dist/stores/remoteChanges.d.ts.map +1 -1
  56. package/dist/stores/remoteChanges.js +33 -22
  57. package/dist/stores/remoteChanges.js.map +1 -1
  58. package/dist/supabase/client.d.ts.map +1 -1
  59. package/dist/supabase/client.js +12 -2
  60. package/dist/supabase/client.js.map +1 -1
  61. package/dist/supabase/validate.d.ts +1 -1
  62. package/dist/supabase/validate.d.ts.map +1 -1
  63. package/dist/supabase/validate.js +2 -2
  64. package/dist/supabase/validate.js.map +1 -1
  65. package/dist/types.d.ts +12 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/utils.d.ts +7 -0
  68. package/dist/utils.d.ts.map +1 -1
  69. package/dist/utils.js +16 -1
  70. package/dist/utils.js.map +1 -1
  71. package/package.json +3 -2
package/README.md CHANGED
@@ -13,6 +13,7 @@ A local-first, offline-capable sync engine for **SvelteKit + Supabase + Dexie**
13
13
  - **Intent-based sync operations** -- operations preserve intent (`increment`, `set`, `create`, `delete`) instead of just final state, enabling smarter coalescing and conflict handling.
14
14
  - **Three-tier conflict resolution** -- field-level diffing, numeric merge fields, and configurable exclusion lists let you resolve conflicts precisely rather than with blanket last-write-wins.
15
15
  - **Offline authentication** -- credential caching and offline session tokens let users sign in and work without connectivity; sessions reconcile automatically on reconnect.
16
+ - **Single-user auth mode** -- for personal apps, replace email/password with a local PIN code or password gate backed by Supabase anonymous auth. Setup, unlock, lock, and gate change are all handled by the engine with full offline support.
16
17
  - **Realtime subscriptions** -- Supabase Realtime channels push remote changes into local state instantly, with duplicate-delivery guards to prevent re-processing.
17
18
  - **Operation coalescing** -- batches of rapid local writes (e.g., 50 individual increments) are compressed into a single outbound operation, reducing sync traffic dramatically.
18
19
  - **Tombstone management** -- soft deletes are propagated cleanly, and stale tombstones are garbage-collected after a configurable retention period.
@@ -20,14 +21,12 @@ A local-first, offline-capable sync engine for **SvelteKit + Supabase + Dexie**
20
21
 
21
22
  ## Quick start
22
23
 
23
- Install from GitHub Packages:
24
+ Install from npm:
24
25
 
25
26
  ```bash
26
- npm install @prabhask5/stellar-engine@^1.0.0
27
+ npm install @prabhask5/stellar-engine
27
28
  ```
28
29
 
29
- > Requires an `.npmrc` with `@stellar:registry=https://npm.pkg.github.com`.
30
-
31
30
  Initialize the engine at app startup (e.g., in a SvelteKit root `+layout.ts`):
32
31
 
33
32
  ```ts
@@ -41,7 +40,6 @@ initEngine({
41
40
  tables: [
42
41
  {
43
42
  supabaseName: 'projects',
44
- dexieTable: 'projects',
45
43
  columns: 'id, name, created_at, updated_at, is_deleted, user_id',
46
44
  },
47
45
  // ...more tables
@@ -68,6 +66,39 @@ const auth = await resolveAuthState();
68
66
  if (auth.authMode !== 'none') await startSyncEngine();
69
67
  ```
70
68
 
69
+ ### Single-user mode
70
+
71
+ For personal apps with a PIN code gate instead of email/password:
72
+
73
+ ```ts
74
+ import { initEngine, startSyncEngine, supabase } from '@prabhask5/stellar-engine';
75
+ import { initConfig } from '@prabhask5/stellar-engine/config';
76
+ import { resolveAuthState } from '@prabhask5/stellar-engine/auth';
77
+
78
+ initEngine({
79
+ prefix: 'myapp',
80
+ supabase,
81
+ tables: [/* ... */],
82
+ database: {/* ... */},
83
+ auth: {
84
+ mode: 'single-user',
85
+ singleUser: { gateType: 'code', codeLength: 4 },
86
+ enableOfflineAuth: true,
87
+ },
88
+ });
89
+
90
+ await initConfig();
91
+ const auth = await resolveAuthState();
92
+
93
+ if (!auth.singleUserSetUp) {
94
+ // Show setup screen → call setupSingleUser(code, profile)
95
+ } else if (auth.authMode === 'none') {
96
+ // Show unlock screen → call unlockSingleUser(code)
97
+ } else {
98
+ await startSyncEngine();
99
+ }
100
+ ```
101
+
71
102
  ## Subpath exports
72
103
 
73
104
  Import only what you need via subpath exports:
@@ -76,12 +107,12 @@ Import only what you need via subpath exports:
76
107
  |---|---|
77
108
  | `@prabhask5/stellar-engine` | `initEngine`, `startSyncEngine`, `runFullSync`, `supabase`, `getDb`, `validateSupabaseCredentials` |
78
109
  | `@prabhask5/stellar-engine/data` | All engine CRUD + query operations (`engineCreate`, `engineUpdate`, etc.) |
79
- | `@prabhask5/stellar-engine/auth` | All auth functions (`signIn`, `signUp`, `resolveAuthState`, `isAdmin`, etc.) |
110
+ | `@prabhask5/stellar-engine/auth` | All auth functions (`signIn`, `signUp`, `resolveAuthState`, `isAdmin`, single-user: `setupSingleUser`, `unlockSingleUser`, `lockSingleUser`, etc.) |
80
111
  | `@prabhask5/stellar-engine/stores` | Reactive stores + event subscriptions (`syncStatusStore`, `authState`, `onSyncComplete`, etc.) |
81
- | `@prabhask5/stellar-engine/types` | All type exports (`Session`, `SyncEngineConfig`, `BatchOperation`, etc.) |
82
- | `@prabhask5/stellar-engine/utils` | Utility functions (`generateId`, `now`, `calculateNewOrder`, `debug`, etc.) |
112
+ | `@prabhask5/stellar-engine/types` | All type exports (`Session`, `SyncEngineConfig`, `BatchOperation`, `SingleUserConfig`, etc.) |
113
+ | `@prabhask5/stellar-engine/utils` | Utility functions (`generateId`, `now`, `calculateNewOrder`, `snakeToCamel`, `debug`, etc.) |
83
114
  | `@prabhask5/stellar-engine/actions` | Svelte `use:` actions (`remoteChangeAnimation`, `trackEditing`, `triggerLocalAnimation`) |
84
- | `@prabhask5/stellar-engine/config` | Runtime config (`initConfig`, `getConfig`, `setConfig`) |
115
+ | `@prabhask5/stellar-engine/config` | Runtime config (`initConfig`, `getConfig`, `setConfig`, `getDexieTableFor`) |
85
116
 
86
117
  The root export (`@prabhask5/stellar-engine`) re-exports everything for backward compatibility.
87
118
 
@@ -89,7 +120,7 @@ The root export (`@prabhask5/stellar-engine`) re-exports everything for backward
89
120
 
90
121
  **Supabase**
91
122
 
92
- Your Supabase project needs tables matching the `supabaseName` entries in your config. Each table should have at minimum:
123
+ Your Supabase project needs tables matching the `supabaseName` entries in your config. The corresponding Dexie (IndexedDB) table name is automatically derived from `supabaseName` using `snakeToCamel()` conversion (e.g., `goal_lists` becomes `goalLists`). Each table should have at minimum:
93
124
  - `id` (uuid primary key)
94
125
  - `updated_at` (timestamptz) -- used as the sync cursor
95
126
  - `is_deleted` (boolean, default false) -- for soft-delete / tombstone support
@@ -99,7 +130,7 @@ Row-Level Security policies should scope reads and writes to the authenticated u
99
130
 
100
131
  **Dexie (IndexedDB)**
101
132
 
102
- When you provide a `database` config to `initEngine`, the engine creates and manages the Dexie instance for you. System tables (`syncQueue`, `conflictHistory`, `offlineCredentials`, `offlineSession`) are automatically merged into every schema version -- you only declare your application tables:
133
+ When you provide a `database` config to `initEngine`, the engine creates and manages the Dexie instance for you. System tables (`syncQueue`, `conflictHistory`, `offlineCredentials`, `offlineSession`, `singleUserConfig`) are automatically merged into every schema version -- you only declare your application tables. Note that the store keys use the **camelCase** Dexie table names (auto-derived from `supabaseName` via `snakeToCamel()`):
103
134
 
104
135
  ```ts
105
136
  database: {
@@ -160,7 +191,7 @@ Alternatively, you can provide a pre-created Dexie instance via the `db` config
160
191
  |---|---|
161
192
  | `initEngine(config)` | Initialize the engine with table definitions, Supabase client, and Dexie instance. |
162
193
  | `getEngineConfig()` | Retrieve the current config (throws if not initialized). |
163
- | `SyncEngineConfig` / `TableConfig` | TypeScript interfaces for the config objects. |
194
+ | `SyncEngineConfig` / `TableConfig` | TypeScript interfaces for the config objects. `TableConfig` uses `supabaseName` only; Dexie table names are auto-derived. |
164
195
 
165
196
  ### Engine lifecycle
166
197
 
@@ -196,6 +227,21 @@ Alternatively, you can provide a pre-created Dexie instance via the `db` config
196
227
  | `cacheOfflineCredentials` / `getOfflineCredentials` / `verifyOfflineCredentials` / `clearOfflineCredentials` | Store and verify credentials locally for offline sign-in. |
197
228
  | `createOfflineSession` / `getValidOfflineSession` / `clearOfflineSession` | Manage offline session tokens in IndexedDB. |
198
229
 
230
+ ### Single-user auth
231
+
232
+ For personal apps that don't need email/password accounts. Uses a local PIN code or password gate with Supabase anonymous auth behind the scenes. Enable by setting `auth.mode: 'single-user'` in the engine config. Requires "Allow anonymous sign-ins" enabled in Supabase Authentication settings.
233
+
234
+ | Export | Description |
235
+ |---|---|
236
+ | `isSingleUserSetUp()` | Check if initial setup is complete. |
237
+ | `getSingleUserInfo()` | Get display info (profile, gate type) for the unlock screen. |
238
+ | `setupSingleUser(gate, profile)` | First-time setup: create gate, anonymous Supabase user, and store config. |
239
+ | `unlockSingleUser(gate)` | Verify gate and restore session (online or offline). |
240
+ | `lockSingleUser()` | Stop sync and reset auth state without destroying data. |
241
+ | `changeSingleUserGate(oldGate, newGate)` | Change the PIN code or password. |
242
+ | `updateSingleUserProfile(profile)` | Update profile in IndexedDB and Supabase metadata. |
243
+ | `resetSingleUser()` | Full reset: clear config, sign out, wipe local data. |
244
+
199
245
  ### Queue
200
246
 
201
247
  | Export | Description |
@@ -254,6 +300,7 @@ Alternatively, you can provide a pre-created Dexie instance via the `db` config
254
300
  | `generateId()` | Generate a UUID. |
255
301
  | `now()` | Current ISO timestamp string. |
256
302
  | `calculateNewOrder(before, after)` | Fractional ordering helper for drag-and-drop reorder. |
303
+ | `snakeToCamel(str)` | Convert a `snake_case` string to `camelCase` (also strips invalid characters). Used internally to derive Dexie table names from `supabaseName`. |
257
304
  | `getDeviceId()` | Stable per-device identifier (persisted in localStorage). |
258
305
  | `debugLog` / `debugWarn` / `debugError` | Prefixed console helpers (gated by `setDebugMode`). |
259
306
 
@@ -1 +1 @@
1
- {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAGlD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAUlD"}
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAGlD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAYlD"}
@@ -11,6 +11,9 @@ import { getEngineConfig } from '../config';
11
11
  export function isAdmin(user) {
12
12
  try {
13
13
  const config = getEngineConfig();
14
+ // Single-user mode: always admin
15
+ if (config.auth?.mode === 'single-user')
16
+ return true;
14
17
  if (config.auth?.adminCheck) {
15
18
  return config.auth.adminCheck(user);
16
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;QACjC,IAAI,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;QACjC,iCAAiC;QACjC,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,aAAa;YAAE,OAAO,IAAI,CAAC;QACrD,IAAI,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared Crypto Utilities
3
+ * SHA-256 hashing for passwords, codes, and other values.
4
+ */
5
+ /**
6
+ * Hash a value using SHA-256 via Web Crypto API.
7
+ * Returns a 64-character hex string.
8
+ */
9
+ export declare function hashValue(value: string): Promise<string>;
10
+ /**
11
+ * Check if a stored value is already hashed (64-char hex string).
12
+ */
13
+ export declare function isAlreadyHashed(value: string): boolean;
14
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared Crypto Utilities
3
+ * SHA-256 hashing for passwords, codes, and other values.
4
+ */
5
+ /**
6
+ * Hash a value using SHA-256 via Web Crypto API.
7
+ * Returns a 64-character hex string.
8
+ */
9
+ export async function hashValue(value) {
10
+ const encoder = new TextEncoder();
11
+ const data = encoder.encode(value);
12
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
13
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
14
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
15
+ }
16
+ /**
17
+ * Check if a stored value is already hashed (64-char hex string).
18
+ */
19
+ export function isAlreadyHashed(value) {
20
+ return /^[0-9a-f]{64}$/.test(value);
21
+ }
22
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/auth/crypto.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa;IAC3C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"offlineCredentials.d.ts","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAK3D;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQhF;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmC9C;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWzF;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D"}
1
+ {"version":3,"file":"offlineCredentials.d.ts","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAM3D;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAmCf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQhF;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8C9C;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzF;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { getEngineConfig } from '../config';
6
6
  import { debugWarn, debugError } from '../debug';
7
+ import { hashValue, isAlreadyHashed } from './crypto';
7
8
  const CREDENTIALS_ID = 'current_user';
8
9
  /**
9
10
  * Cache user credentials for offline login
@@ -21,11 +22,12 @@ export async function cacheOfflineCredentials(email, password, user, _session) {
21
22
  const profile = config.auth?.profileExtractor
22
23
  ? config.auth.profileExtractor(user.user_metadata || {})
23
24
  : (user.user_metadata || {});
25
+ const hashedPassword = await hashValue(password);
24
26
  const credentials = {
25
27
  id: CREDENTIALS_ID,
26
28
  userId: user.id,
27
29
  email: email,
28
- password: password,
30
+ password: hashedPassword,
29
31
  profile,
30
32
  cachedAt: new Date().toISOString()
31
33
  };
@@ -76,7 +78,18 @@ export async function verifyOfflineCredentials(email, password, expectedUserId)
76
78
  debugWarn('[Auth] No password stored in credentials');
77
79
  return { valid: false, reason: 'no_stored_password' };
78
80
  }
79
- if (credentials.password !== password) {
81
+ // Compare passwords: if stored password is hashed, hash the input and compare;
82
+ // if legacy plaintext, compare directly for backwards compatibility
83
+ let passwordMatch;
84
+ if (isAlreadyHashed(credentials.password)) {
85
+ const hashedInput = await hashValue(password);
86
+ passwordMatch = credentials.password === hashedInput;
87
+ }
88
+ else {
89
+ // Legacy plaintext comparison
90
+ passwordMatch = credentials.password === password;
91
+ }
92
+ if (!passwordMatch) {
80
93
  debugWarn('[Auth] Password mismatch (stored length:', credentials.password.length, ', entered length:', password.length, ')');
81
94
  return { valid: false, reason: 'password_mismatch' };
82
95
  }
@@ -92,8 +105,9 @@ export async function updateOfflineCredentialsPassword(newPassword) {
92
105
  return;
93
106
  }
94
107
  const db = getEngineConfig().db;
108
+ const hashedPassword = await hashValue(newPassword);
95
109
  await db.table('offlineCredentials').update(CREDENTIALS_ID, {
96
- password: newPassword,
110
+ password: hashedPassword,
97
111
  cachedAt: new Date().toISOString()
98
112
  });
99
113
  }
@@ -1 +1 @@
1
- {"version":3,"file":"offlineCredentials.js","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"offlineCredentials.js","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,8 @@ export interface AuthStateResult {
11
11
  session: Session | null;
12
12
  authMode: 'supabase' | 'offline' | 'none';
13
13
  offlineProfile: OfflineCredentials | null;
14
+ /** Whether single-user mode has been set up (only present when mode === 'single-user') */
15
+ singleUserSetUp?: boolean;
14
16
  }
15
17
  /**
16
18
  * Resolve the current authentication state.
@@ -1 +1 @@
1
- {"version":3,"file":"resolveAuthState.d.ts","sourceRoot":"","sources":["../../src/auth/resolveAuthState.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAMnD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1C,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC,CAoDjE"}
1
+ {"version":3,"file":"resolveAuthState.d.ts","sourceRoot":"","sources":["../../src/auth/resolveAuthState.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAOnD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1C,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,0FAA0F;IAC1F,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC,CA2DjE"}
@@ -8,6 +8,7 @@
8
8
  import { getSession, isSessionExpired } from '../supabase/auth';
9
9
  import { getValidOfflineSession, clearOfflineSession } from './offlineSession';
10
10
  import { getOfflineCredentials } from './offlineCredentials';
11
+ import { getEngineConfig } from '../config';
11
12
  import { debugWarn, debugError } from '../debug';
12
13
  /**
13
14
  * Resolve the current authentication state.
@@ -19,6 +20,12 @@ import { debugWarn, debugError } from '../debug';
19
20
  */
20
21
  export async function resolveAuthState() {
21
22
  try {
23
+ // ── SINGLE-USER MODE ──────────────────────────────────────────
24
+ const engineConfig = getEngineConfig();
25
+ if (engineConfig.auth?.mode === 'single-user') {
26
+ return resolveSingleUserAuthState();
27
+ }
28
+ // ── MULTI-USER MODE (default) ─────────────────────────────────
22
29
  const isOffline = typeof navigator !== 'undefined' && !navigator.onLine;
23
30
  // Get session once and reuse (egress optimization)
24
31
  const session = await getSession();
@@ -66,4 +73,57 @@ export async function resolveAuthState() {
66
73
  return { session: null, authMode: 'none', offlineProfile: null };
67
74
  }
68
75
  }
76
+ /**
77
+ * Resolve auth state for single-user mode.
78
+ *
79
+ * - If no config exists: user hasn't set up yet → authMode: 'none', singleUserSetUp: false
80
+ * - If config exists and valid session: → authMode: 'supabase', singleUserSetUp: true
81
+ * - If config exists, offline with cached session: → authMode: 'supabase', singleUserSetUp: true
82
+ * - If config exists, offline with offline session: → authMode: 'offline', singleUserSetUp: true
83
+ * - If config exists but no session: locked → authMode: 'none', singleUserSetUp: true
84
+ */
85
+ async function resolveSingleUserAuthState() {
86
+ try {
87
+ const db = getEngineConfig().db;
88
+ if (!db) {
89
+ return { session: null, authMode: 'none', offlineProfile: null, singleUserSetUp: false };
90
+ }
91
+ const config = await db.table('singleUserConfig').get('config');
92
+ if (!config) {
93
+ return { session: null, authMode: 'none', offlineProfile: null, singleUserSetUp: false };
94
+ }
95
+ // Config exists — check session
96
+ const session = await getSession();
97
+ const hasValidSession = session && !isSessionExpired(session);
98
+ if (hasValidSession) {
99
+ return { session, authMode: 'supabase', offlineProfile: null, singleUserSetUp: true };
100
+ }
101
+ // Check for offline session
102
+ const isOffline = typeof navigator !== 'undefined' && !navigator.onLine;
103
+ if (isOffline) {
104
+ // Even expired cached Supabase session is usable offline
105
+ if (session) {
106
+ return { session, authMode: 'supabase', offlineProfile: null, singleUserSetUp: true };
107
+ }
108
+ const offlineSession = await getValidOfflineSession();
109
+ if (offlineSession) {
110
+ const offlineProfile = {
111
+ id: 'current_user',
112
+ userId: offlineSession.userId,
113
+ email: '',
114
+ password: config.gateHash,
115
+ profile: config.profile,
116
+ cachedAt: new Date().toISOString()
117
+ };
118
+ return { session: null, authMode: 'offline', offlineProfile, singleUserSetUp: true };
119
+ }
120
+ }
121
+ // No valid session — locked
122
+ return { session: null, authMode: 'none', offlineProfile: null, singleUserSetUp: true };
123
+ }
124
+ catch (e) {
125
+ debugError('[Auth] Failed to resolve single-user auth state:', e);
126
+ return { session: null, authMode: 'none', offlineProfile: null, singleUserSetUp: false };
127
+ }
128
+ }
69
129
  //# sourceMappingURL=resolveAuthState.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveAuthState.js","sourceRoot":"","sources":["../../src/auth/resolveAuthState.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAQjD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QAExE,mDAAmD;QACnD,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;QACnC,MAAM,eAAe,GAAG,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAE9D,6CAA6C;QAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,eAAe,EAAE,CAAC;gBACpB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;YACjE,CAAC;YACD,+DAA+D;YAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QACnE,CAAC;QAED,8EAA8E;QAC9E,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QACjE,CAAC;QAED,wDAAwD;QACxD,MAAM,cAAc,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAEtD,IAAI,cAAc,EAAE,CAAC;YACnB,8DAA8D;YAC9D,MAAM,OAAO,GAAG,MAAM,qBAAqB,EAAE,CAAC;YAC9C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,EAAE,CAAC;gBACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;YACzE,CAAC;YACD,sDAAsD;YACtD,SAAS,CAAC,6EAA6E,CAAC,CAAC;YACzF,MAAM,mBAAmB,EAAE,CAAC;QAC9B,CAAC;QAED,iCAAiC;QACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,gEAAgE;QAChE,qDAAqD;QACrD,UAAU,CAAC,6DAA6D,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC;YACH,IAAI,OAAO,YAAY,KAAK,WAAW,EAAE,CAAC;gBACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC1E,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"resolveAuthState.js","sourceRoot":"","sources":["../../src/auth/resolveAuthState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Single-User Auth Module
3
+ *
4
+ * Implements a local gate (code or password) verified against a SHA-256 hash
5
+ * stored in IndexedDB. Uses Supabase anonymous auth for session/token management
6
+ * and RLS compliance. Falls back to offline auth when connectivity is unavailable.
7
+ */
8
+ import type { SingleUserConfig } from '../types';
9
+ /**
10
+ * Check if single-user mode has been set up (config exists in IndexedDB).
11
+ */
12
+ export declare function isSingleUserSetUp(): Promise<boolean>;
13
+ /**
14
+ * Get non-sensitive display info about the single user.
15
+ * Returns null if not set up.
16
+ */
17
+ export declare function getSingleUserInfo(): Promise<{
18
+ profile: Record<string, unknown>;
19
+ gateType: SingleUserConfig['gateType'];
20
+ codeLength?: 4 | 6;
21
+ } | null>;
22
+ /**
23
+ * First-time setup: hash gate, create anonymous Supabase user (if online),
24
+ * store config, and set auth state.
25
+ */
26
+ export declare function setupSingleUser(gate: string, profile: Record<string, unknown>): Promise<{
27
+ error: string | null;
28
+ }>;
29
+ /**
30
+ * Unlock: verify gate hash, restore Supabase session or fall back to offline auth.
31
+ */
32
+ export declare function unlockSingleUser(gate: string): Promise<{
33
+ error: string | null;
34
+ }>;
35
+ /**
36
+ * Lock: stop sync engine, reset auth state to 'none'.
37
+ * Does NOT destroy session, data, or sign out of Supabase.
38
+ */
39
+ export declare function lockSingleUser(): Promise<void>;
40
+ /**
41
+ * Change the gate (code/password). Verifies old gate first.
42
+ */
43
+ export declare function changeSingleUserGate(oldGate: string, newGate: string): Promise<{
44
+ error: string | null;
45
+ }>;
46
+ /**
47
+ * Update profile in IndexedDB and Supabase user_metadata.
48
+ */
49
+ export declare function updateSingleUserProfile(profile: Record<string, unknown>): Promise<{
50
+ error: string | null;
51
+ }>;
52
+ /**
53
+ * Full reset: clear config, sign out of Supabase, clear all data.
54
+ */
55
+ export declare function resetSingleUser(): Promise<{
56
+ error: string | null;
57
+ }>;
58
+ //# sourceMappingURL=singleUser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleUser.d.ts","sourceRoot":"","sources":["../../src/auth/singleUser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA4CjD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO1D;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACpB,GAAG,IAAI,CAAC,CAQR;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAgHnC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAkHnC;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAYpD;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAoDnC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA0DnC;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA2BzE"}