@framers/sql-storage-adapter 0.7.2 → 0.7.4

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 (194) hide show
  1. package/README.md +41 -0
  2. package/dist/adapters/electron/preload.cjs +289 -0
  3. package/dist/adapters/electron/renderer.d.ts +9 -0
  4. package/dist/adapters/electron/renderer.d.ts.map +1 -0
  5. package/dist/adapters/electron/renderer.js +8 -0
  6. package/dist/adapters/electron/renderer.js.map +1 -0
  7. package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.d.ts +2 -0
  8. package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.js +133 -0
  9. package/dist/cjs/adapters/baseStorageAdapter.d.ts +368 -0
  10. package/dist/cjs/adapters/baseStorageAdapter.js +744 -0
  11. package/dist/cjs/adapters/betterSqliteAdapter.d.ts +124 -0
  12. package/dist/cjs/adapters/betterSqliteAdapter.js +334 -0
  13. package/dist/cjs/adapters/capacitorSqliteAdapter.d.ts +83 -0
  14. package/dist/cjs/adapters/capacitorSqliteAdapter.js +250 -0
  15. package/dist/cjs/adapters/electron/electronMainAdapter.d.ts +251 -0
  16. package/dist/cjs/adapters/electron/electronMainAdapter.js +392 -0
  17. package/dist/cjs/adapters/electron/electronRendererAdapter.d.ts +177 -0
  18. package/dist/cjs/adapters/electron/electronRendererAdapter.js +283 -0
  19. package/dist/cjs/adapters/electron/index.d.ts +74 -0
  20. package/dist/cjs/adapters/electron/index.js +86 -0
  21. package/dist/cjs/adapters/electron/ipc/channels.d.ts +196 -0
  22. package/dist/cjs/adapters/electron/ipc/channels.js +127 -0
  23. package/dist/cjs/adapters/electron/ipc/index.d.ts +11 -0
  24. package/dist/cjs/adapters/electron/ipc/index.js +38 -0
  25. package/dist/cjs/adapters/electron/ipc/protocol.d.ts +78 -0
  26. package/dist/cjs/adapters/electron/ipc/protocol.js +341 -0
  27. package/dist/cjs/adapters/electron/ipc/types.d.ts +248 -0
  28. package/dist/cjs/adapters/electron/ipc/types.js +15 -0
  29. package/dist/cjs/adapters/electron/migration/autoMigrator.d.ts +184 -0
  30. package/dist/cjs/adapters/electron/migration/autoMigrator.js +441 -0
  31. package/dist/cjs/adapters/electron/migration/index.d.ts +9 -0
  32. package/dist/cjs/adapters/electron/migration/index.js +29 -0
  33. package/dist/cjs/adapters/electron/preload.d.ts +126 -0
  34. package/dist/cjs/adapters/electron/preload.js +233 -0
  35. package/dist/cjs/adapters/electron/recovery/corruptionDetector.d.ts +214 -0
  36. package/dist/cjs/adapters/electron/recovery/corruptionDetector.js +359 -0
  37. package/dist/cjs/adapters/electron/recovery/index.d.ts +11 -0
  38. package/dist/cjs/adapters/electron/recovery/index.js +34 -0
  39. package/dist/cjs/adapters/electron/recovery/walCheckpoint.d.ts +186 -0
  40. package/dist/cjs/adapters/electron/recovery/walCheckpoint.js +263 -0
  41. package/dist/cjs/adapters/electron/renderer.d.ts +9 -0
  42. package/dist/cjs/adapters/electron/renderer.js +33 -0
  43. package/dist/cjs/adapters/electron/window/index.d.ts +9 -0
  44. package/dist/cjs/adapters/electron/window/index.js +29 -0
  45. package/dist/cjs/adapters/electron/window/windowManager.d.ts +190 -0
  46. package/dist/cjs/adapters/electron/window/windowManager.js +321 -0
  47. package/dist/cjs/adapters/indexedDbAdapter.d.ts +490 -0
  48. package/dist/cjs/adapters/indexedDbAdapter.js +993 -0
  49. package/dist/cjs/adapters/postgresAdapter.d.ts +424 -0
  50. package/dist/cjs/adapters/postgresAdapter.js +889 -0
  51. package/dist/cjs/adapters/sqlJsAdapter.d.ts +64 -0
  52. package/dist/cjs/adapters/sqlJsAdapter.js +268 -0
  53. package/dist/cjs/adapters/supabase.d.ts +58 -0
  54. package/dist/cjs/adapters/supabase.js +401 -0
  55. package/dist/cjs/codecs/BrowserBlobCodec.d.ts +14 -0
  56. package/dist/cjs/codecs/BrowserBlobCodec.js +57 -0
  57. package/dist/cjs/codecs/NodeBlobCodec.d.ts +14 -0
  58. package/dist/cjs/codecs/NodeBlobCodec.js +59 -0
  59. package/dist/cjs/core/contracts/blobCodec.d.ts +15 -0
  60. package/dist/cjs/core/contracts/blobCodec.js +15 -0
  61. package/dist/cjs/core/contracts/context.d.ts +221 -0
  62. package/dist/cjs/core/contracts/context.js +15 -0
  63. package/dist/cjs/core/contracts/dialect.d.ts +154 -0
  64. package/dist/cjs/core/contracts/dialect.js +15 -0
  65. package/dist/cjs/core/contracts/events.d.ts +225 -0
  66. package/dist/cjs/core/contracts/events.js +15 -0
  67. package/dist/cjs/core/contracts/exporter.d.ts +13 -0
  68. package/dist/cjs/core/contracts/exporter.js +15 -0
  69. package/dist/cjs/core/contracts/extensions.d.ts +73 -0
  70. package/dist/cjs/core/contracts/extensions.js +15 -0
  71. package/dist/cjs/core/contracts/features.d.ts +25 -0
  72. package/dist/cjs/core/contracts/features.js +61 -0
  73. package/dist/cjs/core/contracts/fts.d.ts +66 -0
  74. package/dist/cjs/core/contracts/fts.js +15 -0
  75. package/dist/cjs/core/contracts/hooks.d.ts +481 -0
  76. package/dist/cjs/core/contracts/hooks.js +140 -0
  77. package/dist/cjs/core/contracts/index.d.ts +260 -0
  78. package/dist/cjs/core/contracts/index.js +41 -0
  79. package/dist/cjs/core/contracts/limitations.d.ts +46 -0
  80. package/dist/cjs/core/contracts/limitations.js +218 -0
  81. package/dist/cjs/core/contracts/performance.d.ts +308 -0
  82. package/dist/cjs/core/contracts/performance.js +144 -0
  83. package/dist/cjs/core/database.d.ts +220 -0
  84. package/dist/cjs/core/database.js +136 -0
  85. package/dist/cjs/core/portable.d.ts +24 -0
  86. package/dist/cjs/core/portable.js +68 -0
  87. package/dist/cjs/core/resolver.d.ts +77 -0
  88. package/dist/cjs/core/resolver.js +235 -0
  89. package/dist/cjs/dialects/PostgresDialect.d.ts +35 -0
  90. package/dist/cjs/dialects/PostgresDialect.js +143 -0
  91. package/dist/cjs/dialects/SqliteDialect.d.ts +35 -0
  92. package/dist/cjs/dialects/SqliteDialect.js +142 -0
  93. package/dist/cjs/dialects/shared.d.ts +17 -0
  94. package/dist/cjs/dialects/shared.js +90 -0
  95. package/dist/cjs/exporters/PostgresExporter.d.ts +14 -0
  96. package/dist/cjs/exporters/PostgresExporter.js +61 -0
  97. package/dist/cjs/exporters/SqliteFileExporter.d.ts +16 -0
  98. package/dist/cjs/exporters/SqliteFileExporter.js +43 -0
  99. package/dist/cjs/features/auth/authJsAdapter.d.ts +110 -0
  100. package/dist/cjs/features/auth/authJsAdapter.js +251 -0
  101. package/dist/cjs/features/backup/cloudBackup.d.ts +219 -0
  102. package/dist/cjs/features/backup/cloudBackup.js +209 -0
  103. package/dist/cjs/features/kv/idb.d.ts +79 -0
  104. package/dist/cjs/features/kv/idb.js +32 -0
  105. package/dist/cjs/features/kv/index.d.ts +14 -0
  106. package/dist/cjs/features/kv/index.js +40 -0
  107. package/dist/cjs/features/kv/indexedDbKeyValueStore.d.ts +77 -0
  108. package/dist/cjs/features/kv/indexedDbKeyValueStore.js +116 -0
  109. package/dist/cjs/features/kv/keyValueStore.d.ts +48 -0
  110. package/dist/cjs/features/kv/keyValueStore.js +67 -0
  111. package/dist/cjs/features/kv/sqlKeyValueStore.d.ts +34 -0
  112. package/dist/cjs/features/kv/sqlKeyValueStore.js +81 -0
  113. package/dist/cjs/features/kv/valueCodec.d.ts +35 -0
  114. package/dist/cjs/features/kv/valueCodec.js +426 -0
  115. package/dist/cjs/features/migrations/dataExport.d.ts +77 -0
  116. package/dist/cjs/features/migrations/dataExport.js +194 -0
  117. package/dist/cjs/features/migrations/dataImport.d.ts +54 -0
  118. package/dist/cjs/features/migrations/dataImport.js +300 -0
  119. package/dist/cjs/features/migrations/migration.d.ts +89 -0
  120. package/dist/cjs/features/migrations/migration.js +187 -0
  121. package/dist/cjs/features/migrations/schemaMigrations.d.ts +93 -0
  122. package/dist/cjs/features/migrations/schemaMigrations.js +220 -0
  123. package/dist/cjs/features/sync/conflicts/conflictResolver.d.ts +222 -0
  124. package/dist/cjs/features/sync/conflicts/conflictResolver.js +366 -0
  125. package/dist/cjs/features/sync/conflicts/index.d.ts +9 -0
  126. package/dist/cjs/features/sync/conflicts/index.js +21 -0
  127. package/dist/cjs/features/sync/crossPlatformSync.d.ts +281 -0
  128. package/dist/cjs/features/sync/crossPlatformSync.js +551 -0
  129. package/dist/cjs/features/sync/devices/deviceManager.d.ts +243 -0
  130. package/dist/cjs/features/sync/devices/deviceManager.js +450 -0
  131. package/dist/cjs/features/sync/devices/index.d.ts +10 -0
  132. package/dist/cjs/features/sync/devices/index.js +21 -0
  133. package/dist/cjs/features/sync/index.d.ts +37 -0
  134. package/dist/cjs/features/sync/index.js +111 -0
  135. package/dist/cjs/features/sync/protocol/index.d.ts +11 -0
  136. package/dist/cjs/features/sync/protocol/index.js +23 -0
  137. package/dist/cjs/features/sync/protocol/messages.d.ts +348 -0
  138. package/dist/cjs/features/sync/protocol/messages.js +215 -0
  139. package/dist/cjs/features/sync/protocol/vectorClock.d.ts +164 -0
  140. package/dist/cjs/features/sync/protocol/vectorClock.js +256 -0
  141. package/dist/cjs/features/sync/syncManager.d.ts +348 -0
  142. package/dist/cjs/features/sync/syncManager.js +455 -0
  143. package/dist/cjs/features/sync/tables/index.d.ts +10 -0
  144. package/dist/cjs/features/sync/tables/index.js +21 -0
  145. package/dist/cjs/features/sync/tables/syncLogManager.d.ts +216 -0
  146. package/dist/cjs/features/sync/tables/syncLogManager.js +423 -0
  147. package/dist/cjs/features/sync/transport/httpTransport.d.ts +123 -0
  148. package/dist/cjs/features/sync/transport/httpTransport.js +346 -0
  149. package/dist/cjs/features/sync/transport/index.d.ts +12 -0
  150. package/dist/cjs/features/sync/transport/index.js +25 -0
  151. package/dist/cjs/features/sync/transport/transport.d.ts +259 -0
  152. package/dist/cjs/features/sync/transport/transport.js +163 -0
  153. package/dist/cjs/features/sync/transport/websocketTransport.d.ts +126 -0
  154. package/dist/cjs/features/sync/transport/websocketTransport.js +342 -0
  155. package/dist/cjs/fts/PostgresFts.d.ts +29 -0
  156. package/dist/cjs/fts/PostgresFts.js +77 -0
  157. package/dist/cjs/fts/SqliteFts5.d.ts +22 -0
  158. package/dist/cjs/fts/SqliteFts5.js +58 -0
  159. package/dist/cjs/index.d.ts +31 -0
  160. package/dist/cjs/index.js +103 -0
  161. package/dist/cjs/package.json +3 -0
  162. package/dist/cjs/shared/connectionGate.d.ts +94 -0
  163. package/dist/cjs/shared/connectionGate.js +77 -0
  164. package/dist/cjs/shared/errors.d.ts +23 -0
  165. package/dist/cjs/shared/errors.js +45 -0
  166. package/dist/cjs/shared/logger.d.ts +26 -0
  167. package/dist/cjs/shared/logger.js +40 -0
  168. package/dist/cjs/shared/parameterUtils.d.ts +16 -0
  169. package/dist/cjs/shared/parameterUtils.js +59 -0
  170. package/dist/cjs/shared/postgresTarget.d.ts +11 -0
  171. package/dist/cjs/shared/postgresTarget.js +30 -0
  172. package/dist/cjs/shared/segment.d.ts +43 -0
  173. package/dist/cjs/shared/segment.js +63 -0
  174. package/dist/cjs/shared/serializedTransaction.d.ts +14 -0
  175. package/dist/cjs/shared/serializedTransaction.js +74 -0
  176. package/dist/cjs/shared/splitSqlStatements.d.ts +27 -0
  177. package/dist/cjs/shared/splitSqlStatements.js +163 -0
  178. package/dist/cjs/shared/sqlPlaceholders.d.ts +48 -0
  179. package/dist/cjs/shared/sqlPlaceholders.js +263 -0
  180. package/dist/cjs/types/index.d.ts +29 -0
  181. package/dist/cjs/types/index.js +33 -0
  182. package/dist/core/portable.d.ts +24 -0
  183. package/dist/core/portable.d.ts.map +1 -0
  184. package/dist/core/portable.js +24 -0
  185. package/dist/core/portable.js.map +1 -0
  186. package/dist/features/auth/authJsAdapter.d.ts +110 -0
  187. package/dist/features/auth/authJsAdapter.d.ts.map +1 -0
  188. package/dist/features/auth/authJsAdapter.js +248 -0
  189. package/dist/features/auth/authJsAdapter.js.map +1 -0
  190. package/dist/index.d.ts +1 -0
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.js +1 -0
  193. package/dist/index.js.map +1 -1
  194. package/package.json +149 -24
@@ -0,0 +1,744 @@
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 baseStorageAdapter_exports = {};
19
+ __export(baseStorageAdapter_exports, {
20
+ BaseStorageAdapter: () => BaseStorageAdapter
21
+ });
22
+ module.exports = __toCommonJS(baseStorageAdapter_exports);
23
+ var import_performance = require("../core/contracts/performance.js");
24
+ var import_hooks = require("../core/contracts/hooks.js");
25
+ var import_logger = require("../shared/logger.js");
26
+ var AdapterState;
27
+ (function(AdapterState2) {
28
+ AdapterState2["CLOSED"] = "closed";
29
+ AdapterState2["OPENING"] = "opening";
30
+ AdapterState2["OPEN"] = "open";
31
+ AdapterState2["CLOSING"] = "closing";
32
+ AdapterState2["ERROR"] = "error";
33
+ })(AdapterState || (AdapterState = {}));
34
+ class BaseStorageAdapter {
35
+ /**
36
+ * Creates a new adapter instance.
37
+ *
38
+ * @param options - Configuration options for the adapter
39
+ *
40
+ * @example Default balanced tier
41
+ * ```typescript
42
+ * const adapter = new MyAdapter();
43
+ * ```
44
+ *
45
+ * @example Fast tier for development
46
+ * ```typescript
47
+ * const adapter = new MyAdapter({
48
+ * performance: { tier: 'fast' },
49
+ * verbose: true
50
+ * });
51
+ * ```
52
+ *
53
+ * @example With RAG hooks
54
+ * ```typescript
55
+ * const adapter = new MyAdapter({
56
+ * hooks: {
57
+ * onBeforeWrite: async (ctx) => {
58
+ * ctx.metadata = { embedding: await embed(ctx.parameters) };
59
+ * return ctx;
60
+ * }
61
+ * }
62
+ * });
63
+ * ```
64
+ */
65
+ constructor(options = {}) {
66
+ this.state = AdapterState.CLOSED;
67
+ this.metrics = {
68
+ totalQueries: 0,
69
+ totalMutations: 0,
70
+ totalTransactions: 0,
71
+ totalErrors: 0,
72
+ averageQueryDuration: 0,
73
+ openedAt: null,
74
+ slowQueries: 0,
75
+ retries: 0
76
+ };
77
+ this.queryCache = /* @__PURE__ */ new Map();
78
+ this.cacheStats = {
79
+ hits: 0,
80
+ misses: 0,
81
+ hitRatio: 0,
82
+ size: 0,
83
+ bytesUsed: 0,
84
+ evictions: 0,
85
+ invalidations: 0
86
+ };
87
+ this.queryDurations = [];
88
+ this.MAX_DURATION_SAMPLES = 100;
89
+ this.options = options;
90
+ const perfConfig = options.performance ?? {};
91
+ const resolvedSettings = (0, import_performance.resolvePerformanceConfig)(perfConfig);
92
+ if (options.validateSQL !== void 0) {
93
+ this.performanceSettings = {
94
+ ...resolvedSettings,
95
+ validateSql: options.validateSQL
96
+ };
97
+ } else {
98
+ this.performanceSettings = resolvedSettings;
99
+ }
100
+ this.hooks = options.hooks ?? {};
101
+ }
102
+ // ============================================================================
103
+ // Public Interface (Template Methods)
104
+ // ============================================================================
105
+ /**
106
+ * Opens the adapter connection.
107
+ * Handles state management and delegates to subclass implementation.
108
+ */
109
+ async open(options) {
110
+ if (this.state === AdapterState.OPEN) {
111
+ this.log("Adapter already open, skipping open()");
112
+ return;
113
+ }
114
+ if (this.state === AdapterState.OPENING) {
115
+ throw new Error(`[${this.kind}] Adapter is already opening`);
116
+ }
117
+ this.state = AdapterState.OPENING;
118
+ try {
119
+ await this.performOpen(options);
120
+ this.state = AdapterState.OPEN;
121
+ this.metrics.openedAt = /* @__PURE__ */ new Date();
122
+ this.log("Adapter opened successfully");
123
+ } catch (error) {
124
+ this.state = AdapterState.ERROR;
125
+ this.metrics.totalErrors++;
126
+ throw this.wrapError("Failed to open adapter", error);
127
+ }
128
+ }
129
+ /**
130
+ * Executes a mutation statement (INSERT, UPDATE, DELETE).
131
+ *
132
+ * Invokes `onBeforeWrite` and `onAfterWrite` hooks if configured.
133
+ * Supports retry on transient errors based on performance tier.
134
+ */
135
+ async run(statement, parameters) {
136
+ this.assertOpen();
137
+ this.validateStatement(statement);
138
+ const startTime = Date.now();
139
+ const operationId = (0, import_hooks.generateOperationId)();
140
+ let context = {
141
+ operationId,
142
+ operation: "run",
143
+ startTime,
144
+ adapterKind: this.kind,
145
+ statement,
146
+ parameters,
147
+ affectedTables: this.extractTables(statement)
148
+ };
149
+ try {
150
+ if (this.hooks.onBeforeWrite) {
151
+ const hookResult = await this.hooks.onBeforeWrite(context);
152
+ if (hookResult === void 0) {
153
+ return { changes: 0 };
154
+ }
155
+ context = hookResult;
156
+ }
157
+ const result = await this.executeWithRetry(() => this.performRun(context.statement, context.parameters), context);
158
+ const duration = Date.now() - startTime;
159
+ this.metrics.totalMutations++;
160
+ this.trackDuration(duration);
161
+ this.checkSlowQuery(duration, context.statement);
162
+ if (this.performanceSettings.cacheEnabled && context.affectedTables) {
163
+ this.invalidateCache(context.affectedTables);
164
+ }
165
+ this.log(`Mutation executed: ${statement.substring(0, 50)}... (${result.changes} rows affected)`);
166
+ if (this.hooks.onAfterWrite) {
167
+ await this.hooks.onAfterWrite(context, result);
168
+ }
169
+ return result;
170
+ } catch (error) {
171
+ this.metrics.totalErrors++;
172
+ const wrappedError = this.wrapError(`Failed to execute mutation: ${statement}`, error);
173
+ if (this.hooks.onError) {
174
+ const hookResult = await this.hooks.onError(wrappedError, context);
175
+ if (hookResult === void 0) {
176
+ return { changes: 0 };
177
+ }
178
+ throw hookResult;
179
+ }
180
+ throw wrappedError;
181
+ }
182
+ }
183
+ /**
184
+ * Retrieves a single row.
185
+ *
186
+ * Supports caching based on performance tier and invokes query hooks.
187
+ */
188
+ async get(statement, parameters) {
189
+ this.assertOpen();
190
+ this.validateStatement(statement);
191
+ const startTime = Date.now();
192
+ const operationId = (0, import_hooks.generateOperationId)();
193
+ const cacheKey = this.getCacheKey("get", statement, parameters);
194
+ if (this.performanceSettings.cacheEnabled) {
195
+ const cached = this.getFromCache(cacheKey);
196
+ if (cached !== void 0) {
197
+ return cached;
198
+ }
199
+ }
200
+ let context = {
201
+ operationId,
202
+ operation: "get",
203
+ startTime,
204
+ adapterKind: this.kind,
205
+ statement,
206
+ parameters,
207
+ affectedTables: this.extractTables(statement)
208
+ };
209
+ try {
210
+ if (this.hooks.onBeforeQuery) {
211
+ const hookResult = await this.hooks.onBeforeQuery(context);
212
+ if (hookResult === void 0) {
213
+ return null;
214
+ }
215
+ context = hookResult;
216
+ }
217
+ let result = await this.executeWithRetry(() => this.performGet(context.statement, context.parameters), context);
218
+ const duration = Date.now() - startTime;
219
+ this.metrics.totalQueries++;
220
+ this.trackDuration(duration);
221
+ this.checkSlowQuery(duration, context.statement);
222
+ this.log(`Query executed: ${statement.substring(0, 50)}... (${result ? "1 row" : "no rows"})`);
223
+ if (this.hooks.onAfterQuery) {
224
+ const hookResult = await this.hooks.onAfterQuery(context, result);
225
+ if (hookResult !== void 0) {
226
+ result = hookResult;
227
+ }
228
+ }
229
+ if (this.performanceSettings.cacheEnabled) {
230
+ this.setCache(cacheKey, result, context.affectedTables);
231
+ }
232
+ return result;
233
+ } catch (error) {
234
+ this.metrics.totalErrors++;
235
+ const wrappedError = this.wrapError(`Failed to execute query: ${statement}`, error);
236
+ if (this.hooks.onError) {
237
+ const hookResult = await this.hooks.onError(wrappedError, context);
238
+ if (hookResult === void 0) {
239
+ return null;
240
+ }
241
+ throw hookResult;
242
+ }
243
+ throw wrappedError;
244
+ }
245
+ }
246
+ /**
247
+ * Retrieves all rows.
248
+ *
249
+ * Supports caching based on performance tier and invokes query hooks.
250
+ */
251
+ async all(statement, parameters) {
252
+ this.assertOpen();
253
+ this.validateStatement(statement);
254
+ const startTime = Date.now();
255
+ const operationId = (0, import_hooks.generateOperationId)();
256
+ const cacheKey = this.getCacheKey("all", statement, parameters);
257
+ if (this.performanceSettings.cacheEnabled) {
258
+ const cached = this.getFromCache(cacheKey);
259
+ if (cached !== void 0) {
260
+ return cached;
261
+ }
262
+ }
263
+ let context = {
264
+ operationId,
265
+ operation: "all",
266
+ startTime,
267
+ adapterKind: this.kind,
268
+ statement,
269
+ parameters,
270
+ affectedTables: this.extractTables(statement)
271
+ };
272
+ try {
273
+ if (this.hooks.onBeforeQuery) {
274
+ const hookResult = await this.hooks.onBeforeQuery(context);
275
+ if (hookResult === void 0) {
276
+ return [];
277
+ }
278
+ context = hookResult;
279
+ }
280
+ let results = await this.executeWithRetry(() => this.performAll(context.statement, context.parameters), context);
281
+ const duration = Date.now() - startTime;
282
+ this.metrics.totalQueries++;
283
+ this.trackDuration(duration);
284
+ this.checkSlowQuery(duration, context.statement);
285
+ this.log(`Query executed: ${statement.substring(0, 50)}... (${results.length} rows)`);
286
+ if (this.hooks.onAfterQuery) {
287
+ const hookResult = await this.hooks.onAfterQuery(context, results);
288
+ if (hookResult !== void 0) {
289
+ results = hookResult;
290
+ }
291
+ }
292
+ if (this.performanceSettings.cacheEnabled) {
293
+ this.setCache(cacheKey, results, context.affectedTables);
294
+ }
295
+ return results;
296
+ } catch (error) {
297
+ this.metrics.totalErrors++;
298
+ const wrappedError = this.wrapError(`Failed to execute query: ${statement}`, error);
299
+ if (this.hooks.onError) {
300
+ const hookResult = await this.hooks.onError(wrappedError, context);
301
+ if (hookResult === void 0) {
302
+ return [];
303
+ }
304
+ throw hookResult;
305
+ }
306
+ throw wrappedError;
307
+ }
308
+ }
309
+ /**
310
+ * Executes a SQL script.
311
+ */
312
+ async exec(script) {
313
+ this.assertOpen();
314
+ if (!script || !script.trim()) {
315
+ throw new Error("SQL script cannot be empty");
316
+ }
317
+ const startTime = Date.now();
318
+ try {
319
+ await this.performExec(script);
320
+ this.trackDuration(Date.now() - startTime);
321
+ this.log(`Script executed successfully`);
322
+ } catch (error) {
323
+ this.metrics.totalErrors++;
324
+ throw this.wrapError("Failed to execute script", error);
325
+ }
326
+ }
327
+ /**
328
+ * Executes a transaction.
329
+ *
330
+ * Invokes transaction hooks and invalidates cache on completion.
331
+ */
332
+ async transaction(fn) {
333
+ this.assertOpen();
334
+ const startTime = Date.now();
335
+ const operationId = (0, import_hooks.generateOperationId)();
336
+ let context = {
337
+ operationId,
338
+ operation: "transaction",
339
+ startTime,
340
+ adapterKind: this.kind
341
+ };
342
+ try {
343
+ if (this.hooks.onBeforeTransaction) {
344
+ const hookResult = await this.hooks.onBeforeTransaction(context);
345
+ if (hookResult === void 0) {
346
+ throw new Error("Transaction aborted by hook");
347
+ }
348
+ context = hookResult;
349
+ }
350
+ const result = await this.performTransaction(fn);
351
+ context.outcome = "committed";
352
+ this.metrics.totalTransactions++;
353
+ this.trackDuration(Date.now() - startTime);
354
+ this.log("Transaction committed successfully");
355
+ if (this.performanceSettings.cacheEnabled) {
356
+ this.clearCache();
357
+ }
358
+ if (this.hooks.onAfterTransaction) {
359
+ await this.hooks.onAfterTransaction(context);
360
+ }
361
+ return result;
362
+ } catch (error) {
363
+ context.outcome = "rolled_back";
364
+ this.metrics.totalErrors++;
365
+ const wrappedError = this.wrapError("Transaction failed", error);
366
+ if (this.hooks.onAfterTransaction) {
367
+ await this.hooks.onAfterTransaction(context);
368
+ }
369
+ if (this.hooks.onError) {
370
+ const hookResult = await this.hooks.onError(wrappedError, context);
371
+ if (hookResult === void 0) {
372
+ throw new Error("Transaction rolled back");
373
+ }
374
+ throw hookResult;
375
+ }
376
+ throw wrappedError;
377
+ }
378
+ }
379
+ /**
380
+ * Closes the adapter connection.
381
+ */
382
+ async close() {
383
+ if (this.state === AdapterState.CLOSED) {
384
+ this.log("Adapter already closed, skipping close()");
385
+ return;
386
+ }
387
+ if (this.state === AdapterState.CLOSING) {
388
+ throw new Error(`[${this.kind}] Adapter is already closing`);
389
+ }
390
+ this.state = AdapterState.CLOSING;
391
+ try {
392
+ await this.performClose();
393
+ this.state = AdapterState.CLOSED;
394
+ this.log("Adapter closed successfully");
395
+ } catch (error) {
396
+ this.state = AdapterState.ERROR;
397
+ throw this.wrapError("Failed to close adapter", error);
398
+ }
399
+ }
400
+ /**
401
+ * Executes a batch of operations (optional).
402
+ */
403
+ async batch(operations) {
404
+ this.assertOpen();
405
+ if (!this.capabilities.has("batch")) {
406
+ throw new Error(`[${this.kind}] Batch operations are not supported`);
407
+ }
408
+ if (!this.performBatch) {
409
+ throw new Error(`[${this.kind}] Batch operations not implemented`);
410
+ }
411
+ if (!operations || operations.length === 0) {
412
+ throw new Error("Batch operations cannot be empty");
413
+ }
414
+ const startTime = Date.now();
415
+ try {
416
+ const result = await this.performBatch(operations);
417
+ this.trackDuration(Date.now() - startTime);
418
+ this.log(`Batch executed: ${result.successful} successful, ${result.failed} failed`);
419
+ return result;
420
+ } catch (error) {
421
+ this.metrics.totalErrors++;
422
+ throw this.wrapError("Batch execution failed", error);
423
+ }
424
+ }
425
+ /**
426
+ * Creates a prepared statement (optional).
427
+ */
428
+ prepare(statement) {
429
+ this.assertOpen();
430
+ if (!this.capabilities.has("prepared")) {
431
+ throw new Error(`[${this.kind}] Prepared statements are not supported`);
432
+ }
433
+ if (!this.performPrepare) {
434
+ throw new Error(`[${this.kind}] Prepared statements not implemented`);
435
+ }
436
+ this.validateStatement(statement);
437
+ return this.performPrepare(statement);
438
+ }
439
+ // ============================================================================
440
+ // Protected Helper Methods (Available to subclasses)
441
+ // ============================================================================
442
+ /**
443
+ * Asserts that adapter is in open state.
444
+ * @throws {Error} If adapter is not open
445
+ */
446
+ assertOpen() {
447
+ if (this.state !== AdapterState.OPEN) {
448
+ throw new Error(`[${this.kind}] Adapter is not open (current state: ${this.state})`);
449
+ }
450
+ }
451
+ /**
452
+ * Validates SQL statement.
453
+ * @throws {Error} If statement is invalid
454
+ */
455
+ validateStatement(statement) {
456
+ if (!this.performanceSettings.validateSql) {
457
+ return;
458
+ }
459
+ if (!statement || !statement.trim()) {
460
+ throw new Error("SQL statement cannot be empty");
461
+ }
462
+ if (statement.includes("--") && !statement.includes("-- ")) {
463
+ this.log("Warning: SQL comment detected in statement");
464
+ }
465
+ }
466
+ // ============================================================================
467
+ // Cache Management
468
+ // ============================================================================
469
+ /**
470
+ * Generates a cache key for a query.
471
+ * Includes operation type to prevent get/all cache collisions.
472
+ */
473
+ getCacheKey(operation, statement, parameters) {
474
+ const paramStr = parameters ? JSON.stringify(parameters) : "";
475
+ return `${operation}::${statement}::${paramStr}`;
476
+ }
477
+ /**
478
+ * Gets a value from cache if valid.
479
+ */
480
+ getFromCache(key) {
481
+ const entry = this.queryCache.get(key);
482
+ if (!entry) {
483
+ this.cacheStats.misses++;
484
+ this.updateCacheHitRatio();
485
+ return void 0;
486
+ }
487
+ if (Date.now() > entry.expiresAt) {
488
+ this.queryCache.delete(key);
489
+ this.cacheStats.misses++;
490
+ this.updateCacheHitRatio();
491
+ return void 0;
492
+ }
493
+ entry.hits++;
494
+ entry.lastAccessedAt = Date.now();
495
+ this.cacheStats.hits++;
496
+ this.updateCacheHitRatio();
497
+ return entry.data;
498
+ }
499
+ /**
500
+ * Sets a value in cache.
501
+ */
502
+ setCache(key, data, affectedTables) {
503
+ if (this.queryCache.size >= this.performanceSettings.cacheMaxEntries) {
504
+ this.evictLRU();
505
+ }
506
+ const now = Date.now();
507
+ const entry = {
508
+ data,
509
+ createdAt: now,
510
+ expiresAt: now + this.performanceSettings.cacheTtlMs,
511
+ affectedTables: affectedTables ?? [],
512
+ hits: 0,
513
+ lastAccessedAt: now
514
+ };
515
+ this.queryCache.set(key, entry);
516
+ this.cacheStats.size = this.queryCache.size;
517
+ }
518
+ /**
519
+ * Invalidates cache entries for specific tables.
520
+ */
521
+ invalidateCache(tables) {
522
+ const tableSet = new Set(tables.map((t) => t.toLowerCase()));
523
+ for (const [key, entry] of this.queryCache.entries()) {
524
+ const hasAffectedTable = entry.affectedTables.some((t) => tableSet.has(t.toLowerCase()));
525
+ if (hasAffectedTable) {
526
+ this.queryCache.delete(key);
527
+ this.cacheStats.invalidations++;
528
+ }
529
+ }
530
+ this.cacheStats.size = this.queryCache.size;
531
+ }
532
+ /**
533
+ * Clears entire cache.
534
+ */
535
+ clearCache() {
536
+ this.queryCache.clear();
537
+ this.cacheStats.size = 0;
538
+ }
539
+ /**
540
+ * Evicts least recently used cache entry.
541
+ */
542
+ evictLRU() {
543
+ let oldest = null;
544
+ for (const [key, entry] of this.queryCache.entries()) {
545
+ if (!oldest || entry.lastAccessedAt < oldest.time) {
546
+ oldest = { key, time: entry.lastAccessedAt };
547
+ }
548
+ }
549
+ if (oldest) {
550
+ this.queryCache.delete(oldest.key);
551
+ this.cacheStats.evictions++;
552
+ }
553
+ }
554
+ /**
555
+ * Updates cache hit ratio.
556
+ */
557
+ updateCacheHitRatio() {
558
+ const total = this.cacheStats.hits + this.cacheStats.misses;
559
+ this.cacheStats.hitRatio = total > 0 ? this.cacheStats.hits / total : 0;
560
+ }
561
+ // ============================================================================
562
+ // Retry Logic
563
+ // ============================================================================
564
+ /**
565
+ * Executes an operation with retry logic.
566
+ */
567
+ async executeWithRetry(operation, context) {
568
+ if (!this.performanceSettings.retryOnError) {
569
+ return operation();
570
+ }
571
+ let lastError;
572
+ for (let attempt = 0; attempt <= this.performanceSettings.maxRetries; attempt++) {
573
+ try {
574
+ return await operation();
575
+ } catch (error) {
576
+ lastError = error instanceof Error ? error : new Error(String(error));
577
+ if (!(0, import_performance.isTransientError)(lastError) || attempt >= this.performanceSettings.maxRetries) {
578
+ throw lastError;
579
+ }
580
+ this.metrics.retries++;
581
+ const delay = this.performanceSettings.retryDelayMs * Math.pow(2, attempt);
582
+ this.log(`Retrying operation ${context.operationId} in ${delay}ms (attempt ${attempt + 1})`);
583
+ await this.sleep(delay);
584
+ }
585
+ }
586
+ throw lastError;
587
+ }
588
+ /**
589
+ * Sleep utility for retry delays.
590
+ */
591
+ sleep(ms) {
592
+ return new Promise((resolve) => setTimeout(resolve, ms));
593
+ }
594
+ // ============================================================================
595
+ // SQL Parsing Utilities
596
+ // ============================================================================
597
+ /**
598
+ * Extracts table names from a SQL statement.
599
+ *
600
+ * @remarks
601
+ * Simple regex-based extraction. For complex queries, consider
602
+ * using a proper SQL parser.
603
+ */
604
+ extractTables(statement) {
605
+ const tables = [];
606
+ const upperStatement = statement.toUpperCase();
607
+ const fromMatch = upperStatement.match(/FROM\s+([^\s,;(]+)/gi);
608
+ if (fromMatch) {
609
+ fromMatch.forEach((match) => {
610
+ const table = match.replace(/^FROM\s+/i, "").trim();
611
+ if (table && !table.startsWith("(")) {
612
+ tables.push(table.toLowerCase());
613
+ }
614
+ });
615
+ }
616
+ const insertMatch = statement.match(/INSERT\s+INTO\s+([^\s(]+)/i);
617
+ if (insertMatch?.[1]) {
618
+ tables.push(insertMatch[1].toLowerCase());
619
+ }
620
+ const updateMatch = statement.match(/UPDATE\s+([^\s]+)/i);
621
+ if (updateMatch?.[1]) {
622
+ tables.push(updateMatch[1].toLowerCase());
623
+ }
624
+ const deleteMatch = statement.match(/DELETE\s+FROM\s+([^\s]+)/i);
625
+ if (deleteMatch?.[1]) {
626
+ tables.push(deleteMatch[1].toLowerCase());
627
+ }
628
+ const joinMatches = statement.match(/JOIN\s+([^\s]+)/gi);
629
+ if (joinMatches) {
630
+ joinMatches.forEach((match) => {
631
+ const table = match.replace(/^JOIN\s+/i, "").trim();
632
+ tables.push(table.toLowerCase());
633
+ });
634
+ }
635
+ return [...new Set(tables)];
636
+ }
637
+ /**
638
+ * Checks if query duration exceeds slow query threshold.
639
+ */
640
+ checkSlowQuery(duration, statement) {
641
+ if (duration > this.performanceSettings.slowQueryThresholdMs) {
642
+ this.metrics.slowQueries++;
643
+ this.log(`SLOW QUERY (${duration}ms): ${statement.substring(0, 100)}...`);
644
+ }
645
+ }
646
+ /**
647
+ * Wraps an error with adapter context.
648
+ */
649
+ wrapError(message, error) {
650
+ const originalMessage = error instanceof Error ? error.message : String(error);
651
+ const wrappedError = new Error(`[${this.kind}] ${message}: ${originalMessage}`);
652
+ if (error instanceof Error && error.stack) {
653
+ wrappedError.stack = error.stack;
654
+ }
655
+ return wrappedError;
656
+ }
657
+ /**
658
+ * Logs a message if verbose mode is enabled.
659
+ */
660
+ log(message) {
661
+ if (this.options.verbose) {
662
+ const logger = (0, import_logger.getStorageLogger)();
663
+ (logger.debug ?? logger.info).call(logger, { scope: this.kind }, message);
664
+ }
665
+ }
666
+ /**
667
+ * Logs a warning message (always outputs regardless of verbose).
668
+ */
669
+ logWarn(message) {
670
+ (0, import_logger.getStorageLogger)().warn({ scope: this.kind }, message);
671
+ }
672
+ /**
673
+ * Tracks query duration for performance metrics.
674
+ */
675
+ trackDuration(duration) {
676
+ if (!this.options.trackPerformance) {
677
+ return;
678
+ }
679
+ this.queryDurations.push(duration);
680
+ if (this.queryDurations.length > this.MAX_DURATION_SAMPLES) {
681
+ this.queryDurations.shift();
682
+ }
683
+ const sum = this.queryDurations.reduce((a, b) => a + b, 0);
684
+ this.metrics.averageQueryDuration = sum / this.queryDurations.length;
685
+ }
686
+ // ============================================================================
687
+ // Public Utility Methods
688
+ // ============================================================================
689
+ /**
690
+ * Gets current adapter state.
691
+ */
692
+ getState() {
693
+ return this.state;
694
+ }
695
+ /**
696
+ * Gets performance metrics.
697
+ */
698
+ getMetrics() {
699
+ return {
700
+ ...this.metrics,
701
+ cache: this.performanceSettings.cacheEnabled ? { ...this.cacheStats } : void 0
702
+ };
703
+ }
704
+ /**
705
+ * Gets cache statistics.
706
+ */
707
+ getCacheStats() {
708
+ if (!this.performanceSettings.cacheEnabled) {
709
+ return null;
710
+ }
711
+ return { ...this.cacheStats };
712
+ }
713
+ /**
714
+ * Gets current performance settings.
715
+ */
716
+ getPerformanceSettings() {
717
+ return { ...this.performanceSettings };
718
+ }
719
+ /**
720
+ * Checks if adapter is open.
721
+ */
722
+ isOpen() {
723
+ return this.state === AdapterState.OPEN;
724
+ }
725
+ /**
726
+ * Checks if adapter is closed.
727
+ */
728
+ isClosed() {
729
+ return this.state === AdapterState.CLOSED;
730
+ }
731
+ /**
732
+ * Gets uptime in milliseconds.
733
+ */
734
+ getUptime() {
735
+ if (!this.metrics.openedAt) {
736
+ return 0;
737
+ }
738
+ return Date.now() - this.metrics.openedAt.getTime();
739
+ }
740
+ }
741
+ // Annotate the CommonJS export names for ESM import in node:
742
+ 0 && (module.exports = {
743
+ BaseStorageAdapter
744
+ });