supastash 0.1.0

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 (232) hide show
  1. package/README.md +250 -0
  2. package/dist/core/config/index.d.ts +90 -0
  3. package/dist/core/config/index.d.ts.map +1 -0
  4. package/dist/core/config/index.js +111 -0
  5. package/dist/core/schemaManager/index.d.ts +19 -0
  6. package/dist/core/schemaManager/index.d.ts.map +1 -0
  7. package/dist/core/schemaManager/index.js +53 -0
  8. package/dist/db/adapters/expo_sqlite.d.ts +3 -0
  9. package/dist/db/adapters/expo_sqlite.d.ts.map +1 -0
  10. package/dist/db/adapters/expo_sqlite.js +21 -0
  11. package/dist/db/adapters/rn_nitro.d.ts +3 -0
  12. package/dist/db/adapters/rn_nitro.d.ts.map +1 -0
  13. package/dist/db/adapters/rn_nitro.js +22 -0
  14. package/dist/db/adapters/rn_sqlite_storage.d.ts +3 -0
  15. package/dist/db/adapters/rn_sqlite_storage.d.ts.map +1 -0
  16. package/dist/db/adapters/rn_sqlite_storage.js +22 -0
  17. package/dist/db/dbErrorMsg.d.ts +2 -0
  18. package/dist/db/dbErrorMsg.d.ts.map +1 -0
  19. package/dist/db/dbErrorMsg.js +28 -0
  20. package/dist/db/dbInitializer.d.ts +7 -0
  21. package/dist/db/dbInitializer.d.ts.map +1 -0
  22. package/dist/db/dbInitializer.js +33 -0
  23. package/dist/hooks/supastashData/addPayloadToUI.d.ts +2 -0
  24. package/dist/hooks/supastashData/addPayloadToUI.d.ts.map +1 -0
  25. package/dist/hooks/supastashData/addPayloadToUI.js +36 -0
  26. package/dist/hooks/supastashData/dataState.d.ts +8 -0
  27. package/dist/hooks/supastashData/dataState.d.ts.map +1 -0
  28. package/dist/hooks/supastashData/dataState.js +7 -0
  29. package/dist/hooks/supastashData/eventQueues.d.ts +5 -0
  30. package/dist/hooks/supastashData/eventQueues.d.ts.map +1 -0
  31. package/dist/hooks/supastashData/eventQueues.js +54 -0
  32. package/dist/hooks/supastashData/fetchCalls.d.ts +9 -0
  33. package/dist/hooks/supastashData/fetchCalls.d.ts.map +1 -0
  34. package/dist/hooks/supastashData/fetchCalls.js +63 -0
  35. package/dist/hooks/supastashData/index.d.ts +53 -0
  36. package/dist/hooks/supastashData/index.d.ts.map +1 -0
  37. package/dist/hooks/supastashData/index.js +95 -0
  38. package/dist/hooks/supastashData/realtimeSubscription.d.ts +5 -0
  39. package/dist/hooks/supastashData/realtimeSubscription.d.ts.map +1 -0
  40. package/dist/hooks/supastashData/realtimeSubscription.js +95 -0
  41. package/dist/hooks/supastashData/registerSub.d.ts +4 -0
  42. package/dist/hooks/supastashData/registerSub.d.ts.map +1 -0
  43. package/dist/hooks/supastashData/registerSub.js +20 -0
  44. package/dist/hooks/supastashLogic.d.ts +19 -0
  45. package/dist/hooks/supastashLogic.d.ts.map +1 -0
  46. package/dist/hooks/supastashLogic.js +67 -0
  47. package/dist/hooks/syncEngine/index.d.ts +9 -0
  48. package/dist/hooks/syncEngine/index.d.ts.map +1 -0
  49. package/dist/hooks/syncEngine/index.js +66 -0
  50. package/dist/hooks/syncEngine/pullFromRemote/index.d.ts +5 -0
  51. package/dist/hooks/syncEngine/pullFromRemote/index.d.ts.map +1 -0
  52. package/dist/hooks/syncEngine/pullFromRemote/index.js +28 -0
  53. package/dist/hooks/syncEngine/pushLocal/index.d.ts +5 -0
  54. package/dist/hooks/syncEngine/pushLocal/index.d.ts.map +1 -0
  55. package/dist/hooks/syncEngine/pushLocal/index.js +36 -0
  56. package/dist/index.d.ts +14 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +10 -0
  59. package/dist/store/tableFilters.d.ts +3 -0
  60. package/dist/store/tableFilters.d.ts.map +1 -0
  61. package/dist/store/tableFilters.js +2 -0
  62. package/dist/types/expoSqlite.types.d.ts +57 -0
  63. package/dist/types/index.d.ts +5 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +1 -0
  66. package/dist/types/query.types.d.ts +153 -0
  67. package/dist/types/realtimeData.types.d.ts +186 -0
  68. package/dist/types/rnNitroSqlite.types.d.ts +101 -0
  69. package/dist/types/rnSqliteStorage.types.d.ts +67 -0
  70. package/dist/types/schemaManager.types.d.ts +44 -0
  71. package/dist/types/supastashConfig.types.d.ts +72 -0
  72. package/dist/types/syncEngine.types.d.ts +12 -0
  73. package/dist/utils/connection.d.ts +2 -0
  74. package/dist/utils/connection.d.ts.map +1 -0
  75. package/dist/utils/connection.js +7 -0
  76. package/dist/utils/events/eventBus.d.ts +3 -0
  77. package/dist/utils/events/eventBus.d.ts.map +1 -0
  78. package/dist/utils/events/eventBus.js +4 -0
  79. package/dist/utils/fetchData/buildFilter.d.ts +7 -0
  80. package/dist/utils/fetchData/buildFilter.d.ts.map +1 -0
  81. package/dist/utils/fetchData/buildFilter.js +13 -0
  82. package/dist/utils/fetchData/createTable.d.ts +8 -0
  83. package/dist/utils/fetchData/createTable.d.ts.map +1 -0
  84. package/dist/utils/fetchData/createTable.js +48 -0
  85. package/dist/utils/fetchData/deleteData.d.ts +3 -0
  86. package/dist/utils/fetchData/deleteData.d.ts.map +1 -0
  87. package/dist/utils/fetchData/deleteData.js +25 -0
  88. package/dist/utils/fetchData/fetchLocalData.d.ts +9 -0
  89. package/dist/utils/fetchData/fetchLocalData.d.ts.map +1 -0
  90. package/dist/utils/fetchData/fetchLocalData.js +51 -0
  91. package/dist/utils/fetchData/getKeyType.d.ts +7 -0
  92. package/dist/utils/fetchData/getKeyType.d.ts.map +1 -0
  93. package/dist/utils/fetchData/getKeyType.js +25 -0
  94. package/dist/utils/fetchData/initialFetch.d.ts +3 -0
  95. package/dist/utils/fetchData/initialFetch.d.ts.map +1 -0
  96. package/dist/utils/fetchData/initialFetch.js +20 -0
  97. package/dist/utils/fetchData/realTimeCall.d.ts +5 -0
  98. package/dist/utils/fetchData/realTimeCall.d.ts.map +1 -0
  99. package/dist/utils/fetchData/realTimeCall.js +43 -0
  100. package/dist/utils/fetchData/realTimeManager.d.ts +40 -0
  101. package/dist/utils/fetchData/realTimeManager.d.ts.map +1 -0
  102. package/dist/utils/fetchData/realTimeManager.js +262 -0
  103. package/dist/utils/fetchData/receiveData.d.ts +3 -0
  104. package/dist/utils/fetchData/receiveData.d.ts.map +1 -0
  105. package/dist/utils/fetchData/receiveData.js +38 -0
  106. package/dist/utils/fetchData/setDataInBatches.d.ts +3 -0
  107. package/dist/utils/fetchData/setDataInBatches.d.ts.map +1 -0
  108. package/dist/utils/fetchData/setDataInBatches.js +20 -0
  109. package/dist/utils/fetchData/validatePayload.d.ts +7 -0
  110. package/dist/utils/fetchData/validatePayload.d.ts.map +1 -0
  111. package/dist/utils/fetchData/validatePayload.js +18 -0
  112. package/dist/utils/genUUID.d.ts +2 -0
  113. package/dist/utils/genUUID.d.ts.map +1 -0
  114. package/dist/utils/genUUID.js +7 -0
  115. package/dist/utils/getTableSchema.d.ts +8 -0
  116. package/dist/utils/getTableSchema.d.ts.map +1 -0
  117. package/dist/utils/getTableSchema.js +28 -0
  118. package/dist/utils/logs.d.ts +7 -0
  119. package/dist/utils/logs.d.ts.map +1 -0
  120. package/dist/utils/logs.js +15 -0
  121. package/dist/utils/query/builder/crud.d.ts +50 -0
  122. package/dist/utils/query/builder/crud.d.ts.map +1 -0
  123. package/dist/utils/query/builder/crud.js +83 -0
  124. package/dist/utils/query/builder/filters.d.ts +145 -0
  125. package/dist/utils/query/builder/filters.d.ts.map +1 -0
  126. package/dist/utils/query/builder/filters.js +223 -0
  127. package/dist/utils/query/builder/index.d.ts +45 -0
  128. package/dist/utils/query/builder/index.d.ts.map +1 -0
  129. package/dist/utils/query/builder/index.js +60 -0
  130. package/dist/utils/query/builder/mainQuery.d.ts +10 -0
  131. package/dist/utils/query/builder/mainQuery.d.ts.map +1 -0
  132. package/dist/utils/query/builder/mainQuery.js +63 -0
  133. package/dist/utils/query/helpers/localDb/getLocalMethod.d.ts +15 -0
  134. package/dist/utils/query/helpers/localDb/getLocalMethod.d.ts.map +1 -0
  135. package/dist/utils/query/helpers/localDb/getLocalMethod.js +24 -0
  136. package/dist/utils/query/helpers/localDb/localQueryBuilder.d.ts +34 -0
  137. package/dist/utils/query/helpers/localDb/localQueryBuilder.d.ts.map +1 -0
  138. package/dist/utils/query/helpers/localDb/localQueryBuilder.js +52 -0
  139. package/dist/utils/query/helpers/mainQueryHelpers.d.ts +12 -0
  140. package/dist/utils/query/helpers/mainQueryHelpers.d.ts.map +1 -0
  141. package/dist/utils/query/helpers/mainQueryHelpers.js +164 -0
  142. package/dist/utils/query/helpers/queryValidator.d.ts +7 -0
  143. package/dist/utils/query/helpers/queryValidator.d.ts.map +1 -0
  144. package/dist/utils/query/helpers/queryValidator.js +27 -0
  145. package/dist/utils/query/helpers/remoteDb/queryFilterBuilder.d.ts +12 -0
  146. package/dist/utils/query/helpers/remoteDb/queryFilterBuilder.d.ts.map +1 -0
  147. package/dist/utils/query/helpers/remoteDb/queryFilterBuilder.js +41 -0
  148. package/dist/utils/query/helpers/remoteDb/queryUtils.d.ts +8 -0
  149. package/dist/utils/query/helpers/remoteDb/queryUtils.d.ts.map +1 -0
  150. package/dist/utils/query/helpers/remoteDb/queryUtils.js +30 -0
  151. package/dist/utils/query/localDbQuery/delete.d.ts +16 -0
  152. package/dist/utils/query/localDbQuery/delete.d.ts.map +1 -0
  153. package/dist/utils/query/localDbQuery/delete.js +54 -0
  154. package/dist/utils/query/localDbQuery/index.d.ts +8 -0
  155. package/dist/utils/query/localDbQuery/index.d.ts.map +1 -0
  156. package/dist/utils/query/localDbQuery/index.js +15 -0
  157. package/dist/utils/query/localDbQuery/insert.d.ts +10 -0
  158. package/dist/utils/query/localDbQuery/insert.d.ts.map +1 -0
  159. package/dist/utils/query/localDbQuery/insert.js +64 -0
  160. package/dist/utils/query/localDbQuery/select.d.ts +13 -0
  161. package/dist/utils/query/localDbQuery/select.d.ts.map +1 -0
  162. package/dist/utils/query/localDbQuery/select.js +41 -0
  163. package/dist/utils/query/localDbQuery/update.d.ts +11 -0
  164. package/dist/utils/query/localDbQuery/update.d.ts.map +1 -0
  165. package/dist/utils/query/localDbQuery/update.js +56 -0
  166. package/dist/utils/query/localDbQuery/upsert.d.ts +9 -0
  167. package/dist/utils/query/localDbQuery/upsert.d.ts.map +1 -0
  168. package/dist/utils/query/localDbQuery/upsert.js +67 -0
  169. package/dist/utils/query/remoteQuery/supabaseQuery.d.ts +8 -0
  170. package/dist/utils/query/remoteQuery/supabaseQuery.d.ts.map +1 -0
  171. package/dist/utils/query/remoteQuery/supabaseQuery.js +124 -0
  172. package/dist/utils/schema/createSyncStatus.d.ts +9 -0
  173. package/dist/utils/schema/createSyncStatus.d.ts.map +1 -0
  174. package/dist/utils/schema/createSyncStatus.js +23 -0
  175. package/dist/utils/schema/wipeTables.d.ts +66 -0
  176. package/dist/utils/schema/wipeTables.d.ts.map +1 -0
  177. package/dist/utils/schema/wipeTables.js +124 -0
  178. package/dist/utils/serializer.d.ts +8 -0
  179. package/dist/utils/serializer.d.ts.map +1 -0
  180. package/dist/utils/serializer.js +32 -0
  181. package/dist/utils/supabaseClientErr.d.ts +2 -0
  182. package/dist/utils/supabaseClientErr.d.ts.map +1 -0
  183. package/dist/utils/supabaseClientErr.js +15 -0
  184. package/dist/utils/sync/getAllTables.d.ts +2 -0
  185. package/dist/utils/sync/getAllTables.d.ts.map +1 -0
  186. package/dist/utils/sync/getAllTables.js +7 -0
  187. package/dist/utils/sync/pullFromRemote/getLastDeletedInfo.d.ts +13 -0
  188. package/dist/utils/sync/pullFromRemote/getLastDeletedInfo.d.ts.map +1 -0
  189. package/dist/utils/sync/pullFromRemote/getLastDeletedInfo.js +29 -0
  190. package/dist/utils/sync/pullFromRemote/getLastPulledInfo.d.ts +13 -0
  191. package/dist/utils/sync/pullFromRemote/getLastPulledInfo.d.ts.map +1 -0
  192. package/dist/utils/sync/pullFromRemote/getLastPulledInfo.js +29 -0
  193. package/dist/utils/sync/pullFromRemote/pullData.d.ts +9 -0
  194. package/dist/utils/sync/pullFromRemote/pullData.d.ts.map +1 -0
  195. package/dist/utils/sync/pullFromRemote/pullData.js +71 -0
  196. package/dist/utils/sync/pullFromRemote/pullDeletedData.d.ts +12 -0
  197. package/dist/utils/sync/pullFromRemote/pullDeletedData.d.ts.map +1 -0
  198. package/dist/utils/sync/pullFromRemote/pullDeletedData.js +69 -0
  199. package/dist/utils/sync/pullFromRemote/stringifyFields.d.ts +2 -0
  200. package/dist/utils/sync/pullFromRemote/stringifyFields.d.ts.map +1 -0
  201. package/dist/utils/sync/pullFromRemote/stringifyFields.js +11 -0
  202. package/dist/utils/sync/pullFromRemote/updateLocalDb.d.ts +14 -0
  203. package/dist/utils/sync/pullFromRemote/updateLocalDb.d.ts.map +1 -0
  204. package/dist/utils/sync/pullFromRemote/updateLocalDb.js +128 -0
  205. package/dist/utils/sync/pushLocal/deleteChunks.d.ts +8 -0
  206. package/dist/utils/sync/pushLocal/deleteChunks.d.ts.map +1 -0
  207. package/dist/utils/sync/pushLocal/deleteChunks.js +51 -0
  208. package/dist/utils/sync/pushLocal/getAllUnsyncedData.d.ts +14 -0
  209. package/dist/utils/sync/pushLocal/getAllUnsyncedData.d.ts.map +1 -0
  210. package/dist/utils/sync/pushLocal/getAllUnsyncedData.js +70 -0
  211. package/dist/utils/sync/pushLocal/parseFields.d.ts +7 -0
  212. package/dist/utils/sync/pushLocal/parseFields.d.ts.map +1 -0
  213. package/dist/utils/sync/pushLocal/parseFields.js +24 -0
  214. package/dist/utils/sync/pushLocal/sendUnsyncedToSupabase.d.ts +6 -0
  215. package/dist/utils/sync/pushLocal/sendUnsyncedToSupabase.d.ts.map +1 -0
  216. package/dist/utils/sync/pushLocal/sendUnsyncedToSupabase.js +43 -0
  217. package/dist/utils/sync/pushLocal/uploadChunk.d.ts +8 -0
  218. package/dist/utils/sync/pushLocal/uploadChunk.d.ts.map +1 -0
  219. package/dist/utils/sync/pushLocal/uploadChunk.js +117 -0
  220. package/dist/utils/sync/refreshTables.d.ts +12 -0
  221. package/dist/utils/sync/refreshTables.d.ts.map +1 -0
  222. package/dist/utils/sync/refreshTables.js +40 -0
  223. package/dist/utils/syncStatus.d.ts +67 -0
  224. package/dist/utils/syncStatus.d.ts.map +1 -0
  225. package/dist/utils/syncStatus.js +97 -0
  226. package/dist/utils/syncUpdate.d.ts +7 -0
  227. package/dist/utils/syncUpdate.d.ts.map +1 -0
  228. package/dist/utils/syncUpdate.js +19 -0
  229. package/dist/utils/tableValidator.d.ts +12 -0
  230. package/dist/utils/tableValidator.d.ts.map +1 -0
  231. package/dist/utils/tableValidator.js +23 -0
  232. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # Supastash
2
+
3
+ **Offline-First Sync Engine for Supabase + React Native.**
4
+
5
+ Supastash makes it effortless to build offline-capable mobile apps using **SQLite for local-first storage** and **Supabase for cloud sync**. Designed for React Native, Supastash handles syncing, conflict resolution, realtime updates, and local querying so you can focus on features, not infrastructure.
6
+
7
+ ---
8
+
9
+ ### 📚 Documentation
10
+
11
+ [Documentation (Coming Soon)](https://...)
12
+
13
+ ---
14
+
15
+ ## 🚀 Features
16
+
17
+ - 🔁 **Two-way sync** with Supabase
18
+ - 💾 **Local-first querying** with React Native SQLite
19
+ - ⚡ **Realtime updates** using Supabase channels
20
+ - 🔌 **Pluggable SQLite adapters** (`expo-sqlite`, `react-native-nitro-sqlite`, `react-native-sqlite-storage`)
21
+ - ✅ **Built-in deduplication**, conflict resolution, and background retries
22
+ - 🧠 Designed to support **event batching**, **job staging**, and fine-grained sync control
23
+
24
+ ---
25
+
26
+ ## 📦 Installation
27
+
28
+ ```bash
29
+ npm install supastash
30
+ # or
31
+ yarn add supastash
32
+ ```
33
+
34
+ ### 📎 Peer Dependencies (You MUST install these)
35
+
36
+ ```bash
37
+ npm install @supabase/supabase-js
38
+ @react-native-community/netinfo
39
+ react
40
+ react-native
41
+
42
+ # Choose one SQLite adapter:
43
+ npm install expo-sqlite
44
+ # or
45
+ npm install react-native-nitro-sqlite
46
+ # or
47
+ npm install react-native-sqlite-storage
48
+ ```
49
+
50
+ > `sqliteClientType` must match your adapter: "expo", "rn-nitro", or "rn-storage"
51
+
52
+ ---
53
+
54
+ ## ⚙️ Setup
55
+
56
+ ```ts
57
+ // lib/supastash.ts
58
+ import { configureSupastash, defineLocalSchema } from "supastash";
59
+ import { supabase } from "./supabase";
60
+ import { openDatabaseAsync } from "expo-sqlite";
61
+
62
+ configureSupastash({
63
+ supabaseClient: supabase,
64
+ dbName: "supastash_db",
65
+ sqliteClient: { openDatabaseAsync },
66
+ sqliteClientType: "expo",
67
+ onSchemaInit: () => {
68
+ defineLocalSchema(
69
+ "users",
70
+ {
71
+ id: "TEXT PRIMARY KEY",
72
+ name: "TEXT",
73
+ email: "TEXT",
74
+ created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
75
+ updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
76
+ },
77
+ true
78
+ );
79
+ },
80
+ debugMode: true,
81
+ syncEngine: {
82
+ push: true,
83
+ pull: false, // ⚠️ Pull sync is disabled by default.
84
+ },
85
+ });
86
+ ```
87
+
88
+ Then initialize early:
89
+
90
+ ```ts
91
+ // _layout.tsx or App.tsx
92
+ import "@/lib/supastash";
93
+
94
+ export default function RootLayout() {
95
+ return <Stack />;
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 🚨 Key Notes
102
+
103
+ - Supabase tables **must** include:
104
+
105
+ - `id`, `created_at`, `updated_at`, `deleted_at`
106
+ - Use `updated_at` for conflict resolution
107
+ - Avoid null primary keys
108
+ - Apply `deleted_at IS NULL` filter in queries
109
+
110
+ - To enable schema reflection, create this Supabase RPC:
111
+
112
+ ```sql
113
+ create or replace function get_column_names(table_name text)
114
+ returns table(column_name text)
115
+ security definer
116
+ as $$
117
+ select column_name
118
+ from information_schema.columns
119
+ where table_schema = 'public'
120
+ and table_name = table_name;
121
+ $$ language sql;
122
+
123
+ grant execute on function get_column_names(text) to anon, authenticated;
124
+ ```
125
+
126
+ ---
127
+
128
+ ## 🧪 Basic Usage
129
+
130
+ ### `useSupatashData` hook
131
+
132
+ ```ts
133
+ import { useSupatashData } from "supastash";
134
+
135
+ type Order = {
136
+ id: string;
137
+ user_id: string;
138
+ deleted_at: string | null;
139
+ updated_at: string;
140
+ created_at: string;
141
+ };
142
+
143
+ const { data: orders, dataMap: ordersMap } = useSupatashData<Order>("orders");
144
+
145
+ // Filtered
146
+ const { userId } = useAuth();
147
+ const { data: userOrders } = useSupatashData<Order>("orders", {
148
+ filter: { column: "user_id", operator: "eq", value: userId },
149
+ shouldFetch: !!userId,
150
+ });
151
+ ```
152
+
153
+ ### `useSupatash`
154
+
155
+ ```ts
156
+ import { useSupatash } from "supastash";
157
+
158
+ const { dbReady } = useSupatash();
159
+ if (!dbReady) return null;
160
+ return <Stack />;
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 📘 API Overview
166
+
167
+ ### `configureSupastash(config)`
168
+
169
+ Initialize sync system.
170
+
171
+ ### `useSupatashData(table, options)`
172
+
173
+ Access data with local cache, syncing, filtering, etc.
174
+
175
+ Returns:
176
+
177
+ ```ts
178
+ {
179
+ data: R[];
180
+ dataMap: Map<string, R>;
181
+ trigger: () => void;
182
+ cancel: () => void;
183
+ }
184
+ ```
185
+
186
+ ### `refreshTable(table: string)` / `refreshAllTables()`
187
+
188
+ Force-refresh any or all table data.
189
+
190
+ ---
191
+
192
+ ## 🔄 Sync Internals
193
+
194
+ - Safe writes with `created_at`, `updated_at`, `deleted_at`
195
+ - Retries with **exponential backoff**
196
+ - Batched inserts, updates, deletes
197
+ - Real-time changes are applied directly to local cache
198
+
199
+ ---
200
+
201
+ ## 📁 Project Structure
202
+
203
+ ```
204
+ src/
205
+ ├─ core/ # Supabase + sync logic
206
+ ├─ hooks/ # Main React hooks
207
+ ├─ types/ # Type definitions
208
+ ├─ utils/ # Helper utilities
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 🔧 Testing
214
+
215
+ ```bash
216
+ yarn test
217
+ ```
218
+
219
+ Uses `vitest` for unit testing.
220
+
221
+ ---
222
+
223
+ ## 🧱 Roadmap
224
+
225
+ - [ ] Per-table sync intervals
226
+ - [ ] Smart job resumption
227
+ - [ ] Sync versioning
228
+ - [ ] Live change audit logs
229
+
230
+ ---
231
+
232
+ ## 🤝 Contributing
233
+
234
+ ```bash
235
+ yarn dev
236
+ ```
237
+
238
+ Open a PR with tests and typed signatures. PRs welcome.
239
+
240
+ ---
241
+
242
+ ## 📜 License
243
+
244
+ MIT License © Ezekiel Akpan
245
+
246
+ ---
247
+
248
+ ## 💬 Questions?
249
+
250
+ Open an issue or reach out on [X @0xZekeA](https://x.com/0xZekeA)
@@ -0,0 +1,90 @@
1
+ import { SupastashConfig, SupastashSQLiteClientTypes } from "../../types/supastashConfig.types";
2
+ /**
3
+ * Initializes the Supastash client.
4
+ *
5
+ * This must be called **once** during app startup (e.g., in `_layout.tsx` or `App.tsx`)
6
+ * before using any Supastash hooks or features.
7
+ *
8
+ * ⚠️ Pull sync is **disabled by default** to avoid unfiltered data fetches.
9
+ * Enable it only if you've configured **Row Level Security (RLS)** on your Supabase tables.
10
+ * The `useSupatashData` hook always performs filtered pull queries when `filter` is provided in the `options`, making it safe to use even when pull is disabled globally.
11
+ *
12
+ * @example
13
+ * import { supabase } from "./supabase";
14
+ * import { openDatabaseAsync } from "expo-sqlite";
15
+ * import { configureSupastash, defineLocalSchema } from "supastash";
16
+ *
17
+ * configureSupastash({
18
+ * dbName: "supastash_db",
19
+ * supabaseClient: supabase,
20
+ * sqliteClient: { openDatabaseAsync },
21
+ * sqliteClientType: "expo",
22
+ * onSchemaInit: () => {
23
+ * defineLocalSchema("users", {
24
+ * id: "TEXT PRIMARY KEY",
25
+ * name: "TEXT",
26
+ * email: "TEXT",
27
+ * created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
28
+ * updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
29
+ * });
30
+ * },
31
+ * });
32
+ *
33
+ * @param config - Configuration options for Supastash
34
+ * @param config.dbName - SQLite database name (default: `"supastash_db"`)
35
+ * @param config.supabaseClient - Supabase client instance (**required**)
36
+ * @param config.sqliteClient - SQLite client adapter (**required**)
37
+ * @param config.sqliteClientType - SQLite engine: `"expo" | "rn-storage" | "rn-nitro"` (**required**)
38
+ * @param config.onSchemaInit - Optional callback to define local table schemas
39
+ * @param config.debugMode - Enables debug logging (default: `false`)
40
+ * @param config.listeners - Max number of active Realtime subscriptions (default: `250`)
41
+ * @param config.excludeTables - Tables to exclude from sync (default: `{ pull: [], push: [] }`)
42
+ * @param config.pollingInterval - Polling interval for sync (default: `{ pull: 30000, push: 30000 }`)
43
+ * @param config.syncEngine - Control pull/push sync behavior (`push: true`, `pull: false` by default, `useFiltersFromStore: true` by default)
44
+ */
45
+ export declare function configureSupastash<T extends SupastashSQLiteClientTypes>(config: SupastashConfig<T> & {
46
+ sqliteClientType: T;
47
+ }): void;
48
+ /**
49
+ * Returns the current Supastash configuration.
50
+ *
51
+ * Throws an error if `configureSupastash` has not been called.
52
+ *
53
+ * @returns {SupastashConfig<T>} The active Supastash configuration
54
+ *
55
+ * @throws Will throw if Supastash is not configured or if essential values like
56
+ * `supabaseClient` or `sqliteClient` are missing.
57
+ *
58
+ * @example
59
+ * const config = getSupastashConfig();
60
+ * const dbName = config.dbName;
61
+ */
62
+ export declare function getSupastashConfig<T extends SupastashSQLiteClientTypes>(): SupastashConfig<T>;
63
+ /**
64
+ * Wrapper for `configureSupastash` that takes a strongly typed object containing
65
+ * the Supastash config, including optional schema initialization.
66
+ *
67
+ * Useful for abstracting config logic into a central setup file.
68
+ *
69
+ * @param config.dbOptions - The Supastash configuration object
70
+ * @param config.dbOptions.onSchemaInit - Optional callback to define the local schema
71
+ *
72
+ * @example
73
+ * defineSupastashConfig({
74
+ * dbOptions: {
75
+ * dbName: "supastash_db",
76
+ * supabaseClient,
77
+ * sqliteClient,
78
+ * sqliteClientType: "expo",
79
+ * onSchemaInit: () => {
80
+ * defineLocalSchema("orders", { id: "TEXT PRIMARY KEY", status: "TEXT" });
81
+ * },
82
+ * },
83
+ * });
84
+ */
85
+ export declare function defineSupastashConfig<T extends SupastashSQLiteClientTypes>(config: {
86
+ dbOptions: SupastashConfig<T> & {
87
+ onSchemaInit?: () => void;
88
+ };
89
+ }): void;
90
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,0BAA0B,EAC3B,MAAM,mCAAmC,CAAC;AAuB3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,0BAA0B,EACrE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG;IAAE,gBAAgB,EAAE,CAAC,CAAA;CAAE,QAOrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,SAAS,0BAA0B,KACjC,eAAe,CAAC,CAAC,CAAC,CAEtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS,0BAA0B,EACpC,MAAM,EAAE;IACR,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG;QAC9B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;KAC3B,CAAC;CACH,QAEA"}
@@ -0,0 +1,111 @@
1
+ let _config = {
2
+ dbName: "supastash_db",
3
+ supabaseClient: null,
4
+ sqliteClient: null,
5
+ sqliteClientType: null,
6
+ excludeTables: { pull: [], push: [] },
7
+ pollingInterval: {
8
+ pull: 30000,
9
+ push: 30000,
10
+ },
11
+ syncEngine: {
12
+ push: true,
13
+ pull: false,
14
+ useFiltersFromStore: true,
15
+ },
16
+ listeners: 250,
17
+ debugMode: false,
18
+ };
19
+ let _configured = false;
20
+ /**
21
+ * Initializes the Supastash client.
22
+ *
23
+ * This must be called **once** during app startup (e.g., in `_layout.tsx` or `App.tsx`)
24
+ * before using any Supastash hooks or features.
25
+ *
26
+ * ⚠️ Pull sync is **disabled by default** to avoid unfiltered data fetches.
27
+ * Enable it only if you've configured **Row Level Security (RLS)** on your Supabase tables.
28
+ * The `useSupatashData` hook always performs filtered pull queries when `filter` is provided in the `options`, making it safe to use even when pull is disabled globally.
29
+ *
30
+ * @example
31
+ * import { supabase } from "./supabase";
32
+ * import { openDatabaseAsync } from "expo-sqlite";
33
+ * import { configureSupastash, defineLocalSchema } from "supastash";
34
+ *
35
+ * configureSupastash({
36
+ * dbName: "supastash_db",
37
+ * supabaseClient: supabase,
38
+ * sqliteClient: { openDatabaseAsync },
39
+ * sqliteClientType: "expo",
40
+ * onSchemaInit: () => {
41
+ * defineLocalSchema("users", {
42
+ * id: "TEXT PRIMARY KEY",
43
+ * name: "TEXT",
44
+ * email: "TEXT",
45
+ * created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
46
+ * updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
47
+ * });
48
+ * },
49
+ * });
50
+ *
51
+ * @param config - Configuration options for Supastash
52
+ * @param config.dbName - SQLite database name (default: `"supastash_db"`)
53
+ * @param config.supabaseClient - Supabase client instance (**required**)
54
+ * @param config.sqliteClient - SQLite client adapter (**required**)
55
+ * @param config.sqliteClientType - SQLite engine: `"expo" | "rn-storage" | "rn-nitro"` (**required**)
56
+ * @param config.onSchemaInit - Optional callback to define local table schemas
57
+ * @param config.debugMode - Enables debug logging (default: `false`)
58
+ * @param config.listeners - Max number of active Realtime subscriptions (default: `250`)
59
+ * @param config.excludeTables - Tables to exclude from sync (default: `{ pull: [], push: [] }`)
60
+ * @param config.pollingInterval - Polling interval for sync (default: `{ pull: 30000, push: 30000 }`)
61
+ * @param config.syncEngine - Control pull/push sync behavior (`push: true`, `pull: false` by default, `useFiltersFromStore: true` by default)
62
+ */
63
+ export function configureSupastash(config) {
64
+ _config = {
65
+ ..._config,
66
+ ...config,
67
+ };
68
+ _configured = true;
69
+ }
70
+ /**
71
+ * Returns the current Supastash configuration.
72
+ *
73
+ * Throws an error if `configureSupastash` has not been called.
74
+ *
75
+ * @returns {SupastashConfig<T>} The active Supastash configuration
76
+ *
77
+ * @throws Will throw if Supastash is not configured or if essential values like
78
+ * `supabaseClient` or `sqliteClient` are missing.
79
+ *
80
+ * @example
81
+ * const config = getSupastashConfig();
82
+ * const dbName = config.dbName;
83
+ */
84
+ export function getSupastashConfig() {
85
+ return _config;
86
+ }
87
+ /**
88
+ * Wrapper for `configureSupastash` that takes a strongly typed object containing
89
+ * the Supastash config, including optional schema initialization.
90
+ *
91
+ * Useful for abstracting config logic into a central setup file.
92
+ *
93
+ * @param config.dbOptions - The Supastash configuration object
94
+ * @param config.dbOptions.onSchemaInit - Optional callback to define the local schema
95
+ *
96
+ * @example
97
+ * defineSupastashConfig({
98
+ * dbOptions: {
99
+ * dbName: "supastash_db",
100
+ * supabaseClient,
101
+ * sqliteClient,
102
+ * sqliteClientType: "expo",
103
+ * onSchemaInit: () => {
104
+ * defineLocalSchema("orders", { id: "TEXT PRIMARY KEY", status: "TEXT" });
105
+ * },
106
+ * },
107
+ * });
108
+ */
109
+ export function defineSupastashConfig(config) {
110
+ configureSupastash(config.dbOptions);
111
+ }
@@ -0,0 +1,19 @@
1
+ import { LocalSchemaDefinition } from "../../types/schemaManager.types";
2
+ /**
3
+ * Defines the schema for a local table manually
4
+ *
5
+ * @example
6
+ * defineLocalSchema("users", {
7
+ * id: "TEXT NOT NULL",
8
+ * name: "TEXT NOT NULL",
9
+ * email: "TEXT NOT NULL",
10
+ * }, true // deletes previous schema if true. Must be true if schema already exists
11
+ * // ⚠️ Living option as true will continually delete table on load.
12
+ * );
13
+ *
14
+ * @param tableName - The name of the table
15
+ * @param schema - The schema for the table
16
+ * @param deletePreviousSchema - Whether to delete the previous schema. Default(false)
17
+ */
18
+ export declare function defineLocalSchema(tableName: string, schema: LocalSchemaDefinition, deletePreviousSchema?: boolean): Promise<void>;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/schemaManager/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAIxE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,qBAAqB,EAC7B,oBAAoB,UAAQ,iBA6C7B"}
@@ -0,0 +1,53 @@
1
+ import { getSupastashDb } from "../../db/dbInitializer";
2
+ import { clearSchemaCache } from "../../utils/getTableSchema";
3
+ import log from "../../utils/logs";
4
+ /**
5
+ * Defines the schema for a local table manually
6
+ *
7
+ * @example
8
+ * defineLocalSchema("users", {
9
+ * id: "TEXT NOT NULL",
10
+ * name: "TEXT NOT NULL",
11
+ * email: "TEXT NOT NULL",
12
+ * }, true // deletes previous schema if true. Must be true if schema already exists
13
+ * // ⚠️ Living option as true will continually delete table on load.
14
+ * );
15
+ *
16
+ * @param tableName - The name of the table
17
+ * @param schema - The schema for the table
18
+ * @param deletePreviousSchema - Whether to delete the previous schema. Default(false)
19
+ */
20
+ export async function defineLocalSchema(tableName, schema, deletePreviousSchema = false) {
21
+ if (!schema.id) {
22
+ throw new Error(`'id' of type UUID column is required for table ${tableName}`);
23
+ }
24
+ try {
25
+ const db = await getSupastashDb();
26
+ // Include the columns that must be in the schema
27
+ const safeSchema = {
28
+ ...schema,
29
+ created_at: "TEXT NOT NULL",
30
+ updated_at: "TEXT NOT NULL",
31
+ synced_at: "TEXT DEFAULT NULL",
32
+ deleted_at: "TEXT DEFAULT NULL",
33
+ };
34
+ const schemaString = Object.entries(safeSchema)
35
+ .map(([key, value]) => `${key} ${value}`)
36
+ .join(", ");
37
+ const sql = `CREATE TABLE IF NOT EXISTS ${tableName} (${schemaString});`;
38
+ if (deletePreviousSchema) {
39
+ const dropSql = `DROP TABLE IF EXISTS ${tableName}`;
40
+ const clearSyncStatusSql = `DELETE FROM supastash_sync_status WHERE table_name = '${tableName}'`;
41
+ const clearDeleteStatusSql = `DELETE FROM supastash_deleted_status WHERE table_name = '${tableName}'`;
42
+ await db.execAsync(dropSql);
43
+ await db.execAsync(clearSyncStatusSql);
44
+ await db.execAsync(clearDeleteStatusSql);
45
+ await clearSchemaCache(tableName);
46
+ log(`[Supastash] Dropped table ${tableName}`);
47
+ }
48
+ await db.execAsync(sql);
49
+ }
50
+ catch (error) {
51
+ console.error(`[Supastash] Error defining schema for table ${tableName}`, error);
52
+ }
53
+ }
@@ -0,0 +1,3 @@
1
+ import { SupastashSQLiteAdapter } from "../../types/supastashConfig.types";
2
+ export declare const SQLiteAdapterExpo: SupastashSQLiteAdapter;
3
+ //# sourceMappingURL=expo_sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expo_sqlite.d.ts","sourceRoot":"","sources":["../../../src/db/adapters/expo_sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAEvB,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,iBAAiB,EAAE,sBA8B/B,CAAC"}
@@ -0,0 +1,21 @@
1
+ export const SQLiteAdapterExpo = {
2
+ async openDatabaseAsync(name, sqliteClient) {
3
+ const db = await sqliteClient.openDatabaseAsync(name);
4
+ return {
5
+ runAsync: async (sql, params) => {
6
+ await db.runAsync(sql, params ?? []);
7
+ },
8
+ execAsync: async (statement) => {
9
+ await db.execAsync(statement);
10
+ },
11
+ getAllAsync: async (sql, params) => {
12
+ const result = await db.getAllAsync(sql, params ?? []);
13
+ return result ?? [];
14
+ },
15
+ getFirstAsync: async (sql, params) => {
16
+ const result = await db.getFirstAsync(sql, params ?? []);
17
+ return result ?? null;
18
+ },
19
+ };
20
+ },
21
+ };
@@ -0,0 +1,3 @@
1
+ import { SupastashSQLiteAdapter } from "../../types/supastashConfig.types";
2
+ export declare const SQLiteAdapterNitro: SupastashSQLiteAdapter;
3
+ //# sourceMappingURL=rn_nitro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rn_nitro.d.ts","sourceRoot":"","sources":["../../../src/db/adapters/rn_nitro.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAEvB,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,kBAAkB,EAAE,sBA+BhC,CAAC"}
@@ -0,0 +1,22 @@
1
+ export const SQLiteAdapterNitro = {
2
+ async openDatabaseAsync(name, sqliteClient) {
3
+ const db = await sqliteClient.open({ name, location: "default" });
4
+ return {
5
+ runAsync: async (sql, params) => {
6
+ await db.executeAsync(sql, params ?? []);
7
+ },
8
+ execAsync: async (statement) => {
9
+ await db.executeAsync(statement);
10
+ },
11
+ getAllAsync: async (sql, params) => {
12
+ const result = await db.executeAsync(sql, params ?? []);
13
+ const mainResult = result.rows?._array ?? [];
14
+ return mainResult;
15
+ },
16
+ getFirstAsync: async (sql, params) => {
17
+ const result = await db.executeAsync(sql, params ?? []);
18
+ return result.rows?._array?.[0] ?? null;
19
+ },
20
+ };
21
+ },
22
+ };
@@ -0,0 +1,3 @@
1
+ import { SupastashSQLiteAdapter } from "../../types/supastashConfig.types";
2
+ export declare const SQLiteAdapterStorage: SupastashSQLiteAdapter;
3
+ //# sourceMappingURL=rn_sqlite_storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rn_sqlite_storage.d.ts","sourceRoot":"","sources":["../../../src/db/adapters/rn_sqlite_storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAEvB,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,oBAAoB,EAAE,sBA+BlC,CAAC"}
@@ -0,0 +1,22 @@
1
+ export const SQLiteAdapterStorage = {
2
+ async openDatabaseAsync(name, sqliteClient) {
3
+ const db = await sqliteClient.openDatabase({ name });
4
+ return {
5
+ runAsync: async (sql, params) => {
6
+ await db.executeSql(sql, params ?? []);
7
+ },
8
+ execAsync: async (statement) => {
9
+ await db.executeSql(statement);
10
+ },
11
+ getAllAsync: async (sql, params) => {
12
+ const result = await db.executeSql(sql, params ?? []);
13
+ const mainResult = result.map((r, index) => r.rows.item(index));
14
+ return mainResult ?? [];
15
+ },
16
+ getFirstAsync: async (sql, params) => {
17
+ const result = await db.executeSql(sql, params ?? []);
18
+ return result[0].rows.item(0) ?? null;
19
+ },
20
+ };
21
+ },
22
+ };
@@ -0,0 +1,2 @@
1
+ export declare const supastashDbErrorMsg: string;
2
+ //# sourceMappingURL=dbErrorMsg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbErrorMsg.d.ts","sourceRoot":"","sources":["../../src/db/dbErrorMsg.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,QA4BxB,CAAC"}
@@ -0,0 +1,28 @@
1
+ export const supastashDbErrorMsg = "SQLite client not found. Please provide a SQLite client in the config. /n/n" +
2
+ "Supastash supports the following SQLite clients: \n" +
3
+ " - expo-sqlite \n" +
4
+ " - react-native-nitro-sqlite \n" +
5
+ " - react-native-sqlite-storage \n" +
6
+ "\n\n" +
7
+ "Add a SQLite client to your config and set the sqliteClientType to the client you are using. \n" +
8
+ "Example: \n" +
9
+ "import { openDatabaseAsync } from 'expo-sqlite';\n" +
10
+ "configureSupastash({ \n" +
11
+ " sqliteClient: { openDatabaseAsync }, \n" +
12
+ " sqliteClientType: 'expo', \n" +
13
+ "});" +
14
+ "\n\n" +
15
+ "or \n" +
16
+ "import { open } from 'react-native-nitro-sqlite';\n" +
17
+ "configureSupastash({ \n" +
18
+ " sqliteClient: { open }, \n" +
19
+ " sqliteClientType: 'rn-nitro', \n" +
20
+ "});" +
21
+ "\n\n" +
22
+ "or \n" +
23
+ "import { openDatabaseAsync } from 'react-native-sqlite-storage';\n" +
24
+ "configureSupastash({ \n" +
25
+ " sqliteClient: { openDatabaseAsync }, \n" +
26
+ " sqliteClientType: 'rn-storage', \n" +
27
+ "});" +
28
+ "\n\n";
@@ -0,0 +1,7 @@
1
+ import { SupastashSQLiteDatabase } from "../types/supastashConfig.types";
2
+ /**
3
+ * Gets the supastash database
4
+ * @returns The supastash database
5
+ */
6
+ export declare function getSupastashDb(): Promise<SupastashSQLiteDatabase>;
7
+ //# sourceMappingURL=dbInitializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dbInitializer.d.ts","sourceRoot":"","sources":["../../src/db/dbInitializer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAQzE;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAsBvE"}