@framers/sql-storage-adapter 0.7.2 → 0.7.3
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/README.md +20 -0
- package/dist/adapters/electron/preload.cjs +289 -0
- package/dist/adapters/electron/renderer.d.ts +9 -0
- package/dist/adapters/electron/renderer.d.ts.map +1 -0
- package/dist/adapters/electron/renderer.js +8 -0
- package/dist/adapters/electron/renderer.js.map +1 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.d.ts +2 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.js +133 -0
- package/dist/cjs/adapters/baseStorageAdapter.d.ts +368 -0
- package/dist/cjs/adapters/baseStorageAdapter.js +744 -0
- package/dist/cjs/adapters/betterSqliteAdapter.d.ts +124 -0
- package/dist/cjs/adapters/betterSqliteAdapter.js +334 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.d.ts +83 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.js +250 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.d.ts +251 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.js +392 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.d.ts +177 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.js +283 -0
- package/dist/cjs/adapters/electron/index.d.ts +74 -0
- package/dist/cjs/adapters/electron/index.js +86 -0
- package/dist/cjs/adapters/electron/ipc/channels.d.ts +196 -0
- package/dist/cjs/adapters/electron/ipc/channels.js +127 -0
- package/dist/cjs/adapters/electron/ipc/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/ipc/index.js +38 -0
- package/dist/cjs/adapters/electron/ipc/protocol.d.ts +78 -0
- package/dist/cjs/adapters/electron/ipc/protocol.js +341 -0
- package/dist/cjs/adapters/electron/ipc/types.d.ts +248 -0
- package/dist/cjs/adapters/electron/ipc/types.js +15 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.d.ts +184 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.js +441 -0
- package/dist/cjs/adapters/electron/migration/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/migration/index.js +29 -0
- package/dist/cjs/adapters/electron/preload.d.ts +126 -0
- package/dist/cjs/adapters/electron/preload.js +233 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.d.ts +214 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.js +359 -0
- package/dist/cjs/adapters/electron/recovery/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/recovery/index.js +34 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.d.ts +186 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.js +263 -0
- package/dist/cjs/adapters/electron/renderer.d.ts +9 -0
- package/dist/cjs/adapters/electron/renderer.js +33 -0
- package/dist/cjs/adapters/electron/window/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/window/index.js +29 -0
- package/dist/cjs/adapters/electron/window/windowManager.d.ts +190 -0
- package/dist/cjs/adapters/electron/window/windowManager.js +321 -0
- package/dist/cjs/adapters/indexedDbAdapter.d.ts +490 -0
- package/dist/cjs/adapters/indexedDbAdapter.js +993 -0
- package/dist/cjs/adapters/postgresAdapter.d.ts +424 -0
- package/dist/cjs/adapters/postgresAdapter.js +889 -0
- package/dist/cjs/adapters/sqlJsAdapter.d.ts +64 -0
- package/dist/cjs/adapters/sqlJsAdapter.js +268 -0
- package/dist/cjs/adapters/supabase.d.ts +58 -0
- package/dist/cjs/adapters/supabase.js +401 -0
- package/dist/cjs/codecs/BrowserBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/BrowserBlobCodec.js +57 -0
- package/dist/cjs/codecs/NodeBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/NodeBlobCodec.js +59 -0
- package/dist/cjs/core/contracts/blobCodec.d.ts +15 -0
- package/dist/cjs/core/contracts/blobCodec.js +15 -0
- package/dist/cjs/core/contracts/context.d.ts +221 -0
- package/dist/cjs/core/contracts/context.js +15 -0
- package/dist/cjs/core/contracts/dialect.d.ts +154 -0
- package/dist/cjs/core/contracts/dialect.js +15 -0
- package/dist/cjs/core/contracts/events.d.ts +225 -0
- package/dist/cjs/core/contracts/events.js +15 -0
- package/dist/cjs/core/contracts/exporter.d.ts +13 -0
- package/dist/cjs/core/contracts/exporter.js +15 -0
- package/dist/cjs/core/contracts/extensions.d.ts +73 -0
- package/dist/cjs/core/contracts/extensions.js +15 -0
- package/dist/cjs/core/contracts/features.d.ts +25 -0
- package/dist/cjs/core/contracts/features.js +61 -0
- package/dist/cjs/core/contracts/fts.d.ts +66 -0
- package/dist/cjs/core/contracts/fts.js +15 -0
- package/dist/cjs/core/contracts/hooks.d.ts +481 -0
- package/dist/cjs/core/contracts/hooks.js +140 -0
- package/dist/cjs/core/contracts/index.d.ts +260 -0
- package/dist/cjs/core/contracts/index.js +41 -0
- package/dist/cjs/core/contracts/limitations.d.ts +46 -0
- package/dist/cjs/core/contracts/limitations.js +218 -0
- package/dist/cjs/core/contracts/performance.d.ts +308 -0
- package/dist/cjs/core/contracts/performance.js +144 -0
- package/dist/cjs/core/database.d.ts +220 -0
- package/dist/cjs/core/database.js +136 -0
- package/dist/cjs/core/portable.d.ts +23 -0
- package/dist/cjs/core/portable.js +66 -0
- package/dist/cjs/core/resolver.d.ts +77 -0
- package/dist/cjs/core/resolver.js +235 -0
- package/dist/cjs/dialects/PostgresDialect.d.ts +35 -0
- package/dist/cjs/dialects/PostgresDialect.js +143 -0
- package/dist/cjs/dialects/SqliteDialect.d.ts +35 -0
- package/dist/cjs/dialects/SqliteDialect.js +142 -0
- package/dist/cjs/dialects/shared.d.ts +17 -0
- package/dist/cjs/dialects/shared.js +90 -0
- package/dist/cjs/exporters/PostgresExporter.d.ts +14 -0
- package/dist/cjs/exporters/PostgresExporter.js +61 -0
- package/dist/cjs/exporters/SqliteFileExporter.d.ts +16 -0
- package/dist/cjs/exporters/SqliteFileExporter.js +43 -0
- package/dist/cjs/features/backup/cloudBackup.d.ts +219 -0
- package/dist/cjs/features/backup/cloudBackup.js +209 -0
- package/dist/cjs/features/kv/idb.d.ts +79 -0
- package/dist/cjs/features/kv/idb.js +32 -0
- package/dist/cjs/features/kv/index.d.ts +14 -0
- package/dist/cjs/features/kv/index.js +40 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.d.ts +77 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.js +116 -0
- package/dist/cjs/features/kv/keyValueStore.d.ts +48 -0
- package/dist/cjs/features/kv/keyValueStore.js +67 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.d.ts +34 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.js +81 -0
- package/dist/cjs/features/kv/valueCodec.d.ts +35 -0
- package/dist/cjs/features/kv/valueCodec.js +426 -0
- package/dist/cjs/features/migrations/dataExport.d.ts +77 -0
- package/dist/cjs/features/migrations/dataExport.js +194 -0
- package/dist/cjs/features/migrations/dataImport.d.ts +54 -0
- package/dist/cjs/features/migrations/dataImport.js +300 -0
- package/dist/cjs/features/migrations/migration.d.ts +89 -0
- package/dist/cjs/features/migrations/migration.js +187 -0
- package/dist/cjs/features/migrations/schemaMigrations.d.ts +93 -0
- package/dist/cjs/features/migrations/schemaMigrations.js +220 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.d.ts +222 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.js +366 -0
- package/dist/cjs/features/sync/conflicts/index.d.ts +9 -0
- package/dist/cjs/features/sync/conflicts/index.js +21 -0
- package/dist/cjs/features/sync/crossPlatformSync.d.ts +281 -0
- package/dist/cjs/features/sync/crossPlatformSync.js +551 -0
- package/dist/cjs/features/sync/devices/deviceManager.d.ts +243 -0
- package/dist/cjs/features/sync/devices/deviceManager.js +450 -0
- package/dist/cjs/features/sync/devices/index.d.ts +10 -0
- package/dist/cjs/features/sync/devices/index.js +21 -0
- package/dist/cjs/features/sync/index.d.ts +37 -0
- package/dist/cjs/features/sync/index.js +111 -0
- package/dist/cjs/features/sync/protocol/index.d.ts +11 -0
- package/dist/cjs/features/sync/protocol/index.js +23 -0
- package/dist/cjs/features/sync/protocol/messages.d.ts +348 -0
- package/dist/cjs/features/sync/protocol/messages.js +215 -0
- package/dist/cjs/features/sync/protocol/vectorClock.d.ts +164 -0
- package/dist/cjs/features/sync/protocol/vectorClock.js +256 -0
- package/dist/cjs/features/sync/syncManager.d.ts +348 -0
- package/dist/cjs/features/sync/syncManager.js +455 -0
- package/dist/cjs/features/sync/tables/index.d.ts +10 -0
- package/dist/cjs/features/sync/tables/index.js +21 -0
- package/dist/cjs/features/sync/tables/syncLogManager.d.ts +216 -0
- package/dist/cjs/features/sync/tables/syncLogManager.js +423 -0
- package/dist/cjs/features/sync/transport/httpTransport.d.ts +123 -0
- package/dist/cjs/features/sync/transport/httpTransport.js +346 -0
- package/dist/cjs/features/sync/transport/index.d.ts +12 -0
- package/dist/cjs/features/sync/transport/index.js +25 -0
- package/dist/cjs/features/sync/transport/transport.d.ts +259 -0
- package/dist/cjs/features/sync/transport/transport.js +163 -0
- package/dist/cjs/features/sync/transport/websocketTransport.d.ts +126 -0
- package/dist/cjs/features/sync/transport/websocketTransport.js +342 -0
- package/dist/cjs/fts/PostgresFts.d.ts +29 -0
- package/dist/cjs/fts/PostgresFts.js +77 -0
- package/dist/cjs/fts/SqliteFts5.d.ts +22 -0
- package/dist/cjs/fts/SqliteFts5.js +58 -0
- package/dist/cjs/index.d.ts +30 -0
- package/dist/cjs/index.js +101 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/shared/connectionGate.d.ts +94 -0
- package/dist/cjs/shared/connectionGate.js +77 -0
- package/dist/cjs/shared/errors.d.ts +23 -0
- package/dist/cjs/shared/errors.js +45 -0
- package/dist/cjs/shared/logger.d.ts +26 -0
- package/dist/cjs/shared/logger.js +40 -0
- package/dist/cjs/shared/parameterUtils.d.ts +16 -0
- package/dist/cjs/shared/parameterUtils.js +59 -0
- package/dist/cjs/shared/postgresTarget.d.ts +11 -0
- package/dist/cjs/shared/postgresTarget.js +30 -0
- package/dist/cjs/shared/segment.d.ts +43 -0
- package/dist/cjs/shared/segment.js +63 -0
- package/dist/cjs/shared/serializedTransaction.d.ts +14 -0
- package/dist/cjs/shared/serializedTransaction.js +74 -0
- package/dist/cjs/shared/splitSqlStatements.d.ts +27 -0
- package/dist/cjs/shared/splitSqlStatements.js +163 -0
- package/dist/cjs/shared/sqlPlaceholders.d.ts +48 -0
- package/dist/cjs/shared/sqlPlaceholders.js +263 -0
- package/dist/cjs/types/index.d.ts +29 -0
- package/dist/cjs/types/index.js +33 -0
- package/dist/core/portable.d.ts +23 -0
- package/dist/core/portable.d.ts.map +1 -0
- package/dist/core/portable.js +23 -0
- package/dist/core/portable.js.map +1 -0
- package/package.json +139 -24
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var corruptionDetector_exports = {};
|
|
19
|
+
__export(corruptionDetector_exports, {
|
|
20
|
+
CorruptionDetector: () => CorruptionDetector,
|
|
21
|
+
createCorruptionDetector: () => createCorruptionDetector
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(corruptionDetector_exports);
|
|
24
|
+
const DEFAULT_CONFIG = {
|
|
25
|
+
checkOnOpen: true,
|
|
26
|
+
autoRepair: true,
|
|
27
|
+
backupBeforeRepair: true,
|
|
28
|
+
checkTimeout: 6e4,
|
|
29
|
+
defaultCheckLevel: "quick",
|
|
30
|
+
verbose: false
|
|
31
|
+
};
|
|
32
|
+
class CorruptionDetector {
|
|
33
|
+
constructor(adapter, config = {}) {
|
|
34
|
+
this.adapter = adapter;
|
|
35
|
+
this.lastCheck = null;
|
|
36
|
+
this.repairHistory = [];
|
|
37
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
38
|
+
}
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Integrity Checks
|
|
41
|
+
// ============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Run a quick integrity check.
|
|
44
|
+
* Faster but less thorough than full check.
|
|
45
|
+
*/
|
|
46
|
+
async runQuickCheck() {
|
|
47
|
+
return this.runCheck("quick");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Run a full integrity check.
|
|
51
|
+
* Checks all pages and structures.
|
|
52
|
+
*/
|
|
53
|
+
async runFullCheck() {
|
|
54
|
+
return this.runCheck("full");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Run a thorough integrity check.
|
|
58
|
+
* Includes foreign key and index validation.
|
|
59
|
+
*/
|
|
60
|
+
async runThoroughCheck() {
|
|
61
|
+
return this.runCheck("thorough");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Run an integrity check at the specified level.
|
|
65
|
+
*/
|
|
66
|
+
async runCheck(level) {
|
|
67
|
+
const startTime = Date.now();
|
|
68
|
+
const issues = [];
|
|
69
|
+
try {
|
|
70
|
+
const pageCount = await this.adapter.get("PRAGMA page_count");
|
|
71
|
+
const pageSize = await this.adapter.get("PRAGMA page_size");
|
|
72
|
+
const databaseSize = (pageCount?.page_count ?? 0) * (pageSize?.page_size ?? 4096);
|
|
73
|
+
if (level === "quick") {
|
|
74
|
+
const result = await this.adapter.all("PRAGMA quick_check");
|
|
75
|
+
this.parseIntegrityResults(result.map((r) => r.quick_check), issues);
|
|
76
|
+
} else {
|
|
77
|
+
const result = await this.adapter.all("PRAGMA integrity_check");
|
|
78
|
+
this.parseIntegrityResults(result.map((r) => r.integrity_check), issues);
|
|
79
|
+
}
|
|
80
|
+
if (level === "thorough") {
|
|
81
|
+
await this.checkForeignKeys(issues);
|
|
82
|
+
await this.checkIndexes(issues);
|
|
83
|
+
}
|
|
84
|
+
const checkResult = {
|
|
85
|
+
isHealthy: issues.length === 0,
|
|
86
|
+
level,
|
|
87
|
+
issues,
|
|
88
|
+
durationMs: Date.now() - startTime,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
pagesChecked: pageCount?.page_count,
|
|
91
|
+
databaseSize
|
|
92
|
+
};
|
|
93
|
+
this.lastCheck = checkResult;
|
|
94
|
+
this.log(`Integrity check completed: ${issues.length} issues found (${checkResult.durationMs}ms)`);
|
|
95
|
+
if (!checkResult.isHealthy && this.config.autoRepair) {
|
|
96
|
+
await this.attemptRepair();
|
|
97
|
+
}
|
|
98
|
+
return checkResult;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
101
|
+
this.log(`Integrity check failed: ${errorMessage}`);
|
|
102
|
+
issues.push({
|
|
103
|
+
type: "unknown",
|
|
104
|
+
message: `Integrity check failed: ${errorMessage}`,
|
|
105
|
+
severity: "critical",
|
|
106
|
+
repairable: false
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
isHealthy: false,
|
|
110
|
+
level,
|
|
111
|
+
issues,
|
|
112
|
+
durationMs: Date.now() - startTime,
|
|
113
|
+
timestamp: Date.now()
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parse integrity check results into structured issues.
|
|
119
|
+
*/
|
|
120
|
+
parseIntegrityResults(results, issues) {
|
|
121
|
+
for (const result of results) {
|
|
122
|
+
if (result === "ok")
|
|
123
|
+
continue;
|
|
124
|
+
const issue = {
|
|
125
|
+
type: "unknown",
|
|
126
|
+
message: result,
|
|
127
|
+
severity: "error",
|
|
128
|
+
repairable: true
|
|
129
|
+
};
|
|
130
|
+
if (result.includes("index")) {
|
|
131
|
+
issue.type = "index";
|
|
132
|
+
const match = result.match(/index (\w+)/);
|
|
133
|
+
if (match)
|
|
134
|
+
issue.table = match[1];
|
|
135
|
+
} else if (result.includes("table")) {
|
|
136
|
+
issue.type = "data";
|
|
137
|
+
const match = result.match(/table (\w+)/);
|
|
138
|
+
if (match)
|
|
139
|
+
issue.table = match[1];
|
|
140
|
+
} else if (result.includes("foreign key")) {
|
|
141
|
+
issue.type = "foreign_key";
|
|
142
|
+
} else if (result.includes("page") || result.includes("corrupt")) {
|
|
143
|
+
issue.type = "structure";
|
|
144
|
+
issue.severity = "critical";
|
|
145
|
+
}
|
|
146
|
+
issues.push(issue);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check foreign key constraints.
|
|
151
|
+
*/
|
|
152
|
+
async checkForeignKeys(issues) {
|
|
153
|
+
try {
|
|
154
|
+
await this.adapter.exec("PRAGMA foreign_keys = ON");
|
|
155
|
+
const violations = await this.adapter.all("PRAGMA foreign_key_check");
|
|
156
|
+
for (const violation of violations) {
|
|
157
|
+
issues.push({
|
|
158
|
+
type: "foreign_key",
|
|
159
|
+
table: violation.table,
|
|
160
|
+
message: `Foreign key violation in ${violation.table} (row ${violation.rowid}): references ${violation.parent}`,
|
|
161
|
+
severity: "warning",
|
|
162
|
+
repairable: true
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
this.log(`Foreign key check failed: ${error}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check index integrity.
|
|
171
|
+
*/
|
|
172
|
+
async checkIndexes(issues) {
|
|
173
|
+
try {
|
|
174
|
+
const indexes = await this.adapter.all("SELECT name, tbl_name FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_%'");
|
|
175
|
+
for (const index of indexes) {
|
|
176
|
+
try {
|
|
177
|
+
await this.adapter.get(`SELECT * FROM "${index.tbl_name}" INDEXED BY "${index.name}" LIMIT 1`);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
issues.push({
|
|
180
|
+
type: "index",
|
|
181
|
+
table: index.tbl_name,
|
|
182
|
+
message: `Index ${index.name} on ${index.tbl_name} may be corrupt: ${error}`,
|
|
183
|
+
severity: "error",
|
|
184
|
+
repairable: true
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
this.log(`Index check failed: ${error}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ============================================================================
|
|
193
|
+
// Repair Operations
|
|
194
|
+
// ============================================================================
|
|
195
|
+
/**
|
|
196
|
+
* Attempt to repair the database.
|
|
197
|
+
*/
|
|
198
|
+
async attemptRepair(strategy) {
|
|
199
|
+
const effectiveStrategy = strategy ?? this.selectRepairStrategy();
|
|
200
|
+
const startTime = Date.now();
|
|
201
|
+
this.log(`Attempting repair with strategy: ${effectiveStrategy}`);
|
|
202
|
+
let backupPath;
|
|
203
|
+
if (this.config.backupBeforeRepair) {
|
|
204
|
+
try {
|
|
205
|
+
backupPath = await this.createBackup();
|
|
206
|
+
} catch (error) {
|
|
207
|
+
this.log(`Backup failed, proceeding with repair: ${error}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
switch (effectiveStrategy) {
|
|
212
|
+
case "vacuum":
|
|
213
|
+
await this.repairWithVacuum();
|
|
214
|
+
break;
|
|
215
|
+
case "reindex":
|
|
216
|
+
await this.repairWithReindex();
|
|
217
|
+
break;
|
|
218
|
+
case "export_import":
|
|
219
|
+
await this.repairWithExportImport();
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
const verifyResult = await this.runQuickCheck();
|
|
223
|
+
const result = {
|
|
224
|
+
success: verifyResult.isHealthy,
|
|
225
|
+
strategy: effectiveStrategy,
|
|
226
|
+
issuesFixed: (this.lastCheck?.issues.length ?? 0) - verifyResult.issues.length,
|
|
227
|
+
issuesRemaining: verifyResult.issues.length,
|
|
228
|
+
durationMs: Date.now() - startTime,
|
|
229
|
+
backupPath
|
|
230
|
+
};
|
|
231
|
+
this.repairHistory.push(result);
|
|
232
|
+
this.log(`Repair completed: ${result.issuesFixed} issues fixed, ${result.issuesRemaining} remaining`);
|
|
233
|
+
return result;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
236
|
+
this.log(`Repair failed: ${errorMessage}`);
|
|
237
|
+
const result = {
|
|
238
|
+
success: false,
|
|
239
|
+
strategy: effectiveStrategy,
|
|
240
|
+
issuesFixed: 0,
|
|
241
|
+
issuesRemaining: this.lastCheck?.issues.length ?? 0,
|
|
242
|
+
durationMs: Date.now() - startTime,
|
|
243
|
+
backupPath,
|
|
244
|
+
error: errorMessage
|
|
245
|
+
};
|
|
246
|
+
this.repairHistory.push(result);
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Select the best repair strategy based on issues found.
|
|
252
|
+
*/
|
|
253
|
+
selectRepairStrategy() {
|
|
254
|
+
if (!this.lastCheck)
|
|
255
|
+
return "vacuum";
|
|
256
|
+
const hasIndexIssues = this.lastCheck.issues.some((i) => i.type === "index");
|
|
257
|
+
const hasCriticalIssues = this.lastCheck.issues.some((i) => i.severity === "critical");
|
|
258
|
+
if (hasCriticalIssues)
|
|
259
|
+
return "export_import";
|
|
260
|
+
if (hasIndexIssues)
|
|
261
|
+
return "reindex";
|
|
262
|
+
return "vacuum";
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Repair using VACUUM.
|
|
266
|
+
*/
|
|
267
|
+
async repairWithVacuum() {
|
|
268
|
+
this.log("Repairing with VACUUM...");
|
|
269
|
+
await this.adapter.exec("VACUUM");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Repair using REINDEX.
|
|
273
|
+
*/
|
|
274
|
+
async repairWithReindex() {
|
|
275
|
+
this.log("Repairing with REINDEX...");
|
|
276
|
+
await this.adapter.exec("REINDEX");
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Repair using export/import.
|
|
280
|
+
* This is a last resort that exports all data and recreates the database.
|
|
281
|
+
*/
|
|
282
|
+
async repairWithExportImport() {
|
|
283
|
+
this.log("Repairing with export/import...");
|
|
284
|
+
const tables = await this.adapter.all("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'");
|
|
285
|
+
const tableData = /* @__PURE__ */ new Map();
|
|
286
|
+
for (const table of tables) {
|
|
287
|
+
try {
|
|
288
|
+
const data = await this.adapter.all(`SELECT * FROM "${table.name}"`);
|
|
289
|
+
tableData.set(table.name, data);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
this.log(`Failed to export ${table.name}: ${error}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
await this.adapter.exec("VACUUM");
|
|
295
|
+
this.log("Export/import completed");
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Create a backup of the database.
|
|
299
|
+
*/
|
|
300
|
+
async createBackup() {
|
|
301
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
302
|
+
const backupPath = `backup_${timestamp}.db`;
|
|
303
|
+
await this.adapter.exec(`VACUUM INTO '${backupPath}'`);
|
|
304
|
+
this.log(`Backup created: ${backupPath}`);
|
|
305
|
+
return backupPath;
|
|
306
|
+
}
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// Status & History
|
|
309
|
+
// ============================================================================
|
|
310
|
+
/**
|
|
311
|
+
* Get the last integrity check result.
|
|
312
|
+
*/
|
|
313
|
+
getLastCheck() {
|
|
314
|
+
return this.lastCheck;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get repair history.
|
|
318
|
+
*/
|
|
319
|
+
getRepairHistory() {
|
|
320
|
+
return [...this.repairHistory];
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if the database appears healthy.
|
|
324
|
+
*/
|
|
325
|
+
isHealthy() {
|
|
326
|
+
return this.lastCheck?.isHealthy ?? true;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get summary of database health.
|
|
330
|
+
*/
|
|
331
|
+
getHealthSummary() {
|
|
332
|
+
return {
|
|
333
|
+
isHealthy: this.lastCheck?.isHealthy ?? true,
|
|
334
|
+
lastCheckAt: this.lastCheck?.timestamp ?? null,
|
|
335
|
+
issueCount: this.lastCheck?.issues.length ?? 0,
|
|
336
|
+
repairAttempts: this.repairHistory.length,
|
|
337
|
+
successfulRepairs: this.repairHistory.filter((r) => r.success).length
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
// ============================================================================
|
|
341
|
+
// Utilities
|
|
342
|
+
// ============================================================================
|
|
343
|
+
/**
|
|
344
|
+
* Log a message if verbose mode is enabled.
|
|
345
|
+
*/
|
|
346
|
+
log(message) {
|
|
347
|
+
if (this.config.verbose) {
|
|
348
|
+
console.log(`[CorruptionDetector] ${message}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function createCorruptionDetector(adapter, config = {}) {
|
|
353
|
+
return new CorruptionDetector(adapter, config);
|
|
354
|
+
}
|
|
355
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
356
|
+
0 && (module.exports = {
|
|
357
|
+
CorruptionDetector,
|
|
358
|
+
createCorruptionDetector
|
|
359
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery Module for Electron SQL Storage Adapter.
|
|
3
|
+
*
|
|
4
|
+
* Provides database health monitoring, WAL management,
|
|
5
|
+
* corruption detection, and automatic repair capabilities.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { WalCheckpointManager, createWalCheckpointManager, type WalCheckpointConfig, type CheckpointStrategy, type CheckpointResult, type WalStatus, } from './walCheckpoint.js';
|
|
10
|
+
export { CorruptionDetector, createCorruptionDetector, type CorruptionDetectorConfig, type IntegrityCheckLevel, type IntegrityCheckResult, type CorruptionIssue, type RepairStrategy, type RepairResult, } from './corruptionDetector.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var recovery_exports = {};
|
|
19
|
+
__export(recovery_exports, {
|
|
20
|
+
CorruptionDetector: () => import_corruptionDetector.CorruptionDetector,
|
|
21
|
+
WalCheckpointManager: () => import_walCheckpoint.WalCheckpointManager,
|
|
22
|
+
createCorruptionDetector: () => import_corruptionDetector.createCorruptionDetector,
|
|
23
|
+
createWalCheckpointManager: () => import_walCheckpoint.createWalCheckpointManager
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(recovery_exports);
|
|
26
|
+
var import_walCheckpoint = require("./walCheckpoint.js");
|
|
27
|
+
var import_corruptionDetector = require("./corruptionDetector.js");
|
|
28
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
29
|
+
0 && (module.exports = {
|
|
30
|
+
CorruptionDetector,
|
|
31
|
+
WalCheckpointManager,
|
|
32
|
+
createCorruptionDetector,
|
|
33
|
+
createWalCheckpointManager
|
|
34
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WAL (Write-Ahead Logging) Checkpoint Manager.
|
|
3
|
+
*
|
|
4
|
+
* Manages SQLite WAL checkpointing for optimal performance and
|
|
5
|
+
* data durability. Handles automatic and manual checkpointing
|
|
6
|
+
* with configurable strategies.
|
|
7
|
+
*
|
|
8
|
+
* ## WAL Mode Benefits
|
|
9
|
+
* - Better concurrency (readers don't block writers)
|
|
10
|
+
* - Faster write performance
|
|
11
|
+
* - Atomic commits
|
|
12
|
+
*
|
|
13
|
+
* ## Checkpoint Strategies
|
|
14
|
+
* - PASSIVE: Non-blocking, best for background checkpointing
|
|
15
|
+
* - FULL: Blocks until complete, ensures WAL is cleared
|
|
16
|
+
* - RESTART: Blocks and restarts WAL, minimizes WAL growth
|
|
17
|
+
* - TRUNCATE: Most aggressive, truncates WAL file
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const manager = new WalCheckpointManager(adapter, {
|
|
22
|
+
* checkpointInterval: 300000,
|
|
23
|
+
* strategy: 'PASSIVE',
|
|
24
|
+
* maxWalSize: 50 * 1024 * 1024, // 50MB
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* await manager.start();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import type { StorageAdapter } from '../../../core/contracts/index.js';
|
|
31
|
+
/**
|
|
32
|
+
* WAL checkpoint strategy.
|
|
33
|
+
*/
|
|
34
|
+
export type CheckpointStrategy = 'PASSIVE' | 'FULL' | 'RESTART' | 'TRUNCATE';
|
|
35
|
+
/**
|
|
36
|
+
* WAL checkpoint manager configuration.
|
|
37
|
+
*/
|
|
38
|
+
export interface WalCheckpointConfig {
|
|
39
|
+
/** Interval between automatic checkpoints in milliseconds (default: 300000 = 5 min) */
|
|
40
|
+
checkpointInterval?: number;
|
|
41
|
+
/** Checkpoint strategy (default: 'PASSIVE') */
|
|
42
|
+
strategy?: CheckpointStrategy;
|
|
43
|
+
/** Maximum WAL size in bytes before forced checkpoint (default: 50MB) */
|
|
44
|
+
maxWalSize?: number;
|
|
45
|
+
/** Page size in bytes (default: 4096) */
|
|
46
|
+
pageSize?: number;
|
|
47
|
+
/** Auto-checkpoint page threshold (default: 1000) */
|
|
48
|
+
autoCheckpointPages?: number;
|
|
49
|
+
/** Synchronous mode (default: 'NORMAL') */
|
|
50
|
+
synchronous?: 'OFF' | 'NORMAL' | 'FULL' | 'EXTRA';
|
|
51
|
+
/** Enable verbose logging (default: false) */
|
|
52
|
+
verbose?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Result of a checkpoint operation.
|
|
56
|
+
*/
|
|
57
|
+
export interface CheckpointResult {
|
|
58
|
+
/** Whether the checkpoint was successful */
|
|
59
|
+
success: boolean;
|
|
60
|
+
/** Strategy used for this checkpoint */
|
|
61
|
+
strategy: CheckpointStrategy;
|
|
62
|
+
/** Number of WAL frames before checkpoint */
|
|
63
|
+
walFramesBefore: number;
|
|
64
|
+
/** Number of WAL frames after checkpoint */
|
|
65
|
+
walFramesAfter: number;
|
|
66
|
+
/** Number of frames moved to database */
|
|
67
|
+
framesCheckpointed: number;
|
|
68
|
+
/** Number of busy frames that couldn't be checkpointed */
|
|
69
|
+
busyFrames: number;
|
|
70
|
+
/** Time taken in milliseconds */
|
|
71
|
+
durationMs: number;
|
|
72
|
+
/** Timestamp of checkpoint */
|
|
73
|
+
timestamp: number;
|
|
74
|
+
/** Error message if failed */
|
|
75
|
+
error?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* WAL status information.
|
|
79
|
+
*/
|
|
80
|
+
export interface WalStatus {
|
|
81
|
+
/** Whether WAL mode is enabled */
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
/** Current WAL file size in bytes */
|
|
84
|
+
walSize: number;
|
|
85
|
+
/** Approximate number of pages in WAL */
|
|
86
|
+
walPages: number;
|
|
87
|
+
/** Last checkpoint timestamp */
|
|
88
|
+
lastCheckpoint: number | null;
|
|
89
|
+
/** Number of checkpoints performed */
|
|
90
|
+
totalCheckpoints: number;
|
|
91
|
+
/** Auto-checkpoint threshold in pages */
|
|
92
|
+
autoCheckpointThreshold: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* WAL Checkpoint Manager.
|
|
96
|
+
*
|
|
97
|
+
* Manages automatic and manual WAL checkpointing for SQLite databases.
|
|
98
|
+
* Ensures optimal performance and data durability.
|
|
99
|
+
*/
|
|
100
|
+
export declare class WalCheckpointManager {
|
|
101
|
+
private readonly adapter;
|
|
102
|
+
private readonly config;
|
|
103
|
+
private checkpointTimer;
|
|
104
|
+
private lastCheckpoint;
|
|
105
|
+
private totalCheckpoints;
|
|
106
|
+
private isRunning;
|
|
107
|
+
constructor(adapter: StorageAdapter, config?: WalCheckpointConfig);
|
|
108
|
+
/**
|
|
109
|
+
* Start the checkpoint manager.
|
|
110
|
+
*/
|
|
111
|
+
start(): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Stop the checkpoint manager.
|
|
114
|
+
*/
|
|
115
|
+
stop(): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Configure WAL mode on the database.
|
|
118
|
+
*/
|
|
119
|
+
private configureWalMode;
|
|
120
|
+
/**
|
|
121
|
+
* Disable WAL mode and switch back to DELETE journal mode.
|
|
122
|
+
*/
|
|
123
|
+
disableWalMode(): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Perform a WAL checkpoint.
|
|
126
|
+
*
|
|
127
|
+
* @param strategy - Checkpoint strategy (defaults to config strategy)
|
|
128
|
+
* @returns Checkpoint result
|
|
129
|
+
*/
|
|
130
|
+
checkpoint(strategy?: CheckpointStrategy): Promise<CheckpointResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Force a full checkpoint that clears the WAL.
|
|
133
|
+
*/
|
|
134
|
+
forceFullCheckpoint(): Promise<CheckpointResult>;
|
|
135
|
+
/**
|
|
136
|
+
* Start the automatic checkpoint timer.
|
|
137
|
+
*/
|
|
138
|
+
private startCheckpointTimer;
|
|
139
|
+
/**
|
|
140
|
+
* Stop the automatic checkpoint timer.
|
|
141
|
+
*/
|
|
142
|
+
private stopCheckpointTimer;
|
|
143
|
+
/**
|
|
144
|
+
* Get current WAL status.
|
|
145
|
+
*/
|
|
146
|
+
getStatus(): Promise<WalStatus>;
|
|
147
|
+
/**
|
|
148
|
+
* Get current WAL size in bytes.
|
|
149
|
+
*/
|
|
150
|
+
private getWalSize;
|
|
151
|
+
/**
|
|
152
|
+
* Get current WAL frame count.
|
|
153
|
+
*/
|
|
154
|
+
private getWalFrameCount;
|
|
155
|
+
/**
|
|
156
|
+
* Check if WAL mode is currently enabled.
|
|
157
|
+
*/
|
|
158
|
+
isWalEnabled(): Promise<boolean>;
|
|
159
|
+
/**
|
|
160
|
+
* Get checkpoint statistics.
|
|
161
|
+
*/
|
|
162
|
+
getStats(): {
|
|
163
|
+
totalCheckpoints: number;
|
|
164
|
+
lastCheckpoint: number | null;
|
|
165
|
+
isRunning: boolean;
|
|
166
|
+
checkpointInterval: number;
|
|
167
|
+
strategy: CheckpointStrategy;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Update checkpoint configuration at runtime.
|
|
171
|
+
*/
|
|
172
|
+
updateConfig(newConfig: Partial<WalCheckpointConfig>): void;
|
|
173
|
+
/**
|
|
174
|
+
* Log a message if verbose mode is enabled.
|
|
175
|
+
*/
|
|
176
|
+
private log;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create a WAL Checkpoint Manager.
|
|
180
|
+
*
|
|
181
|
+
* @param adapter - Storage adapter to manage
|
|
182
|
+
* @param config - Checkpoint configuration
|
|
183
|
+
* @returns WalCheckpointManager instance
|
|
184
|
+
*/
|
|
185
|
+
export declare function createWalCheckpointManager(adapter: StorageAdapter, config?: WalCheckpointConfig): WalCheckpointManager;
|
|
186
|
+
//# sourceMappingURL=walCheckpoint.d.ts.map
|