@prabhask5/stellar-engine 1.1.18 → 1.2.1

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 (95) hide show
  1. package/README.md +448 -321
  2. package/dist/bin/commands.d.ts +14 -0
  3. package/dist/bin/commands.d.ts.map +1 -0
  4. package/dist/bin/commands.js +68 -0
  5. package/dist/bin/commands.js.map +1 -0
  6. package/dist/bin/install-pwa.d.ts +20 -6
  7. package/dist/bin/install-pwa.d.ts.map +1 -1
  8. package/dist/bin/install-pwa.js +111 -184
  9. package/dist/bin/install-pwa.js.map +1 -1
  10. package/dist/config.d.ts +67 -11
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +265 -24
  13. package/dist/config.js.map +1 -1
  14. package/dist/crdt/awareness.d.ts +128 -0
  15. package/dist/crdt/awareness.d.ts.map +1 -0
  16. package/dist/crdt/awareness.js +284 -0
  17. package/dist/crdt/awareness.js.map +1 -0
  18. package/dist/crdt/channel.d.ts +165 -0
  19. package/dist/crdt/channel.d.ts.map +1 -0
  20. package/dist/crdt/channel.js +522 -0
  21. package/dist/crdt/channel.js.map +1 -0
  22. package/dist/crdt/config.d.ts +58 -0
  23. package/dist/crdt/config.d.ts.map +1 -0
  24. package/dist/crdt/config.js +123 -0
  25. package/dist/crdt/config.js.map +1 -0
  26. package/dist/crdt/helpers.d.ts +104 -0
  27. package/dist/crdt/helpers.d.ts.map +1 -0
  28. package/dist/crdt/helpers.js +116 -0
  29. package/dist/crdt/helpers.js.map +1 -0
  30. package/dist/crdt/offline.d.ts +58 -0
  31. package/dist/crdt/offline.d.ts.map +1 -0
  32. package/dist/crdt/offline.js +130 -0
  33. package/dist/crdt/offline.js.map +1 -0
  34. package/dist/crdt/persistence.d.ts +65 -0
  35. package/dist/crdt/persistence.d.ts.map +1 -0
  36. package/dist/crdt/persistence.js +171 -0
  37. package/dist/crdt/persistence.js.map +1 -0
  38. package/dist/crdt/provider.d.ts +109 -0
  39. package/dist/crdt/provider.d.ts.map +1 -0
  40. package/dist/crdt/provider.js +543 -0
  41. package/dist/crdt/provider.js.map +1 -0
  42. package/dist/crdt/store.d.ts +111 -0
  43. package/dist/crdt/store.d.ts.map +1 -0
  44. package/dist/crdt/store.js +158 -0
  45. package/dist/crdt/store.js.map +1 -0
  46. package/dist/crdt/types.d.ts +281 -0
  47. package/dist/crdt/types.d.ts.map +1 -0
  48. package/dist/crdt/types.js +26 -0
  49. package/dist/crdt/types.js.map +1 -0
  50. package/dist/database.d.ts +66 -1
  51. package/dist/database.d.ts.map +1 -1
  52. package/dist/database.js +133 -7
  53. package/dist/database.js.map +1 -1
  54. package/dist/diagnostics.d.ts +75 -0
  55. package/dist/diagnostics.d.ts.map +1 -1
  56. package/dist/diagnostics.js +114 -2
  57. package/dist/diagnostics.js.map +1 -1
  58. package/dist/engine.d.ts.map +1 -1
  59. package/dist/engine.js +21 -1
  60. package/dist/engine.js.map +1 -1
  61. package/dist/entries/crdt.d.ts +32 -0
  62. package/dist/entries/crdt.d.ts.map +1 -0
  63. package/dist/entries/crdt.js +52 -0
  64. package/dist/entries/crdt.js.map +1 -0
  65. package/dist/entries/types.d.ts +5 -3
  66. package/dist/entries/types.d.ts.map +1 -1
  67. package/dist/entries/utils.d.ts +1 -0
  68. package/dist/entries/utils.d.ts.map +1 -1
  69. package/dist/entries/utils.js +8 -0
  70. package/dist/entries/utils.js.map +1 -1
  71. package/dist/entries/vite.d.ts +1 -1
  72. package/dist/entries/vite.d.ts.map +1 -1
  73. package/dist/index.d.ts +9 -2
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +14 -0
  76. package/dist/index.js.map +1 -1
  77. package/dist/schema.d.ts +150 -0
  78. package/dist/schema.d.ts.map +1 -0
  79. package/dist/schema.js +891 -0
  80. package/dist/schema.js.map +1 -0
  81. package/dist/sw/build/vite-plugin.d.ts +93 -18
  82. package/dist/sw/build/vite-plugin.d.ts.map +1 -1
  83. package/dist/sw/build/vite-plugin.js +356 -22
  84. package/dist/sw/build/vite-plugin.js.map +1 -1
  85. package/dist/types.d.ts +139 -0
  86. package/dist/types.d.ts.map +1 -1
  87. package/package.json +9 -3
  88. package/dist/operations.d.ts +0 -73
  89. package/dist/operations.d.ts.map +0 -1
  90. package/dist/operations.js +0 -227
  91. package/dist/operations.js.map +0 -1
  92. package/dist/reconnectHandler.d.ts +0 -16
  93. package/dist/reconnectHandler.d.ts.map +0 -1
  94. package/dist/reconnectHandler.js +0 -21
  95. package/dist/reconnectHandler.js.map +0 -1
package/README.md CHANGED
@@ -1,49 +1,92 @@
1
- # @prabhask5/stellar-engine [![npm version](https://img.shields.io/npm/v/@prabhask5/stellar-engine.svg?style=flat)](https://www.npmjs.com/package/@prabhask5/stellar-engine)
1
+ # @prabhask5/stellar-engine [![npm version](https://img.shields.io/npm/v/@prabhask5/stellar-engine.svg?style=flat)](https://www.npmjs.com/package/@prabhask5/stellar-engine) [![Made with Supabase](https://supabase.com/badge-made-with-supabase-dark.svg)](https://supabase.com)
2
2
 
3
- A local-first, offline-capable sync engine for **SvelteKit + Supabase + Dexie** applications. All reads come from IndexedDB, all writes land locally first, and a background sync loop ships changes to Supabase -- so your app stays fast and functional regardless of network state.
3
+ An offline-first, real-time sync engine for **Supabase + Dexie.js** applications. All reads come from IndexedDB, all writes land locally first, and a background sync loop ships changes to Supabase -- so your app stays fast and functional regardless of network state. Optional SvelteKit integrations are included for teams building with Svelte 5.
4
4
 
5
5
  ## Documentation
6
6
 
7
7
  - [API Reference](./API_REFERENCE.md) -- full signatures, parameters, and usage examples for every public export
8
8
  - [Architecture](./ARCHITECTURE.md) -- internal design, data flow, and module responsibilities
9
- - [Framework Integration](./FRAMEWORKS.md) -- SvelteKit-specific patterns and conventions
9
+ - [Frameworks](./FRAMEWORKS.md) -- More reading on
10
+ frameworks used in stellar-engine
10
11
 
11
12
  ## Features
12
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
- - **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
- - **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, use a simplified PIN code or password gate backed by real Supabase email/password auth. The user provides an email during setup; the PIN is padded to meet Supabase's minimum password length and verified server-side. Setup, unlock, lock, and gate change are all handled by the engine with full offline support.
17
- - **Realtime subscriptions** -- Supabase Realtime channels push remote changes into local state instantly, with duplicate-delivery guards to prevent re-processing.
18
- - **Operation coalescing** -- batches of rapid local writes (e.g., 50 individual increments) are compressed into a single outbound operation, reducing sync traffic dramatically.
19
- - **Tombstone management** -- soft deletes are propagated cleanly, and stale tombstones are garbage-collected after a configurable retention period.
20
- - **Egress optimization** -- column-level select lists and ownership filters ensure only the data your client needs is fetched from Supabase.
14
+ - **Schema-driven configuration** -- declare tables once and the engine auto-generates Dexie stores, database versioning, TypeScript interfaces, and Supabase SQL
15
+ - **Intent-based sync operations** -- operations preserve intent (`increment`, `set`, `create`, `delete`) instead of final state, enabling smarter coalescing and conflict handling
16
+ - **6-step operation coalescing** -- 50 rapid writes are compressed into 1 outbound operation, dramatically reducing sync traffic
17
+ - **Three-tier conflict resolution** -- field-level auto-merge for non-overlapping changes, different-field merge, and same-field resolution (`local_pending` > `delete_wins` > `last_write_wins` with device ID tiebreaker)
18
+ - **Offline authentication** -- SHA-256 credential caching and offline session tokens let users sign in and work without connectivity; sessions reconcile automatically on reconnect
19
+ - **Single-user PIN/password auth** -- simplified gate backed by real Supabase email/password auth; PIN is padded to meet minimum length and verified server-side
20
+ - **Device verification** -- email OTP for untrusted devices with 90-day trust duration
21
+ - **Realtime subscriptions** -- Supabase Realtime WebSocket push with echo suppression and deduplication against polling
22
+ - **Tombstone management** -- soft deletes with configurable garbage collection
23
+ - **Egress optimization** -- column-level selects, operation coalescing, push-only mode when realtime is healthy, cursor-based pulls
24
+ - **CRDT collaborative editing** -- optional Yjs-based subsystem for real-time multi-user editing via Supabase Broadcast
25
+ - **Demo mode** -- sandboxed database, zero Supabase connections, mock auth for instant onboarding experiences
26
+ - **Reactive stores** -- Svelte-compatible stores for sync status, auth state, network state, and remote changes
27
+ - **Store factories** -- `createCollectionStore` and `createDetailStore` for boilerplate-free reactive data layers
28
+ - **Svelte actions** -- `remoteChangeAnimation`, `trackEditing`, `triggerLocalAnimation` for declarative UI behavior
29
+ - **SQL generation** -- auto-generate `CREATE TABLE` statements, RLS policies, and migrations from your schema config
30
+ - **TypeScript generation** -- auto-generate interfaces from schema
31
+ - **Diagnostics** -- comprehensive runtime diagnostics covering sync, queue, realtime, conflicts, egress, and network
32
+ - **Debug utilities** -- opt-in debug logging and `window` debug utilities for browser console inspection
33
+ - **SvelteKit integration** (optional) -- layout helpers, server handlers, email confirmation, service worker lifecycle, and auth hydration
34
+ - **PWA scaffolding CLI** -- `stellar-engine install pwa` generates a complete SvelteKit PWA project (34+ files)
35
+
36
+ ### Use cases
37
+
38
+ - Productivity and task management apps
39
+ - Notion-like block editors (with CRDT collaborative editing)
40
+ - Personal finance trackers (numeric merge across devices)
41
+ - File and asset management UIs (fractional ordering for drag-and-drop)
42
+ - Habit trackers and daily planners
43
+ - Knowledge bases and note-taking apps
44
+ - Any app needing offline-first multi-device sync
21
45
 
22
46
  ## Quick start
23
47
 
24
- Install from npm:
25
-
26
- ```bash
27
- npm install @prabhask5/stellar-engine
28
- ```
48
+ ```ts
49
+ // ─── Install ───────────────────────────────────────────────────────
50
+ // npm install @prabhask5/stellar-engine
29
51
 
30
- Initialize the engine at app startup (e.g., in a SvelteKit root `+layout.ts`):
52
+ // ─── 1. Initialize the engine ──────────────────────────────────────
53
+ // Call once at app startup (e.g., root layout, main entry point)
31
54
 
32
- ```ts
33
- import { initEngine, startSyncEngine, supabase } from '@prabhask5/stellar-engine';
55
+ import {
56
+ initEngine,
57
+ startSyncEngine,
58
+ supabase,
59
+ getDb,
60
+ resetDatabase,
61
+ validateSupabaseCredentials,
62
+ validateSchema,
63
+ } from '@prabhask5/stellar-engine';
34
64
  import { initConfig } from '@prabhask5/stellar-engine/config';
35
65
  import { resolveAuthState } from '@prabhask5/stellar-engine/auth';
36
66
 
37
67
  initEngine({
38
68
  prefix: 'myapp',
39
69
  supabase,
70
+ // Schema-driven: declare tables once, engine handles the rest
40
71
  tables: [
41
72
  {
42
- supabaseName: 'projects',
43
- columns: 'id, name, created_at, updated_at, is_deleted, user_id',
73
+ supabaseName: 'projects', // Supabase table name
74
+ // Dexie name auto-derived: 'projects'
75
+ columns: 'id, name, description, sort_order, created_at, updated_at, is_deleted, user_id',
76
+ ownershipFilter: 'user_id', // RLS-aware egress filter
77
+ mergeFields: [], // Fields for numeric merge
78
+ excludeFromConflictResolution: ['updated_at'], // Fields to skip in conflict diffing
79
+ },
80
+ {
81
+ supabaseName: 'tasks',
82
+ columns: 'id, title, project_id, count, sort_order, created_at, updated_at, is_deleted, user_id',
83
+ ownershipFilter: 'user_id',
84
+ mergeFields: ['count'], // Numeric merge: concurrent increments add up
44
85
  },
45
- // ...more tables
46
86
  ],
87
+
88
+ // Declarative database versioning -- system tables (syncQueue, conflictHistory,
89
+ // offlineCredentials, offlineSession, singleUserConfig) are auto-merged
47
90
  database: {
48
91
  name: 'MyAppDB',
49
92
  versions: [
@@ -52,434 +95,518 @@ initEngine({
52
95
  stores: {
53
96
  projects: 'id, user_id, updated_at',
54
97
  tasks: 'id, project_id, user_id, updated_at',
55
- }
56
- }
57
- ]
98
+ },
99
+ },
100
+ ],
58
101
  },
102
+
103
+ // Auth config -- single-user mode with 4-digit PIN
59
104
  auth: {
105
+ singleUser: { gateType: 'code', codeLength: 4 },
60
106
  enableOfflineAuth: true,
107
+ // emailConfirmation: { enabled: true },
108
+ // deviceVerification: { enabled: true },
61
109
  },
62
- });
63
110
 
64
- await initConfig();
65
- const auth = await resolveAuthState();
66
- if (auth.authMode !== 'none') await startSyncEngine();
67
- ```
68
-
69
- ### Single-user mode
70
-
71
- For personal apps with a PIN code gate backed by real Supabase email/password auth:
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';
111
+ // Demo mode config (optional)
112
+ demo: {
113
+ seedData: async (db) => {
114
+ await db.table('projects').bulkPut([
115
+ { id: 'demo-1', name: 'Sample Project', sort_order: 1, is_deleted: false },
116
+ ]);
117
+ },
118
+ mockProfile: { email: 'demo@example.com', firstName: 'Demo', lastName: 'User' },
119
+ },
77
120
 
78
- initEngine({
79
- prefix: 'myapp',
80
- supabase,
81
- tables: [/* ... */],
82
- database: {/* ... */},
83
- auth: {
84
- singleUser: { gateType: 'code', codeLength: 4 },
85
- enableOfflineAuth: true,
86
- // emailConfirmation: { enabled: true }, // require email confirmation on setup
87
- // deviceVerification: { enabled: true }, // require OTP verification on new devices
121
+ // CRDT config (optional)
122
+ crdt: {
123
+ persistIntervalMs: 30000,
124
+ maxOfflineDocuments: 50,
88
125
  },
89
126
  });
90
127
 
128
+ // ─── 2. Resolve auth and start the engine ──────────────────────────
129
+
91
130
  await initConfig();
92
131
  const auth = await resolveAuthState();
93
132
 
94
133
  if (!auth.singleUserSetUp) {
95
- // Show setup screen → call setupSingleUser(code, profile, email)
96
- // Returns { error, confirmationRequired }
97
- // If confirmationRequired, prompt user to check email then call completeSingleUserSetup()
134
+ // First-time setup flow
135
+ // call setupSingleUser(code, profile, email) from your UI
98
136
  } else if (auth.authMode === 'none') {
99
- // Show unlock screen → call unlockSingleUser(code)
100
- // Returns { error, deviceVerificationRequired?, maskedEmail? }
101
- // If deviceVerificationRequired, prompt for OTP then call completeDeviceVerification(tokenHash?)
137
+ // Locked -- show unlock screen
138
+ // call unlockSingleUser(code) from your UI
102
139
  } else {
140
+ // Authenticated -- start syncing
103
141
  await startSyncEngine();
104
142
  }
105
- ```
106
143
 
107
- ## Install PWA Command
144
+ // ─── 3. CRUD operations ────────────────────────────────────────────
145
+
146
+ import {
147
+ engineCreate,
148
+ engineUpdate,
149
+ engineDelete,
150
+ engineIncrement,
151
+ engineBatchWrite,
152
+ queryAll,
153
+ queryOne,
154
+ engineGetOrCreate,
155
+ } from '@prabhask5/stellar-engine/data';
156
+ import { generateId, now } from '@prabhask5/stellar-engine/utils';
157
+
158
+ // Create
159
+ const projectId = generateId();
160
+ await engineCreate('projects', {
161
+ id: projectId,
162
+ name: 'New Project',
163
+ sort_order: 1,
164
+ created_at: now(),
165
+ updated_at: now(),
166
+ is_deleted: false,
167
+ user_id: 'current-user-id',
168
+ });
108
169
 
109
- Scaffold a complete offline-first PWA project with an interactive walkthrough:
170
+ // Update (only changed fields are synced)
171
+ await engineUpdate('tasks', taskId, {
172
+ title: 'Updated title',
173
+ updated_at: now(),
174
+ });
110
175
 
111
- ```bash
112
- npx @prabhask5/stellar-engine install pwa
113
- ```
176
+ // Delete (soft delete -- tombstone managed by engine)
177
+ await engineDelete('tasks', taskId);
114
178
 
115
- The wizard guides you through each option (app name, short name, prefix, description), validates input inline, shows a confirmation summary, then scaffolds with animated progress.
179
+ // Increment (intent-preserved -- concurrent increments merge correctly)
180
+ await engineIncrement('tasks', taskId, 'count', 1);
116
181
 
117
- ### What it generates
182
+ // Query all rows from local IndexedDB
183
+ const projects = await queryAll('projects');
118
184
 
119
- The command creates a full SvelteKit 2 + Svelte 5 project with:
185
+ // Query a single row
186
+ const project = await queryOne('projects', projectId);
120
187
 
121
- **Configuration files (8):** `vite.config.ts`, `tsconfig.json`, `svelte.config.js`, `eslint.config.js`, `.prettierrc`, `.prettierignore`, `knip.json`, `.gitignore`
188
+ // Get or create (atomic upsert)
189
+ const { record, created } = await engineGetOrCreate('projects', projectId, {
190
+ id: projectId,
191
+ name: 'Default Project',
192
+ sort_order: 0,
193
+ created_at: now(),
194
+ updated_at: now(),
195
+ is_deleted: false,
196
+ user_id: 'current-user-id',
197
+ });
122
198
 
123
- **Documentation (3):** `README.md`, `ARCHITECTURE.md`, `FRAMEWORKS.md`
199
+ // Batch writes (multiple operations in one sync push)
200
+ await engineBatchWrite([
201
+ { type: 'create', table: 'tasks', data: { id: generateId(), title: 'Task 1', project_id: projectId, count: 0, sort_order: 1, created_at: now(), updated_at: now(), is_deleted: false, user_id: 'current-user-id' } },
202
+ { type: 'create', table: 'tasks', data: { id: generateId(), title: 'Task 2', project_id: projectId, count: 0, sort_order: 2, created_at: now(), updated_at: now(), is_deleted: false, user_id: 'current-user-id' } },
203
+ { type: 'update', table: 'projects', id: projectId, data: { updated_at: now() } },
204
+ ]);
124
205
 
125
- **Static assets (13):** `manifest.json`, `offline.html`, placeholder SVG icons (app, dark, maskable, favicon, monochrome, splash, apple-touch), email template placeholders (signup, change-email, device-verification)
206
+ // ─── 4. Reactive store factories ───────────────────────────────────
126
207
 
127
- **Database (1):** `supabase-schema.sql` with helper functions, example table pattern, and `trusted_devices` table
208
+ import { createCollectionStore, createDetailStore } from '@prabhask5/stellar-engine/stores';
128
209
 
129
- **Source files (2):** `src/app.html` (PWA-ready with iOS meta tags, landscape blocker, zoom prevention, SW registration), `src/app.d.ts`
210
+ // Collection store -- live-updating list from IndexedDB
211
+ const projectsStore = createCollectionStore('projects', {
212
+ filter: (p) => !p.is_deleted,
213
+ sort: (a, b) => a.sort_order - b.sort_order,
214
+ });
215
+ // Subscribe: projectsStore.subscribe(items => { ... })
130
216
 
131
- **Route files (16):**
132
- | File | What stellar-engine manages | What you customize (TODO) |
133
- |------|---------------------------|--------------------------|
134
- | `src/routes/+layout.ts` | Auth resolution, config init, sync engine startup via `resolveAuthState()`, `initConfig()`, `startSyncEngine()` | `initEngine()` config with your database schema |
135
- | `src/routes/+layout.svelte` | Auth state hydration via `hydrateAuthState()` | App shell (navbar, tab bar, overlays) |
136
- | `src/routes/+page.svelte` | Imports `resolveFirstName`, `onSyncComplete`, `authState`; derives `firstName` reactively | Home page UI |
137
- | `src/routes/+error.svelte` | — | Error page UI |
138
- | `src/routes/setup/+page.ts` | Config check, session validation via `getConfig()`, `getValidSession()` | — (fully managed) |
139
- | `src/routes/setup/+page.svelte` | Imports `setConfig`, `isOnline`, `pollForNewServiceWorker` | Setup wizard UI |
140
- | `src/routes/policy/+page.svelte` | — | Privacy policy content |
141
- | `src/routes/login/+page.svelte` | All auth functions: `setupSingleUser`, `unlockSingleUser`, `getSingleUserInfo`, `completeSingleUserSetup`, `completeDeviceVerification`, `pollDeviceVerification`, `fetchRemoteGateConfig`, `linkSingleUserDevice`, `sendDeviceVerification` | Login page UI |
142
- | `src/routes/confirm/+page.svelte` | Email confirmation via `handleEmailConfirmation()`, `broadcastAuthConfirmed()` | Confirmation page UI |
143
- | `src/routes/api/config/+server.ts` | Fully managed: `getServerConfig()` | — |
144
- | `src/routes/api/setup/deploy/+server.ts` | Fully managed: `deployToVercel()` | — |
145
- | `src/routes/api/setup/validate/+server.ts` | Fully managed: `createValidateHandler()` | — |
146
- | `src/routes/[...catchall]/+page.ts` | Redirect to `/` | — |
147
- | `src/routes/(protected)/+layout.ts` | Auth guard via `resolveAuthState()` with login redirect | — (fully managed) |
148
- | `src/routes/(protected)/+layout.svelte` | — | Protected area chrome |
149
- | `src/routes/(protected)/profile/+page.svelte` | All profile functions: `changeSingleUserGate`, `updateSingleUserProfile`, `getSingleUserInfo`, `changeSingleUserEmail`, `completeSingleUserEmailChange`, `resetDatabase`, `getTrustedDevices`, `removeTrustedDevice`, `getCurrentDeviceId`, `isDebugMode`, `setDebugMode` | Profile page UI |
217
+ // Detail store -- single record by ID
218
+ const projectDetail = createDetailStore('projects', projectId);
219
+ // Subscribe: projectDetail.subscribe(record => { ... })
150
220
 
151
- **Library (1):** `src/lib/types.ts` with re-exports from stellar-engine + app-specific type stubs
221
+ // ─── 5. Reactive stores ───────────────────────────────────────────
152
222
 
153
- **Git hooks (1):** `.husky/pre-commit` with lint + format + validate
223
+ import {
224
+ syncStatusStore,
225
+ authState,
226
+ isOnline,
227
+ remoteChangesStore,
228
+ onSyncComplete,
229
+ } from '@prabhask5/stellar-engine/stores';
154
230
 
155
- ### Interactive Prompts
231
+ // Listen for sync completions
232
+ onSyncComplete(() => {
233
+ console.log('Sync cycle finished');
234
+ });
156
235
 
157
- | Prompt | Required | Description |
158
- |--------|----------|-------------|
159
- | App Name | Yes | Full app name (e.g., "Stellar Planner") |
160
- | Short Name | Yes | Short name for PWA home screen (under 12 chars) |
161
- | Prefix | Yes | Lowercase key for localStorage, caches, SW (auto-suggested from name) |
162
- | Description | No | App description (default: "A self-hosted offline-first PWA") |
236
+ // ─── 6. CRDT collaborative editing ────────────────────────────────
237
+
238
+ import {
239
+ openDocument,
240
+ closeDocument,
241
+ createSharedText,
242
+ createBlockDocument,
243
+ updateCursor,
244
+ getCollaborators,
245
+ onCollaboratorsChange,
246
+ } from '@prabhask5/stellar-engine/crdt';
247
+
248
+ // Open a collaborative document (uses Supabase Broadcast -- zero DB writes per keystroke)
249
+ const provider = await openDocument('doc-1', 'page-1', {
250
+ offlineEnabled: true,
251
+ initialPresence: { name: 'Alice' },
252
+ });
163
253
 
164
- ## Subpath exports
254
+ // Use with any Yjs-compatible editor (Tiptap, BlockNote, etc.)
255
+ const { content, meta } = createBlockDocument(provider.doc);
256
+ meta.set('title', 'My Page');
257
+
258
+ // Track collaborator cursors and presence
259
+ const unsub = onCollaboratorsChange('doc-1', (collaborators) => {
260
+ // Update avatar list, cursor positions, etc.
261
+ });
165
262
 
166
- Import only what you need via subpath exports:
263
+ await closeDocument('doc-1');
167
264
 
168
- | Subpath | Contents |
169
- |---|---|
170
- | `@prabhask5/stellar-engine` | `initEngine`, `startSyncEngine`, `runFullSync`, `supabase`, `getDb`, `validateSupabaseCredentials`, `validateSchema` |
171
- | `@prabhask5/stellar-engine/data` | All engine CRUD + query operations (`engineCreate`, `engineUpdate`, etc.) |
172
- | `@prabhask5/stellar-engine/auth` | All auth functions (`resolveAuthState`, `signOut`, `setupSingleUser`, `unlockSingleUser`, `lockSingleUser`, `completeSingleUserSetup`, `completeDeviceVerification`, `changeSingleUserEmail`, `completeSingleUserEmailChange`, `padPin`, etc.) |
173
- | `@prabhask5/stellar-engine/stores` | Reactive stores + event subscriptions (`syncStatusStore`, `authState`, `onSyncComplete`, etc.) |
174
- | `@prabhask5/stellar-engine/types` | All type exports (`Session`, `SyncEngineConfig`, `BatchOperation`, `SingleUserConfig`, etc.) |
175
- | `@prabhask5/stellar-engine/utils` | Utility functions (`generateId`, `now`, `calculateNewOrder`, `snakeToCamel`, `debug`, etc.) |
176
- | `@prabhask5/stellar-engine/actions` | Svelte `use:` actions (`remoteChangeAnimation`, `trackEditing`, `triggerLocalAnimation`, `truncateTooltip`) |
177
- | `@prabhask5/stellar-engine/kit` | SvelteKit route helpers, server APIs, load functions, confirmation, auth hydration |
178
- | `@prabhask5/stellar-engine/components/SyncStatus` | Sync status indicator Svelte component |
179
- | `@prabhask5/stellar-engine/components/DeferredChangesBanner` | Cross-device conflict banner Svelte component |
180
- | `@prabhask5/stellar-engine/config` | Runtime config (`initConfig`, `getConfig`, `setConfig`, `getDexieTableFor`) |
181
-
182
- The root export (`@prabhask5/stellar-engine`) re-exports everything for backward compatibility.
183
-
184
- ## Requirements
185
-
186
- **Supabase**
187
-
188
- 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:
189
- - `id` (uuid primary key)
190
- - `updated_at` (timestamptz) -- used as the sync cursor
191
- - `is_deleted` (boolean, default false) -- for soft-delete / tombstone support
192
- - An ownership column (e.g., `user_id`) if you use `ownershipFilter`
193
-
194
- Row-Level Security policies should scope reads and writes to the authenticated user.
195
-
196
- **Single-user mode additional requirements:**
197
-
198
- Single-user mode uses real Supabase email/password auth where the PIN is padded to meet Supabase's minimum password length. The user provides an email during setup, and the PIN is verified server-side.
199
-
200
- If `deviceVerification` is enabled in the auth config, you need a `trusted_devices` table:
201
-
202
- ```sql
203
- CREATE TABLE trusted_devices (
204
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
205
- user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
206
- device_id text NOT NULL,
207
- device_label text,
208
- trusted_at timestamptz DEFAULT now() NOT NULL,
209
- last_used_at timestamptz DEFAULT now() NOT NULL,
210
- UNIQUE(user_id, device_id)
211
- );
212
- ALTER TABLE trusted_devices ENABLE ROW LEVEL SECURITY;
213
- CREATE POLICY "Users manage own devices" ON trusted_devices FOR ALL
214
- USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
215
- ```
265
+ // ─── 7. Demo mode ─────────────────────────────────────────────────
266
+
267
+ import { setDemoMode } from '@prabhask5/stellar-engine';
216
268
 
217
- If `emailConfirmation` is enabled, Supabase email templates must be configured. See [EMAIL_TEMPLATES.md](./EMAIL_TEMPLATES.md) for the full HTML templates for signup confirmation, email change confirmation, and device verification emails.
269
+ // Toggle demo mode (requires full page reload)
270
+ setDemoMode(true);
271
+ window.location.href = '/';
272
+
273
+ // In demo mode:
274
+ // - Uses '${name}_demo' IndexedDB (real DB never opened)
275
+ // - Zero Supabase network requests
276
+ // - authMode === 'demo', protected routes work with mock data
277
+ // - seedData callback runs on each page load
278
+
279
+ // ─── 8. Diagnostics and debug ──────────────────────────────────────
280
+
281
+ import {
282
+ setDebugMode,
283
+ isDebugMode,
284
+ getSyncDiagnostics,
285
+ getQueueDiagnostics,
286
+ getRealtimeDiagnostics,
287
+ getConflictDiagnostics,
288
+ getEgressDiagnostics,
289
+ getNetworkDiagnostics,
290
+ } from '@prabhask5/stellar-engine/utils';
291
+
292
+ setDebugMode(true);
293
+
294
+ // Runtime diagnostics
295
+ const syncInfo = getSyncDiagnostics();
296
+ const queueInfo = getQueueDiagnostics();
297
+ const egressInfo = getEgressDiagnostics();
298
+
299
+ // ─── 9. SQL and TypeScript generation ──────────────────────────────
300
+
301
+ import {
302
+ generateCreateTableSQL,
303
+ generateRLSPolicies,
304
+ generateTypeScriptInterfaces,
305
+ } from '@prabhask5/stellar-engine/utils';
306
+
307
+ // Auto-generate Supabase SQL from your schema config
308
+ const sql = generateCreateTableSQL('projects', tableConfig);
309
+ const rls = generateRLSPolicies('projects', tableConfig);
310
+ const tsInterfaces = generateTypeScriptInterfaces(allTables);
311
+ ```
218
312
 
219
- **Schema validation:** The engine automatically validates that all configured tables (and `trusted_devices` when `deviceVerification.enabled`) exist in Supabase on the first sync. Missing tables are reported via `syncStatusStore` and the debug console.
313
+ ## Commands
220
314
 
221
- **Dexie (IndexedDB)**
315
+ ### Install PWA
222
316
 
223
- 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()`):
317
+ Scaffold a complete offline-first SvelteKit PWA project with an interactive walkthrough:
224
318
 
225
- ```ts
226
- database: {
227
- name: 'MyAppDB',
228
- versions: [
229
- {
230
- version: 1,
231
- stores: {
232
- projects: 'id, user_id, updated_at',
233
- tasks: 'id, project_id, user_id, updated_at',
234
- }
235
- }
236
- ]
237
- }
319
+ ```bash
320
+ npx @prabhask5/stellar-engine install pwa
238
321
  ```
239
322
 
240
- Alternatively, you can provide a pre-created Dexie instance via the `db` config option for full control.
323
+ The wizard prompts for:
241
324
 
242
- ## Architecture
325
+ | Prompt | Required | Description |
326
+ |--------|----------|-------------|
327
+ | App Name | Yes | Full app name (e.g., "Stellar Planner") |
328
+ | Short Name | Yes | Short name for PWA home screen (under 12 chars) |
329
+ | Prefix | Yes | Lowercase key for localStorage, caches, SW (auto-suggested from name) |
330
+ | Description | No | App description (default: "A self-hosted offline-first PWA") |
243
331
 
244
- ```
245
- +---------------------+
246
- | Application UI |
247
- +---------------------+
248
- |
249
- v
250
- +---------------------+ +-------------------+
251
- | Repositories | ----> | Dexie (IDB) |
252
- | (read/write local) | | - app tables |
253
- +---------------------+ | - syncQueue |
254
- | | - conflictHistory |
255
- | queueSyncOperation +-------------------+
256
- v ^
257
- +---------------------+ |
258
- | Sync Engine | ---------------+
259
- | - coalesce ops | hydrate / reconcile
260
- | - push to remote |
261
- | - pull from remote |
262
- | - resolve conflicts |
263
- +---------------------+
264
- |
265
- v
266
- +---------------------+ +---------------------+
267
- | Supabase REST | | Supabase Realtime |
268
- | (push / pull) | | (live subscriptions)|
269
- +---------------------+ +---------------------+
270
- ```
332
+ Generates **34+ files** for a production-ready SvelteKit 2 + Svelte 5 project:
271
333
 
272
- 1. **Repositories** read from and write to Dexie, then enqueue a `SyncOperationItem` describing the intent of the change.
273
- 2. **The engine** debounces outbound operations, coalesces them, and pushes to Supabase via REST. On pull, it fetches rows newer than the local sync cursor and reconciles them with any pending local operations.
274
- 3. **Realtime** channels deliver server-side changes immediately, skipping the next poll cycle when the subscription is healthy.
334
+ - **Config files (8):** `vite.config.ts`, `tsconfig.json`, `svelte.config.js`, `eslint.config.js`, `.prettierrc`, `.prettierignore`, `knip.json`, `.gitignore`
335
+ - **Documentation (3):** `README.md`, `ARCHITECTURE.md`, `FRAMEWORKS.md`
336
+ - **Static assets (13):** `manifest.json`, `offline.html`, placeholder SVG icons, email template placeholders
337
+ - **Database (1):** `supabase-schema.sql` with helper functions, example tables, and `trusted_devices` table
338
+ - **Source files (2):** `src/app.html` (PWA-ready with iOS meta tags, SW registration), `src/app.d.ts`
339
+ - **Route files (16):** Root layout, login, setup, profile, protected area, API endpoints, catch-all redirect
340
+ - **Library (1):** `src/lib/types.ts` with re-exports and app-specific type stubs
341
+ - **Git hooks (1):** `.husky/pre-commit` with lint + format + validate
275
342
 
276
343
  ## API overview
277
344
 
278
- ### Configuration
345
+ ### Engine Configuration and Lifecycle
279
346
 
280
347
  | Export | Description |
281
348
  |---|---|
282
- | `initEngine(config)` | Initialize the engine with table definitions, Supabase client, and Dexie instance. |
283
- | `getEngineConfig()` | Retrieve the current config (throws if not initialized). |
284
- | `SyncEngineConfig` / `TableConfig` | TypeScript interfaces for the config objects. `TableConfig` uses `supabaseName` only; Dexie table names are auto-derived. |
349
+ | `initEngine(config)` | Initialize the engine with table definitions, auth, database, and optional CRDT/demo config |
350
+ | `startSyncEngine()` | Start the sync loop, realtime subscriptions, and event listeners |
351
+ | `stopSyncEngine()` | Tear down sync loop and subscriptions cleanly |
352
+ | `runFullSync()` | Run a complete pull-then-push cycle |
353
+ | `scheduleSyncPush()` | Trigger a debounced push of pending operations |
354
+ | `getEngineConfig()` | Retrieve the current engine config (throws if not initialized) |
355
+ | `validateSupabaseCredentials()` | Verify Supabase URL and anon key are valid |
356
+ | `validateSchema()` | Validate all configured tables exist in Supabase |
285
357
 
286
- ### Engine lifecycle
358
+ ### Database
287
359
 
288
360
  | Export | Description |
289
361
  |---|---|
290
- | `startSyncEngine()` | Start the sync loop, realtime subscriptions, and event listeners. |
291
- | `stopSyncEngine()` | Tear down everything cleanly. |
292
- | `scheduleSyncPush()` | Trigger a debounced push of pending operations. |
293
- | `runFullSync()` | Run a complete pull-then-push cycle. |
294
- | `clearLocalCache()` | Wipe all local application data. |
295
- | `clearPendingSyncQueue()` | Drop all pending outbound operations. |
362
+ | `supabase` | The configured `SupabaseClient` instance |
363
+ | `getDb()` | Get the Dexie database instance |
364
+ | `resetDatabase()` | Drop and recreate the local IndexedDB database |
365
+ | `clearLocalCache()` | Wipe all local application data |
366
+ | `clearPendingSyncQueue()` | Drop all pending outbound operations |
367
+ | `getSupabaseAsync()` | Async getter that waits for initialization |
368
+ | `resetSupabaseClient()` | Tear down and reinitialize the Supabase client |
296
369
 
297
- ### Entity tracking
370
+ ### CRUD and Query Operations
298
371
 
299
372
  | Export | Description |
300
373
  |---|---|
301
- | `markEntityModified(table, id)` | Record that an entity was recently modified locally (prevents incoming realtime from overwriting). |
302
- | `onSyncComplete(callback)` | Register a callback invoked after each successful sync cycle. |
303
-
304
- ### Auth Utilities
374
+ | `engineCreate(table, data)` | Create a record locally and enqueue sync |
375
+ | `engineUpdate(table, id, data)` | Update specific fields locally and enqueue sync |
376
+ | `engineDelete(table, id)` | Soft-delete a record (tombstone) |
377
+ | `engineIncrement(table, id, field, delta)` | Intent-preserving numeric increment |
378
+ | `engineBatchWrite(operations)` | Execute multiple operations in a single sync push |
379
+ | `engineGetOrCreate(table, id, defaults)` | Atomic get-or-create (upsert) |
380
+ | `queryAll(table, options?)` | Query all rows from local IndexedDB |
381
+ | `queryOne(table, id)` | Query a single row by ID |
382
+ | `markEntityModified(table, id)` | Suppress incoming realtime overwrites for a recently modified entity |
383
+
384
+ ### Authentication -- Core
305
385
 
306
386
  | Export | Description |
307
387
  |---|---|
308
- | `signOut` | Full teardown: stops sync, clears caches, signs out of Supabase. |
309
- | `resendConfirmationEmail` | Resend signup confirmation email. |
310
- | `getUserProfile` / `updateProfile` | Profile read/write via Supabase user metadata. |
311
- | `verifyOtp` | Verify OTP token hash from confirmation email links. |
312
- | `getValidSession` | Get a non-expired Supabase session, or `null`. |
313
- | `resolveFirstName(session, offline, fallback?)` | Resolve display name from session or offline profile with configurable fallback. |
314
- | `resolveUserId(session, offline)` | Extract user UUID from session or offline credentials. |
315
- | `resolveAvatarInitial(session, offline, fallback?)` | Single uppercase initial for avatar display. |
316
-
317
- ### Single-user auth
388
+ | `resolveAuthState()` | Determine current auth state (online, offline, or none) |
389
+ | `signOut()` | Full teardown: stop sync, clear caches, sign out of Supabase |
390
+ | `getValidSession()` | Get a non-expired Supabase session, or `null` |
391
+ | `verifyOtp(tokenHash)` | Verify OTP token hash from email confirmation links |
392
+ | `resendConfirmationEmail()` | Resend signup confirmation email |
393
+ | `getUserProfile()` | Read profile from Supabase user metadata |
394
+ | `updateProfile(data)` | Write profile to Supabase user metadata |
318
395
 
319
- For personal apps that use a simplified PIN or password gate. Uses real Supabase email/password auth where the PIN is padded to meet minimum password length. Enable by setting `auth.singleUser` in the engine config.
396
+ ### Authentication -- Single-User
320
397
 
321
398
  | Export | Description |
322
399
  |---|---|
323
- | `isSingleUserSetUp()` | Check if initial setup is complete. |
324
- | `getSingleUserInfo()` | Get display info (profile, gate type) for the unlock screen. |
325
- | `setupSingleUser(gate, profile, email)` | First-time setup: create gate, Supabase email/password user, and store config. Returns `{ error, confirmationRequired }`. |
326
- | `unlockSingleUser(gate)` | Verify gate and restore session (online or offline). Returns `{ error, deviceVerificationRequired?, maskedEmail? }`. |
327
- | `completeSingleUserSetup()` | Called after the user confirms their email (when `emailConfirmation` is enabled). |
328
- | `completeDeviceVerification(tokenHash?)` | Called after the user completes device OTP verification (when `deviceVerification` is enabled). |
329
- | `lockSingleUser()` | Stop sync and reset auth state without destroying data. |
330
- | `changeSingleUserGate(oldGate, newGate)` | Change the PIN code or password. |
331
- | `updateSingleUserProfile(profile)` | Update profile in IndexedDB and Supabase metadata. |
332
- | `changeSingleUserEmail(newEmail)` | Request email change for single-user mode. Returns `{ error, confirmationRequired }`. |
333
- | `completeSingleUserEmailChange()` | Finalize email change: refresh session, update IndexedDB config and cached credentials. |
334
- | `resetSingleUser()` | Full reset: clear config, sign out, wipe local data. |
335
- | `padPin(pin)` | Pad a PIN to meet Supabase's minimum password length requirement. |
336
-
337
- ### Queue
400
+ | `setupSingleUser(gate, profile, email)` | First-time setup: create gate, Supabase user, and store config |
401
+ | `unlockSingleUser(gate)` | Verify gate and restore session (online or offline) |
402
+ | `lockSingleUser()` | Stop sync and reset auth state without destroying data |
403
+ | `isSingleUserSetUp()` | Check if initial setup is complete |
404
+ | `getSingleUserInfo()` | Get display info (profile, gate type) for the unlock screen |
405
+ | `changeSingleUserGate(oldGate, newGate)` | Change PIN code or password |
406
+ | `updateSingleUserProfile(profile)` | Update profile in IndexedDB and Supabase metadata |
407
+ | `changeSingleUserEmail(newEmail)` | Request email change |
408
+ | `completeSingleUserEmailChange()` | Finalize email change after confirmation |
409
+ | `resetSingleUser()` | Full reset: clear config, sign out, wipe local data |
410
+ | `padPin(pin)` | Pad a PIN to meet Supabase's minimum password length |
411
+
412
+ ### Authentication -- Device Verification
338
413
 
339
414
  | Export | Description |
340
415
  |---|---|
341
- | `queueSyncOperation(item)` | Enqueue a raw `SyncOperationItem`. |
342
- | `queueCreateOperation(table, id, payload)` | Enqueue entity creation. |
343
- | `queueDeleteOperation(table, id)` | Enqueue a soft delete. |
344
- | `coalescePendingOps()` | Compress the outbox in-place (called automatically before push). |
345
- | `getPendingSync()` / `getPendingEntityIds()` | Inspect the current outbox. |
416
+ | `completeDeviceVerification(tokenHash?)` | Complete device OTP verification |
417
+ | `sendDeviceVerification()` | Send device verification email |
418
+ | `pollDeviceVerification()` | Poll for device verification completion |
419
+ | `linkSingleUserDevice()` | Link current device to user after verification |
420
+ | `getTrustedDevices()` | List all trusted devices for current user |
421
+ | `removeTrustedDevice(deviceId)` | Remove a trusted device |
422
+ | `getCurrentDeviceId()` | Get the stable device identifier |
423
+ | `fetchRemoteGateConfig()` | Fetch gate config from Supabase for cross-device setup |
346
424
 
347
- ### Conflict resolution
425
+ ### Authentication -- Display Utilities
348
426
 
349
427
  | Export | Description |
350
428
  |---|---|
351
- | `resolveConflicts(table, localEntity, remoteEntity, pendingOps)` | Three-tier field-level conflict resolver. Returns the merged entity. |
429
+ | `resolveFirstName(session, offline, fallback?)` | Resolve display name from session or offline profile |
430
+ | `resolveUserId(session, offline)` | Extract user UUID from session or offline credentials |
431
+ | `resolveAvatarInitial(session, offline, fallback?)` | Single uppercase initial for avatar display |
352
432
 
353
- ### Realtime
433
+ ### Reactive Stores
354
434
 
355
435
  | Export | Description |
356
436
  |---|---|
357
- | `startRealtimeSubscriptions()` / `stopRealtimeSubscriptions()` | Manage Supabase Realtime channels for all configured tables. |
358
- | `isRealtimeHealthy()` | Realtime connection health check. |
359
- | `wasRecentlyProcessedByRealtime(table, id)` | Guard against duplicate processing. |
360
- | `onRealtimeDataUpdate(callback)` | Register a handler for incoming realtime changes. |
437
+ | `syncStatusStore` | Current `SyncStatus`, last sync time, and errors |
438
+ | `authState` | Reactive auth state object (`mode`, `session`, `offlineProfile`, `isLoading`) |
439
+ | `isAuthenticated` | Derived boolean for auth status |
440
+ | `userDisplayInfo` | Derived display name and avatar info |
441
+ | `isOnline` | Reactive boolean reflecting network state |
442
+ | `remoteChangesStore` | Tracks entities recently changed by remote peers |
443
+ | `createRecentChangeIndicator(table, id)` | Derived indicator for UI highlighting of remote changes |
444
+ | `createPendingDeleteIndicator(table, id)` | Derived indicator for entities awaiting delete confirmation |
445
+ | `onSyncComplete(callback)` | Register a callback invoked after each successful sync cycle |
446
+ | `onRealtimeDataUpdate(callback)` | Register a handler for incoming realtime changes |
447
+
448
+ ### Store Factories
361
449
 
362
- ### Stores (Svelte 5 compatible)
450
+ | Export | Description |
451
+ |---|---|
452
+ | `createCollectionStore(table, options?)` | Live-updating list store from IndexedDB with filter and sort |
453
+ | `createDetailStore(table, id)` | Single-record store by ID |
454
+
455
+ ### Realtime
363
456
 
364
457
  | Export | Description |
365
458
  |---|---|
366
- | `syncStatusStore` | Reactive store exposing current `SyncStatus`, last sync time, and errors. |
367
- | `remoteChangesStore` | Tracks which entities were recently changed by remote peers. |
368
- | `createRecentChangeIndicator(table, id)` | Derived indicator for UI highlighting of remote changes. |
369
- | `createPendingDeleteIndicator(table, id)` | Derived indicator for entities awaiting delete confirmation. |
370
- | `isOnline` | Reactive boolean reflecting network state. |
371
- | `authState` / `isAuthenticated` / `userDisplayInfo` | Reactive auth status stores. |
459
+ | `startRealtimeSubscriptions()` | Start Supabase Realtime channels for all configured tables |
460
+ | `stopRealtimeSubscriptions()` | Stop all Realtime channels |
461
+ | `isRealtimeHealthy()` | Realtime connection health check |
462
+ | `wasRecentlyProcessedByRealtime(table, id)` | Guard against duplicate processing |
372
463
 
373
- ### Supabase client
464
+ ### Runtime Config
374
465
 
375
466
  | Export | Description |
376
467
  |---|---|
377
- | `supabase` | The configured `SupabaseClient` instance. |
378
- | `getSupabaseAsync()` | Async getter that waits for initialization. |
379
- | `resetSupabaseClient()` | Tear down and reinitialize the client. |
468
+ | `initConfig()` | Initialize runtime configuration |
469
+ | `getConfig()` | Get current config |
470
+ | `setConfig(config)` | Update runtime config |
471
+ | `waitForConfig()` | Async getter that waits for config initialization |
472
+ | `isConfigured()` | Check if config is initialized |
473
+ | `clearConfigCache()` | Clear cached config |
474
+ | `getDexieTableFor(supabaseName)` | Get the Dexie table name for a Supabase table name |
380
475
 
381
- ### Runtime config
476
+ ### Diagnostics and Debug
382
477
 
383
478
  | Export | Description |
384
479
  |---|---|
385
- | `initConfig` / `getConfig` / `waitForConfig` / `setConfig` | Manage app-level runtime configuration (e.g., feature flags loaded from the server). |
386
- | `isConfigured()` / `clearConfigCache()` | Status and cache management. |
480
+ | `getSyncDiagnostics()` | Sync cycle statistics and recent cycle details |
481
+ | `getQueueDiagnostics()` | Pending operation queue state |
482
+ | `getRealtimeDiagnostics()` | Realtime connection state and health |
483
+ | `getConflictDiagnostics()` | Conflict resolution history and stats |
484
+ | `getEgressDiagnostics()` | Data transfer from Supabase (bytes, per-table breakdown) |
485
+ | `getNetworkDiagnostics()` | Network state and connectivity info |
486
+ | `setDebugMode(enabled)` | Enable/disable debug logging |
487
+ | `isDebugMode()` | Check if debug mode is active |
488
+ | `debugLog` / `debugWarn` / `debugError` | Prefixed console helpers (gated by debug mode) |
489
+
490
+ When debug mode is enabled, the engine exposes utilities on `window` using your configured prefix (e.g., `window.__myappSyncStats()`, `window.__myappEgress()`, `window.__myappTombstones()`, `window.__myappSync.forceFullSync()`).
387
491
 
388
492
  ### Utilities
389
493
 
390
494
  | Export | Description |
391
495
  |---|---|
392
- | `generateId()` | Generate a UUID. |
393
- | `now()` | Current ISO timestamp string. |
394
- | `calculateNewOrder(before, after)` | Fractional ordering helper for drag-and-drop reorder. |
395
- | `snakeToCamel(str)` | Convert a `snake_case` string to `camelCase` (also strips invalid characters). Used internally to derive Dexie table names from `supabaseName`. |
396
- | `getDeviceId()` | Stable per-device identifier (persisted in localStorage). |
397
- | `debugLog` / `debugWarn` / `debugError` | Prefixed console helpers (gated by `setDebugMode`). |
496
+ | `generateId()` | Generate a UUID |
497
+ | `now()` | Current ISO timestamp string |
498
+ | `calculateNewOrder(before, after)` | Fractional ordering helper for drag-and-drop reorder |
499
+ | `snakeToCamel(str)` | Convert `snake_case` to `camelCase` |
500
+ | `getDeviceId()` | Stable per-device identifier (persisted in localStorage) |
501
+
502
+ ### SQL and TypeScript Generation
398
503
 
399
- ### Browser console debug utilities
504
+ | Export | Description |
505
+ |---|---|
506
+ | `generateCreateTableSQL(name, config)` | Generate `CREATE TABLE` statement from schema config |
507
+ | `generateRLSPolicies(name, config)` | Generate Row-Level Security policies |
508
+ | `generateTypeScriptInterfaces(tables)` | Generate TypeScript interfaces from all table configs |
400
509
 
401
- When debug mode is enabled, the engine exposes utilities on the `window` object using the configured app prefix (e.g. `stellar`):
510
+ ### Svelte Actions
402
511
 
403
- | Window property | Description |
512
+ | Export | Description |
404
513
  |---|---|
405
- | `window.__<prefix>SyncStats()` | View sync cycle statistics (total cycles, recent cycle details, trigger types). |
406
- | `window.__<prefix>Egress()` | Monitor data transfer from Supabase (total bytes, per-table breakdown, recent cycles). |
407
- | `window.__<prefix>Tombstones()` | Check soft-deleted record counts across all tables. |
408
- | `window.__<prefix>Tombstones({ cleanup: true })` | Manually trigger tombstone cleanup. |
409
- | `window.__<prefix>Tombstones({ cleanup: true, force: true })` | Force server cleanup (bypasses 24-hour interval). |
410
- | `window.__<prefix>Sync.forceFullSync()` | Reset sync cursor, clear local data, and re-download everything from server. |
411
- | `window.__<prefix>Sync.resetSyncCursor()` | Clear the stored cursor so the next sync pulls all data. |
412
- | `window.__<prefix>Sync.sync()` | Trigger a manual sync cycle. |
413
- | `window.__<prefix>Sync.getStatus()` | View current sync cursor and pending operation count. |
414
- | `window.__<prefix>Sync.checkConnection()` | Test Supabase connectivity. |
415
- | `window.__<prefix>Sync.realtimeStatus()` | Check realtime connection state and health. |
416
-
417
- ### Svelte actions
514
+ | `remoteChangeAnimation` | `use:` action that animates an element when a remote change arrives |
515
+ | `trackEditing` | Action that signals the engine a field is being actively edited (suppresses incoming overwrites) |
516
+ | `triggerLocalAnimation` | Programmatically trigger the local-change animation on a node |
517
+ | `truncateTooltip` | Action that shows a tooltip with full text when content is truncated |
518
+
519
+ ### Svelte Components
418
520
 
419
521
  | Export | Description |
420
522
  |---|---|
421
- | `remoteChangeAnimation` | Svelte `use:` action that animates an element when a remote change arrives. |
422
- | `trackEditing` | Action that signals the engine a field is being actively edited (suppresses incoming overwrites). |
423
- | `triggerLocalAnimation` | Programmatically trigger the local-change animation on a node. |
424
- | `truncateTooltip` | Action that shows a tooltip with full text when content is truncated via CSS overflow. |
523
+ | `@prabhask5/stellar-engine/components/SyncStatus` | Animated sync-state indicator with tooltip and PWA refresh (offline/syncing/synced/error/pending states) |
524
+ | `@prabhask5/stellar-engine/components/DeferredChangesBanner` | Cross-device data conflict notification with Update/Dismiss/Show Changes actions and diff preview |
525
+ | `@prabhask5/stellar-engine/components/DemoBanner` | Demo mode indicator banner |
425
526
 
426
- ### Svelte components
527
+ ### SvelteKit Helpers (optional)
528
+
529
+ These require `svelte ^5.0.0` as a peer dependency.
427
530
 
428
531
  | Export | Description |
429
532
  |---|---|
430
- | `@prabhask5/stellar-engine/components/SyncStatus` | Full Svelte 5 component for animated sync-state indicator with tooltip and PWA refresh. Shows offline/syncing/synced/error/pending states with live indicator. |
431
- | `@prabhask5/stellar-engine/components/DeferredChangesBanner` | Full Svelte 5 component for cross-device data conflict notification. Shows when another device pushes changes while user is editing. Provides Update/Dismiss/Show Changes actions with diff preview. |
533
+ | Layout load functions | `resolveAuthState` integration for `+layout.ts` |
534
+ | Server handlers | Factory functions for API routes (`getServerConfig`, `createValidateHandler`, `deployToVercel`) |
535
+ | Email confirmation | `handleEmailConfirmation()`, `broadcastAuthConfirmed()` |
536
+ | SW lifecycle | `monitorSwLifecycle()`, `handleSwUpdate()`, `pollForNewServiceWorker()` |
537
+ | Auth hydration | `hydrateAuthState()` for `+layout.svelte` |
432
538
 
433
- The `UpdatePrompt` component is **not** shipped as a stellar-engine export. Instead, it is generated by `stellar-engine install pwa` at `src/lib/components/UpdatePrompt.svelte` with TODO UI placeholders. The generated component imports `monitorSwLifecycle` and `handleSwUpdate` from `@prabhask5/stellar-engine/kit` for all SW lifecycle logic.
539
+ ### CRDT Collaborative Editing
434
540
 
435
- ## Use cases
541
+ | Export | Description |
542
+ |---|---|
543
+ | `openDocument(docId, pageId, options?)` | Open a collaborative document via Supabase Broadcast |
544
+ | `closeDocument(docId)` | Close and clean up a document |
545
+ | `createSharedText(doc)` | Create a shared Yjs text type |
546
+ | `createBlockDocument(doc)` | Create a block-based document structure |
547
+ | `updateCursor(docId, cursor)` | Update cursor position for presence |
548
+ | `getCollaborators(docId)` | Get current collaborators |
549
+ | `onCollaboratorsChange(docId, callback)` | Subscribe to collaborator changes |
550
+ | `enableOffline(docId)` / `disableOffline(docId)` | Toggle offline persistence |
436
551
 
437
- - **Productivity and task management apps** -- offline-capable task boards, habit trackers, daily planners with cross-device sync.
438
- - **Notion-like editors** -- block-based documents where each block is a synced entity with field-level conflict resolution.
439
- - **Personal finance trackers** -- numeric merge fields handle concurrent balance adjustments across devices.
440
- - **File and asset management UIs** -- fractional ordering keeps drag-and-drop sort order consistent without rewriting every row.
552
+ ### Types
441
553
 
442
- ## Demo Mode
554
+ All TypeScript types are available from `@prabhask5/stellar-engine/types`:
443
555
 
444
- stellar-engine includes a built-in demo mode that provides a completely isolated sandbox for consumer apps. When active:
556
+ `Session`, `SyncEngineConfig`, `TableConfig`, `BatchOperation`, `SingleUserConfig`, `DemoConfig`, `SyncStatus`, `AuthState`, `CRDTConfig`, and more.
445
557
 
446
- - **Separate database**: Uses `${name}_demo` IndexedDB — the real DB is never opened
447
- - **No Supabase**: Zero network requests to the backend
448
- - **Mock auth**: `authMode === 'demo'` — protected routes work with mock data only
449
- - **Auto-seeded**: Consumer's `seedData(db)` populates the demo DB on each page load
450
- - **Full isolation**: Page reload required to enter/exit (complete engine teardown)
558
+ ## Subpath exports
451
559
 
452
- ### Quick Start
560
+ Import only what you need:
453
561
 
454
- 1. Define a `DemoConfig` with mock data and profile:
562
+ | Subpath | Contents |
563
+ |---|---|
564
+ | `@prabhask5/stellar-engine` | Core: `initEngine`, `startSyncEngine`, `runFullSync`, `supabase`, `getDb`, `resetDatabase`, `validateSupabaseCredentials`, `validateSchema`, CRUD, auth, stores, and all re-exports |
565
+ | `@prabhask5/stellar-engine/data` | CRUD + query operations + helpers |
566
+ | `@prabhask5/stellar-engine/auth` | All auth functions |
567
+ | `@prabhask5/stellar-engine/stores` | Reactive stores + store factories + event subscriptions |
568
+ | `@prabhask5/stellar-engine/types` | All type exports |
569
+ | `@prabhask5/stellar-engine/utils` | Utilities + debug + diagnostics + SQL/TS generation |
570
+ | `@prabhask5/stellar-engine/actions` | Svelte `use:` actions |
571
+ | `@prabhask5/stellar-engine/config` | Runtime config + `getDexieTableFor` |
572
+ | `@prabhask5/stellar-engine/vite` | Vite plugin |
573
+ | `@prabhask5/stellar-engine/kit` | SvelteKit helpers (optional) |
574
+ | `@prabhask5/stellar-engine/crdt` | CRDT collaborative editing |
575
+ | `@prabhask5/stellar-engine/components/SyncStatus` | Sync indicator component |
576
+ | `@prabhask5/stellar-engine/components/DeferredChangesBanner` | Conflict banner component |
577
+ | `@prabhask5/stellar-engine/components/DemoBanner` | Demo mode banner component |
578
+
579
+ ## Demo mode
580
+
581
+ stellar-engine includes a built-in demo mode that provides a completely isolated sandbox. When active:
582
+
583
+ - **Separate database** -- uses `${name}_demo` IndexedDB; the real database is never opened
584
+ - **No Supabase** -- zero network requests to the backend
585
+ - **Mock auth** -- `authMode === 'demo'`; protected routes work with mock data only
586
+ - **Auto-seeded** -- your `seedData(db)` callback populates the demo database on each page load
587
+ - **Full isolation** -- page reload required to enter/exit (complete engine teardown)
455
588
 
456
589
  ```ts
457
590
  import type { DemoConfig } from '@prabhask5/stellar-engine';
591
+ import { setDemoMode } from '@prabhask5/stellar-engine';
458
592
 
593
+ // Define demo config in initEngine
459
594
  const demoConfig: DemoConfig = {
460
595
  seedData: async (db) => {
461
- await db.table('items').bulkPut([{ id: '1', name: 'Sample', ... }]);
596
+ await db.table('projects').bulkPut([
597
+ { id: 'demo-1', name: 'Sample Project', sort_order: 1, is_deleted: false },
598
+ ]);
462
599
  },
463
600
  mockProfile: { email: 'demo@example.com', firstName: 'Demo', lastName: 'User' },
464
601
  };
465
- ```
466
-
467
- 2. Pass it to `initEngine()`:
468
602
 
469
- ```ts
470
- initEngine({ ..., demo: demoConfig });
471
- ```
472
-
473
- 3. Toggle demo mode:
603
+ initEngine({ /* ...config */, demo: demoConfig });
474
604
 
475
- ```ts
476
- import { setDemoMode } from '@prabhask5/stellar-engine';
605
+ // Toggle demo mode from your UI
477
606
  setDemoMode(true);
478
607
  window.location.href = '/'; // Full reload required
479
608
  ```
480
609
 
481
- The `stellar-engine install pwa` scaffolding generates demo files automatically.
482
-
483
610
  ## License
484
611
 
485
612
  Private -- not yet published under an open-source license.