remult-sqlite-s3 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.
@@ -0,0 +1,285 @@
1
+ import { SqlDatabase } from 'remult';
2
+ import { Options, Database } from 'better-sqlite3';
3
+ import { SqliteCoreDataProvider } from 'remult/remult-sqlite-core-js';
4
+
5
+ /**
6
+ * Public types for BetterSqlite3S3DataProvider
7
+ */
8
+ interface BetterSqlite3S3Options {
9
+ /** Path to SQLite database file */
10
+ file: string;
11
+ /** better-sqlite3 options (optional) */
12
+ sqliteOptions?: {
13
+ readonly?: boolean;
14
+ fileMustExist?: boolean;
15
+ timeout?: number;
16
+ verbose?: (message?: unknown, ...additionalArgs: unknown[]) => void;
17
+ nativeBinding?: string;
18
+ };
19
+ s3: {
20
+ /** S3 bucket name */
21
+ bucket: string;
22
+ /**
23
+ * Unique database name (required)
24
+ * Used to namespace all S3 keys for this database.
25
+ * Must be unique per database to prevent data conflicts.
26
+ * Example: "myapp-users", "myapp-orders", "prod-main"
27
+ */
28
+ databaseName: string;
29
+ /** Optional key prefix prepended to all S3 keys (e.g., "databases/") */
30
+ keyPrefix?: string;
31
+ /** AWS region (default: "us-east-1") */
32
+ region?: string;
33
+ /** Custom endpoint for S3-compatible storage (MinIO, R2, Backblaze, etc.) */
34
+ endpoint?: string;
35
+ /** Explicit credentials (optional - uses AWS SDK default chain if not provided) */
36
+ credentials?: {
37
+ accessKeyId: string;
38
+ secretAccessKey: string;
39
+ };
40
+ /** Force path style for S3-compatible storage like MinIO */
41
+ forcePathStyle?: boolean;
42
+ };
43
+ sync?: {
44
+ /**
45
+ * Sync mode:
46
+ * - 'async': Fast writes, eventual durability (default)
47
+ * - 'sync': Wait for S3 confirmation on every write
48
+ */
49
+ mode?: 'async' | 'sync';
50
+ /** Sync WAL to S3 when it reaches this size in bytes (default: 1MB) */
51
+ walThreshold?: number;
52
+ /** Create full snapshot every N milliseconds (default: 5 minutes) */
53
+ snapshotInterval?: number;
54
+ /** S3 operation retry count (default: 3) */
55
+ maxRetries?: number;
56
+ /** Lock TTL in milliseconds (default: 60000) */
57
+ lockTtl?: number;
58
+ /** Lock refresh interval in milliseconds (default: 30000) */
59
+ lockRefresh?: number;
60
+ /** Force acquire lock even if another process holds it (use only in development) */
61
+ forceLock?: boolean;
62
+ };
63
+ /** Called on sync events (uploads, recoveries, errors) */
64
+ onEvent?: (event: S3SyncEvent) => void;
65
+ /** Enable verbose logging */
66
+ verbose?: boolean;
67
+ }
68
+ type S3SyncEvent = {
69
+ type: 'wal_uploaded';
70
+ generation: string;
71
+ index: number;
72
+ size: number;
73
+ } | {
74
+ type: 'snapshot_uploaded';
75
+ generation: string;
76
+ size: number;
77
+ } | {
78
+ type: 'recovery_started';
79
+ generation: string;
80
+ } | {
81
+ type: 'recovery_completed';
82
+ generation: string;
83
+ segments: number;
84
+ } | {
85
+ type: 'lock_acquired';
86
+ lockId: string;
87
+ } | {
88
+ type: 'lock_lost';
89
+ reason: string;
90
+ } | {
91
+ type: 'sync_error';
92
+ error: Error;
93
+ context: string;
94
+ willRetry: boolean;
95
+ } | {
96
+ type: 'initialized';
97
+ generation: string;
98
+ recovered: boolean;
99
+ };
100
+ interface S3Config {
101
+ bucket: string;
102
+ databaseName: string;
103
+ keyPrefix: string;
104
+ region: string;
105
+ endpoint?: string;
106
+ credentials?: {
107
+ accessKeyId: string;
108
+ secretAccessKey: string;
109
+ };
110
+ forcePathStyle?: boolean;
111
+ }
112
+ interface SyncConfig {
113
+ mode: 'async' | 'sync';
114
+ walThreshold: number;
115
+ snapshotInterval: number;
116
+ maxRetries: number;
117
+ lockTtl: number;
118
+ lockRefresh: number;
119
+ forceLock: boolean;
120
+ }
121
+
122
+ /**
123
+ * S3 Sync Manager
124
+ * Coordinates lock, WAL, and snapshot operations
125
+ */
126
+
127
+ interface S3SyncManagerOptions {
128
+ dbPath: string;
129
+ sqliteOptions?: Options;
130
+ s3: S3Config;
131
+ sync: SyncConfig;
132
+ onEvent: (event: S3SyncEvent) => void;
133
+ verbose: boolean;
134
+ }
135
+ declare class S3SyncManager {
136
+ private s3;
137
+ private lock;
138
+ private walManager;
139
+ private recoveryManager;
140
+ private db;
141
+ private dbPath;
142
+ private sqliteOptions?;
143
+ private syncConfig;
144
+ private onEvent;
145
+ private verbose;
146
+ private generation;
147
+ private snapshotTimer;
148
+ private closed;
149
+ constructor(options: S3SyncManagerOptions);
150
+ /**
151
+ * Initialize the sync manager
152
+ * - Acquire lock
153
+ * - Recover from S3 if needed
154
+ * - Open database
155
+ * - Start sync timers
156
+ */
157
+ initialize(): Promise<Database>;
158
+ /**
159
+ * Called after write operations
160
+ */
161
+ onWrite(): Promise<void>;
162
+ /**
163
+ * Force sync WAL to S3
164
+ */
165
+ syncWal(): Promise<void>;
166
+ /**
167
+ * Force full sync (WAL + check for snapshot)
168
+ */
169
+ sync(): Promise<void>;
170
+ /**
171
+ * Create a full snapshot
172
+ */
173
+ snapshot(): Promise<void>;
174
+ /**
175
+ * Close the sync manager
176
+ */
177
+ close(): Promise<void>;
178
+ /**
179
+ * Get the current generation
180
+ */
181
+ getGeneration(): string;
182
+ private createGenerationInfo;
183
+ private updateGenerationInfo;
184
+ private startSnapshotTimer;
185
+ private stopSnapshotTimer;
186
+ private log;
187
+ }
188
+
189
+ /**
190
+ * BetterSqlite3S3DataProvider
191
+ * A Remult data provider that wraps better-sqlite3 with automatic S3 synchronization
192
+ */
193
+
194
+ /**
195
+ * BetterSqlite3S3DataProvider
196
+ *
197
+ * A drop-in replacement for BetterSqlite3DataProvider with automatic S3 synchronization.
198
+ *
199
+ * Usage:
200
+ * ```typescript
201
+ * const provider = new BetterSqlite3S3DataProvider({
202
+ * file: './mydb.sqlite',
203
+ * s3: {
204
+ * bucket: 'my-bucket',
205
+ * databaseName: 'myapp-main', // Required: unique name for this database
206
+ * keyPrefix: 'databases/', // Optional: prefix for all keys
207
+ * },
208
+ * });
209
+ * await provider.init();
210
+ * const dataProvider = new SqlDatabase(provider);
211
+ * ```
212
+ */
213
+ declare class BetterSqlite3S3DataProvider extends SqliteCoreDataProvider {
214
+ private db;
215
+ private syncManager;
216
+ private options;
217
+ private initialized;
218
+ private initPromise;
219
+ constructor(options: BetterSqlite3S3Options);
220
+ /**
221
+ * Async initialization - must be called before use
222
+ */
223
+ init(): Promise<void>;
224
+ private doInit;
225
+ /**
226
+ * Get the underlying database (for advanced use cases)
227
+ */
228
+ get rawDatabase(): Database;
229
+ /**
230
+ * Get the sync manager (for manual sync control)
231
+ */
232
+ get sync(): S3SyncManager;
233
+ /**
234
+ * Force sync to S3
235
+ */
236
+ forceSync(): Promise<void>;
237
+ /**
238
+ * Force a full snapshot
239
+ */
240
+ snapshot(): Promise<void>;
241
+ /**
242
+ * Close the database and release resources
243
+ */
244
+ close(): Promise<void>;
245
+ /**
246
+ * Check if the provider is initialized
247
+ */
248
+ get isInitialized(): boolean;
249
+ /**
250
+ * Get the current generation ID
251
+ */
252
+ get generation(): string;
253
+ private ensureInitialized;
254
+ private applyDefaults;
255
+ }
256
+
257
+ /**
258
+ * remult-sqlite-s3
259
+ * A Remult data provider that syncs SQLite to S3 for serverless deployments
260
+ */
261
+
262
+ /**
263
+ * Creates an S3-synced SQLite data provider for Remult.
264
+ * Handles initialization and graceful shutdown automatically.
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * import { remultApi } from 'remult/remult-express'
269
+ * import { createS3DataProvider } from 'remult-sqlite-s3'
270
+ *
271
+ * export const api = remultApi({
272
+ * dataProvider: createS3DataProvider({
273
+ * file: './mydb.sqlite',
274
+ * s3: {
275
+ * bucket: 'my-bucket',
276
+ * databaseName: 'my-database',
277
+ * },
278
+ * }),
279
+ * entities: [Task],
280
+ * })
281
+ * ```
282
+ */
283
+ declare function createS3DataProvider(options: BetterSqlite3S3Options): () => Promise<SqlDatabase>;
284
+
285
+ export { BetterSqlite3S3DataProvider, type BetterSqlite3S3Options, type S3Config, type S3SyncEvent, S3SyncManager, type SyncConfig, createS3DataProvider };