remult-sqlite-github 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.
- package/LICENSE +21 -0
- package/README.md +326 -0
- package/dist/index.cjs +1100 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +487 -0
- package/dist/index.d.ts +487 -0
- package/dist/index.js +1068 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { SqlImplementation, SqlCommand, EntityMetadata, FieldMetadata, SqlDatabase } from 'remult';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Database type from better-sqlite3
|
|
6
|
+
*/
|
|
7
|
+
type DatabaseType = Database.Database;
|
|
8
|
+
/**
|
|
9
|
+
* SQLite configuration options
|
|
10
|
+
*/
|
|
11
|
+
interface SqliteOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Enable foreign key constraints
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
foreignKeys?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* SQLite busy timeout in milliseconds
|
|
19
|
+
* @default 5000
|
|
20
|
+
*/
|
|
21
|
+
busyTimeout?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Additional PRAGMA statements to execute on connection
|
|
24
|
+
*/
|
|
25
|
+
pragmas?: Record<string, string | number | boolean>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* GitHub authentication configuration
|
|
29
|
+
*/
|
|
30
|
+
interface GitHubAuthConfig {
|
|
31
|
+
/**
|
|
32
|
+
* Personal Access Token for authentication
|
|
33
|
+
* Use either token OR appId/privateKey/installationId
|
|
34
|
+
*/
|
|
35
|
+
token?: string;
|
|
36
|
+
/**
|
|
37
|
+
* GitHub App ID for app-based authentication
|
|
38
|
+
*/
|
|
39
|
+
appId?: number;
|
|
40
|
+
/**
|
|
41
|
+
* GitHub App private key (PEM format)
|
|
42
|
+
*/
|
|
43
|
+
privateKey?: string;
|
|
44
|
+
/**
|
|
45
|
+
* GitHub App installation ID
|
|
46
|
+
*/
|
|
47
|
+
installationId?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* GitHub repository configuration
|
|
51
|
+
*/
|
|
52
|
+
interface GitHubRepoConfig extends GitHubAuthConfig {
|
|
53
|
+
/**
|
|
54
|
+
* Repository owner (username or organization)
|
|
55
|
+
*/
|
|
56
|
+
owner: string;
|
|
57
|
+
/**
|
|
58
|
+
* Repository name
|
|
59
|
+
*/
|
|
60
|
+
repo: string;
|
|
61
|
+
/**
|
|
62
|
+
* Branch to use for storage
|
|
63
|
+
* @default "main"
|
|
64
|
+
*/
|
|
65
|
+
branch?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Path prefix in repository
|
|
68
|
+
* @default ""
|
|
69
|
+
*/
|
|
70
|
+
path?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Sync configuration options
|
|
74
|
+
*/
|
|
75
|
+
interface SyncConfig {
|
|
76
|
+
/**
|
|
77
|
+
* Interval between automatic snapshots in milliseconds
|
|
78
|
+
* @default 30000 (30 seconds)
|
|
79
|
+
*/
|
|
80
|
+
snapshotInterval?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Only create snapshot if there are changes
|
|
83
|
+
* @default true
|
|
84
|
+
*/
|
|
85
|
+
snapshotOnChange?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Enable WAL mode segmentation for incremental uploads
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
enableWal?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* WAL file size threshold before creating new segment (bytes)
|
|
93
|
+
* Only applies when enableWal is true
|
|
94
|
+
* @default 1048576 (1MB)
|
|
95
|
+
*/
|
|
96
|
+
walThreshold?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Maximum retry attempts for failed operations
|
|
99
|
+
* @default 3
|
|
100
|
+
*/
|
|
101
|
+
maxRetries?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Enable gzip compression for uploads
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
compression?: boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Main configuration options for the GitHub data provider
|
|
110
|
+
*/
|
|
111
|
+
interface BetterSqlite3GitHubOptions {
|
|
112
|
+
/**
|
|
113
|
+
* Local SQLite database file path
|
|
114
|
+
*/
|
|
115
|
+
file: string;
|
|
116
|
+
/**
|
|
117
|
+
* SQLite configuration options
|
|
118
|
+
*/
|
|
119
|
+
sqliteOptions?: SqliteOptions;
|
|
120
|
+
/**
|
|
121
|
+
* GitHub repository configuration
|
|
122
|
+
*/
|
|
123
|
+
github: GitHubRepoConfig;
|
|
124
|
+
/**
|
|
125
|
+
* Sync configuration options
|
|
126
|
+
*/
|
|
127
|
+
sync?: SyncConfig;
|
|
128
|
+
/**
|
|
129
|
+
* Event callback for sync events
|
|
130
|
+
*/
|
|
131
|
+
onEvent?: (event: GitHubSyncEvent) => void;
|
|
132
|
+
/**
|
|
133
|
+
* Enable verbose logging
|
|
134
|
+
* @default false
|
|
135
|
+
*/
|
|
136
|
+
verbose?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Events emitted during sync operations
|
|
140
|
+
*/
|
|
141
|
+
type GitHubSyncEvent = {
|
|
142
|
+
type: "snapshot_uploaded";
|
|
143
|
+
sha: string;
|
|
144
|
+
size: number;
|
|
145
|
+
compressed: boolean;
|
|
146
|
+
} | {
|
|
147
|
+
type: "wal_uploaded";
|
|
148
|
+
index: number;
|
|
149
|
+
size: number;
|
|
150
|
+
} | {
|
|
151
|
+
type: "recovery_started";
|
|
152
|
+
branch: string;
|
|
153
|
+
} | {
|
|
154
|
+
type: "recovery_completed";
|
|
155
|
+
sha: string;
|
|
156
|
+
} | {
|
|
157
|
+
type: "commit_created";
|
|
158
|
+
sha: string;
|
|
159
|
+
message: string;
|
|
160
|
+
} | {
|
|
161
|
+
type: "sync_error";
|
|
162
|
+
error: Error;
|
|
163
|
+
context: string;
|
|
164
|
+
willRetry: boolean;
|
|
165
|
+
} | {
|
|
166
|
+
type: "rate_limit_hit";
|
|
167
|
+
resetAt: Date;
|
|
168
|
+
remaining: number;
|
|
169
|
+
} | {
|
|
170
|
+
type: "initialized";
|
|
171
|
+
recovered: boolean;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Internal configuration for GitHubOperations
|
|
175
|
+
*/
|
|
176
|
+
interface GitHubConfig {
|
|
177
|
+
owner: string;
|
|
178
|
+
repo: string;
|
|
179
|
+
branch: string;
|
|
180
|
+
path: string;
|
|
181
|
+
token?: string;
|
|
182
|
+
appId?: number;
|
|
183
|
+
privateKey?: string;
|
|
184
|
+
installationId?: number;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Result of a file retrieval operation
|
|
188
|
+
*/
|
|
189
|
+
interface GitHubFileResult {
|
|
190
|
+
content: Buffer;
|
|
191
|
+
sha: string;
|
|
192
|
+
size: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* File listing entry
|
|
196
|
+
*/
|
|
197
|
+
interface GitHubFileEntry {
|
|
198
|
+
name: string;
|
|
199
|
+
path: string;
|
|
200
|
+
sha: string;
|
|
201
|
+
size: number;
|
|
202
|
+
type: "file" | "dir";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Rate limit information
|
|
206
|
+
*/
|
|
207
|
+
interface RateLimitInfo {
|
|
208
|
+
remaining: number;
|
|
209
|
+
resetAt: Date;
|
|
210
|
+
limit: number;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Result of a recovery operation
|
|
214
|
+
*/
|
|
215
|
+
interface RecoveryResult {
|
|
216
|
+
recovered: boolean;
|
|
217
|
+
sha?: string;
|
|
218
|
+
size?: number;
|
|
219
|
+
compressed?: boolean;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Snapshot metadata stored alongside the database file
|
|
223
|
+
*/
|
|
224
|
+
interface SnapshotMetadata {
|
|
225
|
+
timestamp: string;
|
|
226
|
+
size: number;
|
|
227
|
+
checksum: string;
|
|
228
|
+
compressed: boolean;
|
|
229
|
+
walIndex?: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Internal sync manager options
|
|
233
|
+
*/
|
|
234
|
+
interface GitHubSyncManagerOptions {
|
|
235
|
+
dbPath: string;
|
|
236
|
+
github: GitHubConfig;
|
|
237
|
+
sqliteOptions?: SqliteOptions;
|
|
238
|
+
sync: Required<SyncConfig>;
|
|
239
|
+
onEvent?: (event: GitHubSyncEvent) => void;
|
|
240
|
+
verbose: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Orchestrates database sync operations with GitHub
|
|
245
|
+
*/
|
|
246
|
+
declare class GitHubSyncManager {
|
|
247
|
+
private options;
|
|
248
|
+
private github;
|
|
249
|
+
private recovery;
|
|
250
|
+
private db;
|
|
251
|
+
private hasChanges;
|
|
252
|
+
private currentSha;
|
|
253
|
+
private snapshotTimer;
|
|
254
|
+
private isInitialized;
|
|
255
|
+
private isClosed;
|
|
256
|
+
constructor(options: GitHubSyncManagerOptions);
|
|
257
|
+
/**
|
|
258
|
+
* Initialize the sync manager: recover from GitHub if needed, open database
|
|
259
|
+
*/
|
|
260
|
+
initialize(): Promise<DatabaseType>;
|
|
261
|
+
/**
|
|
262
|
+
* Open the SQLite database with configured options
|
|
263
|
+
*/
|
|
264
|
+
private openDatabase;
|
|
265
|
+
/**
|
|
266
|
+
* Called after database writes to track changes
|
|
267
|
+
*/
|
|
268
|
+
onWrite(): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* Create a snapshot and upload to GitHub
|
|
271
|
+
*/
|
|
272
|
+
snapshot(): Promise<void>;
|
|
273
|
+
/**
|
|
274
|
+
* Upload file with automatic conflict resolution
|
|
275
|
+
*/
|
|
276
|
+
private uploadWithConflictRetry;
|
|
277
|
+
/**
|
|
278
|
+
* Force an immediate sync (alias for snapshot)
|
|
279
|
+
*/
|
|
280
|
+
forceSync(): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* Close the sync manager and perform final sync
|
|
283
|
+
*/
|
|
284
|
+
close(): Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Start the automatic snapshot timer
|
|
287
|
+
*/
|
|
288
|
+
private startSnapshotTimer;
|
|
289
|
+
/**
|
|
290
|
+
* Get the raw database instance
|
|
291
|
+
*/
|
|
292
|
+
get database(): DatabaseType | null;
|
|
293
|
+
/**
|
|
294
|
+
* Check if there are pending changes
|
|
295
|
+
*/
|
|
296
|
+
get hasPendingChanges(): boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Check if the manager is initialized
|
|
299
|
+
*/
|
|
300
|
+
get initialized(): boolean;
|
|
301
|
+
/**
|
|
302
|
+
* Emit an event if handler is registered
|
|
303
|
+
*/
|
|
304
|
+
private emitEvent;
|
|
305
|
+
/**
|
|
306
|
+
* Log a message if verbose mode is enabled
|
|
307
|
+
*/
|
|
308
|
+
private log;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* GitHub REST API operations wrapper with retry logic and rate limit handling
|
|
313
|
+
*/
|
|
314
|
+
declare class GitHubOperations {
|
|
315
|
+
private config;
|
|
316
|
+
private maxRetries;
|
|
317
|
+
private cachedToken;
|
|
318
|
+
private tokenExpiry;
|
|
319
|
+
private onEvent?;
|
|
320
|
+
private verbose;
|
|
321
|
+
constructor(config: GitHubConfig, maxRetries?: number, onEvent?: (event: GitHubSyncEvent) => void, verbose?: boolean);
|
|
322
|
+
/**
|
|
323
|
+
* Get a file from the repository
|
|
324
|
+
*/
|
|
325
|
+
getFile(path: string): Promise<GitHubFileResult | null>;
|
|
326
|
+
/**
|
|
327
|
+
* Get a large file using the blob API
|
|
328
|
+
*/
|
|
329
|
+
private getBlob;
|
|
330
|
+
/**
|
|
331
|
+
* Create or update a file in the repository
|
|
332
|
+
* Returns the new commit SHA
|
|
333
|
+
*/
|
|
334
|
+
putFile(path: string, content: Buffer, message: string, sha?: string): Promise<string>;
|
|
335
|
+
/**
|
|
336
|
+
* Delete a file from the repository
|
|
337
|
+
*/
|
|
338
|
+
deleteFile(path: string, sha: string, message: string): Promise<void>;
|
|
339
|
+
/**
|
|
340
|
+
* List files in a directory
|
|
341
|
+
*/
|
|
342
|
+
listFiles(path: string): Promise<GitHubFileEntry[]>;
|
|
343
|
+
/**
|
|
344
|
+
* Check current rate limit status
|
|
345
|
+
*/
|
|
346
|
+
checkRateLimit(): Promise<RateLimitInfo>;
|
|
347
|
+
/**
|
|
348
|
+
* Ensure the branch exists, create if it doesn't
|
|
349
|
+
* For empty repos, we skip branch creation since files can be pushed directly
|
|
350
|
+
*/
|
|
351
|
+
ensureBranch(): Promise<void>;
|
|
352
|
+
/**
|
|
353
|
+
* Get the full path including configured prefix
|
|
354
|
+
*/
|
|
355
|
+
private getFullPath;
|
|
356
|
+
/**
|
|
357
|
+
* Make an authenticated fetch request
|
|
358
|
+
*/
|
|
359
|
+
private fetch;
|
|
360
|
+
/**
|
|
361
|
+
* Get authentication token (PAT or GitHub App installation token)
|
|
362
|
+
*/
|
|
363
|
+
private getAuthToken;
|
|
364
|
+
/**
|
|
365
|
+
* Generate a JWT for GitHub App authentication
|
|
366
|
+
*/
|
|
367
|
+
private generateJWT;
|
|
368
|
+
/**
|
|
369
|
+
* Base64 URL encode a string
|
|
370
|
+
*/
|
|
371
|
+
private base64UrlEncode;
|
|
372
|
+
/**
|
|
373
|
+
* Execute a function with retry logic and exponential backoff
|
|
374
|
+
*/
|
|
375
|
+
private withRetry;
|
|
376
|
+
/**
|
|
377
|
+
* Handle rate limit headers from response
|
|
378
|
+
*/
|
|
379
|
+
private handleRateLimitHeaders;
|
|
380
|
+
/**
|
|
381
|
+
* Sleep for the specified duration
|
|
382
|
+
*/
|
|
383
|
+
private sleep;
|
|
384
|
+
/**
|
|
385
|
+
* Emit an event if handler is registered
|
|
386
|
+
*/
|
|
387
|
+
private emitEvent;
|
|
388
|
+
/**
|
|
389
|
+
* Log a message if verbose mode is enabled
|
|
390
|
+
*/
|
|
391
|
+
private log;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Custom error for conflict (SHA mismatch) situations
|
|
395
|
+
*/
|
|
396
|
+
declare class ConflictError extends Error {
|
|
397
|
+
constructor(message: string);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* A Remult data provider that syncs SQLite to GitHub
|
|
402
|
+
*/
|
|
403
|
+
declare class BetterSqlite3GitHubDataProvider implements SqlImplementation {
|
|
404
|
+
private syncManager;
|
|
405
|
+
private _isInitialized;
|
|
406
|
+
private initPromise;
|
|
407
|
+
private innerProvider;
|
|
408
|
+
constructor(options: BetterSqlite3GitHubOptions);
|
|
409
|
+
/**
|
|
410
|
+
* Initialize the data provider
|
|
411
|
+
* This must be called before using the provider
|
|
412
|
+
*/
|
|
413
|
+
init(): Promise<void>;
|
|
414
|
+
private doInit;
|
|
415
|
+
/**
|
|
416
|
+
* Ensure the provider is initialized
|
|
417
|
+
*/
|
|
418
|
+
private ensureInitialized;
|
|
419
|
+
private getProvider;
|
|
420
|
+
getLimitSqlSyntax(limit: number, offset: number): string;
|
|
421
|
+
createCommand(): SqlCommand;
|
|
422
|
+
transaction(action: (sql: SqlImplementation) => Promise<void>): Promise<void>;
|
|
423
|
+
entityIsUsedForTheFirstTime(entity: EntityMetadata): Promise<void>;
|
|
424
|
+
end(): Promise<void>;
|
|
425
|
+
get orderByNullsFirst(): boolean | undefined;
|
|
426
|
+
get afterMutation(): VoidFunction | undefined;
|
|
427
|
+
get supportsJsonColumnType(): boolean | undefined;
|
|
428
|
+
get doesNotSupportReturningSyntax(): boolean;
|
|
429
|
+
ensureSchema(entities: EntityMetadata<any>[]): Promise<void>;
|
|
430
|
+
wrapIdentifier(name: string): string;
|
|
431
|
+
addColumnSqlSyntax(x: FieldMetadata, dbName: string, isAlterTable: boolean): string;
|
|
432
|
+
/**
|
|
433
|
+
* Check if a SQL statement is a write operation
|
|
434
|
+
*/
|
|
435
|
+
private isWriteOperation;
|
|
436
|
+
/**
|
|
437
|
+
* Force an immediate sync to GitHub
|
|
438
|
+
*/
|
|
439
|
+
forceSync(): Promise<void>;
|
|
440
|
+
/**
|
|
441
|
+
* Create a snapshot and upload to GitHub
|
|
442
|
+
*/
|
|
443
|
+
snapshot(): Promise<void>;
|
|
444
|
+
/**
|
|
445
|
+
* Close the provider and perform final sync
|
|
446
|
+
*/
|
|
447
|
+
close(): Promise<void>;
|
|
448
|
+
/**
|
|
449
|
+
* Get the raw better-sqlite3 database instance
|
|
450
|
+
*/
|
|
451
|
+
get rawDatabase(): DatabaseType | null;
|
|
452
|
+
/**
|
|
453
|
+
* Get the sync manager instance
|
|
454
|
+
*/
|
|
455
|
+
get sync(): GitHubSyncManager;
|
|
456
|
+
/**
|
|
457
|
+
* Check if the provider is initialized
|
|
458
|
+
*/
|
|
459
|
+
get isInitialized(): boolean;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Create a GitHub-synced SQLite data provider for Remult
|
|
464
|
+
*
|
|
465
|
+
* @param options - Configuration options for the provider
|
|
466
|
+
* @returns A factory function that creates an initialized SqlDatabase
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```typescript
|
|
470
|
+
* import { createGitHubDataProvider } from "remult-sqlite-github";
|
|
471
|
+
*
|
|
472
|
+
* const api = remultApi({
|
|
473
|
+
* dataProvider: createGitHubDataProvider({
|
|
474
|
+
* file: "./mydb.sqlite",
|
|
475
|
+
* github: {
|
|
476
|
+
* owner: "your-username",
|
|
477
|
+
* repo: "your-database-repo",
|
|
478
|
+
* token: process.env.GITHUB_TOKEN,
|
|
479
|
+
* },
|
|
480
|
+
* }),
|
|
481
|
+
* entities: [Task],
|
|
482
|
+
* });
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
declare function createGitHubDataProvider(options: BetterSqlite3GitHubOptions): () => Promise<SqlDatabase>;
|
|
486
|
+
|
|
487
|
+
export { BetterSqlite3GitHubDataProvider, type BetterSqlite3GitHubOptions, ConflictError, type DatabaseType, type GitHubAuthConfig, GitHubOperations, type GitHubRepoConfig, type GitHubSyncEvent, GitHubSyncManager, type RateLimitInfo, type RecoveryResult, type SnapshotMetadata, type SqliteOptions, type SyncConfig, createGitHubDataProvider };
|