@gl-life/gl-life-database 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/API.md +1486 -0
  2. package/LICENSE +190 -0
  3. package/README.md +480 -0
  4. package/dist/cache/index.d.ts +4 -0
  5. package/dist/cache/index.d.ts.map +1 -0
  6. package/dist/cache/invalidation.d.ts +156 -0
  7. package/dist/cache/invalidation.d.ts.map +1 -0
  8. package/dist/cache/kv-cache.d.ts +79 -0
  9. package/dist/cache/kv-cache.d.ts.map +1 -0
  10. package/dist/cache/memory-cache.d.ts +68 -0
  11. package/dist/cache/memory-cache.d.ts.map +1 -0
  12. package/dist/cloudforge/d1-adapter.d.ts +67 -0
  13. package/dist/cloudforge/d1-adapter.d.ts.map +1 -0
  14. package/dist/cloudforge/do-storage.d.ts +51 -0
  15. package/dist/cloudforge/do-storage.d.ts.map +1 -0
  16. package/dist/cloudforge/index.d.ts +4 -0
  17. package/dist/cloudforge/index.d.ts.map +1 -0
  18. package/dist/cloudforge/r2-backup.d.ts +38 -0
  19. package/dist/cloudforge/r2-backup.d.ts.map +1 -0
  20. package/dist/connection/index.d.ts +2 -0
  21. package/dist/connection/index.d.ts.map +1 -0
  22. package/dist/connection/manager.d.ts +54 -0
  23. package/dist/connection/manager.d.ts.map +1 -0
  24. package/dist/index.cjs +4762 -0
  25. package/dist/index.cjs.map +1 -0
  26. package/dist/index.d.ts +18 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +4701 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/migration/index.d.ts +4 -0
  31. package/dist/migration/index.d.ts.map +1 -0
  32. package/dist/migration/loader.d.ts +88 -0
  33. package/dist/migration/loader.d.ts.map +1 -0
  34. package/dist/migration/runner.d.ts +91 -0
  35. package/dist/migration/runner.d.ts.map +1 -0
  36. package/dist/migration/seeder.d.ts +95 -0
  37. package/dist/migration/seeder.d.ts.map +1 -0
  38. package/dist/query/builder.d.ts +47 -0
  39. package/dist/query/builder.d.ts.map +1 -0
  40. package/dist/query/index.d.ts +3 -0
  41. package/dist/query/index.d.ts.map +1 -0
  42. package/dist/query/raw.d.ts +92 -0
  43. package/dist/query/raw.d.ts.map +1 -0
  44. package/dist/tenant/context.d.ts +52 -0
  45. package/dist/tenant/context.d.ts.map +1 -0
  46. package/dist/tenant/index.d.ts +4 -0
  47. package/dist/tenant/index.d.ts.map +1 -0
  48. package/dist/tenant/query-wrapper.d.ts +96 -0
  49. package/dist/tenant/query-wrapper.d.ts.map +1 -0
  50. package/dist/tenant/schema-manager.d.ts +185 -0
  51. package/dist/tenant/schema-manager.d.ts.map +1 -0
  52. package/dist/transaction/index.d.ts +2 -0
  53. package/dist/transaction/index.d.ts.map +1 -0
  54. package/dist/transaction/transaction.d.ts +51 -0
  55. package/dist/transaction/transaction.d.ts.map +1 -0
  56. package/dist/types/cache.d.ts +214 -0
  57. package/dist/types/cache.d.ts.map +1 -0
  58. package/dist/types/cloudforge.d.ts +753 -0
  59. package/dist/types/cloudforge.d.ts.map +1 -0
  60. package/dist/types/connection.d.ts +91 -0
  61. package/dist/types/connection.d.ts.map +1 -0
  62. package/dist/types/index.d.ts +10 -0
  63. package/dist/types/index.d.ts.map +1 -0
  64. package/dist/types/migration.d.ts +225 -0
  65. package/dist/types/migration.d.ts.map +1 -0
  66. package/dist/types/plugin.d.ts +432 -0
  67. package/dist/types/plugin.d.ts.map +1 -0
  68. package/dist/types/query-builder.d.ts +217 -0
  69. package/dist/types/query-builder.d.ts.map +1 -0
  70. package/dist/types/seed.d.ts +187 -0
  71. package/dist/types/seed.d.ts.map +1 -0
  72. package/dist/types/tenant.d.ts +140 -0
  73. package/dist/types/tenant.d.ts.map +1 -0
  74. package/dist/types/transaction.d.ts +144 -0
  75. package/dist/types/transaction.d.ts.map +1 -0
  76. package/package.json +78 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,4762 @@
1
+ 'use strict';
2
+
3
+ var glLifeCore = require('@gl-life/gl-life-core');
4
+ var crypto = require('crypto');
5
+ var async_hooks = require('async_hooks');
6
+ var fs = require('fs');
7
+ var path = require('path');
8
+ var zlib = require('zlib');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
31
+ var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
32
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
33
+
34
+ // src/index.ts
35
+ var InternalConnection = class {
36
+ id;
37
+ config;
38
+ connected = false;
39
+ lastPing = null;
40
+ constructor(id, config) {
41
+ this.id = id;
42
+ this.config = config;
43
+ }
44
+ get isConnected() {
45
+ return this.connected;
46
+ }
47
+ async connect() {
48
+ try {
49
+ this.connected = true;
50
+ this.lastPing = /* @__PURE__ */ new Date();
51
+ return glLifeCore.Result.ok(void 0);
52
+ } catch (error) {
53
+ return glLifeCore.Result.err({
54
+ type: "CONNECTION_FAILED",
55
+ message: `Failed to connect: ${error}`,
56
+ cause: error
57
+ });
58
+ }
59
+ }
60
+ async disconnect() {
61
+ try {
62
+ this.connected = false;
63
+ this.lastPing = null;
64
+ return glLifeCore.Result.ok(void 0);
65
+ } catch (error) {
66
+ return glLifeCore.Result.err({
67
+ type: "CONNECTION_FAILED",
68
+ message: `Failed to disconnect: ${error}`,
69
+ cause: error
70
+ });
71
+ }
72
+ }
73
+ async execute(sql, params) {
74
+ if (!this.connected) {
75
+ return glLifeCore.Result.err({
76
+ type: "CONNECTION_CLOSED",
77
+ message: "Connection is not active"
78
+ });
79
+ }
80
+ try {
81
+ return glLifeCore.Result.ok({});
82
+ } catch (error) {
83
+ return glLifeCore.Result.err({
84
+ type: "UNKNOWN",
85
+ message: `Query execution failed: ${error}`,
86
+ cause: error
87
+ });
88
+ }
89
+ }
90
+ async ping() {
91
+ if (!this.connected) {
92
+ return glLifeCore.Result.ok(false);
93
+ }
94
+ try {
95
+ this.lastPing = /* @__PURE__ */ new Date();
96
+ return glLifeCore.Result.ok(true);
97
+ } catch (error) {
98
+ return glLifeCore.Result.err({
99
+ type: "UNKNOWN",
100
+ message: `Ping failed: ${error}`,
101
+ cause: error
102
+ });
103
+ }
104
+ }
105
+ };
106
+ var DatabaseConnectionManager = class {
107
+ config;
108
+ logger;
109
+ pool = [];
110
+ available = [];
111
+ active = /* @__PURE__ */ new Set();
112
+ initialized = false;
113
+ destroyed = false;
114
+ connectionIdCounter = 0;
115
+ constructor(config, logger) {
116
+ if (!config.name || config.name.trim() === "") {
117
+ throw new Error("Database name is required");
118
+ }
119
+ if (config.maxConnections !== void 0 && config.maxConnections < 0) {
120
+ throw new Error("maxConnections must be >= 0");
121
+ }
122
+ this.config = {
123
+ maxConnections: 10,
124
+ timeout: 3e4,
125
+ autoReconnect: false,
126
+ maxRetries: 3,
127
+ retryDelay: 1e3,
128
+ ...config
129
+ };
130
+ this.logger = logger;
131
+ }
132
+ /**
133
+ * Get manager configuration
134
+ */
135
+ getConfig() {
136
+ return { ...this.config };
137
+ }
138
+ /**
139
+ * Get number of active connections
140
+ */
141
+ getActiveCount() {
142
+ return this.active.size;
143
+ }
144
+ /**
145
+ * Initialize the connection manager
146
+ */
147
+ async initialize() {
148
+ if (this.initialized) {
149
+ return glLifeCore.Result.ok(void 0);
150
+ }
151
+ if (this.destroyed) {
152
+ return glLifeCore.Result.err({
153
+ type: "CONNECTION_CLOSED",
154
+ message: "Manager has been destroyed"
155
+ });
156
+ }
157
+ this.logger.info("Initializing DatabaseConnectionManager", {
158
+ name: this.config.name,
159
+ maxConnections: this.config.maxConnections
160
+ });
161
+ try {
162
+ this.initialized = true;
163
+ this.logger.info("DatabaseConnectionManager initialized successfully");
164
+ return glLifeCore.Result.ok(void 0);
165
+ } catch (error) {
166
+ this.initialized = false;
167
+ return glLifeCore.Result.err({
168
+ type: "CONNECTION_FAILED",
169
+ message: `Initialization failed: ${error}`,
170
+ cause: error
171
+ });
172
+ }
173
+ }
174
+ /**
175
+ * Acquire a connection from the pool
176
+ */
177
+ async acquire() {
178
+ if (this.destroyed) {
179
+ this.logger.error("Cannot acquire connection: manager destroyed");
180
+ return glLifeCore.Result.err({
181
+ type: "CONNECTION_CLOSED",
182
+ message: "Manager has been destroyed"
183
+ });
184
+ }
185
+ if (!this.initialized) {
186
+ return glLifeCore.Result.err({
187
+ type: "CONNECTION_CLOSED",
188
+ message: "Manager not initialized"
189
+ });
190
+ }
191
+ this.logger.debug("Acquiring connection from pool", {
192
+ available: this.available.length,
193
+ active: this.active.size,
194
+ total: this.pool.length
195
+ });
196
+ if (this.available.length > 0) {
197
+ const conn2 = this.available.pop();
198
+ this.active.add(conn2);
199
+ const pingResult = await conn2.ping();
200
+ if (pingResult.isOk() && pingResult.unwrap()) {
201
+ this.logger.debug("Reusing existing connection", { id: conn2.id });
202
+ return glLifeCore.Result.ok(conn2);
203
+ } else {
204
+ if (this.config.autoReconnect) {
205
+ const reconnectResult = await conn2.connect();
206
+ if (reconnectResult.isOk()) {
207
+ this.logger.debug("Reconnected unhealthy connection", { id: conn2.id });
208
+ return glLifeCore.Result.ok(conn2);
209
+ }
210
+ }
211
+ this.active.delete(conn2);
212
+ this.pool = this.pool.filter((c) => c.id !== conn2.id);
213
+ }
214
+ }
215
+ const maxConnections = this.config.maxConnections;
216
+ if (this.pool.length >= maxConnections) {
217
+ this.logger.error("Connection pool limit reached", {
218
+ max: maxConnections,
219
+ current: this.pool.length
220
+ });
221
+ return glLifeCore.Result.err({
222
+ type: "CONNECTION_FAILED",
223
+ message: `Connection pool limit reached (${maxConnections})`
224
+ });
225
+ }
226
+ const connId = `conn-${++this.connectionIdCounter}`;
227
+ const conn = new InternalConnection(connId, this.config);
228
+ const connectResult = await conn.connect();
229
+ if (connectResult.isErr()) {
230
+ return connectResult;
231
+ }
232
+ this.pool.push(conn);
233
+ this.active.add(conn);
234
+ this.logger.debug("Created new connection", {
235
+ id: conn.id,
236
+ poolSize: this.pool.length
237
+ });
238
+ return glLifeCore.Result.ok(conn);
239
+ }
240
+ /**
241
+ * Release a connection back to the pool
242
+ */
243
+ async release(connection) {
244
+ if (!connection || !connection.id) {
245
+ return glLifeCore.Result.err({
246
+ type: "DATABASE_NOT_FOUND",
247
+ message: "Invalid connection provided"
248
+ });
249
+ }
250
+ const conn = this.pool.find((c) => c.id === connection.id);
251
+ if (!conn) {
252
+ return glLifeCore.Result.err({
253
+ type: "DATABASE_NOT_FOUND",
254
+ message: "Connection not found in pool"
255
+ });
256
+ }
257
+ this.active.delete(conn);
258
+ this.available.push(conn);
259
+ this.logger.debug("Released connection back to pool", {
260
+ id: connection.id,
261
+ available: this.available.length
262
+ });
263
+ return glLifeCore.Result.ok(void 0);
264
+ }
265
+ /**
266
+ * Perform health check on all connections
267
+ */
268
+ async healthCheck() {
269
+ if (this.destroyed) {
270
+ return glLifeCore.Result.ok(false);
271
+ }
272
+ if (!this.initialized) {
273
+ return glLifeCore.Result.ok(false);
274
+ }
275
+ try {
276
+ if (this.pool.length === 0) {
277
+ const testConn = await this.acquire();
278
+ if (testConn.isErr()) {
279
+ return glLifeCore.Result.ok(false);
280
+ }
281
+ await this.release(testConn.unwrap());
282
+ return glLifeCore.Result.ok(true);
283
+ }
284
+ const checks = this.pool.map((conn) => conn.ping());
285
+ const results = await Promise.all(checks);
286
+ const allHealthy = results.every((r) => r.isOk() && r.unwrap() === true);
287
+ if (!allHealthy && this.config.autoReconnect) {
288
+ for (let i = 0; i < this.pool.length; i++) {
289
+ const result = results[i];
290
+ if (result.isErr() || !result.unwrap()) {
291
+ await this.pool[i].connect();
292
+ }
293
+ }
294
+ }
295
+ return glLifeCore.Result.ok(allHealthy);
296
+ } catch (error) {
297
+ return glLifeCore.Result.err({
298
+ type: "UNKNOWN",
299
+ message: `Health check failed: ${error}`,
300
+ cause: error
301
+ });
302
+ }
303
+ }
304
+ /**
305
+ * Destroy the connection manager and close all connections
306
+ */
307
+ async destroy() {
308
+ if (this.destroyed) {
309
+ return glLifeCore.Result.ok(void 0);
310
+ }
311
+ this.logger.info("Destroying DatabaseConnectionManager", {
312
+ totalConnections: this.pool.length,
313
+ activeConnections: this.active.size
314
+ });
315
+ try {
316
+ const closePromises = this.pool.map((conn) => conn.disconnect());
317
+ await Promise.all(closePromises);
318
+ this.pool = [];
319
+ this.available = [];
320
+ this.active.clear();
321
+ this.initialized = false;
322
+ this.destroyed = true;
323
+ this.logger.info("DatabaseConnectionManager destroyed successfully");
324
+ return glLifeCore.Result.ok(void 0);
325
+ } catch (error) {
326
+ return glLifeCore.Result.err({
327
+ type: "UNKNOWN",
328
+ message: `Destroy failed: ${error}`,
329
+ cause: error
330
+ });
331
+ }
332
+ }
333
+ };
334
+ var TypeSafeQueryBuilder = class _TypeSafeQueryBuilder {
335
+ tableName;
336
+ metaDataService;
337
+ logger;
338
+ selectedColumns = [];
339
+ whereConditions = [];
340
+ joinClauses = [];
341
+ orderByClauses = [];
342
+ groupByFields = [];
343
+ havingCondition = null;
344
+ limitValue = null;
345
+ offsetValue = null;
346
+ constructor(tableName, metaDataService, logger) {
347
+ if (!tableName || tableName.trim() === "") {
348
+ throw new Error("Table name is required");
349
+ }
350
+ this.tableName = tableName;
351
+ this.metaDataService = metaDataService;
352
+ this.logger = logger;
353
+ }
354
+ table(table) {
355
+ this.tableName = table;
356
+ return this;
357
+ }
358
+ select(...columns) {
359
+ this.selectedColumns.push(...columns);
360
+ return this;
361
+ }
362
+ where(field, operator, value) {
363
+ this.whereConditions.push({ field, operator, value, isOr: false });
364
+ return this;
365
+ }
366
+ orWhere(field, operator, value) {
367
+ this.whereConditions.push({ field, operator, value, isOr: true });
368
+ return this;
369
+ }
370
+ whereIn(field, values) {
371
+ this.whereConditions.push({ field, operator: "IN", value: values, isOr: false });
372
+ return this;
373
+ }
374
+ join(table, leftField, rightField, type = "INNER") {
375
+ this.joinClauses.push({ type, table, left: leftField, right: rightField });
376
+ return this;
377
+ }
378
+ orderBy(field, direction = "ASC") {
379
+ this.orderByClauses.push({ field, direction });
380
+ return this;
381
+ }
382
+ groupBy(...fields) {
383
+ this.groupByFields.push(...fields);
384
+ return this;
385
+ }
386
+ having(condition) {
387
+ this.havingCondition = condition;
388
+ return this;
389
+ }
390
+ limit(limit) {
391
+ this.limitValue = limit;
392
+ return this;
393
+ }
394
+ offset(offset) {
395
+ this.offsetValue = offset;
396
+ return this;
397
+ }
398
+ async insert(data) {
399
+ try {
400
+ this.logger.debug("Insert operation", { table: this.tableName, data });
401
+ return glLifeCore.Result.ok(data);
402
+ } catch (error) {
403
+ return glLifeCore.Result.err({
404
+ type: "EXECUTION_FAILED",
405
+ message: `Insert failed: ${error}`,
406
+ cause: error
407
+ });
408
+ }
409
+ }
410
+ async insertMany(data) {
411
+ try {
412
+ this.logger.debug("Insert many operation", { table: this.tableName, count: data.length });
413
+ return glLifeCore.Result.ok(data);
414
+ } catch (error) {
415
+ return glLifeCore.Result.err({
416
+ type: "EXECUTION_FAILED",
417
+ message: `Insert many failed: ${error}`,
418
+ cause: error
419
+ });
420
+ }
421
+ }
422
+ async update(data) {
423
+ try {
424
+ this.logger.debug("Update operation", { table: this.tableName, data });
425
+ return glLifeCore.Result.ok(1);
426
+ } catch (error) {
427
+ return glLifeCore.Result.err({
428
+ type: "EXECUTION_FAILED",
429
+ message: `Update failed: ${error}`,
430
+ cause: error
431
+ });
432
+ }
433
+ }
434
+ async delete() {
435
+ try {
436
+ this.logger.debug("Delete operation", { table: this.tableName });
437
+ return glLifeCore.Result.ok(1);
438
+ } catch (error) {
439
+ return glLifeCore.Result.err({
440
+ type: "EXECUTION_FAILED",
441
+ message: `Delete failed: ${error}`,
442
+ cause: error
443
+ });
444
+ }
445
+ }
446
+ async first() {
447
+ try {
448
+ this.logger.debug("First operation", { table: this.tableName });
449
+ return glLifeCore.Result.ok(null);
450
+ } catch (error) {
451
+ return glLifeCore.Result.err({
452
+ type: "EXECUTION_FAILED",
453
+ message: `First failed: ${error}`,
454
+ cause: error
455
+ });
456
+ }
457
+ }
458
+ async get() {
459
+ try {
460
+ this.logger.debug("Get operation", { table: this.tableName });
461
+ return glLifeCore.Result.ok([]);
462
+ } catch (error) {
463
+ return glLifeCore.Result.err({
464
+ type: "EXECUTION_FAILED",
465
+ message: `Get failed: ${error}`,
466
+ cause: error
467
+ });
468
+ }
469
+ }
470
+ async count(field = "*") {
471
+ try {
472
+ this.logger.debug("Count operation", { table: this.tableName, field });
473
+ return glLifeCore.Result.ok(0);
474
+ } catch (error) {
475
+ return glLifeCore.Result.err({
476
+ type: "EXECUTION_FAILED",
477
+ message: `Count failed: ${error}`,
478
+ cause: error
479
+ });
480
+ }
481
+ }
482
+ toSQL() {
483
+ const columns = this.selectedColumns.length > 0 ? this.selectedColumns.join(", ") : "*";
484
+ let sql = `SELECT ${columns} FROM ${this.tableName}`;
485
+ if (this.joinClauses.length > 0) {
486
+ sql += " " + this.joinClauses.map((j) => `${j.type} JOIN ${j.table} ON ${j.left} = ${j.right}`).join(" ");
487
+ }
488
+ if (this.whereConditions.length > 0) {
489
+ const whereParts = this.whereConditions.map((w, i) => {
490
+ const prefix = i === 0 ? "WHERE" : w.isOr ? "OR" : "AND";
491
+ return `${prefix} ${w.field} ${w.operator} ?`;
492
+ });
493
+ sql += " " + whereParts.join(" ");
494
+ }
495
+ if (this.groupByFields.length > 0) {
496
+ sql += ` GROUP BY ${this.groupByFields.join(", ")}`;
497
+ }
498
+ if (this.havingCondition) {
499
+ sql += ` HAVING ${this.havingCondition}`;
500
+ }
501
+ if (this.orderByClauses.length > 0) {
502
+ sql += " ORDER BY " + this.orderByClauses.map((o) => `${o.field} ${o.direction}`).join(", ");
503
+ }
504
+ if (this.limitValue !== null) {
505
+ sql += ` LIMIT ${this.limitValue}`;
506
+ }
507
+ if (this.offsetValue !== null) {
508
+ sql += ` OFFSET ${this.offsetValue}`;
509
+ }
510
+ return sql;
511
+ }
512
+ getBindings() {
513
+ return this.whereConditions.map((w) => w.value).filter((v) => v !== void 0);
514
+ }
515
+ clone() {
516
+ const cloned = new _TypeSafeQueryBuilder(this.tableName, this.metaDataService, this.logger);
517
+ cloned.selectedColumns = [...this.selectedColumns];
518
+ cloned.whereConditions = [...this.whereConditions];
519
+ cloned.joinClauses = [...this.joinClauses];
520
+ cloned.orderByClauses = [...this.orderByClauses];
521
+ cloned.groupByFields = [...this.groupByFields];
522
+ cloned.havingCondition = this.havingCondition;
523
+ cloned.limitValue = this.limitValue;
524
+ cloned.offsetValue = this.offsetValue;
525
+ return cloned;
526
+ }
527
+ };
528
+ var RawSQLExecutor = class {
529
+ connection;
530
+ logger;
531
+ constructor(connection, logger) {
532
+ this.connection = connection;
533
+ this.logger = logger;
534
+ }
535
+ /**
536
+ * Execute a raw SQL query with parameters
537
+ *
538
+ * Uses parameterized queries to prevent SQL injection.
539
+ * All parameter values are properly escaped by the database driver.
540
+ *
541
+ * @param sql - SQL query string with ? placeholders
542
+ * @param params - Parameter values to bind to placeholders
543
+ * @param options - Execution options
544
+ * @returns Result containing array of rows or error
545
+ */
546
+ async execute(sql, params = [], options) {
547
+ const validationResult = this.validateSQL(sql, params);
548
+ if (validationResult.isErr()) {
549
+ return validationResult;
550
+ }
551
+ try {
552
+ this.logger.debug("Executing raw SQL", {
553
+ sql: sql.substring(0, 100),
554
+ paramCount: params.length
555
+ });
556
+ const result = await this.connection.execute(sql, params);
557
+ if (result.isErr()) {
558
+ return glLifeCore.Result.err({
559
+ type: "EXECUTION_FAILED",
560
+ message: "Query execution failed",
561
+ cause: result
562
+ });
563
+ }
564
+ const data = result.unwrap();
565
+ let rows;
566
+ if (data && typeof data === "object" && "rows" in data) {
567
+ rows = data.rows;
568
+ } else if (Array.isArray(data)) {
569
+ rows = data;
570
+ } else {
571
+ rows = [];
572
+ }
573
+ this.logger.debug("Query executed successfully", {
574
+ rowCount: rows.length
575
+ });
576
+ return glLifeCore.Result.ok(rows);
577
+ } catch (error) {
578
+ this.logger.error("Raw SQL execution failed", {
579
+ error,
580
+ sql: sql.substring(0, 100)
581
+ });
582
+ return glLifeCore.Result.err({
583
+ type: "EXECUTION_FAILED",
584
+ message: `Query execution failed: ${error instanceof Error ? error.message : String(error)}`,
585
+ cause: error
586
+ });
587
+ }
588
+ }
589
+ /**
590
+ * Execute a query and return the first row or null
591
+ *
592
+ * @param sql - SQL query string
593
+ * @param params - Parameter values
594
+ * @param options - Execution options
595
+ * @returns Result containing single row or null
596
+ */
597
+ async executeOne(sql, params = [], options) {
598
+ const result = await this.execute(sql, params, options);
599
+ if (result.isErr()) {
600
+ return result;
601
+ }
602
+ const rows = result.unwrap();
603
+ return glLifeCore.Result.ok(rows.length > 0 ? rows[0] : null);
604
+ }
605
+ /**
606
+ * Execute multiple queries in batch
607
+ *
608
+ * All queries execute sequentially. If any query fails, execution stops
609
+ * and an error is returned.
610
+ *
611
+ * @param queries - Array of SQL queries with parameters
612
+ * @returns Result containing array of results or error
613
+ */
614
+ async executeBatch(queries) {
615
+ const results = [];
616
+ try {
617
+ this.logger.debug("Executing batch queries", { count: queries.length });
618
+ for (const query of queries) {
619
+ const result = await this.execute(query.sql, query.params || []);
620
+ if (result.isErr()) {
621
+ return glLifeCore.Result.err({
622
+ type: "EXECUTION_FAILED",
623
+ message: "Batch execution failed",
624
+ cause: result
625
+ });
626
+ }
627
+ results.push(result.unwrap());
628
+ }
629
+ this.logger.debug("Batch execution completed", {
630
+ queryCount: queries.length,
631
+ totalRows: results.reduce((sum, rows) => sum + rows.length, 0)
632
+ });
633
+ return glLifeCore.Result.ok(results);
634
+ } catch (error) {
635
+ this.logger.error("Batch execution failed", { error });
636
+ return glLifeCore.Result.err({
637
+ type: "EXECUTION_FAILED",
638
+ message: `Batch execution failed: ${error instanceof Error ? error.message : String(error)}`,
639
+ cause: error
640
+ });
641
+ }
642
+ }
643
+ /**
644
+ * Validate SQL query and parameters
645
+ *
646
+ * Checks for:
647
+ * - Empty/whitespace-only queries
648
+ * - Parameter count matching placeholder count
649
+ *
650
+ * @param sql - SQL query string
651
+ * @param params - Parameter values
652
+ * @returns Result indicating validation success or error
653
+ */
654
+ validateSQL(sql, params) {
655
+ if (!sql || sql.trim().length === 0) {
656
+ return glLifeCore.Result.err({
657
+ type: "INVALID_SQL",
658
+ message: "SQL query cannot be empty"
659
+ });
660
+ }
661
+ const placeholderCount = (sql.match(/\?/g) || []).length;
662
+ if (placeholderCount !== params.length) {
663
+ return glLifeCore.Result.err({
664
+ type: "PARAMETER_MISMATCH",
665
+ message: `Parameter count mismatch: expected ${placeholderCount} but got ${params.length}`
666
+ });
667
+ }
668
+ return glLifeCore.Result.ok(void 0);
669
+ }
670
+ };
671
+ var DatabaseTransaction = class {
672
+ _id;
673
+ db;
674
+ _logger;
675
+ _config;
676
+ _state = "PENDING";
677
+ startTime = null;
678
+ timeoutHandle = null;
679
+ constructor(db, logger, config = {}) {
680
+ this._id = this.generateTransactionId();
681
+ this.db = db;
682
+ this._logger = logger;
683
+ this._config = {
684
+ isolationLevel: config.isolationLevel || "READ COMMITTED",
685
+ timeout: config.timeout || 3e4,
686
+ autoRollback: config.autoRollback !== void 0 ? config.autoRollback : true
687
+ };
688
+ }
689
+ generateTransactionId() {
690
+ return `tx_${crypto.randomBytes(16).toString("hex")}`;
691
+ }
692
+ get id() {
693
+ return this._id;
694
+ }
695
+ get state() {
696
+ return this._state;
697
+ }
698
+ get config() {
699
+ return this._config;
700
+ }
701
+ get isActive() {
702
+ return this._state === "ACTIVE";
703
+ }
704
+ async begin() {
705
+ if (this._state !== "PENDING") {
706
+ return glLifeCore.Result.err({
707
+ type: "ALREADY_STARTED",
708
+ message: `Transaction ${this._id} is already ${this._state}`
709
+ });
710
+ }
711
+ try {
712
+ this._logger.debug("Beginning transaction", {
713
+ id: this._id,
714
+ isolationLevel: this._config.isolationLevel
715
+ });
716
+ await this.db.execute("BEGIN", []);
717
+ this._state = "ACTIVE";
718
+ this.startTime = Date.now();
719
+ if (this._config.timeout && this._config.timeout > 0) {
720
+ this.timeoutHandle = setTimeout(() => {
721
+ this.handleTimeout();
722
+ }, this._config.timeout);
723
+ }
724
+ return glLifeCore.Result.ok(void 0);
725
+ } catch (error) {
726
+ this._logger.error("Failed to begin transaction", { error, id: this._id });
727
+ return glLifeCore.Result.err({
728
+ type: "BEGIN_FAILED",
729
+ message: `Failed to begin transaction: ${error instanceof Error ? error.message : String(error)}`,
730
+ cause: error
731
+ });
732
+ }
733
+ }
734
+ async commit() {
735
+ if (this._state === "PENDING") {
736
+ return glLifeCore.Result.err({
737
+ type: "NOT_STARTED",
738
+ message: `Transaction ${this._id} has not been started`
739
+ });
740
+ }
741
+ if (this._state === "COMMITTED" || this._state === "ROLLED_BACK") {
742
+ return glLifeCore.Result.err({
743
+ type: "ALREADY_COMPLETED",
744
+ message: `Transaction ${this._id} is already ${this._state}`
745
+ });
746
+ }
747
+ try {
748
+ this._logger.debug("Committing transaction", { id: this._id });
749
+ await this.db.execute("COMMIT", []);
750
+ this._state = "COMMITTED";
751
+ this.clearTimeout();
752
+ const duration = this.startTime ? Date.now() - this.startTime : 0;
753
+ this._logger.info("Transaction committed", { id: this._id, duration });
754
+ return glLifeCore.Result.ok(void 0);
755
+ } catch (error) {
756
+ this._logger.error("Failed to commit transaction", { error, id: this._id });
757
+ return glLifeCore.Result.err({
758
+ type: "COMMIT_FAILED",
759
+ message: `Failed to commit transaction: ${error instanceof Error ? error.message : String(error)}`,
760
+ cause: error
761
+ });
762
+ }
763
+ }
764
+ async rollback() {
765
+ if (this._state === "PENDING") {
766
+ return glLifeCore.Result.err({
767
+ type: "NOT_STARTED",
768
+ message: `Transaction ${this._id} has not been started`
769
+ });
770
+ }
771
+ if (this._state === "COMMITTED" || this._state === "ROLLED_BACK") {
772
+ return glLifeCore.Result.err({
773
+ type: "ALREADY_COMPLETED",
774
+ message: `Transaction ${this._id} is already ${this._state}`
775
+ });
776
+ }
777
+ try {
778
+ this._logger.debug("Rolling back transaction", { id: this._id });
779
+ await this.db.execute("ROLLBACK", []);
780
+ this._state = "ROLLED_BACK";
781
+ this.clearTimeout();
782
+ const duration = this.startTime ? Date.now() - this.startTime : 0;
783
+ this._logger.info("Transaction rolled back", { id: this._id, duration });
784
+ return glLifeCore.Result.ok(void 0);
785
+ } catch (error) {
786
+ this._logger.error("Failed to rollback transaction", { error, id: this._id });
787
+ return glLifeCore.Result.err({
788
+ type: "ROLLBACK_FAILED",
789
+ message: `Failed to rollback transaction: ${error instanceof Error ? error.message : String(error)}`,
790
+ cause: error
791
+ });
792
+ }
793
+ }
794
+ async savepoint(name) {
795
+ if (!this.isActive) {
796
+ return glLifeCore.Result.err({
797
+ type: "NOT_STARTED",
798
+ message: `Cannot create savepoint: transaction ${this._id} is not active`
799
+ });
800
+ }
801
+ try {
802
+ this._logger.debug("Creating savepoint", { id: this._id, savepoint: name });
803
+ await this.db.execute(`SAVEPOINT ${name}`, []);
804
+ return glLifeCore.Result.ok(name);
805
+ } catch (error) {
806
+ this._logger.error("Failed to create savepoint", { error, id: this._id, savepoint: name });
807
+ return glLifeCore.Result.err({
808
+ type: "UNKNOWN",
809
+ message: `Failed to create savepoint: ${error instanceof Error ? error.message : String(error)}`,
810
+ cause: error
811
+ });
812
+ }
813
+ }
814
+ async rollbackToSavepoint(name) {
815
+ if (!this.isActive) {
816
+ return glLifeCore.Result.err({
817
+ type: "NOT_STARTED",
818
+ message: `Cannot rollback to savepoint: transaction ${this._id} is not active`
819
+ });
820
+ }
821
+ try {
822
+ this._logger.debug("Rolling back to savepoint", { id: this._id, savepoint: name });
823
+ await this.db.execute(`ROLLBACK TO SAVEPOINT ${name}`, []);
824
+ return glLifeCore.Result.ok(void 0);
825
+ } catch (error) {
826
+ this._logger.error("Failed to rollback to savepoint", { error, id: this._id, savepoint: name });
827
+ return glLifeCore.Result.err({
828
+ type: "UNKNOWN",
829
+ message: `Failed to rollback to savepoint: ${error instanceof Error ? error.message : String(error)}`,
830
+ cause: error
831
+ });
832
+ }
833
+ }
834
+ async releaseSavepoint(name) {
835
+ if (!this.isActive) {
836
+ return glLifeCore.Result.err({
837
+ type: "NOT_STARTED",
838
+ message: `Cannot release savepoint: transaction ${this._id} is not active`
839
+ });
840
+ }
841
+ try {
842
+ this._logger.debug("Releasing savepoint", { id: this._id, savepoint: name });
843
+ await this.db.execute(`RELEASE SAVEPOINT ${name}`, []);
844
+ return glLifeCore.Result.ok(void 0);
845
+ } catch (error) {
846
+ this._logger.error("Failed to release savepoint", { error, id: this._id, savepoint: name });
847
+ return glLifeCore.Result.err({
848
+ type: "UNKNOWN",
849
+ message: `Failed to release savepoint: ${error instanceof Error ? error.message : String(error)}`,
850
+ cause: error
851
+ });
852
+ }
853
+ }
854
+ async execute(sql, params = []) {
855
+ if (!this.isActive) {
856
+ return glLifeCore.Result.err({
857
+ type: "NOT_STARTED",
858
+ message: `Cannot execute query: transaction ${this._id} is not active`
859
+ });
860
+ }
861
+ try {
862
+ this._logger.debug("Executing query in transaction", {
863
+ id: this._id,
864
+ sql: sql.substring(0, 100)
865
+ });
866
+ const result = await this.db.execute(sql, params);
867
+ return glLifeCore.Result.ok(result);
868
+ } catch (error) {
869
+ this._logger.error("Query execution failed in transaction", {
870
+ error,
871
+ id: this._id,
872
+ sql: sql.substring(0, 100)
873
+ });
874
+ return glLifeCore.Result.err({
875
+ type: "UNKNOWN",
876
+ message: `Query execution failed: ${error instanceof Error ? error.message : String(error)}`,
877
+ cause: error
878
+ });
879
+ }
880
+ }
881
+ queryBuilder(table) {
882
+ const mockMetaDataService = {
883
+ getAllMappings: async () => [],
884
+ getMapping: async () => null
885
+ };
886
+ return new TypeSafeQueryBuilder(table, mockMetaDataService, this._logger);
887
+ }
888
+ async run(callback) {
889
+ if (this._state === "PENDING") {
890
+ const beginResult = await this.begin();
891
+ if (beginResult.isErr()) {
892
+ return beginResult;
893
+ }
894
+ }
895
+ try {
896
+ const result = await callback(this);
897
+ if (result.isErr()) {
898
+ if (this._config.autoRollback) {
899
+ await this.rollback();
900
+ }
901
+ return glLifeCore.Result.err({
902
+ type: "UNKNOWN",
903
+ message: "Transaction callback returned error",
904
+ cause: result
905
+ });
906
+ }
907
+ const commitResult = await this.commit();
908
+ if (commitResult.isErr()) {
909
+ return commitResult;
910
+ }
911
+ return glLifeCore.Result.ok(result.unwrap());
912
+ } catch (error) {
913
+ if (this._config.autoRollback) {
914
+ await this.rollback();
915
+ }
916
+ return glLifeCore.Result.err({
917
+ type: "UNKNOWN",
918
+ message: `Transaction failed: ${error instanceof Error ? error.message : String(error)}`,
919
+ cause: error
920
+ });
921
+ }
922
+ }
923
+ handleTimeout() {
924
+ if (this.isActive) {
925
+ this._logger.warn("Transaction timeout", {
926
+ id: this._id,
927
+ timeout: this._config.timeout
928
+ });
929
+ this.rollback().then((result) => {
930
+ if (result.isErr()) {
931
+ this._logger.error("Failed to rollback timed out transaction", {
932
+ id: this._id,
933
+ error: "Transaction rollback failed"
934
+ });
935
+ }
936
+ });
937
+ }
938
+ }
939
+ clearTimeout() {
940
+ if (this.timeoutHandle) {
941
+ clearTimeout(this.timeoutHandle);
942
+ this.timeoutHandle = null;
943
+ }
944
+ }
945
+ };
946
+ var TenantContextManager = class _TenantContextManager {
947
+ storage;
948
+ _config;
949
+ logger;
950
+ currentTenant = null;
951
+ constructor(config, logger) {
952
+ this._config = config;
953
+ this.logger = logger;
954
+ this.storage = new async_hooks.AsyncLocalStorage();
955
+ }
956
+ get tenant() {
957
+ return this.getTenant();
958
+ }
959
+ get config() {
960
+ return this._config;
961
+ }
962
+ get hasTenant() {
963
+ return this.currentTenant !== null;
964
+ }
965
+ async setTenant(tenantId) {
966
+ this.validateTenantId(tenantId);
967
+ this.logger.debug("Setting tenant", { tenantId });
968
+ const metadata = {
969
+ id: tenantId,
970
+ name: tenantId,
971
+ createdAt: /* @__PURE__ */ new Date(),
972
+ updatedAt: /* @__PURE__ */ new Date(),
973
+ isActive: true
974
+ };
975
+ this.currentTenant = metadata;
976
+ }
977
+ async setTenantWithMetadata(metadata) {
978
+ this.validateTenantId(metadata.id);
979
+ this.logger.debug("Setting tenant with metadata", {
980
+ tenantId: metadata.id,
981
+ name: metadata.name
982
+ });
983
+ this.currentTenant = metadata;
984
+ }
985
+ async clearTenant() {
986
+ this.logger.debug("Clearing tenant context");
987
+ this.currentTenant = null;
988
+ }
989
+ getTenantId() {
990
+ if (this.currentTenant) {
991
+ return glLifeCore.Option.some(this.currentTenant.id);
992
+ }
993
+ return glLifeCore.Option.none();
994
+ }
995
+ getTenant() {
996
+ if (this.currentTenant) {
997
+ return glLifeCore.Option.some(this.currentTenant);
998
+ }
999
+ return glLifeCore.Option.none();
1000
+ }
1001
+ shouldIsolateTable(table) {
1002
+ if (!this._config.enforceIsolation) {
1003
+ return false;
1004
+ }
1005
+ if (this._config.excludedTables?.includes(table)) {
1006
+ return false;
1007
+ }
1008
+ return true;
1009
+ }
1010
+ applyTenantIsolation(sql, params = []) {
1011
+ const tenantId = this.getTenantId();
1012
+ if (tenantId.isNone()) {
1013
+ throw new Error("No tenant context set. Cannot apply tenant isolation.");
1014
+ }
1015
+ const tenant = tenantId.unwrap();
1016
+ const tableMatch = sql.match(/(?:FROM|INTO|UPDATE)\s+([a-zA-Z_][a-zA-Z0-9_]*)/i);
1017
+ if (tableMatch) {
1018
+ const tableName = tableMatch[1];
1019
+ if (!this.shouldIsolateTable(tableName)) {
1020
+ return { sql, params };
1021
+ }
1022
+ }
1023
+ switch (this._config.isolationStrategy) {
1024
+ case "ROW":
1025
+ return this.applyRowLevelIsolation(sql, params, tenant);
1026
+ case "SCHEMA":
1027
+ return this.applySchemaIsolation(sql, params, tenant);
1028
+ case "DATABASE":
1029
+ return { sql, params };
1030
+ case "HYBRID":
1031
+ const schemaResult = this.applySchemaIsolation(sql, params, tenant);
1032
+ return this.applyRowLevelIsolation(schemaResult.sql, schemaResult.params, tenant);
1033
+ default:
1034
+ return { sql, params };
1035
+ }
1036
+ }
1037
+ getTenantDatabase() {
1038
+ const tenantId = this.getTenantId();
1039
+ if (tenantId.isNone()) {
1040
+ return glLifeCore.Option.none();
1041
+ }
1042
+ if (this._config.isolationStrategy !== "DATABASE" && this._config.isolationStrategy !== "HYBRID") {
1043
+ return glLifeCore.Option.none();
1044
+ }
1045
+ const tenant = tenantId.unwrap();
1046
+ const baseName = this._config.databaseName || "app";
1047
+ const dbName = `${baseName}_${tenant}`;
1048
+ return glLifeCore.Option.some(dbName);
1049
+ }
1050
+ getTenantSchema() {
1051
+ const tenantId = this.getTenantId();
1052
+ if (tenantId.isNone()) {
1053
+ return glLifeCore.Option.none();
1054
+ }
1055
+ if (this._config.isolationStrategy !== "SCHEMA" && this._config.isolationStrategy !== "HYBRID") {
1056
+ return glLifeCore.Option.none();
1057
+ }
1058
+ const tenant = tenantId.unwrap();
1059
+ const baseName = this._config.schemaName || "app";
1060
+ const schemaName = `${baseName}_${tenant}`;
1061
+ return glLifeCore.Option.some(schemaName);
1062
+ }
1063
+ async withTenant(tenantId, callback) {
1064
+ const previousTenant = this.currentTenant;
1065
+ try {
1066
+ await this.setTenant(tenantId);
1067
+ const result = await callback();
1068
+ return result;
1069
+ } finally {
1070
+ if (previousTenant) {
1071
+ this.currentTenant = previousTenant;
1072
+ } else {
1073
+ this.currentTenant = null;
1074
+ }
1075
+ }
1076
+ }
1077
+ clone() {
1078
+ return new _TenantContextManager(this._config, this.logger);
1079
+ }
1080
+ /**
1081
+ * Validate tenant ID format
1082
+ */
1083
+ validateTenantId(tenantId) {
1084
+ if (!tenantId || typeof tenantId !== "string") {
1085
+ throw new Error("Tenant ID must be a non-empty string");
1086
+ }
1087
+ if (tenantId.trim().length === 0) {
1088
+ throw new Error("Tenant ID cannot be whitespace only");
1089
+ }
1090
+ if (/[;'"`\\]/.test(tenantId)) {
1091
+ throw new Error("Tenant ID contains invalid characters");
1092
+ }
1093
+ }
1094
+ /**
1095
+ * Apply ROW-level tenant isolation to SQL query
1096
+ */
1097
+ applyRowLevelIsolation(sql, params, tenantId) {
1098
+ const tenantColumn = this._config.tenantIdColumn || "tenant_id";
1099
+ const newParams = [...params];
1100
+ const sqlUpper = sql.trim().toUpperCase();
1101
+ if (sqlUpper.startsWith("SELECT")) {
1102
+ if (sql.toUpperCase().includes("WHERE")) {
1103
+ sql = sql.replace(/WHERE/i, `WHERE ${tenantColumn} = ? AND`);
1104
+ newParams.unshift(tenantId);
1105
+ } else {
1106
+ const clauseMatch = sql.match(/(ORDER BY|LIMIT|OFFSET|GROUP BY)/i);
1107
+ if (clauseMatch) {
1108
+ const pos = sql.indexOf(clauseMatch[0]);
1109
+ sql = sql.slice(0, pos) + ` WHERE ${tenantColumn} = ? ` + sql.slice(pos);
1110
+ } else {
1111
+ sql = `${sql} WHERE ${tenantColumn} = ?`;
1112
+ }
1113
+ newParams.push(tenantId);
1114
+ }
1115
+ } else if (sqlUpper.startsWith("INSERT")) {
1116
+ const valuesMatch = sql.match(/\(([^)]+)\)\s+VALUES\s+\(([^)]+)\)/i);
1117
+ if (valuesMatch) {
1118
+ const columns = valuesMatch[1];
1119
+ const placeholders = valuesMatch[2];
1120
+ const newColumns = `${columns}, ${tenantColumn}`;
1121
+ const newPlaceholders = `${placeholders}, ?`;
1122
+ sql = sql.replace(
1123
+ /\(([^)]+)\)\s+VALUES\s+\(([^)]+)\)/i,
1124
+ `(${newColumns}) VALUES (${newPlaceholders})`
1125
+ );
1126
+ newParams.push(tenantId);
1127
+ }
1128
+ } else if (sqlUpper.startsWith("UPDATE")) {
1129
+ if (sql.toUpperCase().includes("WHERE")) {
1130
+ sql = sql.replace(/WHERE/i, `WHERE ${tenantColumn} = ? AND`);
1131
+ newParams.unshift(tenantId);
1132
+ } else {
1133
+ sql = `${sql} WHERE ${tenantColumn} = ?`;
1134
+ newParams.push(tenantId);
1135
+ }
1136
+ } else if (sqlUpper.startsWith("DELETE")) {
1137
+ if (sql.toUpperCase().includes("WHERE")) {
1138
+ sql = sql.replace(/WHERE/i, `WHERE ${tenantColumn} = ? AND`);
1139
+ newParams.unshift(tenantId);
1140
+ } else {
1141
+ sql = `${sql} WHERE ${tenantColumn} = ?`;
1142
+ newParams.push(tenantId);
1143
+ }
1144
+ }
1145
+ return { sql, params: newParams };
1146
+ }
1147
+ /**
1148
+ * Apply SCHEMA-level tenant isolation to SQL query
1149
+ */
1150
+ applySchemaIsolation(sql, params, tenantId) {
1151
+ const schemaName = this.getTenantSchema();
1152
+ if (schemaName.isNone()) {
1153
+ return { sql, params };
1154
+ }
1155
+ const schema = schemaName.unwrap();
1156
+ sql = sql.replace(
1157
+ /(?:FROM|INTO|UPDATE|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*)/gi,
1158
+ (match, tableName) => {
1159
+ if (tableName.includes(".") || this._config.excludedTables?.includes(tableName)) {
1160
+ return match;
1161
+ }
1162
+ return match.replace(tableName, `${schema}.${tableName}`);
1163
+ }
1164
+ );
1165
+ return { sql, params };
1166
+ }
1167
+ };
1168
+
1169
+ // src/tenant/query-wrapper.ts
1170
+ var TenantAwareQueryBuilder = class _TenantAwareQueryBuilder {
1171
+ baseBuilder;
1172
+ tenantContext;
1173
+ logger;
1174
+ isAdminMode = false;
1175
+ constructor(baseBuilder, tenantContext, logger) {
1176
+ this.baseBuilder = baseBuilder;
1177
+ this.tenantContext = tenantContext;
1178
+ this.logger = logger;
1179
+ }
1180
+ /**
1181
+ * Switch to admin mode - bypasses tenant filtering
1182
+ *
1183
+ * Use with caution! Only for administrative queries that need
1184
+ * to access data across all tenants.
1185
+ *
1186
+ * @returns QueryBuilder in admin mode
1187
+ */
1188
+ asAdmin() {
1189
+ const adminBuilder = new _TenantAwareQueryBuilder(
1190
+ this.baseBuilder,
1191
+ this.tenantContext,
1192
+ this.logger
1193
+ );
1194
+ adminBuilder.isAdminMode = true;
1195
+ return adminBuilder;
1196
+ }
1197
+ /**
1198
+ * Switch back to tenant mode from admin mode
1199
+ *
1200
+ * Re-enables automatic tenant filtering
1201
+ *
1202
+ * @returns QueryBuilder in tenant mode
1203
+ */
1204
+ asTenant() {
1205
+ const tenantBuilder = new _TenantAwareQueryBuilder(
1206
+ this.baseBuilder,
1207
+ this.tenantContext,
1208
+ this.logger
1209
+ );
1210
+ tenantBuilder.isAdminMode = false;
1211
+ return tenantBuilder;
1212
+ }
1213
+ /**
1214
+ * Validate that tenant context is set (unless admin mode or excluded table)
1215
+ */
1216
+ validateTenantContext() {
1217
+ if (this.isAdminMode) {
1218
+ return;
1219
+ }
1220
+ const tableName = this.getTableName();
1221
+ if (!this.tenantContext.shouldIsolateTable(tableName)) {
1222
+ return;
1223
+ }
1224
+ if (!this.tenantContext.hasTenant) {
1225
+ throw new Error(
1226
+ "No tenant context set. Cannot execute query without tenant context. Use asAdmin() for admin queries or set tenant via tenantContext.setTenant()"
1227
+ );
1228
+ }
1229
+ }
1230
+ /**
1231
+ * Get table name from base builder
1232
+ */
1233
+ getTableName() {
1234
+ const sql = this.baseBuilder.toSQL();
1235
+ const match = sql.match(/FROM\s+([a-zA-Z_][a-zA-Z0-9_]*)/i);
1236
+ return match ? match[1] : "";
1237
+ }
1238
+ /**
1239
+ * Inject tenant filter into query builder
1240
+ *
1241
+ * This method ensures the correct tenant filter is present.
1242
+ * If a manual tenant_id filter exists, it will be overridden.
1243
+ */
1244
+ injectTenantFilter() {
1245
+ if (this.isAdminMode) {
1246
+ return;
1247
+ }
1248
+ const tableName = this.getTableName();
1249
+ if (!this.tenantContext.shouldIsolateTable(tableName)) {
1250
+ return;
1251
+ }
1252
+ const tenantId = this.tenantContext.getTenantId();
1253
+ if (tenantId.isNone()) {
1254
+ throw new Error("No tenant context set. Cannot inject tenant filter.");
1255
+ }
1256
+ const tenant = tenantId.unwrap();
1257
+ const tenantColumn = this.tenantContext.config.tenantIdColumn || "tenant_id";
1258
+ this.baseBuilder.where(tenantColumn, "=", tenant);
1259
+ }
1260
+ /**
1261
+ * Override tenant_id in data object for INSERT operations
1262
+ */
1263
+ enforceTenantInData(data) {
1264
+ if (this.isAdminMode) {
1265
+ return data;
1266
+ }
1267
+ const tableName = this.getTableName();
1268
+ if (!this.tenantContext.shouldIsolateTable(tableName)) {
1269
+ return data;
1270
+ }
1271
+ const tenantId = this.tenantContext.getTenantId();
1272
+ if (tenantId.isNone()) {
1273
+ throw new Error("No tenant context set. Cannot enforce tenant in INSERT.");
1274
+ }
1275
+ const tenant = tenantId.unwrap();
1276
+ const tenantColumn = this.tenantContext.config.tenantIdColumn || "tenant_id";
1277
+ return {
1278
+ ...data,
1279
+ [tenantColumn]: tenant
1280
+ };
1281
+ }
1282
+ // QueryBuilder interface implementation
1283
+ table(table) {
1284
+ this.baseBuilder.table(table);
1285
+ return this;
1286
+ }
1287
+ select(...columns) {
1288
+ this.baseBuilder.select(...columns);
1289
+ return this;
1290
+ }
1291
+ where(field, operator, value) {
1292
+ this.baseBuilder.where(field, operator, value);
1293
+ return this;
1294
+ }
1295
+ orWhere(field, operator, value) {
1296
+ this.baseBuilder.orWhere(field, operator, value);
1297
+ return this;
1298
+ }
1299
+ whereIn(field, values) {
1300
+ this.baseBuilder.whereIn(field, values);
1301
+ return this;
1302
+ }
1303
+ join(table, leftField, rightField, type = "INNER") {
1304
+ this.baseBuilder.join(table, leftField, rightField, type);
1305
+ return this;
1306
+ }
1307
+ orderBy(field, direction = "ASC") {
1308
+ this.baseBuilder.orderBy(field, direction);
1309
+ return this;
1310
+ }
1311
+ groupBy(...fields) {
1312
+ this.baseBuilder.groupBy(...fields);
1313
+ return this;
1314
+ }
1315
+ having(condition) {
1316
+ this.baseBuilder.having(condition);
1317
+ return this;
1318
+ }
1319
+ limit(limit) {
1320
+ this.baseBuilder.limit(limit);
1321
+ return this;
1322
+ }
1323
+ offset(offset) {
1324
+ this.baseBuilder.offset(offset);
1325
+ return this;
1326
+ }
1327
+ async insert(data) {
1328
+ this.validateTenantContext();
1329
+ const dataWithTenant = this.enforceTenantInData(data);
1330
+ this.logger.debug("Tenant-aware INSERT", {
1331
+ table: this.getTableName(),
1332
+ isAdmin: this.isAdminMode
1333
+ });
1334
+ return this.baseBuilder.insert(dataWithTenant);
1335
+ }
1336
+ async insertMany(data) {
1337
+ this.validateTenantContext();
1338
+ const dataWithTenant = data.map((item) => this.enforceTenantInData(item));
1339
+ this.logger.debug("Tenant-aware INSERT MANY", {
1340
+ table: this.getTableName(),
1341
+ count: data.length,
1342
+ isAdmin: this.isAdminMode
1343
+ });
1344
+ return this.baseBuilder.insertMany(dataWithTenant);
1345
+ }
1346
+ async update(data) {
1347
+ this.validateTenantContext();
1348
+ this.injectTenantFilter();
1349
+ this.logger.debug("Tenant-aware UPDATE", {
1350
+ table: this.getTableName(),
1351
+ isAdmin: this.isAdminMode
1352
+ });
1353
+ return this.baseBuilder.update(data);
1354
+ }
1355
+ async delete() {
1356
+ this.validateTenantContext();
1357
+ this.injectTenantFilter();
1358
+ this.logger.debug("Tenant-aware DELETE", {
1359
+ table: this.getTableName(),
1360
+ isAdmin: this.isAdminMode
1361
+ });
1362
+ return this.baseBuilder.delete();
1363
+ }
1364
+ async first() {
1365
+ this.validateTenantContext();
1366
+ this.injectTenantFilter();
1367
+ this.logger.debug("Tenant-aware FIRST", {
1368
+ table: this.getTableName(),
1369
+ isAdmin: this.isAdminMode
1370
+ });
1371
+ return this.baseBuilder.first();
1372
+ }
1373
+ async get() {
1374
+ this.validateTenantContext();
1375
+ this.injectTenantFilter();
1376
+ this.logger.debug("Tenant-aware GET", {
1377
+ table: this.getTableName(),
1378
+ isAdmin: this.isAdminMode
1379
+ });
1380
+ return this.baseBuilder.get();
1381
+ }
1382
+ async count(field = "*") {
1383
+ this.validateTenantContext();
1384
+ this.injectTenantFilter();
1385
+ this.logger.debug("Tenant-aware COUNT", {
1386
+ table: this.getTableName(),
1387
+ field,
1388
+ isAdmin: this.isAdminMode
1389
+ });
1390
+ return this.baseBuilder.count(field);
1391
+ }
1392
+ toSQL() {
1393
+ this.validateTenantContext();
1394
+ this.injectTenantFilter();
1395
+ return this.baseBuilder.toSQL();
1396
+ }
1397
+ getBindings() {
1398
+ return this.baseBuilder.getBindings();
1399
+ }
1400
+ clone() {
1401
+ const clonedBase = this.baseBuilder.clone();
1402
+ const clonedTenantAware = new _TenantAwareQueryBuilder(
1403
+ clonedBase,
1404
+ this.tenantContext,
1405
+ this.logger
1406
+ );
1407
+ clonedTenantAware.isAdminMode = this.isAdminMode;
1408
+ return clonedTenantAware;
1409
+ }
1410
+ };
1411
+ var TenantSchemaManager = class {
1412
+ connection;
1413
+ logger;
1414
+ config;
1415
+ constructor(connection, logger, config) {
1416
+ this.connection = connection;
1417
+ this.logger = logger;
1418
+ this.config = {
1419
+ schemaVersionTable: "schema_versions",
1420
+ ...config
1421
+ };
1422
+ }
1423
+ /**
1424
+ * Create a new tenant schema
1425
+ *
1426
+ * @param tenantId - Tenant identifier
1427
+ * @param options - Schema creation options
1428
+ * @returns Result with void on success or SchemaManagerError
1429
+ */
1430
+ async createTenantSchema(tenantId, options = {}) {
1431
+ const validationResult = this.validateTenantId(tenantId);
1432
+ if (validationResult.isErr()) {
1433
+ return validationResult;
1434
+ }
1435
+ const schemaName = this.getSchemaName(tenantId);
1436
+ try {
1437
+ this.logger.debug("Creating tenant schema", { tenantId, schemaName });
1438
+ if (options.useTransaction) {
1439
+ await this.connection.execute("BEGIN");
1440
+ }
1441
+ const createResult = await this.connection.execute(
1442
+ `CREATE SCHEMA ${schemaName}`,
1443
+ []
1444
+ );
1445
+ if (createResult.isErr()) {
1446
+ if (options.useTransaction) {
1447
+ await this.connection.execute("ROLLBACK");
1448
+ }
1449
+ const error = createResult.variant.error;
1450
+ if (error.message.includes("already exists")) {
1451
+ return glLifeCore.Result.err({
1452
+ type: "SCHEMA_EXISTS",
1453
+ message: `Schema for tenant ${tenantId} already exists`
1454
+ });
1455
+ }
1456
+ return glLifeCore.Result.err({
1457
+ type: "DATABASE_ERROR",
1458
+ message: `Failed to create schema for tenant ${tenantId}`,
1459
+ cause: error
1460
+ });
1461
+ }
1462
+ if (options.version) {
1463
+ const versionResult = await this.initializeVersionTracking(
1464
+ tenantId,
1465
+ options.version
1466
+ );
1467
+ if (versionResult.isErr()) {
1468
+ if (options.useTransaction) {
1469
+ await this.connection.execute("ROLLBACK");
1470
+ }
1471
+ return versionResult;
1472
+ }
1473
+ }
1474
+ if (options.useTransaction) {
1475
+ await this.connection.execute("COMMIT");
1476
+ }
1477
+ this.logger.info("Tenant schema created", { tenantId, schemaName });
1478
+ return glLifeCore.Result.ok(void 0);
1479
+ } catch (error) {
1480
+ if (options.useTransaction) {
1481
+ await this.connection.execute("ROLLBACK");
1482
+ }
1483
+ this.logger.error("Schema creation failed", { error, tenantId });
1484
+ return glLifeCore.Result.err({
1485
+ type: "DATABASE_ERROR",
1486
+ message: `Schema creation failed for tenant ${tenantId}: ${error}`,
1487
+ cause: error
1488
+ });
1489
+ }
1490
+ }
1491
+ /**
1492
+ * Drop a tenant schema
1493
+ *
1494
+ * @param tenantId - Tenant identifier
1495
+ * @param options - Drop schema options
1496
+ * @returns Result with void on success or SchemaManagerError
1497
+ */
1498
+ async dropTenantSchema(tenantId, options = {}) {
1499
+ const validationResult = this.validateTenantId(tenantId);
1500
+ if (validationResult.isErr()) {
1501
+ return validationResult;
1502
+ }
1503
+ const schemaName = this.getSchemaName(tenantId);
1504
+ try {
1505
+ this.logger.debug("Dropping tenant schema", { tenantId, schemaName });
1506
+ let sql = `DROP SCHEMA ${options.ifExists ? "IF EXISTS " : ""}${schemaName}`;
1507
+ if (options.cascade) {
1508
+ sql += " CASCADE";
1509
+ }
1510
+ const dropResult = await this.connection.execute(sql, []);
1511
+ if (dropResult.isErr()) {
1512
+ const error = dropResult.variant.error;
1513
+ return glLifeCore.Result.err({
1514
+ type: "DATABASE_ERROR",
1515
+ message: `Failed to drop schema for tenant ${tenantId}`,
1516
+ cause: error
1517
+ });
1518
+ }
1519
+ if (options.cleanupVersions) {
1520
+ await this.connection.execute(
1521
+ `DELETE FROM ${this.config.schemaVersionTable} WHERE tenant_id = ?`,
1522
+ [tenantId]
1523
+ );
1524
+ }
1525
+ this.logger.info("Tenant schema dropped", { tenantId, schemaName });
1526
+ return glLifeCore.Result.ok(void 0);
1527
+ } catch (error) {
1528
+ this.logger.error("Schema drop failed", { error, tenantId });
1529
+ return glLifeCore.Result.err({
1530
+ type: "DATABASE_ERROR",
1531
+ message: `Schema drop failed for tenant ${tenantId}: ${error}`,
1532
+ cause: error
1533
+ });
1534
+ }
1535
+ }
1536
+ /**
1537
+ * Check if tenant schema exists
1538
+ *
1539
+ * @param tenantId - Tenant identifier
1540
+ * @returns Result with boolean indicating existence
1541
+ */
1542
+ async schemaExists(tenantId) {
1543
+ const schemaName = this.getSchemaName(tenantId);
1544
+ try {
1545
+ const result = await this.connection.execute(
1546
+ `SELECT schema_name FROM information_schema.schemata WHERE schema_name = ?`,
1547
+ [schemaName]
1548
+ );
1549
+ if (result.isErr()) {
1550
+ return glLifeCore.Result.err({
1551
+ type: "DATABASE_ERROR",
1552
+ message: "Failed to check schema existence",
1553
+ cause: result.variant.error
1554
+ });
1555
+ }
1556
+ const data = result.unwrap();
1557
+ const rows = data.rows || data || [];
1558
+ return glLifeCore.Result.ok(rows.length > 0);
1559
+ } catch (error) {
1560
+ return glLifeCore.Result.err({
1561
+ type: "DATABASE_ERROR",
1562
+ message: `Failed to check schema existence: ${error}`,
1563
+ cause: error
1564
+ });
1565
+ }
1566
+ }
1567
+ /**
1568
+ * Get current schema version for tenant
1569
+ *
1570
+ * @param tenantId - Tenant identifier
1571
+ * @returns Result with version string or null
1572
+ */
1573
+ async getSchemaVersion(tenantId) {
1574
+ try {
1575
+ const result = await this.connection.execute(
1576
+ `SELECT version FROM ${this.config.schemaVersionTable} WHERE tenant_id = ? ORDER BY applied_at DESC LIMIT 1`,
1577
+ [tenantId]
1578
+ );
1579
+ if (result.isErr()) {
1580
+ return glLifeCore.Result.err({
1581
+ type: "DATABASE_ERROR",
1582
+ message: "Failed to get schema version",
1583
+ cause: result.variant.error
1584
+ });
1585
+ }
1586
+ const data = result.unwrap();
1587
+ const rows = data.rows || data || [];
1588
+ if (rows.length === 0) {
1589
+ return glLifeCore.Result.ok(null);
1590
+ }
1591
+ return glLifeCore.Result.ok(rows[0].version);
1592
+ } catch (error) {
1593
+ return glLifeCore.Result.err({
1594
+ type: "DATABASE_ERROR",
1595
+ message: `Failed to get schema version: ${error}`,
1596
+ cause: error
1597
+ });
1598
+ }
1599
+ }
1600
+ /**
1601
+ * Update schema version for tenant
1602
+ *
1603
+ * @param tenantId - Tenant identifier
1604
+ * @param version - New version string
1605
+ * @returns Result with void on success
1606
+ */
1607
+ async updateSchemaVersion(tenantId, version2) {
1608
+ if (!this.isValidVersion(version2)) {
1609
+ return glLifeCore.Result.err({
1610
+ type: "INVALID_VERSION",
1611
+ message: `Invalid version format: ${version2}. Expected semver format (e.g., 1.0.0)`
1612
+ });
1613
+ }
1614
+ try {
1615
+ const result = await this.connection.execute(
1616
+ `INSERT INTO ${this.config.schemaVersionTable} (tenant_id, version, applied_at) VALUES (?, ?, ?)`,
1617
+ [tenantId, version2, /* @__PURE__ */ new Date()]
1618
+ );
1619
+ if (result.isErr()) {
1620
+ return glLifeCore.Result.err({
1621
+ type: "DATABASE_ERROR",
1622
+ message: "Failed to update schema version",
1623
+ cause: result.variant.error
1624
+ });
1625
+ }
1626
+ return glLifeCore.Result.ok(void 0);
1627
+ } catch (error) {
1628
+ return glLifeCore.Result.err({
1629
+ type: "DATABASE_ERROR",
1630
+ message: `Failed to update schema version: ${error}`,
1631
+ cause: error
1632
+ });
1633
+ }
1634
+ }
1635
+ /**
1636
+ * Get version history for tenant
1637
+ *
1638
+ * @param tenantId - Tenant identifier
1639
+ * @returns Result with array of schema versions
1640
+ */
1641
+ async getVersionHistory(tenantId) {
1642
+ try {
1643
+ const result = await this.connection.execute(
1644
+ `SELECT version, applied_at, description FROM ${this.config.schemaVersionTable} WHERE tenant_id = ? ORDER BY applied_at ASC`,
1645
+ [tenantId]
1646
+ );
1647
+ if (result.isErr()) {
1648
+ return glLifeCore.Result.err({
1649
+ type: "DATABASE_ERROR",
1650
+ message: "Failed to get version history",
1651
+ cause: result.variant.error
1652
+ });
1653
+ }
1654
+ const data = result.unwrap();
1655
+ const rows = data.rows || data || [];
1656
+ return glLifeCore.Result.ok(rows);
1657
+ } catch (error) {
1658
+ return glLifeCore.Result.err({
1659
+ type: "DATABASE_ERROR",
1660
+ message: `Failed to get version history: ${error}`,
1661
+ cause: error
1662
+ });
1663
+ }
1664
+ }
1665
+ /**
1666
+ * Apply a migration to tenant schema
1667
+ *
1668
+ * @param tenantId - Tenant identifier
1669
+ * @param migration - Migration definition
1670
+ * @returns Result with void on success
1671
+ */
1672
+ async applyMigration(tenantId, migration) {
1673
+ const schemaName = this.getSchemaName(tenantId);
1674
+ try {
1675
+ this.logger.debug("Applying migration", {
1676
+ tenantId,
1677
+ version: migration.version
1678
+ });
1679
+ const currentVersion = await this.getSchemaVersion(tenantId);
1680
+ if (currentVersion.isOk() && currentVersion.unwrap() === migration.version) {
1681
+ this.logger.info("Migration already applied", {
1682
+ tenantId,
1683
+ version: migration.version
1684
+ });
1685
+ return glLifeCore.Result.ok(void 0);
1686
+ }
1687
+ await this.connection.execute(`SET search_path TO ${schemaName}`, []);
1688
+ const migrationResult = await this.connection.execute(migration.sql, []);
1689
+ if (migrationResult.isErr()) {
1690
+ return glLifeCore.Result.err({
1691
+ type: "MIGRATION_FAILED",
1692
+ message: `Migration ${migration.version} failed for tenant ${tenantId}`,
1693
+ cause: migrationResult.variant.error
1694
+ });
1695
+ }
1696
+ await this.connection.execute(
1697
+ `INSERT INTO ${this.config.schemaVersionTable} (tenant_id, version, applied_at, description) VALUES (?, ?, ?, ?)`,
1698
+ [tenantId, migration.version, /* @__PURE__ */ new Date(), migration.description || ""]
1699
+ );
1700
+ this.logger.info("Migration applied", {
1701
+ tenantId,
1702
+ version: migration.version
1703
+ });
1704
+ return glLifeCore.Result.ok(void 0);
1705
+ } catch (error) {
1706
+ this.logger.error("Migration failed", {
1707
+ error,
1708
+ tenantId,
1709
+ version: migration.version
1710
+ });
1711
+ return glLifeCore.Result.err({
1712
+ type: "MIGRATION_FAILED",
1713
+ message: `Migration failed: ${error}`,
1714
+ cause: error
1715
+ });
1716
+ }
1717
+ }
1718
+ /**
1719
+ * Get the schema name for a tenant
1720
+ *
1721
+ * @param tenantId - Tenant identifier
1722
+ * @returns Schema name
1723
+ */
1724
+ getSchemaName(tenantId) {
1725
+ if (/[;'"`\\]/.test(tenantId)) {
1726
+ throw new Error(`Invalid tenant ID: contains dangerous characters`);
1727
+ }
1728
+ return `${this.config.baseSchemaName}_tenant_${tenantId.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1729
+ }
1730
+ /**
1731
+ * Validate tenant ID format
1732
+ */
1733
+ validateTenantId(tenantId) {
1734
+ if (!tenantId || typeof tenantId !== "string") {
1735
+ return glLifeCore.Result.err({
1736
+ type: "INVALID_TENANT_ID",
1737
+ message: "Tenant ID must be a non-empty string"
1738
+ });
1739
+ }
1740
+ if (tenantId.trim().length === 0) {
1741
+ return glLifeCore.Result.err({
1742
+ type: "INVALID_TENANT_ID",
1743
+ message: "Tenant ID cannot be whitespace only"
1744
+ });
1745
+ }
1746
+ if (/[;'"`\\]/.test(tenantId)) {
1747
+ return glLifeCore.Result.err({
1748
+ type: "INVALID_TENANT_ID",
1749
+ message: "Tenant ID contains invalid characters"
1750
+ });
1751
+ }
1752
+ return glLifeCore.Result.ok(void 0);
1753
+ }
1754
+ /**
1755
+ * Validate version format (simple semver check)
1756
+ */
1757
+ isValidVersion(version2) {
1758
+ return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version2);
1759
+ }
1760
+ /**
1761
+ * Initialize version tracking for new tenant
1762
+ */
1763
+ async initializeVersionTracking(tenantId, version2) {
1764
+ if (!this.isValidVersion(version2)) {
1765
+ return glLifeCore.Result.err({
1766
+ type: "INVALID_VERSION",
1767
+ message: `Invalid version format: ${version2}`
1768
+ });
1769
+ }
1770
+ const result = await this.connection.execute(
1771
+ `INSERT INTO ${this.config.schemaVersionTable} (tenant_id, version, applied_at) VALUES (?, ?, ?)`,
1772
+ [tenantId, version2, /* @__PURE__ */ new Date()]
1773
+ );
1774
+ if (result.isErr()) {
1775
+ return glLifeCore.Result.err({
1776
+ type: "DATABASE_ERROR",
1777
+ message: "Failed to initialize version tracking",
1778
+ cause: result.variant.error
1779
+ });
1780
+ }
1781
+ return glLifeCore.Result.ok(void 0);
1782
+ }
1783
+ };
1784
+ var MemoryCache = class {
1785
+ config;
1786
+ logger;
1787
+ store;
1788
+ lruList = [];
1789
+ // Most recent at end
1790
+ stats = {
1791
+ hits: 0,
1792
+ misses: 0,
1793
+ evictions: 0
1794
+ };
1795
+ constructor(config, logger) {
1796
+ this.config = config;
1797
+ this.logger = logger;
1798
+ this.store = /* @__PURE__ */ new Map();
1799
+ }
1800
+ async get(key) {
1801
+ try {
1802
+ const fullKey = this.getFullKey(key);
1803
+ const entry = this.store.get(fullKey);
1804
+ if (!entry) {
1805
+ this.stats.misses++;
1806
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
1807
+ }
1808
+ if (this.isExpired(entry)) {
1809
+ this.store.delete(fullKey);
1810
+ this.removeLRU(fullKey);
1811
+ this.stats.misses++;
1812
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
1813
+ }
1814
+ this.touchEntry(fullKey, entry);
1815
+ this.stats.hits++;
1816
+ return glLifeCore.Result.ok(glLifeCore.Option.some(entry.value));
1817
+ } catch (error) {
1818
+ this.logger.error("Cache get failed", { error, key });
1819
+ return glLifeCore.Result.err({
1820
+ type: "GET_FAILED",
1821
+ message: `Failed to get cache entry: ${error}`,
1822
+ cause: error
1823
+ });
1824
+ }
1825
+ }
1826
+ async getWithMetadata(key) {
1827
+ try {
1828
+ const fullKey = this.getFullKey(key);
1829
+ const entry = this.store.get(fullKey);
1830
+ if (!entry) {
1831
+ this.stats.misses++;
1832
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
1833
+ }
1834
+ if (this.isExpired(entry)) {
1835
+ this.store.delete(fullKey);
1836
+ this.removeLRU(fullKey);
1837
+ this.stats.misses++;
1838
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
1839
+ }
1840
+ this.touchEntry(fullKey, entry);
1841
+ this.stats.hits++;
1842
+ return glLifeCore.Result.ok(glLifeCore.Option.some(entry));
1843
+ } catch (error) {
1844
+ this.logger.error("Cache getWithMetadata failed", { error, key });
1845
+ return glLifeCore.Result.err({
1846
+ type: "GET_FAILED",
1847
+ message: `Failed to get cache entry with metadata: ${error}`,
1848
+ cause: error
1849
+ });
1850
+ }
1851
+ }
1852
+ async set(key, value, ttl) {
1853
+ try {
1854
+ const fullKey = this.getFullKey(key);
1855
+ const effectiveTTL = ttl !== void 0 ? ttl : this.config.defaultTTL;
1856
+ if (this.config.maxItems && this.store.size >= this.config.maxItems && !this.store.has(fullKey)) {
1857
+ this.evictLRU();
1858
+ }
1859
+ const now = /* @__PURE__ */ new Date();
1860
+ const expiresAt = effectiveTTL > 0 ? new Date(now.getTime() + effectiveTTL * 1e3) : void 0;
1861
+ const metadata = {
1862
+ key: fullKey,
1863
+ createdAt: now,
1864
+ lastAccessedAt: now,
1865
+ accessCount: 0,
1866
+ size: this.estimateSize(value),
1867
+ ttl: effectiveTTL,
1868
+ expiresAt
1869
+ };
1870
+ const entry = {
1871
+ value,
1872
+ metadata
1873
+ };
1874
+ if (this.store.has(fullKey)) {
1875
+ this.removeLRU(fullKey);
1876
+ }
1877
+ this.store.set(fullKey, entry);
1878
+ this.lruList.push(fullKey);
1879
+ this.logger.debug("Cache set", { key, ttl: effectiveTTL });
1880
+ return glLifeCore.Result.ok(void 0);
1881
+ } catch (error) {
1882
+ this.logger.error("Cache set failed", { error, key });
1883
+ return glLifeCore.Result.err({
1884
+ type: "SET_FAILED",
1885
+ message: `Failed to set cache entry: ${error}`,
1886
+ cause: error
1887
+ });
1888
+ }
1889
+ }
1890
+ async delete(key) {
1891
+ try {
1892
+ const fullKey = this.getFullKey(key);
1893
+ const existed = this.store.delete(fullKey);
1894
+ if (existed) {
1895
+ this.removeLRU(fullKey);
1896
+ this.logger.debug("Cache delete", { key });
1897
+ }
1898
+ return glLifeCore.Result.ok(existed);
1899
+ } catch (error) {
1900
+ this.logger.error("Cache delete failed", { error, key });
1901
+ return glLifeCore.Result.err({
1902
+ type: "DELETE_FAILED",
1903
+ message: `Failed to delete cache entry: ${error}`,
1904
+ cause: error
1905
+ });
1906
+ }
1907
+ }
1908
+ async deleteMany(keys) {
1909
+ try {
1910
+ let deleted = 0;
1911
+ for (const key of keys) {
1912
+ const result = await this.delete(key);
1913
+ if (result.isOk() && result.unwrap()) {
1914
+ deleted++;
1915
+ }
1916
+ }
1917
+ return glLifeCore.Result.ok(deleted);
1918
+ } catch (error) {
1919
+ this.logger.error("Cache deleteMany failed", { error });
1920
+ return glLifeCore.Result.err({
1921
+ type: "DELETE_FAILED",
1922
+ message: `Failed to delete multiple cache entries: ${error}`,
1923
+ cause: error
1924
+ });
1925
+ }
1926
+ }
1927
+ async clear() {
1928
+ try {
1929
+ this.store.clear();
1930
+ this.lruList.length = 0;
1931
+ this.logger.debug("Cache cleared");
1932
+ return glLifeCore.Result.ok(void 0);
1933
+ } catch (error) {
1934
+ this.logger.error("Cache clear failed", { error });
1935
+ return glLifeCore.Result.err({
1936
+ type: "CLEAR_FAILED",
1937
+ message: `Failed to clear cache: ${error}`,
1938
+ cause: error
1939
+ });
1940
+ }
1941
+ }
1942
+ async has(key) {
1943
+ try {
1944
+ const fullKey = this.getFullKey(key);
1945
+ const entry = this.store.get(fullKey);
1946
+ if (!entry) {
1947
+ return glLifeCore.Result.ok(false);
1948
+ }
1949
+ if (this.isExpired(entry)) {
1950
+ this.store.delete(fullKey);
1951
+ this.removeLRU(fullKey);
1952
+ return glLifeCore.Result.ok(false);
1953
+ }
1954
+ return glLifeCore.Result.ok(true);
1955
+ } catch (error) {
1956
+ this.logger.error("Cache has failed", { error, key });
1957
+ return glLifeCore.Result.err({
1958
+ type: "GET_FAILED",
1959
+ message: `Failed to check cache key existence: ${error}`,
1960
+ cause: error
1961
+ });
1962
+ }
1963
+ }
1964
+ async invalidate(pattern) {
1965
+ try {
1966
+ const fullPattern = this.config.keyPrefix ? `${this.config.keyPrefix}${pattern}` : pattern;
1967
+ const regex = this.patternToRegex(fullPattern);
1968
+ let invalidated = 0;
1969
+ for (const key of this.store.keys()) {
1970
+ if (regex.test(key)) {
1971
+ this.store.delete(key);
1972
+ this.removeLRU(key);
1973
+ invalidated++;
1974
+ }
1975
+ }
1976
+ this.logger.debug("Cache invalidate", { pattern, count: invalidated });
1977
+ return glLifeCore.Result.ok(invalidated);
1978
+ } catch (error) {
1979
+ this.logger.error("Cache invalidate failed", { error, pattern });
1980
+ return glLifeCore.Result.err({
1981
+ type: "DELETE_FAILED",
1982
+ message: `Failed to invalidate cache entries: ${error}`,
1983
+ cause: error
1984
+ });
1985
+ }
1986
+ }
1987
+ async invalidateTable(table) {
1988
+ try {
1989
+ const pattern = `*${table}*`;
1990
+ return await this.invalidate(pattern);
1991
+ } catch (error) {
1992
+ this.logger.error("Cache invalidateTable failed", { error, table });
1993
+ return glLifeCore.Result.err({
1994
+ type: "DELETE_FAILED",
1995
+ message: `Failed to invalidate table cache: ${error}`,
1996
+ cause: error
1997
+ });
1998
+ }
1999
+ }
2000
+ getStats() {
2001
+ try {
2002
+ const totalAccesses = this.stats.hits + this.stats.misses;
2003
+ const hitRate = totalAccesses > 0 ? this.stats.hits / totalAccesses : 0;
2004
+ let totalSize = 0;
2005
+ for (const entry of this.store.values()) {
2006
+ totalSize += entry.metadata.size;
2007
+ }
2008
+ return glLifeCore.Result.ok({
2009
+ itemCount: this.store.size,
2010
+ totalSize,
2011
+ hits: this.stats.hits,
2012
+ misses: this.stats.misses,
2013
+ hitRate,
2014
+ evictions: this.stats.evictions
2015
+ });
2016
+ } catch (error) {
2017
+ return glLifeCore.Result.err({
2018
+ type: "UNKNOWN",
2019
+ message: `Failed to get cache stats: ${error}`,
2020
+ cause: error
2021
+ });
2022
+ }
2023
+ }
2024
+ resetStats() {
2025
+ try {
2026
+ this.stats.hits = 0;
2027
+ this.stats.misses = 0;
2028
+ this.stats.evictions = 0;
2029
+ return glLifeCore.Result.ok(void 0);
2030
+ } catch (error) {
2031
+ return glLifeCore.Result.err({
2032
+ type: "UNKNOWN",
2033
+ message: `Failed to reset cache stats: ${error}`,
2034
+ cause: error
2035
+ });
2036
+ }
2037
+ }
2038
+ generateKey(sql, params) {
2039
+ const data = {
2040
+ sql,
2041
+ params: params || []
2042
+ };
2043
+ const hash = crypto__default.default.createHash("sha256").update(JSON.stringify(data)).digest("hex");
2044
+ return hash.substring(0, 16);
2045
+ }
2046
+ /**
2047
+ * Get full cache key with prefix
2048
+ */
2049
+ getFullKey(key) {
2050
+ return this.config.keyPrefix ? `${this.config.keyPrefix}${key}` : key;
2051
+ }
2052
+ /**
2053
+ * Check if entry is expired
2054
+ */
2055
+ isExpired(entry) {
2056
+ if (!entry.metadata.expiresAt) {
2057
+ return false;
2058
+ }
2059
+ return /* @__PURE__ */ new Date() > entry.metadata.expiresAt;
2060
+ }
2061
+ /**
2062
+ * Update entry access metadata and LRU position
2063
+ */
2064
+ touchEntry(key, entry) {
2065
+ entry.metadata.lastAccessedAt = /* @__PURE__ */ new Date();
2066
+ entry.metadata.accessCount++;
2067
+ this.removeLRU(key);
2068
+ this.lruList.push(key);
2069
+ }
2070
+ /**
2071
+ * Remove key from LRU list
2072
+ */
2073
+ removeLRU(key) {
2074
+ const index = this.lruList.indexOf(key);
2075
+ if (index > -1) {
2076
+ this.lruList.splice(index, 1);
2077
+ }
2078
+ }
2079
+ /**
2080
+ * Evict least recently used entry
2081
+ */
2082
+ evictLRU() {
2083
+ if (this.lruList.length === 0) {
2084
+ return;
2085
+ }
2086
+ const lruKey = this.lruList[0];
2087
+ this.store.delete(lruKey);
2088
+ this.lruList.shift();
2089
+ this.stats.evictions++;
2090
+ this.logger.debug("LRU eviction", { key: lruKey });
2091
+ }
2092
+ /**
2093
+ * Estimate size of value in bytes
2094
+ */
2095
+ estimateSize(value) {
2096
+ try {
2097
+ return JSON.stringify(value).length;
2098
+ } catch {
2099
+ return 0;
2100
+ }
2101
+ }
2102
+ /**
2103
+ * Convert glob pattern to regex
2104
+ */
2105
+ patternToRegex(pattern) {
2106
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
2107
+ return new RegExp(`^${escaped}$`);
2108
+ }
2109
+ };
2110
+ var KVCache = class {
2111
+ config;
2112
+ kv;
2113
+ logger;
2114
+ stats = {
2115
+ hits: 0,
2116
+ misses: 0
2117
+ };
2118
+ constructor(kv, config, logger) {
2119
+ this.kv = kv;
2120
+ this.config = config;
2121
+ this.logger = logger;
2122
+ }
2123
+ async get(key) {
2124
+ try {
2125
+ const fullKey = this.getFullKey(key);
2126
+ let value = await this.kv.get(fullKey, { type: "json" });
2127
+ if (value === null) {
2128
+ this.stats.misses++;
2129
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
2130
+ }
2131
+ if (typeof value === "string") {
2132
+ try {
2133
+ value = JSON.parse(value);
2134
+ } catch {
2135
+ this.stats.hits++;
2136
+ return glLifeCore.Result.ok(glLifeCore.Option.some(value));
2137
+ }
2138
+ }
2139
+ this.stats.hits++;
2140
+ if (value && typeof value === "object" && "value" in value) {
2141
+ return glLifeCore.Result.ok(glLifeCore.Option.some(value.value));
2142
+ }
2143
+ return glLifeCore.Result.ok(glLifeCore.Option.some(value));
2144
+ } catch (error) {
2145
+ this.logger.error("KV cache get failed", { error, key });
2146
+ return glLifeCore.Result.err({
2147
+ type: "GET_FAILED",
2148
+ message: `Failed to get cache entry from KV: ${error}`,
2149
+ cause: error
2150
+ });
2151
+ }
2152
+ }
2153
+ async getWithMetadata(key) {
2154
+ try {
2155
+ const fullKey = this.getFullKey(key);
2156
+ let value = await this.kv.get(fullKey, { type: "json" });
2157
+ if (value === null) {
2158
+ this.stats.misses++;
2159
+ return glLifeCore.Result.ok(glLifeCore.Option.none());
2160
+ }
2161
+ if (typeof value === "string") {
2162
+ try {
2163
+ value = JSON.parse(value);
2164
+ } catch {
2165
+ const now2 = /* @__PURE__ */ new Date();
2166
+ this.stats.hits++;
2167
+ const entry2 = {
2168
+ value,
2169
+ metadata: {
2170
+ key: fullKey,
2171
+ createdAt: now2,
2172
+ lastAccessedAt: now2,
2173
+ accessCount: 1,
2174
+ size: this.estimateSize(value),
2175
+ ttl: this.config.defaultTTL
2176
+ }
2177
+ };
2178
+ return glLifeCore.Result.ok(glLifeCore.Option.some(entry2));
2179
+ }
2180
+ }
2181
+ this.stats.hits++;
2182
+ if (value && typeof value === "object" && "value" in value && "metadata" in value) {
2183
+ const entry2 = value;
2184
+ entry2.metadata.createdAt = new Date(entry2.metadata.createdAt);
2185
+ entry2.metadata.lastAccessedAt = new Date(entry2.metadata.lastAccessedAt);
2186
+ if (entry2.metadata.expiresAt) {
2187
+ entry2.metadata.expiresAt = new Date(entry2.metadata.expiresAt);
2188
+ }
2189
+ return glLifeCore.Result.ok(glLifeCore.Option.some(entry2));
2190
+ }
2191
+ const now = /* @__PURE__ */ new Date();
2192
+ const entry = {
2193
+ value,
2194
+ metadata: {
2195
+ key: fullKey,
2196
+ createdAt: now,
2197
+ lastAccessedAt: now,
2198
+ accessCount: 1,
2199
+ size: this.estimateSize(value),
2200
+ ttl: this.config.defaultTTL
2201
+ }
2202
+ };
2203
+ return glLifeCore.Result.ok(glLifeCore.Option.some(entry));
2204
+ } catch (error) {
2205
+ this.logger.error("KV cache getWithMetadata failed", { error, key });
2206
+ return glLifeCore.Result.err({
2207
+ type: "GET_FAILED",
2208
+ message: `Failed to get cache entry with metadata from KV: ${error}`,
2209
+ cause: error
2210
+ });
2211
+ }
2212
+ }
2213
+ async set(key, value, ttl) {
2214
+ try {
2215
+ const fullKey = this.getFullKey(key);
2216
+ const effectiveTTL = ttl !== void 0 ? ttl : this.config.defaultTTL;
2217
+ const now = /* @__PURE__ */ new Date();
2218
+ const expiresAt = effectiveTTL > 0 ? new Date(now.getTime() + effectiveTTL * 1e3) : void 0;
2219
+ const metadata = {
2220
+ key: fullKey,
2221
+ createdAt: now,
2222
+ lastAccessedAt: now,
2223
+ accessCount: 0,
2224
+ size: this.estimateSize(value),
2225
+ ttl: effectiveTTL,
2226
+ expiresAt
2227
+ };
2228
+ const entry = {
2229
+ value,
2230
+ metadata
2231
+ };
2232
+ const options = effectiveTTL > 0 ? { expirationTtl: effectiveTTL } : {};
2233
+ await this.kv.put(fullKey, JSON.stringify(entry), options);
2234
+ this.logger.debug("KV cache set", { key, ttl: effectiveTTL });
2235
+ return glLifeCore.Result.ok(void 0);
2236
+ } catch (error) {
2237
+ this.logger.error("KV cache set failed", { error, key });
2238
+ return glLifeCore.Result.err({
2239
+ type: "SET_FAILED",
2240
+ message: `Failed to set cache entry in KV: ${error}`,
2241
+ cause: error
2242
+ });
2243
+ }
2244
+ }
2245
+ async delete(key) {
2246
+ try {
2247
+ const fullKey = this.getFullKey(key);
2248
+ const existed = await this.kv.get(fullKey);
2249
+ await this.kv.delete(fullKey);
2250
+ const wasDeleted = existed !== null;
2251
+ if (wasDeleted) {
2252
+ this.logger.debug("KV cache delete", { key });
2253
+ }
2254
+ return glLifeCore.Result.ok(wasDeleted);
2255
+ } catch (error) {
2256
+ this.logger.error("KV cache delete failed", { error, key });
2257
+ return glLifeCore.Result.err({
2258
+ type: "DELETE_FAILED",
2259
+ message: `Failed to delete cache entry from KV: ${error}`,
2260
+ cause: error
2261
+ });
2262
+ }
2263
+ }
2264
+ async deleteMany(keys) {
2265
+ try {
2266
+ let deleted = 0;
2267
+ for (const key of keys) {
2268
+ const result = await this.delete(key);
2269
+ if (result.isOk() && result.unwrap()) {
2270
+ deleted++;
2271
+ }
2272
+ }
2273
+ return glLifeCore.Result.ok(deleted);
2274
+ } catch (error) {
2275
+ this.logger.error("KV cache deleteMany failed", { error });
2276
+ return glLifeCore.Result.err({
2277
+ type: "DELETE_FAILED",
2278
+ message: `Failed to delete multiple cache entries from KV: ${error}`,
2279
+ cause: error
2280
+ });
2281
+ }
2282
+ }
2283
+ async clear() {
2284
+ try {
2285
+ const prefix = this.config.keyPrefix || "";
2286
+ let cursor;
2287
+ let totalDeleted = 0;
2288
+ do {
2289
+ const listResult = await this.kv.list({
2290
+ prefix,
2291
+ limit: 1e3,
2292
+ cursor
2293
+ });
2294
+ const deletePromises = listResult.keys.map(({ name }) => this.kv.delete(name));
2295
+ await Promise.all(deletePromises);
2296
+ totalDeleted += listResult.keys.length;
2297
+ cursor = listResult.list_complete ? void 0 : listResult.cursor;
2298
+ } while (cursor);
2299
+ this.logger.debug("KV cache cleared", { deleted: totalDeleted });
2300
+ return glLifeCore.Result.ok(void 0);
2301
+ } catch (error) {
2302
+ this.logger.error("KV cache clear failed", { error });
2303
+ return glLifeCore.Result.err({
2304
+ type: "CLEAR_FAILED",
2305
+ message: `Failed to clear KV cache: ${error}`,
2306
+ cause: error
2307
+ });
2308
+ }
2309
+ }
2310
+ async has(key) {
2311
+ try {
2312
+ const fullKey = this.getFullKey(key);
2313
+ const value = await this.kv.get(fullKey);
2314
+ return glLifeCore.Result.ok(value !== null);
2315
+ } catch (error) {
2316
+ this.logger.error("KV cache has failed", { error, key });
2317
+ return glLifeCore.Result.err({
2318
+ type: "GET_FAILED",
2319
+ message: `Failed to check cache key existence in KV: ${error}`,
2320
+ cause: error
2321
+ });
2322
+ }
2323
+ }
2324
+ async invalidate(pattern) {
2325
+ try {
2326
+ const fullPattern = this.config.keyPrefix ? `${this.config.keyPrefix}${pattern}` : pattern;
2327
+ const prefix = fullPattern.split("*")[0].split("?")[0];
2328
+ const regex = this.patternToRegex(fullPattern);
2329
+ let invalidated = 0;
2330
+ let cursor;
2331
+ do {
2332
+ const listResult = await this.kv.list({
2333
+ prefix,
2334
+ limit: 1e3,
2335
+ cursor
2336
+ });
2337
+ const matchingKeys = listResult.keys.map(({ name }) => name).filter((name) => regex.test(name));
2338
+ const deletePromises = matchingKeys.map((name) => this.kv.delete(name));
2339
+ await Promise.all(deletePromises);
2340
+ invalidated += matchingKeys.length;
2341
+ cursor = listResult.list_complete ? void 0 : listResult.cursor;
2342
+ } while (cursor);
2343
+ this.logger.debug("KV cache invalidate", { pattern, count: invalidated });
2344
+ return glLifeCore.Result.ok(invalidated);
2345
+ } catch (error) {
2346
+ this.logger.error("KV cache invalidate failed", { error, pattern });
2347
+ return glLifeCore.Result.err({
2348
+ type: "DELETE_FAILED",
2349
+ message: `Failed to invalidate cache entries in KV: ${error}`,
2350
+ cause: error
2351
+ });
2352
+ }
2353
+ }
2354
+ async invalidateTable(table) {
2355
+ try {
2356
+ const pattern = `*${table}*`;
2357
+ return await this.invalidate(pattern);
2358
+ } catch (error) {
2359
+ this.logger.error("KV cache invalidateTable failed", { error, table });
2360
+ return glLifeCore.Result.err({
2361
+ type: "DELETE_FAILED",
2362
+ message: `Failed to invalidate table cache in KV: ${error}`,
2363
+ cause: error
2364
+ });
2365
+ }
2366
+ }
2367
+ getStats() {
2368
+ try {
2369
+ const totalAccesses = this.stats.hits + this.stats.misses;
2370
+ const hitRate = totalAccesses > 0 ? this.stats.hits / totalAccesses : 0;
2371
+ return glLifeCore.Result.ok({
2372
+ itemCount: 0,
2373
+ // Not available for KV
2374
+ totalSize: 0,
2375
+ // Not available for KV
2376
+ hits: this.stats.hits,
2377
+ misses: this.stats.misses,
2378
+ hitRate,
2379
+ evictions: 0
2380
+ // KV handles eviction internally
2381
+ });
2382
+ } catch (error) {
2383
+ return glLifeCore.Result.err({
2384
+ type: "UNKNOWN",
2385
+ message: `Failed to get KV cache stats: ${error}`,
2386
+ cause: error
2387
+ });
2388
+ }
2389
+ }
2390
+ resetStats() {
2391
+ try {
2392
+ this.stats.hits = 0;
2393
+ this.stats.misses = 0;
2394
+ return glLifeCore.Result.ok(void 0);
2395
+ } catch (error) {
2396
+ return glLifeCore.Result.err({
2397
+ type: "UNKNOWN",
2398
+ message: `Failed to reset KV cache stats: ${error}`,
2399
+ cause: error
2400
+ });
2401
+ }
2402
+ }
2403
+ generateKey(sql, params) {
2404
+ const data = {
2405
+ sql,
2406
+ params: params || []
2407
+ };
2408
+ const hash = crypto__default.default.createHash("sha256").update(JSON.stringify(data)).digest("hex");
2409
+ return hash.substring(0, 16);
2410
+ }
2411
+ /**
2412
+ * Get full cache key with prefix
2413
+ */
2414
+ getFullKey(key) {
2415
+ return this.config.keyPrefix ? `${this.config.keyPrefix}${key}` : key;
2416
+ }
2417
+ /**
2418
+ * Estimate size of value in bytes
2419
+ */
2420
+ estimateSize(value) {
2421
+ try {
2422
+ return JSON.stringify(value).length;
2423
+ } catch {
2424
+ return 0;
2425
+ }
2426
+ }
2427
+ /**
2428
+ * Convert glob pattern to regex
2429
+ */
2430
+ patternToRegex(pattern) {
2431
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
2432
+ return new RegExp(`^${escaped}$`);
2433
+ }
2434
+ };
2435
+ var CacheInvalidator = class {
2436
+ cache;
2437
+ eventBus;
2438
+ logger;
2439
+ // Tag index: tag -> Set<key>
2440
+ tagIndex = /* @__PURE__ */ new Map();
2441
+ // Entry tags: key -> Set<tag>
2442
+ entryTags = /* @__PURE__ */ new Map();
2443
+ // Statistics
2444
+ stats = {
2445
+ totalInvalidations: 0,
2446
+ tagInvalidations: 0,
2447
+ patternInvalidations: 0,
2448
+ keyInvalidations: 0,
2449
+ expiredInvalidations: 0
2450
+ };
2451
+ constructor(cache, eventBus, logger) {
2452
+ this.cache = cache;
2453
+ this.eventBus = eventBus;
2454
+ this.logger = logger;
2455
+ }
2456
+ /**
2457
+ * Tag a cache entry for group invalidation
2458
+ *
2459
+ * @param key - Cache key
2460
+ * @param tags - Array of tags to associate with this entry
2461
+ * @returns Result with void on success
2462
+ */
2463
+ async tagEntry(key, tags) {
2464
+ try {
2465
+ if (!this.entryTags.has(key)) {
2466
+ this.entryTags.set(key, /* @__PURE__ */ new Set());
2467
+ }
2468
+ const entryTagSet = this.entryTags.get(key);
2469
+ for (const tag of tags) {
2470
+ entryTagSet.add(tag);
2471
+ if (!this.tagIndex.has(tag)) {
2472
+ this.tagIndex.set(tag, /* @__PURE__ */ new Set());
2473
+ }
2474
+ this.tagIndex.get(tag).add(key);
2475
+ }
2476
+ this.logger.debug("Tagged cache entry", { key, tags });
2477
+ return glLifeCore.Result.ok(void 0);
2478
+ } catch (error) {
2479
+ this.logger.error("Failed to tag entry", { error, key, tags });
2480
+ return glLifeCore.Result.err({
2481
+ type: "UNKNOWN",
2482
+ message: `Failed to tag entry: ${error}`,
2483
+ cause: error
2484
+ });
2485
+ }
2486
+ }
2487
+ /**
2488
+ * Remove a tag from a cache entry
2489
+ *
2490
+ * @param key - Cache key
2491
+ * @param tag - Tag to remove
2492
+ * @returns Result with void on success
2493
+ */
2494
+ async removeTag(key, tag) {
2495
+ try {
2496
+ const entryTagSet = this.entryTags.get(key);
2497
+ if (entryTagSet) {
2498
+ entryTagSet.delete(tag);
2499
+ if (entryTagSet.size === 0) {
2500
+ this.entryTags.delete(key);
2501
+ }
2502
+ }
2503
+ const tagSet = this.tagIndex.get(tag);
2504
+ if (tagSet) {
2505
+ tagSet.delete(key);
2506
+ if (tagSet.size === 0) {
2507
+ this.tagIndex.delete(tag);
2508
+ }
2509
+ }
2510
+ this.logger.debug("Removed tag from entry", { key, tag });
2511
+ return glLifeCore.Result.ok(void 0);
2512
+ } catch (error) {
2513
+ this.logger.error("Failed to remove tag", { error, key, tag });
2514
+ return glLifeCore.Result.err({
2515
+ type: "UNKNOWN",
2516
+ message: `Failed to remove tag: ${error}`,
2517
+ cause: error
2518
+ });
2519
+ }
2520
+ }
2521
+ /**
2522
+ * Invalidate all entries with a specific tag
2523
+ *
2524
+ * @param tag - Tag to invalidate (supports wildcards)
2525
+ * @returns Result with number of invalidated entries
2526
+ */
2527
+ async invalidateByTag(tag) {
2528
+ try {
2529
+ const keysToInvalidate = /* @__PURE__ */ new Set();
2530
+ if (tag.includes("*") || tag.includes("?")) {
2531
+ const regex = this.patternToRegex(tag);
2532
+ for (const [tagName, keys] of this.tagIndex.entries()) {
2533
+ if (regex.test(tagName)) {
2534
+ for (const key of keys) {
2535
+ keysToInvalidate.add(key);
2536
+ }
2537
+ }
2538
+ }
2539
+ } else {
2540
+ const keys = this.tagIndex.get(tag);
2541
+ if (keys) {
2542
+ for (const key of keys) {
2543
+ keysToInvalidate.add(key);
2544
+ }
2545
+ }
2546
+ }
2547
+ let invalidated = 0;
2548
+ for (const key of keysToInvalidate) {
2549
+ const result = await this.cache.delete(key);
2550
+ if (result.isOk() && result.unwrap()) {
2551
+ invalidated++;
2552
+ await this.cleanupEntryTags(key);
2553
+ }
2554
+ }
2555
+ this.stats.totalInvalidations += invalidated;
2556
+ this.stats.tagInvalidations += invalidated;
2557
+ this.logger.debug("Invalidated by tag", { tag, count: invalidated });
2558
+ await this.eventBus.emit("cache:invalidated", {
2559
+ type: "tag",
2560
+ tag,
2561
+ count: invalidated
2562
+ });
2563
+ return glLifeCore.Result.ok(invalidated);
2564
+ } catch (error) {
2565
+ this.logger.error("Failed to invalidate by tag", { error, tag });
2566
+ return glLifeCore.Result.err({
2567
+ type: "INVALIDATION_FAILED",
2568
+ message: `Failed to invalidate by tag: ${error}`,
2569
+ cause: error
2570
+ });
2571
+ }
2572
+ }
2573
+ /**
2574
+ * Invalidate entries by multiple tags
2575
+ *
2576
+ * @param tags - Array of tags to invalidate
2577
+ * @returns Result with number of invalidated entries
2578
+ */
2579
+ async invalidateByTags(tags) {
2580
+ try {
2581
+ const keysToInvalidate = /* @__PURE__ */ new Set();
2582
+ for (const tag of tags) {
2583
+ const result = await this.getTaggedKeys(tag);
2584
+ if (result.isOk()) {
2585
+ const keys = result.unwrap();
2586
+ for (const key of keys) {
2587
+ keysToInvalidate.add(key);
2588
+ }
2589
+ }
2590
+ }
2591
+ let invalidated = 0;
2592
+ for (const key of keysToInvalidate) {
2593
+ const result = await this.cache.delete(key);
2594
+ if (result.isOk() && result.unwrap()) {
2595
+ invalidated++;
2596
+ await this.cleanupEntryTags(key);
2597
+ }
2598
+ }
2599
+ this.stats.totalInvalidations += invalidated;
2600
+ this.stats.tagInvalidations += invalidated;
2601
+ this.logger.debug("Invalidated by tags", { tags, count: invalidated });
2602
+ return glLifeCore.Result.ok(invalidated);
2603
+ } catch (error) {
2604
+ this.logger.error("Failed to invalidate by tags", { error, tags });
2605
+ return glLifeCore.Result.err({
2606
+ type: "INVALIDATION_FAILED",
2607
+ message: `Failed to invalidate by tags: ${error}`,
2608
+ cause: error
2609
+ });
2610
+ }
2611
+ }
2612
+ /**
2613
+ * Invalidate specific cache keys
2614
+ *
2615
+ * @param keys - Array of keys to invalidate
2616
+ * @returns Result with number of invalidated entries
2617
+ */
2618
+ async invalidateByKeys(keys) {
2619
+ try {
2620
+ let invalidated = 0;
2621
+ for (const key of keys) {
2622
+ const result = await this.cache.delete(key);
2623
+ if (result.isOk() && result.unwrap()) {
2624
+ invalidated++;
2625
+ await this.cleanupEntryTags(key);
2626
+ }
2627
+ }
2628
+ this.stats.totalInvalidations += invalidated;
2629
+ this.stats.keyInvalidations += invalidated;
2630
+ this.logger.debug("Invalidated by keys", { count: invalidated });
2631
+ return glLifeCore.Result.ok(invalidated);
2632
+ } catch (error) {
2633
+ this.logger.error("Failed to invalidate by keys", { error, keys });
2634
+ return glLifeCore.Result.err({
2635
+ type: "INVALIDATION_FAILED",
2636
+ message: `Failed to invalidate by keys: ${error}`,
2637
+ cause: error
2638
+ });
2639
+ }
2640
+ }
2641
+ /**
2642
+ * Invalidate entries matching a pattern
2643
+ *
2644
+ * @param pattern - Pattern to match (supports wildcards)
2645
+ * @returns Result with number of invalidated entries
2646
+ */
2647
+ async invalidateByPattern(pattern) {
2648
+ try {
2649
+ const result = await this.cache.invalidate(pattern);
2650
+ if (result.isOk()) {
2651
+ const count = result.unwrap();
2652
+ this.stats.totalInvalidations += count;
2653
+ this.stats.patternInvalidations += count;
2654
+ }
2655
+ return result;
2656
+ } catch (error) {
2657
+ this.logger.error("Failed to invalidate by pattern", { error, pattern });
2658
+ return glLifeCore.Result.err({
2659
+ type: "INVALIDATION_FAILED",
2660
+ message: `Failed to invalidate by pattern: ${error}`,
2661
+ cause: error
2662
+ });
2663
+ }
2664
+ }
2665
+ /**
2666
+ * Scan and invalidate expired entries
2667
+ *
2668
+ * @returns Result with number of invalidated entries
2669
+ */
2670
+ async invalidateExpired() {
2671
+ try {
2672
+ let invalidated = 0;
2673
+ const allKeys = Array.from(this.entryTags.keys());
2674
+ for (const key of allKeys) {
2675
+ const result = await this.cache.get(key);
2676
+ if (result.isOk() && result.unwrap().isNone()) {
2677
+ await this.cleanupEntryTags(key);
2678
+ invalidated++;
2679
+ }
2680
+ }
2681
+ this.stats.totalInvalidations += invalidated;
2682
+ this.stats.expiredInvalidations += invalidated;
2683
+ this.logger.debug("Invalidated expired entries", { count: invalidated });
2684
+ return glLifeCore.Result.ok(invalidated);
2685
+ } catch (error) {
2686
+ this.logger.error("Failed to invalidate expired entries", { error });
2687
+ return glLifeCore.Result.err({
2688
+ type: "INVALIDATION_FAILED",
2689
+ message: `Failed to invalidate expired entries: ${error}`,
2690
+ cause: error
2691
+ });
2692
+ }
2693
+ }
2694
+ /**
2695
+ * Subscribe to data change events for automatic invalidation
2696
+ *
2697
+ * @returns Unsubscribe function
2698
+ */
2699
+ subscribeToDataChanges() {
2700
+ const unsubscribers = [];
2701
+ unsubscribers.push(
2702
+ this.eventBus.subscribe("*", async (data) => {
2703
+ })
2704
+ );
2705
+ const cacheInvalidateHandler = async (data) => {
2706
+ if (data && data.tag) {
2707
+ await this.invalidateByTag(data.tag);
2708
+ }
2709
+ };
2710
+ const events = [
2711
+ "cache:invalidate:users",
2712
+ "cache:invalidate:posts",
2713
+ "cache:invalidate:comments"
2714
+ ];
2715
+ for (const event of events) {
2716
+ unsubscribers.push(this.eventBus.subscribe(event, cacheInvalidateHandler));
2717
+ }
2718
+ unsubscribers.push(
2719
+ this.eventBus.subscribe("cache:invalidate:batch", async (data) => {
2720
+ if (data && data.tags) {
2721
+ await this.invalidateByTags(data.tags);
2722
+ }
2723
+ })
2724
+ );
2725
+ const dataUpdateHandler = async (data) => {
2726
+ if (data && data.table) {
2727
+ await this.cache.invalidateTable(data.table);
2728
+ }
2729
+ };
2730
+ const dataEvents = ["data:updated:users", "data:updated:posts"];
2731
+ for (const event of dataEvents) {
2732
+ unsubscribers.push(this.eventBus.subscribe(event, dataUpdateHandler));
2733
+ }
2734
+ return () => {
2735
+ for (const unsubscribe of unsubscribers) {
2736
+ unsubscribe();
2737
+ }
2738
+ };
2739
+ }
2740
+ /**
2741
+ * Get all keys tagged with a specific tag
2742
+ *
2743
+ * @param tag - Tag name
2744
+ * @returns Result with array of keys
2745
+ */
2746
+ async getTaggedKeys(tag) {
2747
+ try {
2748
+ const keys = this.tagIndex.get(tag);
2749
+ if (!keys) {
2750
+ return glLifeCore.Result.ok([]);
2751
+ }
2752
+ return glLifeCore.Result.ok(Array.from(keys));
2753
+ } catch (error) {
2754
+ return glLifeCore.Result.err({
2755
+ type: "UNKNOWN",
2756
+ message: `Failed to get tagged keys: ${error}`,
2757
+ cause: error
2758
+ });
2759
+ }
2760
+ }
2761
+ /**
2762
+ * Get all tags for a cache entry
2763
+ *
2764
+ * @param key - Cache key
2765
+ * @returns Result with array of tags
2766
+ */
2767
+ async getEntryTags(key) {
2768
+ try {
2769
+ const tags = this.entryTags.get(key);
2770
+ if (!tags) {
2771
+ return glLifeCore.Result.ok([]);
2772
+ }
2773
+ return glLifeCore.Result.ok(Array.from(tags));
2774
+ } catch (error) {
2775
+ return glLifeCore.Result.err({
2776
+ type: "UNKNOWN",
2777
+ message: `Failed to get entry tags: ${error}`,
2778
+ cause: error
2779
+ });
2780
+ }
2781
+ }
2782
+ /**
2783
+ * Get invalidation statistics
2784
+ *
2785
+ * @returns Result with statistics
2786
+ */
2787
+ getStats() {
2788
+ try {
2789
+ return glLifeCore.Result.ok({ ...this.stats });
2790
+ } catch (error) {
2791
+ return glLifeCore.Result.err({
2792
+ type: "UNKNOWN",
2793
+ message: `Failed to get stats: ${error}`,
2794
+ cause: error
2795
+ });
2796
+ }
2797
+ }
2798
+ /**
2799
+ * Reset invalidation statistics
2800
+ *
2801
+ * @returns Result with void on success
2802
+ */
2803
+ resetStats() {
2804
+ try {
2805
+ this.stats = {
2806
+ totalInvalidations: 0,
2807
+ tagInvalidations: 0,
2808
+ patternInvalidations: 0,
2809
+ keyInvalidations: 0,
2810
+ expiredInvalidations: 0
2811
+ };
2812
+ return glLifeCore.Result.ok(void 0);
2813
+ } catch (error) {
2814
+ return glLifeCore.Result.err({
2815
+ type: "UNKNOWN",
2816
+ message: `Failed to reset stats: ${error}`,
2817
+ cause: error
2818
+ });
2819
+ }
2820
+ }
2821
+ /**
2822
+ * Clean up tag mappings for a deleted entry
2823
+ */
2824
+ async cleanupEntryTags(key) {
2825
+ const tags = this.entryTags.get(key);
2826
+ if (tags) {
2827
+ for (const tag of tags) {
2828
+ const tagSet = this.tagIndex.get(tag);
2829
+ if (tagSet) {
2830
+ tagSet.delete(key);
2831
+ if (tagSet.size === 0) {
2832
+ this.tagIndex.delete(tag);
2833
+ }
2834
+ }
2835
+ }
2836
+ this.entryTags.delete(key);
2837
+ }
2838
+ }
2839
+ /**
2840
+ * Convert glob pattern to regex
2841
+ */
2842
+ patternToRegex(pattern) {
2843
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
2844
+ return new RegExp(`^${escaped}$`);
2845
+ }
2846
+ };
2847
+ var MigrationLoader = class {
2848
+ config;
2849
+ logger;
2850
+ filePattern;
2851
+ constructor(config, logger) {
2852
+ this.config = config;
2853
+ this.logger = logger;
2854
+ this.filePattern = config.filePattern || /^(\d+|V\d+)[_-].*\.ts$/;
2855
+ }
2856
+ /**
2857
+ * Load all migrations from the configured directory
2858
+ *
2859
+ * @returns Result with array of migrations ordered by version
2860
+ */
2861
+ async loadMigrations() {
2862
+ try {
2863
+ if (!fs__namespace.existsSync(this.config.migrationsPath)) {
2864
+ return glLifeCore.Result.err({
2865
+ type: "INVALID_MIGRATION",
2866
+ message: `Migrations directory does not exist: ${this.config.migrationsPath}`
2867
+ });
2868
+ }
2869
+ const files = fs__namespace.readdirSync(this.config.migrationsPath);
2870
+ const migrationFiles = files.filter((file) => this.filePattern.test(file));
2871
+ this.logger.debug("Found migration files", {
2872
+ total: files.length,
2873
+ migrations: migrationFiles.length
2874
+ });
2875
+ const migrations = [];
2876
+ for (const file of migrationFiles) {
2877
+ const filePath = path__namespace.join(this.config.migrationsPath, file);
2878
+ const result = await this.loadMigrationFile(filePath);
2879
+ if (result.isErr()) {
2880
+ return result;
2881
+ }
2882
+ migrations.push(result.unwrap());
2883
+ }
2884
+ const duplicates = this.findDuplicateVersions(migrations);
2885
+ if (duplicates.length > 0) {
2886
+ return glLifeCore.Result.err({
2887
+ type: "INVALID_MIGRATION",
2888
+ message: `Duplicate migration versions found: ${duplicates.join(", ")}`
2889
+ });
2890
+ }
2891
+ migrations.sort((a, b) => a.version - b.version);
2892
+ this.logger.debug("Loaded migrations", { count: migrations.length });
2893
+ return glLifeCore.Result.ok(migrations);
2894
+ } catch (error) {
2895
+ this.logger.error("Failed to load migrations", { error });
2896
+ return glLifeCore.Result.err({
2897
+ type: "UNKNOWN",
2898
+ message: `Failed to load migrations: ${error}`,
2899
+ cause: error
2900
+ });
2901
+ }
2902
+ }
2903
+ /**
2904
+ * Load a single migration file
2905
+ *
2906
+ * @param filePath - Path to migration file
2907
+ * @returns Result with Migration or MigrationError
2908
+ */
2909
+ async loadMigrationFile(filePath) {
2910
+ try {
2911
+ const content = fs__namespace.readFileSync(filePath, "utf-8");
2912
+ const fileUrl = `file://${filePath.replace(/\\/g, "/")}`;
2913
+ const module = await import(fileUrl);
2914
+ const validation = this.validateMigration(module, filePath);
2915
+ if (validation.isErr()) {
2916
+ return validation;
2917
+ }
2918
+ const { version: version2, name, up, down } = module;
2919
+ const id = this.generateMigrationId(version2, name);
2920
+ const checksum = this.computeChecksum(content);
2921
+ const migration = {
2922
+ id,
2923
+ version: version2,
2924
+ name,
2925
+ up,
2926
+ down,
2927
+ checksum
2928
+ };
2929
+ this.logger.debug("Loaded migration", { id, version: version2, name });
2930
+ return glLifeCore.Result.ok(migration);
2931
+ } catch (error) {
2932
+ this.logger.error("Failed to load migration file", { error, filePath });
2933
+ return glLifeCore.Result.err({
2934
+ type: "INVALID_MIGRATION",
2935
+ message: `Failed to load migration file ${filePath}: ${error}`,
2936
+ cause: error
2937
+ });
2938
+ }
2939
+ }
2940
+ /**
2941
+ * Validate migration module structure
2942
+ *
2943
+ * @param module - Migration module
2944
+ * @param filePath - File path for error messages
2945
+ * @returns Result with void on success or MigrationError
2946
+ */
2947
+ validateMigration(module, filePath) {
2948
+ if (module.version === void 0) {
2949
+ return glLifeCore.Result.err({
2950
+ type: "INVALID_MIGRATION",
2951
+ message: `Migration ${filePath} is missing 'version' export`
2952
+ });
2953
+ }
2954
+ if (typeof module.version !== "number") {
2955
+ return glLifeCore.Result.err({
2956
+ type: "INVALID_MIGRATION",
2957
+ message: `Migration ${filePath} has invalid version type (must be number)`
2958
+ });
2959
+ }
2960
+ if (module.version < 0) {
2961
+ return glLifeCore.Result.err({
2962
+ type: "INVALID_MIGRATION",
2963
+ message: `Migration ${filePath} has negative version number`
2964
+ });
2965
+ }
2966
+ if (!module.name) {
2967
+ return glLifeCore.Result.err({
2968
+ type: "INVALID_MIGRATION",
2969
+ message: `Migration ${filePath} is missing 'name' export`
2970
+ });
2971
+ }
2972
+ if (!module.up || typeof module.up !== "function") {
2973
+ return glLifeCore.Result.err({
2974
+ type: "INVALID_MIGRATION",
2975
+ message: `Migration ${filePath} is missing 'up' function`
2976
+ });
2977
+ }
2978
+ if (!module.down || typeof module.down !== "function") {
2979
+ return glLifeCore.Result.err({
2980
+ type: "INVALID_MIGRATION",
2981
+ message: `Migration ${filePath} is missing 'down' function`
2982
+ });
2983
+ }
2984
+ return glLifeCore.Result.ok(void 0);
2985
+ }
2986
+ /**
2987
+ * Find duplicate version numbers in migrations
2988
+ *
2989
+ * @param migrations - Array of migrations
2990
+ * @returns Array of duplicate version numbers
2991
+ */
2992
+ findDuplicateVersions(migrations) {
2993
+ const versionCounts = /* @__PURE__ */ new Map();
2994
+ for (const migration of migrations) {
2995
+ const count = versionCounts.get(migration.version) || 0;
2996
+ versionCounts.set(migration.version, count + 1);
2997
+ }
2998
+ const duplicates = [];
2999
+ for (const [version2, count] of versionCounts.entries()) {
3000
+ if (count > 1) {
3001
+ duplicates.push(version2);
3002
+ }
3003
+ }
3004
+ return duplicates;
3005
+ }
3006
+ /**
3007
+ * Generate unique migration ID from version and name
3008
+ *
3009
+ * @param version - Migration version
3010
+ * @param name - Migration name
3011
+ * @returns Migration ID
3012
+ */
3013
+ generateMigrationId(version2, name) {
3014
+ return `${version2}_${name}`;
3015
+ }
3016
+ /**
3017
+ * Compute SHA-256 checksum of migration content
3018
+ *
3019
+ * @param content - Migration file content
3020
+ * @returns Checksum string
3021
+ */
3022
+ computeChecksum(content) {
3023
+ return crypto__default.default.createHash("sha256").update(content).digest("hex");
3024
+ }
3025
+ /**
3026
+ * Get SQL schema for migration tracking table
3027
+ *
3028
+ * @returns SQL CREATE TABLE statement
3029
+ */
3030
+ getMigrationTableSchema() {
3031
+ const tableName = this.config.migrationTable;
3032
+ return `
3033
+ CREATE TABLE IF NOT EXISTS ${tableName} (
3034
+ id TEXT PRIMARY KEY,
3035
+ version INTEGER NOT NULL UNIQUE,
3036
+ name TEXT NOT NULL,
3037
+ checksum TEXT,
3038
+ status TEXT NOT NULL DEFAULT 'PENDING',
3039
+ executed_at TIMESTAMP,
3040
+ execution_time INTEGER,
3041
+ error TEXT
3042
+ );
3043
+
3044
+ CREATE INDEX IF NOT EXISTS idx_${tableName}_version ON ${tableName}(version);
3045
+ CREATE INDEX IF NOT EXISTS idx_${tableName}_status ON ${tableName}(status);
3046
+ `.trim();
3047
+ }
3048
+ };
3049
+ var MigrationRunner = class {
3050
+ connection;
3051
+ loader;
3052
+ config;
3053
+ logger;
3054
+ constructor(connection, loader, config, logger) {
3055
+ this.connection = connection;
3056
+ this.loader = loader;
3057
+ this.config = config;
3058
+ this.logger = logger;
3059
+ }
3060
+ /**
3061
+ * Create migration table if it doesn't exist
3062
+ */
3063
+ async createMigrationTable() {
3064
+ try {
3065
+ const schema = this.loader.getMigrationTableSchema();
3066
+ const result = await this.connection.execute(schema);
3067
+ if (result.isErr()) {
3068
+ return glLifeCore.Result.err({
3069
+ type: "UNKNOWN",
3070
+ message: "Failed to create migration table",
3071
+ cause: result.variant.error
3072
+ });
3073
+ }
3074
+ this.logger.debug("Migration table created");
3075
+ return glLifeCore.Result.ok(void 0);
3076
+ } catch (error) {
3077
+ this.logger.error("Failed to create migration table", { error });
3078
+ return glLifeCore.Result.err({
3079
+ type: "UNKNOWN",
3080
+ message: `Failed to create migration table: ${error}`,
3081
+ cause: error
3082
+ });
3083
+ }
3084
+ }
3085
+ /**
3086
+ * Get all available migrations
3087
+ */
3088
+ async getMigrations() {
3089
+ return this.loader.loadMigrations();
3090
+ }
3091
+ /**
3092
+ * Get migration history from database
3093
+ */
3094
+ async getHistory() {
3095
+ try {
3096
+ const query = `SELECT * FROM ${this.config.migrationTable} ORDER BY version DESC`;
3097
+ const result = await this.connection.query(query);
3098
+ if (result.isErr()) {
3099
+ return glLifeCore.Result.err({
3100
+ type: "UNKNOWN",
3101
+ message: "Failed to get migration history",
3102
+ cause: result.variant.error
3103
+ });
3104
+ }
3105
+ const rows = result.unwrap();
3106
+ const history = rows.map((row) => ({
3107
+ id: row.id,
3108
+ version: row.version,
3109
+ name: row.name,
3110
+ status: row.status,
3111
+ executedAt: row.executed_at ? new Date(row.executed_at) : void 0,
3112
+ executionTime: row.execution_time,
3113
+ error: row.error,
3114
+ checksum: row.checksum
3115
+ }));
3116
+ return glLifeCore.Result.ok(history);
3117
+ } catch (error) {
3118
+ this.logger.error("Failed to get migration history", { error });
3119
+ return glLifeCore.Result.err({
3120
+ type: "UNKNOWN",
3121
+ message: `Failed to get migration history: ${error}`,
3122
+ cause: error
3123
+ });
3124
+ }
3125
+ }
3126
+ /**
3127
+ * Get pending migrations
3128
+ */
3129
+ async getPending() {
3130
+ try {
3131
+ const migrationsResult = await this.getMigrations();
3132
+ if (migrationsResult.isErr()) {
3133
+ return migrationsResult;
3134
+ }
3135
+ const allMigrations = migrationsResult.unwrap();
3136
+ const historyResult = await this.getHistory();
3137
+ if (historyResult.isErr()) {
3138
+ return glLifeCore.Result.err(historyResult.variant.error);
3139
+ }
3140
+ const history = historyResult.unwrap();
3141
+ const appliedVersions = new Set(
3142
+ history.filter((h) => h.status === "COMPLETED").map((h) => h.version)
3143
+ );
3144
+ const pending = allMigrations.filter((m) => !appliedVersions.has(m.version));
3145
+ return glLifeCore.Result.ok(pending);
3146
+ } catch (error) {
3147
+ this.logger.error("Failed to get pending migrations", { error });
3148
+ return glLifeCore.Result.err({
3149
+ type: "UNKNOWN",
3150
+ message: `Failed to get pending migrations: ${error}`,
3151
+ cause: error
3152
+ });
3153
+ }
3154
+ }
3155
+ /**
3156
+ * Run all pending migrations
3157
+ */
3158
+ async migrate() {
3159
+ try {
3160
+ if (this.config.validateChecksums) {
3161
+ const validationResult = await this.validateChecksums();
3162
+ if (validationResult.isErr()) {
3163
+ return glLifeCore.Result.err(validationResult.variant.error);
3164
+ }
3165
+ }
3166
+ const pendingResult = await this.getPending();
3167
+ if (pendingResult.isErr()) {
3168
+ return glLifeCore.Result.err(pendingResult.variant.error);
3169
+ }
3170
+ const pending = pendingResult.unwrap();
3171
+ const applied = [];
3172
+ for (const migration of pending) {
3173
+ const result = await this.runMigration(migration, "UP");
3174
+ if (result.isErr()) {
3175
+ return glLifeCore.Result.err(result.variant.error);
3176
+ }
3177
+ applied.push(result.unwrap());
3178
+ }
3179
+ this.logger.info("Migrations completed", { count: applied.length });
3180
+ return glLifeCore.Result.ok(applied);
3181
+ } catch (error) {
3182
+ this.logger.error("Migration failed", { error });
3183
+ return glLifeCore.Result.err({
3184
+ type: "UNKNOWN",
3185
+ message: `Migration failed: ${error}`,
3186
+ cause: error
3187
+ });
3188
+ }
3189
+ }
3190
+ /**
3191
+ * Run migrations up to a specific version
3192
+ */
3193
+ async migrateTo(targetVersion) {
3194
+ try {
3195
+ const migrationsResult = await this.getMigrations();
3196
+ if (migrationsResult.isErr()) {
3197
+ return glLifeCore.Result.err(migrationsResult.variant.error);
3198
+ }
3199
+ const allMigrations = migrationsResult.unwrap();
3200
+ const targetMigration = allMigrations.find((m) => m.version === targetVersion);
3201
+ if (!targetMigration) {
3202
+ return glLifeCore.Result.err({
3203
+ type: "MIGRATION_NOT_FOUND",
3204
+ message: `Migration version ${targetVersion} not found`,
3205
+ migrationId: `${targetVersion}`
3206
+ });
3207
+ }
3208
+ const pendingResult = await this.getPending();
3209
+ if (pendingResult.isErr()) {
3210
+ return glLifeCore.Result.err(pendingResult.variant.error);
3211
+ }
3212
+ const pending = pendingResult.unwrap();
3213
+ const toApply = pending.filter((m) => m.version <= targetVersion);
3214
+ const applied = [];
3215
+ for (const migration of toApply) {
3216
+ const result = await this.runMigration(migration, "UP");
3217
+ if (result.isErr()) {
3218
+ return glLifeCore.Result.err(result.variant.error);
3219
+ }
3220
+ applied.push(result.unwrap());
3221
+ }
3222
+ this.logger.info("Migrated to version", { version: targetVersion });
3223
+ return glLifeCore.Result.ok(applied);
3224
+ } catch (error) {
3225
+ this.logger.error("Migration to version failed", { error, targetVersion });
3226
+ return glLifeCore.Result.err({
3227
+ type: "UNKNOWN",
3228
+ message: `Migration to version ${targetVersion} failed: ${error}`,
3229
+ cause: error
3230
+ });
3231
+ }
3232
+ }
3233
+ /**
3234
+ * Rollback the last migration
3235
+ */
3236
+ async rollback() {
3237
+ try {
3238
+ const historyResult = await this.getHistory();
3239
+ if (historyResult.isErr()) {
3240
+ return glLifeCore.Result.err(historyResult.variant.error);
3241
+ }
3242
+ const history = historyResult.unwrap();
3243
+ const completed = history.filter((h) => h.status === "COMPLETED");
3244
+ if (completed.length === 0) {
3245
+ return glLifeCore.Result.err({
3246
+ type: "NOT_APPLIED",
3247
+ message: "No migrations to rollback",
3248
+ migrationId: ""
3249
+ });
3250
+ }
3251
+ const lastMigration = completed[0];
3252
+ const migrationsResult = await this.getMigrations();
3253
+ if (migrationsResult.isErr()) {
3254
+ return glLifeCore.Result.err(migrationsResult.variant.error);
3255
+ }
3256
+ const migrations = migrationsResult.unwrap();
3257
+ const migration = migrations.find((m) => m.id === lastMigration.id);
3258
+ if (!migration) {
3259
+ return glLifeCore.Result.err({
3260
+ type: "MIGRATION_NOT_FOUND",
3261
+ message: `Migration ${lastMigration.id} not found`,
3262
+ migrationId: lastMigration.id
3263
+ });
3264
+ }
3265
+ const result = await this.runMigration(migration, "DOWN");
3266
+ if (result.isErr()) {
3267
+ return glLifeCore.Result.err(result.variant.error);
3268
+ }
3269
+ this.logger.info("Rolled back migration", { id: migration.id });
3270
+ return glLifeCore.Result.ok(result.unwrap());
3271
+ } catch (error) {
3272
+ this.logger.error("Rollback failed", { error });
3273
+ return glLifeCore.Result.err({
3274
+ type: "UNKNOWN",
3275
+ message: `Rollback failed: ${error}`,
3276
+ cause: error
3277
+ });
3278
+ }
3279
+ }
3280
+ /**
3281
+ * Rollback to a specific version
3282
+ */
3283
+ async rollbackTo(targetVersion) {
3284
+ try {
3285
+ const historyResult = await this.getHistory();
3286
+ if (historyResult.isErr()) {
3287
+ return glLifeCore.Result.err(historyResult.variant.error);
3288
+ }
3289
+ const history = historyResult.unwrap();
3290
+ const completed = history.filter((h) => h.status === "COMPLETED" && h.version > targetVersion).sort((a, b) => b.version - a.version);
3291
+ const migrationsResult = await this.getMigrations();
3292
+ if (migrationsResult.isErr()) {
3293
+ return glLifeCore.Result.err(migrationsResult.variant.error);
3294
+ }
3295
+ const allMigrations = migrationsResult.unwrap();
3296
+ const rolledBack = [];
3297
+ for (const historyEntry of completed) {
3298
+ const migration = allMigrations.find((m) => m.id === historyEntry.id);
3299
+ if (!migration) {
3300
+ return glLifeCore.Result.err({
3301
+ type: "MIGRATION_NOT_FOUND",
3302
+ message: `Migration ${historyEntry.id} not found`,
3303
+ migrationId: historyEntry.id
3304
+ });
3305
+ }
3306
+ const result = await this.runMigration(migration, "DOWN");
3307
+ if (result.isErr()) {
3308
+ return glLifeCore.Result.err(result.variant.error);
3309
+ }
3310
+ rolledBack.push(result.unwrap());
3311
+ }
3312
+ this.logger.info("Rolled back to version", { version: targetVersion });
3313
+ return glLifeCore.Result.ok(rolledBack);
3314
+ } catch (error) {
3315
+ this.logger.error("Rollback to version failed", { error, targetVersion });
3316
+ return glLifeCore.Result.err({
3317
+ type: "UNKNOWN",
3318
+ message: `Rollback to version ${targetVersion} failed: ${error}`,
3319
+ cause: error
3320
+ });
3321
+ }
3322
+ }
3323
+ /**
3324
+ * Reset database by rolling back all migrations
3325
+ */
3326
+ async reset() {
3327
+ return this.rollbackTo(0);
3328
+ }
3329
+ /**
3330
+ * Refresh database by resetting and re-running all migrations
3331
+ */
3332
+ async refresh() {
3333
+ try {
3334
+ const resetResult = await this.reset();
3335
+ if (resetResult.isErr()) {
3336
+ return glLifeCore.Result.err(resetResult.variant.error);
3337
+ }
3338
+ const rolledBack = resetResult.unwrap();
3339
+ const migrateResult = await this.migrate();
3340
+ if (migrateResult.isErr()) {
3341
+ return glLifeCore.Result.err(migrateResult.variant.error);
3342
+ }
3343
+ const applied = migrateResult.unwrap();
3344
+ this.logger.info("Database refreshed", {
3345
+ rolledBack: rolledBack.length,
3346
+ applied: applied.length
3347
+ });
3348
+ return glLifeCore.Result.ok({ rolledBack, applied });
3349
+ } catch (error) {
3350
+ this.logger.error("Refresh failed", { error });
3351
+ return glLifeCore.Result.err({
3352
+ type: "UNKNOWN",
3353
+ message: `Refresh failed: ${error}`,
3354
+ cause: error
3355
+ });
3356
+ }
3357
+ }
3358
+ /**
3359
+ * Get current migration version
3360
+ */
3361
+ async getCurrentVersion() {
3362
+ try {
3363
+ const historyResult = await this.getHistory();
3364
+ if (historyResult.isErr()) {
3365
+ return glLifeCore.Result.err(historyResult.variant.error);
3366
+ }
3367
+ const history = historyResult.unwrap();
3368
+ const completed = history.filter((h) => h.status === "COMPLETED");
3369
+ if (completed.length === 0) {
3370
+ return glLifeCore.Result.ok(0);
3371
+ }
3372
+ const maxVersion = Math.max(...completed.map((h) => h.version));
3373
+ return glLifeCore.Result.ok(maxVersion);
3374
+ } catch (error) {
3375
+ this.logger.error("Failed to get current version", { error });
3376
+ return glLifeCore.Result.err({
3377
+ type: "UNKNOWN",
3378
+ message: `Failed to get current version: ${error}`,
3379
+ cause: error
3380
+ });
3381
+ }
3382
+ }
3383
+ /**
3384
+ * Validate migration checksums
3385
+ */
3386
+ async validateChecksums() {
3387
+ try {
3388
+ const migrationsResult = await this.getMigrations();
3389
+ if (migrationsResult.isErr()) {
3390
+ return glLifeCore.Result.err(migrationsResult.variant.error);
3391
+ }
3392
+ const migrations = migrationsResult.unwrap();
3393
+ const historyResult = await this.getHistory();
3394
+ if (historyResult.isErr()) {
3395
+ return glLifeCore.Result.err(historyResult.variant.error);
3396
+ }
3397
+ const history = historyResult.unwrap();
3398
+ const completedMigrations = history.filter((h) => h.status === "COMPLETED");
3399
+ for (const historyEntry of completedMigrations) {
3400
+ const migration = migrations.find((m) => m.id === historyEntry.id);
3401
+ if (!migration) {
3402
+ continue;
3403
+ }
3404
+ if (migration.checksum && historyEntry.checksum) {
3405
+ if (migration.checksum !== historyEntry.checksum) {
3406
+ return glLifeCore.Result.err({
3407
+ type: "CHECKSUM_MISMATCH",
3408
+ message: `Checksum mismatch for migration ${migration.id}`,
3409
+ migrationId: migration.id
3410
+ });
3411
+ }
3412
+ }
3413
+ }
3414
+ return glLifeCore.Result.ok(true);
3415
+ } catch (error) {
3416
+ this.logger.error("Checksum validation failed", { error });
3417
+ return glLifeCore.Result.err({
3418
+ type: "UNKNOWN",
3419
+ message: `Checksum validation failed: ${error}`,
3420
+ cause: error
3421
+ });
3422
+ }
3423
+ }
3424
+ /**
3425
+ * Load migration from file
3426
+ */
3427
+ async loadMigration(filePath) {
3428
+ if (this.loader.loadMigrationFile) {
3429
+ return this.loader.loadMigrationFile(filePath);
3430
+ }
3431
+ return glLifeCore.Result.err({
3432
+ type: "UNKNOWN",
3433
+ message: "Migration loader does not support loading individual files"
3434
+ });
3435
+ }
3436
+ /**
3437
+ * Run a single migration in a transaction
3438
+ */
3439
+ async runMigration(migration, direction) {
3440
+ const startTime = Date.now();
3441
+ const isUp = direction === "UP";
3442
+ const fn = isUp ? migration.up : migration.down;
3443
+ try {
3444
+ await this.updateMigrationStatus(
3445
+ migration,
3446
+ isUp ? "RUNNING" : "RUNNING",
3447
+ void 0,
3448
+ void 0
3449
+ );
3450
+ const runFn = async (conn) => {
3451
+ const context = {
3452
+ execute: async (sql, params) => {
3453
+ const result2 = await conn.execute(sql, params);
3454
+ if (result2.isErr()) {
3455
+ return glLifeCore.Result.err({
3456
+ type: "MIGRATION_FAILED",
3457
+ message: `Migration execution failed: ${result2.variant.error}`,
3458
+ migrationId: migration.id,
3459
+ cause: result2.variant.error
3460
+ });
3461
+ }
3462
+ return glLifeCore.Result.ok(result2.unwrap());
3463
+ },
3464
+ log: (message) => {
3465
+ this.logger.info(message, { migrationId: migration.id });
3466
+ },
3467
+ metadata: {
3468
+ id: migration.id,
3469
+ version: migration.version,
3470
+ name: migration.name,
3471
+ status: "RUNNING"
3472
+ }
3473
+ };
3474
+ return await fn(context);
3475
+ };
3476
+ let result;
3477
+ if (this.config.transactional) {
3478
+ result = await this.connection.transaction(runFn);
3479
+ } else {
3480
+ result = await runFn(this.connection);
3481
+ }
3482
+ const executionTime = Date.now() - startTime;
3483
+ if (result.isErr()) {
3484
+ await this.updateMigrationStatus(
3485
+ migration,
3486
+ "FAILED",
3487
+ executionTime,
3488
+ result.variant.error.message
3489
+ );
3490
+ return glLifeCore.Result.err(result.variant.error);
3491
+ }
3492
+ const finalStatus = isUp ? "COMPLETED" : "ROLLED_BACK";
3493
+ await this.updateMigrationStatus(migration, finalStatus, executionTime, void 0);
3494
+ const metadata = {
3495
+ id: migration.id,
3496
+ version: migration.version,
3497
+ name: migration.name,
3498
+ status: finalStatus,
3499
+ executedAt: /* @__PURE__ */ new Date(),
3500
+ executionTime,
3501
+ checksum: migration.checksum
3502
+ };
3503
+ this.logger.info("Migration executed", {
3504
+ id: migration.id,
3505
+ direction,
3506
+ executionTime
3507
+ });
3508
+ return glLifeCore.Result.ok(metadata);
3509
+ } catch (error) {
3510
+ const executionTime = Date.now() - startTime;
3511
+ await this.updateMigrationStatus(
3512
+ migration,
3513
+ "FAILED",
3514
+ executionTime,
3515
+ String(error)
3516
+ );
3517
+ this.logger.error("Migration execution failed", { error, migration: migration.id });
3518
+ return glLifeCore.Result.err({
3519
+ type: isUp ? "MIGRATION_FAILED" : "ROLLBACK_FAILED",
3520
+ message: `Migration ${migration.id} failed: ${error}`,
3521
+ migrationId: migration.id,
3522
+ cause: error
3523
+ });
3524
+ }
3525
+ }
3526
+ /**
3527
+ * Update migration status in database
3528
+ */
3529
+ async updateMigrationStatus(migration, status, executionTime, error) {
3530
+ try {
3531
+ const tableName = this.config.migrationTable;
3532
+ if (status === "RUNNING") {
3533
+ const insertSQL = `
3534
+ INSERT OR REPLACE INTO ${tableName} (id, version, name, checksum, status, executed_at, execution_time, error)
3535
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3536
+ `;
3537
+ await this.connection.execute(insertSQL, [
3538
+ {
3539
+ id: migration.id,
3540
+ version: migration.version,
3541
+ name: migration.name,
3542
+ checksum: migration.checksum || null,
3543
+ status,
3544
+ executed_at: (/* @__PURE__ */ new Date()).toISOString(),
3545
+ execution_time: null,
3546
+ error: null
3547
+ }
3548
+ ]);
3549
+ } else {
3550
+ const updateSQL = `
3551
+ UPDATE ${tableName}
3552
+ SET status = ?, execution_time = ?, error = ?, executed_at = ?
3553
+ WHERE id = ?
3554
+ `;
3555
+ await this.connection.execute(updateSQL, [
3556
+ {
3557
+ status,
3558
+ execution_time: executionTime || null,
3559
+ error: error || null,
3560
+ executed_at: (/* @__PURE__ */ new Date()).toISOString(),
3561
+ id: migration.id
3562
+ }
3563
+ ]);
3564
+ }
3565
+ } catch (err) {
3566
+ this.logger.error("Failed to update migration status", {
3567
+ error: err,
3568
+ migration: migration.id
3569
+ });
3570
+ }
3571
+ }
3572
+ };
3573
+ var DataSeeder = class {
3574
+ connection;
3575
+ config;
3576
+ logger;
3577
+ seedsCache = null;
3578
+ constructor(connection, config, logger) {
3579
+ this.connection = connection;
3580
+ this.config = config;
3581
+ this.logger = logger;
3582
+ }
3583
+ /**
3584
+ * Create seed table if it doesn't exist
3585
+ */
3586
+ async createSeedTable() {
3587
+ try {
3588
+ const schema = this.getSeedTableSchema();
3589
+ const result = await this.connection.execute(schema);
3590
+ if (result.isErr()) {
3591
+ return glLifeCore.Result.err({
3592
+ type: "UNKNOWN",
3593
+ message: "Failed to create seed table",
3594
+ cause: result.variant.error
3595
+ });
3596
+ }
3597
+ this.logger.debug("Seed table created");
3598
+ return glLifeCore.Result.ok(void 0);
3599
+ } catch (error) {
3600
+ this.logger.error("Failed to create seed table", { error });
3601
+ return glLifeCore.Result.err({
3602
+ type: "UNKNOWN",
3603
+ message: `Failed to create seed table: ${error}`,
3604
+ cause: error
3605
+ });
3606
+ }
3607
+ }
3608
+ /**
3609
+ * Get all available seeds
3610
+ */
3611
+ async getSeeds() {
3612
+ return this.loadSeedsFromDirectory();
3613
+ }
3614
+ /**
3615
+ * Get seed history from database
3616
+ */
3617
+ async getHistory() {
3618
+ try {
3619
+ const query = `SELECT * FROM ${this.config.seedTable} ORDER BY executed_at DESC`;
3620
+ const result = await this.connection.query(query);
3621
+ if (result.isErr()) {
3622
+ return glLifeCore.Result.err({
3623
+ type: "UNKNOWN",
3624
+ message: "Failed to get seed history",
3625
+ cause: result.variant.error
3626
+ });
3627
+ }
3628
+ const rows = result.unwrap();
3629
+ const history = rows.map((row) => ({
3630
+ id: row.id,
3631
+ name: row.name,
3632
+ environment: JSON.parse(row.environment || "[]"),
3633
+ status: row.status,
3634
+ executedAt: row.executed_at ? new Date(row.executed_at) : void 0,
3635
+ executionTime: row.execution_time,
3636
+ error: row.error,
3637
+ checksum: row.checksum
3638
+ }));
3639
+ return glLifeCore.Result.ok(history);
3640
+ } catch (error) {
3641
+ this.logger.error("Failed to get seed history", { error });
3642
+ return glLifeCore.Result.err({
3643
+ type: "UNKNOWN",
3644
+ message: `Failed to get seed history: ${error}`,
3645
+ cause: error
3646
+ });
3647
+ }
3648
+ }
3649
+ /**
3650
+ * Get pending seeds for current environment
3651
+ */
3652
+ async getPending() {
3653
+ try {
3654
+ const seedsResult = await this.getSeeds();
3655
+ if (seedsResult.isErr()) {
3656
+ return seedsResult;
3657
+ }
3658
+ const allSeeds = seedsResult.unwrap();
3659
+ const environmentSeeds = allSeeds.filter(
3660
+ (seed) => this.matchesEnvironment(seed.environment)
3661
+ );
3662
+ const historyResult = await this.getHistory();
3663
+ if (historyResult.isErr()) {
3664
+ return glLifeCore.Result.err(historyResult.variant.error);
3665
+ }
3666
+ const history = historyResult.unwrap();
3667
+ const executedIds = new Set(
3668
+ history.filter((h) => h.status === "COMPLETED").map((h) => h.id)
3669
+ );
3670
+ const pending = environmentSeeds.filter((s) => !executedIds.has(s.id));
3671
+ return glLifeCore.Result.ok(pending);
3672
+ } catch (error) {
3673
+ this.logger.error("Failed to get pending seeds", { error });
3674
+ return glLifeCore.Result.err({
3675
+ type: "UNKNOWN",
3676
+ message: `Failed to get pending seeds: ${error}`,
3677
+ cause: error
3678
+ });
3679
+ }
3680
+ }
3681
+ /**
3682
+ * Run all pending seeds for current environment
3683
+ */
3684
+ async seed() {
3685
+ try {
3686
+ if (this.config.validateChecksums) {
3687
+ const validationResult = await this.validateChecksums();
3688
+ if (validationResult.isErr()) {
3689
+ return glLifeCore.Result.err(validationResult.variant.error);
3690
+ }
3691
+ }
3692
+ const pendingResult = await this.getPending();
3693
+ if (pendingResult.isErr()) {
3694
+ return glLifeCore.Result.err(pendingResult.variant.error);
3695
+ }
3696
+ const pending = pendingResult.unwrap();
3697
+ const executed = [];
3698
+ for (const seed of pending) {
3699
+ const result = await this.executeSeed(seed);
3700
+ if (result.isErr()) {
3701
+ return glLifeCore.Result.err(result.variant.error);
3702
+ }
3703
+ executed.push(result.unwrap());
3704
+ }
3705
+ this.logger.info("Seeds completed", { count: executed.length });
3706
+ return glLifeCore.Result.ok(executed);
3707
+ } catch (error) {
3708
+ this.logger.error("Seeding failed", { error });
3709
+ return glLifeCore.Result.err({
3710
+ type: "UNKNOWN",
3711
+ message: `Seeding failed: ${error}`,
3712
+ cause: error
3713
+ });
3714
+ }
3715
+ }
3716
+ /**
3717
+ * Run a specific seed by ID
3718
+ */
3719
+ async runSeed(seedId) {
3720
+ try {
3721
+ const seedsResult = await this.getSeeds();
3722
+ if (seedsResult.isErr()) {
3723
+ return glLifeCore.Result.err(seedsResult.variant.error);
3724
+ }
3725
+ const allSeeds = seedsResult.unwrap();
3726
+ const seed = allSeeds.find((s) => s.id === seedId);
3727
+ if (!seed) {
3728
+ return glLifeCore.Result.err({
3729
+ type: "SEED_NOT_FOUND",
3730
+ message: `Seed ${seedId} not found`,
3731
+ seedId
3732
+ });
3733
+ }
3734
+ return this.executeSeed(seed);
3735
+ } catch (error) {
3736
+ this.logger.error("Run seed failed", { error, seedId });
3737
+ return glLifeCore.Result.err({
3738
+ type: "UNKNOWN",
3739
+ message: `Run seed ${seedId} failed: ${error}`,
3740
+ cause: error
3741
+ });
3742
+ }
3743
+ }
3744
+ /**
3745
+ * Reset all seeds (clear history)
3746
+ */
3747
+ async reset() {
3748
+ try {
3749
+ const sql = `DELETE FROM ${this.config.seedTable}`;
3750
+ const result = await this.connection.execute(sql);
3751
+ if (result.isErr()) {
3752
+ return glLifeCore.Result.err({
3753
+ type: "UNKNOWN",
3754
+ message: "Failed to reset seeds",
3755
+ cause: result.variant.error
3756
+ });
3757
+ }
3758
+ this.logger.info("All seeds reset");
3759
+ return glLifeCore.Result.ok(void 0);
3760
+ } catch (error) {
3761
+ this.logger.error("Failed to reset seeds", { error });
3762
+ return glLifeCore.Result.err({
3763
+ type: "UNKNOWN",
3764
+ message: `Failed to reset seeds: ${error}`,
3765
+ cause: error
3766
+ });
3767
+ }
3768
+ }
3769
+ /**
3770
+ * Validate seed checksums
3771
+ */
3772
+ async validateChecksums() {
3773
+ try {
3774
+ const seedsResult = await this.getSeeds();
3775
+ if (seedsResult.isErr()) {
3776
+ return glLifeCore.Result.err(seedsResult.variant.error);
3777
+ }
3778
+ const seeds = seedsResult.unwrap();
3779
+ const historyResult = await this.getHistory();
3780
+ if (historyResult.isErr()) {
3781
+ return glLifeCore.Result.err(historyResult.variant.error);
3782
+ }
3783
+ const history = historyResult.unwrap();
3784
+ const completedSeeds = history.filter((h) => h.status === "COMPLETED");
3785
+ for (const historyEntry of completedSeeds) {
3786
+ const seed = seeds.find((s) => s.id === historyEntry.id);
3787
+ if (!seed) {
3788
+ continue;
3789
+ }
3790
+ if (seed.checksum && historyEntry.checksum) {
3791
+ if (seed.checksum !== historyEntry.checksum) {
3792
+ return glLifeCore.Result.err({
3793
+ type: "CHECKSUM_MISMATCH",
3794
+ message: `Checksum mismatch for seed ${seed.id}`,
3795
+ seedId: seed.id
3796
+ });
3797
+ }
3798
+ }
3799
+ }
3800
+ return glLifeCore.Result.ok(true);
3801
+ } catch (error) {
3802
+ this.logger.error("Checksum validation failed", { error });
3803
+ return glLifeCore.Result.err({
3804
+ type: "UNKNOWN",
3805
+ message: `Checksum validation failed: ${error}`,
3806
+ cause: error
3807
+ });
3808
+ }
3809
+ }
3810
+ /**
3811
+ * Load seed from file
3812
+ */
3813
+ async loadSeed(filePath) {
3814
+ return this.loadSeedFile(filePath);
3815
+ }
3816
+ /**
3817
+ * Load seeds from directory
3818
+ */
3819
+ async loadSeedsFromDirectory() {
3820
+ if (this.seedsCache) {
3821
+ return glLifeCore.Result.ok(this.seedsCache);
3822
+ }
3823
+ try {
3824
+ if (!fs__namespace.existsSync(this.config.seedsPath)) {
3825
+ return glLifeCore.Result.err({
3826
+ type: "INVALID_SEED",
3827
+ message: `Seeds directory does not exist: ${this.config.seedsPath}`
3828
+ });
3829
+ }
3830
+ const files = fs__namespace.readdirSync(this.config.seedsPath);
3831
+ const filePattern = this.config.filePattern || /^[0-9]+[_-].*\.ts$/;
3832
+ const seedFiles = files.filter((file) => filePattern.test(file));
3833
+ this.logger.debug("Found seed files", {
3834
+ total: files.length,
3835
+ seeds: seedFiles.length
3836
+ });
3837
+ const seeds = [];
3838
+ for (const file of seedFiles) {
3839
+ const filePath = path__namespace.join(this.config.seedsPath, file);
3840
+ const result = await this.loadSeedFile(filePath);
3841
+ if (result.isErr()) {
3842
+ return result;
3843
+ }
3844
+ seeds.push(result.unwrap());
3845
+ }
3846
+ seeds.sort((a, b) => a.id.localeCompare(b.id));
3847
+ this.seedsCache = seeds;
3848
+ this.logger.debug("Loaded seeds", { count: seeds.length });
3849
+ return glLifeCore.Result.ok(seeds);
3850
+ } catch (error) {
3851
+ this.logger.error("Failed to load seeds", { error });
3852
+ return glLifeCore.Result.err({
3853
+ type: "UNKNOWN",
3854
+ message: `Failed to load seeds: ${error}`,
3855
+ cause: error
3856
+ });
3857
+ }
3858
+ }
3859
+ /**
3860
+ * Load a single seed file
3861
+ */
3862
+ async loadSeedFile(filePath) {
3863
+ try {
3864
+ const content = fs__namespace.readFileSync(filePath, "utf-8");
3865
+ const fileUrl = `file://${filePath.replace(/\\/g, "/")}`;
3866
+ const module = await import(fileUrl);
3867
+ const validation = this.validateSeed(module, filePath);
3868
+ if (validation.isErr()) {
3869
+ return validation;
3870
+ }
3871
+ const { id, name, environment, run } = module;
3872
+ const checksum = this.computeChecksum(content);
3873
+ const seed = {
3874
+ id,
3875
+ name,
3876
+ environment: Array.isArray(environment) ? environment : [environment],
3877
+ run,
3878
+ checksum
3879
+ };
3880
+ this.logger.debug("Loaded seed", { id, name, environment });
3881
+ return glLifeCore.Result.ok(seed);
3882
+ } catch (error) {
3883
+ this.logger.error("Failed to load seed file", { error, filePath });
3884
+ return glLifeCore.Result.err({
3885
+ type: "INVALID_SEED",
3886
+ message: `Failed to load seed file ${filePath}: ${error}`,
3887
+ cause: error
3888
+ });
3889
+ }
3890
+ }
3891
+ /**
3892
+ * Validate seed module structure
3893
+ */
3894
+ validateSeed(module, filePath) {
3895
+ if (!module.id) {
3896
+ return glLifeCore.Result.err({
3897
+ type: "INVALID_SEED",
3898
+ message: `Seed ${filePath} is missing 'id' export`
3899
+ });
3900
+ }
3901
+ if (!module.name) {
3902
+ return glLifeCore.Result.err({
3903
+ type: "INVALID_SEED",
3904
+ message: `Seed ${filePath} is missing 'name' export`
3905
+ });
3906
+ }
3907
+ if (!module.environment) {
3908
+ return glLifeCore.Result.err({
3909
+ type: "INVALID_SEED",
3910
+ message: `Seed ${filePath} is missing 'environment' export`
3911
+ });
3912
+ }
3913
+ if (!module.run || typeof module.run !== "function") {
3914
+ return glLifeCore.Result.err({
3915
+ type: "INVALID_SEED",
3916
+ message: `Seed ${filePath} is missing 'run' function`
3917
+ });
3918
+ }
3919
+ return glLifeCore.Result.ok(void 0);
3920
+ }
3921
+ /**
3922
+ * Check if seed matches current environment
3923
+ */
3924
+ matchesEnvironment(environments) {
3925
+ if (environments.includes("all")) {
3926
+ return true;
3927
+ }
3928
+ return environments.includes(this.config.environment);
3929
+ }
3930
+ /**
3931
+ * Execute a single seed
3932
+ */
3933
+ async executeSeed(seed) {
3934
+ const startTime = Date.now();
3935
+ try {
3936
+ await this.updateSeedStatus(seed, "RUNNING", void 0, void 0);
3937
+ const runFn = async (conn) => {
3938
+ const context = {
3939
+ execute: async (sql, params) => {
3940
+ const result2 = await conn.execute(sql, params);
3941
+ if (result2.isErr()) {
3942
+ return glLifeCore.Result.err({
3943
+ type: "SEED_FAILED",
3944
+ message: `Seed execution failed: ${result2.variant.error}`,
3945
+ seedId: seed.id,
3946
+ cause: result2.variant.error
3947
+ });
3948
+ }
3949
+ return glLifeCore.Result.ok(result2.unwrap());
3950
+ },
3951
+ log: (message) => {
3952
+ this.logger.info(message, { seedId: seed.id });
3953
+ },
3954
+ metadata: {
3955
+ id: seed.id,
3956
+ name: seed.name,
3957
+ environment: seed.environment,
3958
+ status: "RUNNING"
3959
+ },
3960
+ environment: this.config.environment
3961
+ };
3962
+ return await seed.run(context);
3963
+ };
3964
+ let result;
3965
+ if (this.config.transactional) {
3966
+ result = await this.connection.transaction(runFn);
3967
+ } else {
3968
+ result = await runFn(this.connection);
3969
+ }
3970
+ const executionTime = Date.now() - startTime;
3971
+ if (result.isErr()) {
3972
+ await this.updateSeedStatus(seed, "FAILED", executionTime, result.variant.error.message);
3973
+ return glLifeCore.Result.err(result.variant.error);
3974
+ }
3975
+ await this.updateSeedStatus(seed, "COMPLETED", executionTime, void 0);
3976
+ const metadata = {
3977
+ id: seed.id,
3978
+ name: seed.name,
3979
+ environment: seed.environment,
3980
+ status: "COMPLETED",
3981
+ executedAt: /* @__PURE__ */ new Date(),
3982
+ executionTime,
3983
+ checksum: seed.checksum
3984
+ };
3985
+ this.logger.info("Seed executed", {
3986
+ id: seed.id,
3987
+ executionTime
3988
+ });
3989
+ return glLifeCore.Result.ok(metadata);
3990
+ } catch (error) {
3991
+ const executionTime = Date.now() - startTime;
3992
+ await this.updateSeedStatus(seed, "FAILED", executionTime, String(error));
3993
+ this.logger.error("Seed execution failed", { error, seed: seed.id });
3994
+ return glLifeCore.Result.err({
3995
+ type: "SEED_FAILED",
3996
+ message: `Seed ${seed.id} failed: ${error}`,
3997
+ seedId: seed.id,
3998
+ cause: error
3999
+ });
4000
+ }
4001
+ }
4002
+ /**
4003
+ * Update seed status in database
4004
+ */
4005
+ async updateSeedStatus(seed, status, executionTime, error) {
4006
+ try {
4007
+ const tableName = this.config.seedTable;
4008
+ if (status === "RUNNING") {
4009
+ const insertSQL = `
4010
+ INSERT OR REPLACE INTO ${tableName} (id, name, environment, checksum, status, executed_at, execution_time, error)
4011
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
4012
+ `;
4013
+ await this.connection.execute(insertSQL, [
4014
+ seed.id,
4015
+ seed.name,
4016
+ JSON.stringify(seed.environment),
4017
+ seed.checksum || null,
4018
+ status,
4019
+ (/* @__PURE__ */ new Date()).toISOString(),
4020
+ null,
4021
+ null
4022
+ ]);
4023
+ } else {
4024
+ const updateSQL = `
4025
+ UPDATE ${tableName}
4026
+ SET status = ?, execution_time = ?, error = ?, executed_at = ?
4027
+ WHERE id = ?
4028
+ `;
4029
+ await this.connection.execute(updateSQL, [
4030
+ status,
4031
+ executionTime || null,
4032
+ error || null,
4033
+ (/* @__PURE__ */ new Date()).toISOString(),
4034
+ seed.id
4035
+ ]);
4036
+ }
4037
+ } catch (err) {
4038
+ this.logger.error("Failed to update seed status", {
4039
+ error: err,
4040
+ seed: seed.id
4041
+ });
4042
+ }
4043
+ }
4044
+ /**
4045
+ * Compute SHA-256 checksum of seed content
4046
+ */
4047
+ computeChecksum(content) {
4048
+ return crypto__default.default.createHash("sha256").update(content).digest("hex");
4049
+ }
4050
+ /**
4051
+ * Get SQL schema for seed tracking table
4052
+ */
4053
+ getSeedTableSchema() {
4054
+ const tableName = this.config.seedTable;
4055
+ return `
4056
+ CREATE TABLE IF NOT EXISTS ${tableName} (
4057
+ id TEXT PRIMARY KEY,
4058
+ name TEXT NOT NULL,
4059
+ environment TEXT NOT NULL,
4060
+ checksum TEXT,
4061
+ status TEXT NOT NULL DEFAULT 'PENDING',
4062
+ executed_at TIMESTAMP,
4063
+ execution_time INTEGER,
4064
+ error TEXT
4065
+ );
4066
+
4067
+ CREATE INDEX IF NOT EXISTS idx_${tableName}_status ON ${tableName}(status);
4068
+ CREATE INDEX IF NOT EXISTS idx_${tableName}_environment ON ${tableName}(environment);
4069
+ `.trim();
4070
+ }
4071
+ };
4072
+ var D1DatabaseAdapter = class {
4073
+ config;
4074
+ logger;
4075
+ db;
4076
+ constructor(config, logger) {
4077
+ this.config = config;
4078
+ this.logger = logger;
4079
+ this.db = config.database;
4080
+ }
4081
+ /**
4082
+ * Execute a SQL query and return all results
4083
+ */
4084
+ async query(sql, params) {
4085
+ try {
4086
+ if (this.config.enableQueryLogging) {
4087
+ this.logger.debug("Executing D1 query", { sql, params });
4088
+ }
4089
+ const stmt = this.prepareStatement(sql, params);
4090
+ const result = await stmt.all();
4091
+ if (!result.success) {
4092
+ return glLifeCore.Result.err({
4093
+ type: "QUERY_FAILED",
4094
+ message: result.error || "Query failed",
4095
+ sql
4096
+ });
4097
+ }
4098
+ return glLifeCore.Result.ok(result.results || []);
4099
+ } catch (error) {
4100
+ this.logger.error("D1 query failed", { error, sql });
4101
+ return glLifeCore.Result.err({
4102
+ type: "QUERY_FAILED",
4103
+ message: `Query failed: ${error}`,
4104
+ sql,
4105
+ cause: error
4106
+ });
4107
+ }
4108
+ }
4109
+ /**
4110
+ * Execute a SQL query and return first result
4111
+ */
4112
+ async queryFirst(sql, params) {
4113
+ try {
4114
+ if (this.config.enableQueryLogging) {
4115
+ this.logger.debug("Executing D1 queryFirst", { sql, params });
4116
+ }
4117
+ const stmt = this.prepareStatement(sql, params);
4118
+ const result = await stmt.first();
4119
+ return glLifeCore.Result.ok(result);
4120
+ } catch (error) {
4121
+ this.logger.error("D1 queryFirst failed", { error, sql });
4122
+ return glLifeCore.Result.err({
4123
+ type: "QUERY_FAILED",
4124
+ message: `QueryFirst failed: ${error}`,
4125
+ sql,
4126
+ cause: error
4127
+ });
4128
+ }
4129
+ }
4130
+ /**
4131
+ * Execute a SQL statement (INSERT, UPDATE, DELETE)
4132
+ */
4133
+ async execute(sql, params) {
4134
+ try {
4135
+ if (this.config.enableQueryLogging) {
4136
+ this.logger.debug("Executing D1 statement", { sql, params });
4137
+ }
4138
+ const stmt = this.prepareStatement(sql, params);
4139
+ const result = await stmt.run();
4140
+ if (!result.success) {
4141
+ return glLifeCore.Result.err({
4142
+ type: "QUERY_FAILED",
4143
+ message: result.error || "Execution failed",
4144
+ sql
4145
+ });
4146
+ }
4147
+ return glLifeCore.Result.ok(result);
4148
+ } catch (error) {
4149
+ this.logger.error("D1 execute failed", { error, sql });
4150
+ return glLifeCore.Result.err({
4151
+ type: "QUERY_FAILED",
4152
+ message: `Execute failed: ${error}`,
4153
+ sql,
4154
+ cause: error
4155
+ });
4156
+ }
4157
+ }
4158
+ /**
4159
+ * Execute multiple SQL statements in a batch
4160
+ */
4161
+ async batch(statements) {
4162
+ try {
4163
+ const maxBatchSize = this.config.maxBatchSize || 100;
4164
+ if (statements.length > maxBatchSize) {
4165
+ return glLifeCore.Result.err({
4166
+ type: "BATCH_FAILED",
4167
+ message: `Batch size ${statements.length} exceeds maximum ${maxBatchSize}`
4168
+ });
4169
+ }
4170
+ if (statements.length === 0) {
4171
+ return glLifeCore.Result.ok([]);
4172
+ }
4173
+ if (this.config.enableQueryLogging) {
4174
+ this.logger.debug("Executing D1 batch", {
4175
+ count: statements.length
4176
+ });
4177
+ }
4178
+ const preparedStatements = statements.map(
4179
+ ({ sql, params }) => this.prepareStatement(sql, params)
4180
+ );
4181
+ const results = await this.db.batch(preparedStatements);
4182
+ return glLifeCore.Result.ok(results);
4183
+ } catch (error) {
4184
+ this.logger.error("D1 batch failed", { error, count: statements.length });
4185
+ return glLifeCore.Result.err({
4186
+ type: "BATCH_FAILED",
4187
+ message: `Batch execution failed: ${error}`,
4188
+ cause: error
4189
+ });
4190
+ }
4191
+ }
4192
+ /**
4193
+ * Execute raw SQL (for migrations and schema changes)
4194
+ */
4195
+ async exec(sql) {
4196
+ try {
4197
+ if (this.config.enableQueryLogging) {
4198
+ this.logger.debug("Executing D1 raw SQL", { sql });
4199
+ }
4200
+ const result = await this.db.exec(sql);
4201
+ return glLifeCore.Result.ok(result);
4202
+ } catch (error) {
4203
+ this.logger.error("D1 exec failed", { error, sql });
4204
+ return glLifeCore.Result.err({
4205
+ type: "EXEC_FAILED",
4206
+ message: `Exec failed: ${error}`,
4207
+ cause: error
4208
+ });
4209
+ }
4210
+ }
4211
+ /**
4212
+ * Begin a transaction
4213
+ *
4214
+ * Note: D1 doesn't support explicit transactions yet, so this wraps
4215
+ * the callback execution and provides the same interface for compatibility.
4216
+ * Consider using batch() for atomic multi-statement operations.
4217
+ */
4218
+ async transaction(callback) {
4219
+ try {
4220
+ this.logger.debug("Starting D1 transaction (logical)");
4221
+ const result = await callback(this);
4222
+ return result;
4223
+ } catch (error) {
4224
+ this.logger.error("D1 transaction failed", { error });
4225
+ return glLifeCore.Result.err({
4226
+ type: "UNKNOWN",
4227
+ message: `Transaction failed: ${error}`,
4228
+ cause: error
4229
+ });
4230
+ }
4231
+ }
4232
+ /**
4233
+ * Prepare a SQL statement with parameters
4234
+ */
4235
+ prepareStatement(sql, params) {
4236
+ const stmt = this.db.prepare(sql);
4237
+ if (params && params.length > 0) {
4238
+ return stmt.bind(...params);
4239
+ }
4240
+ return stmt;
4241
+ }
4242
+ };
4243
+ var DurableObjectStorageAdapter = class _DurableObjectStorageAdapter {
4244
+ config;
4245
+ storage;
4246
+ logger;
4247
+ constructor(storage, config, logger) {
4248
+ this.storage = storage;
4249
+ this.config = config;
4250
+ this.logger = logger;
4251
+ }
4252
+ /**
4253
+ * Get a single value by key
4254
+ */
4255
+ async get(key) {
4256
+ try {
4257
+ if (this.config.enableLogging) {
4258
+ this.logger.debug("DO Storage operation", { operation: "get", key });
4259
+ }
4260
+ const value = await this.storage.get(key);
4261
+ return glLifeCore.Result.ok(value === void 0 ? null : value);
4262
+ } catch (error) {
4263
+ this.logger.error("DO Storage get failed", { error, key });
4264
+ return glLifeCore.Result.err({
4265
+ type: "STORAGE_FAILED",
4266
+ message: `Failed to get value for key "${key}": ${error}`,
4267
+ key,
4268
+ cause: error
4269
+ });
4270
+ }
4271
+ }
4272
+ /**
4273
+ * Get multiple values by keys
4274
+ */
4275
+ async getMultiple(keys) {
4276
+ try {
4277
+ if (this.config.enableLogging) {
4278
+ this.logger.debug("DO Storage operation", {
4279
+ operation: "getMultiple",
4280
+ keyCount: keys.length
4281
+ });
4282
+ }
4283
+ const result = await this.storage.get(keys);
4284
+ const typedResult = result;
4285
+ return glLifeCore.Result.ok(typedResult);
4286
+ } catch (error) {
4287
+ this.logger.error("DO Storage getMultiple failed", { error, keyCount: keys.length });
4288
+ return glLifeCore.Result.err({
4289
+ type: "STORAGE_FAILED",
4290
+ message: `Failed to get multiple values: ${error}`,
4291
+ cause: error
4292
+ });
4293
+ }
4294
+ }
4295
+ /**
4296
+ * Put a single key-value pair
4297
+ */
4298
+ async put(key, value) {
4299
+ try {
4300
+ if (this.config.enableLogging) {
4301
+ this.logger.debug("DO Storage operation", { operation: "put", key });
4302
+ }
4303
+ await this.storage.put(key, value);
4304
+ return glLifeCore.Result.ok(void 0);
4305
+ } catch (error) {
4306
+ if (error instanceof Error && (error.message.includes("DataCloneError") || error.message.includes("could not be cloned"))) {
4307
+ this.logger.error("DO Storage serialization failed", { error, key });
4308
+ return glLifeCore.Result.err({
4309
+ type: "SERIALIZATION_ERROR",
4310
+ message: `Failed to serialize value for key "${key}": ${error.message}`,
4311
+ cause: error
4312
+ });
4313
+ }
4314
+ if (error instanceof Error && (error.message.includes("QuotaExceededError") || error.message.includes("storage quota exceeded"))) {
4315
+ this.logger.error("DO Storage quota exceeded", { error, key });
4316
+ return glLifeCore.Result.err({
4317
+ type: "QUOTA_EXCEEDED",
4318
+ message: `Storage quota exceeded when setting key "${key}"`
4319
+ });
4320
+ }
4321
+ this.logger.error("DO Storage put failed", { error, key });
4322
+ return glLifeCore.Result.err({
4323
+ type: "STORAGE_FAILED",
4324
+ message: `Failed to put value for key "${key}": ${error}`,
4325
+ key,
4326
+ cause: error
4327
+ });
4328
+ }
4329
+ }
4330
+ /**
4331
+ * Put multiple key-value pairs
4332
+ */
4333
+ async putMultiple(entries) {
4334
+ try {
4335
+ if (this.config.enableLogging) {
4336
+ this.logger.debug("DO Storage operation", {
4337
+ operation: "putMultiple",
4338
+ entryCount: entries.length
4339
+ });
4340
+ }
4341
+ const entriesObj = {};
4342
+ for (const [key, value] of entries) {
4343
+ entriesObj[key] = value;
4344
+ }
4345
+ await this.storage.put(entriesObj);
4346
+ return glLifeCore.Result.ok(void 0);
4347
+ } catch (error) {
4348
+ if (error instanceof Error && (error.message.includes("DataCloneError") || error.message.includes("could not be cloned"))) {
4349
+ this.logger.error("DO Storage serialization failed", { error });
4350
+ return glLifeCore.Result.err({
4351
+ type: "SERIALIZATION_ERROR",
4352
+ message: `Failed to serialize values: ${error.message}`,
4353
+ cause: error
4354
+ });
4355
+ }
4356
+ if (error instanceof Error && (error.message.includes("QuotaExceededError") || error.message.includes("storage quota exceeded"))) {
4357
+ this.logger.error("DO Storage quota exceeded", { error });
4358
+ return glLifeCore.Result.err({
4359
+ type: "QUOTA_EXCEEDED",
4360
+ message: "Storage quota exceeded when setting multiple values"
4361
+ });
4362
+ }
4363
+ this.logger.error("DO Storage putMultiple failed", { error, entryCount: entries.length });
4364
+ return glLifeCore.Result.err({
4365
+ type: "STORAGE_FAILED",
4366
+ message: `Failed to put multiple values: ${error}`,
4367
+ cause: error
4368
+ });
4369
+ }
4370
+ }
4371
+ /**
4372
+ * Delete a single key
4373
+ */
4374
+ async delete(key) {
4375
+ try {
4376
+ if (this.config.enableLogging) {
4377
+ this.logger.debug("DO Storage operation", { operation: "delete", key });
4378
+ }
4379
+ const result = await this.storage.delete(key);
4380
+ return glLifeCore.Result.ok(result);
4381
+ } catch (error) {
4382
+ this.logger.error("DO Storage delete failed", { error, key });
4383
+ return glLifeCore.Result.err({
4384
+ type: "STORAGE_FAILED",
4385
+ message: `Failed to delete key "${key}": ${error}`,
4386
+ key,
4387
+ cause: error
4388
+ });
4389
+ }
4390
+ }
4391
+ /**
4392
+ * Delete multiple keys
4393
+ */
4394
+ async deleteMultiple(keys) {
4395
+ try {
4396
+ if (this.config.enableLogging) {
4397
+ this.logger.debug("DO Storage operation", {
4398
+ operation: "deleteMultiple",
4399
+ keyCount: keys.length
4400
+ });
4401
+ }
4402
+ const result = await this.storage.delete(keys);
4403
+ return glLifeCore.Result.ok(result);
4404
+ } catch (error) {
4405
+ this.logger.error("DO Storage deleteMultiple failed", { error, keyCount: keys.length });
4406
+ return glLifeCore.Result.err({
4407
+ type: "STORAGE_FAILED",
4408
+ message: `Failed to delete multiple keys: ${error}`,
4409
+ cause: error
4410
+ });
4411
+ }
4412
+ }
4413
+ /**
4414
+ * Delete all keys in storage
4415
+ */
4416
+ async deleteAll() {
4417
+ try {
4418
+ if (this.config.enableLogging) {
4419
+ this.logger.debug("DO Storage operation", { operation: "deleteAll" });
4420
+ }
4421
+ await this.storage.deleteAll();
4422
+ return glLifeCore.Result.ok(void 0);
4423
+ } catch (error) {
4424
+ this.logger.error("DO Storage deleteAll failed", { error });
4425
+ return glLifeCore.Result.err({
4426
+ type: "STORAGE_FAILED",
4427
+ message: `Failed to delete all keys: ${error}`,
4428
+ cause: error
4429
+ });
4430
+ }
4431
+ }
4432
+ /**
4433
+ * List keys in storage
4434
+ */
4435
+ async list(options) {
4436
+ try {
4437
+ if (this.config.enableLogging) {
4438
+ this.logger.debug("DO Storage operation", { operation: "list", options });
4439
+ }
4440
+ const result = await this.storage.list(options);
4441
+ return glLifeCore.Result.ok(result);
4442
+ } catch (error) {
4443
+ this.logger.error("DO Storage list failed", { error, options });
4444
+ return glLifeCore.Result.err({
4445
+ type: "STORAGE_FAILED",
4446
+ message: `Failed to list keys: ${error}`,
4447
+ cause: error
4448
+ });
4449
+ }
4450
+ }
4451
+ /**
4452
+ * Execute operations in a transaction
4453
+ */
4454
+ async transaction(callback) {
4455
+ try {
4456
+ if (this.config.enableLogging) {
4457
+ this.logger.debug("DO Storage operation", { operation: "transaction" });
4458
+ }
4459
+ const result = await this.storage.transaction(async (txnStorage) => {
4460
+ const txnAdapter = new _DurableObjectStorageAdapter(
4461
+ txnStorage,
4462
+ this.config,
4463
+ this.logger
4464
+ );
4465
+ return await callback(txnAdapter);
4466
+ });
4467
+ return result;
4468
+ } catch (error) {
4469
+ this.logger.error("DO Storage transaction failed", { error });
4470
+ return glLifeCore.Result.err({
4471
+ type: "TRANSACTION_FAILED",
4472
+ message: `Transaction failed: ${error}`,
4473
+ cause: error
4474
+ });
4475
+ }
4476
+ }
4477
+ };
4478
+ var R2BackupAdapter = class {
4479
+ config;
4480
+ bucket;
4481
+ logger;
4482
+ constructor(config, logger) {
4483
+ this.config = config;
4484
+ this.bucket = config.bucket;
4485
+ this.logger = logger;
4486
+ }
4487
+ /**
4488
+ * Create a backup with compression
4489
+ */
4490
+ async backup(data, options) {
4491
+ try {
4492
+ if (this.config.enableLogging) {
4493
+ this.logger.debug("R2 Backup operation", { operation: "backup" });
4494
+ }
4495
+ const buffer = data instanceof ArrayBuffer ? Buffer.from(data) : data;
4496
+ if (!buffer || buffer.length === 0) {
4497
+ return glLifeCore.Result.err({
4498
+ type: "COMPRESSION_FAILED",
4499
+ message: "Cannot backup empty or invalid data"
4500
+ });
4501
+ }
4502
+ const compressionLevel = options?.compressionLevel ?? this.config.defaultCompressionLevel ?? 6;
4503
+ let compressedData;
4504
+ try {
4505
+ compressedData = zlib.gzipSync(buffer, { level: compressionLevel });
4506
+ } catch (error) {
4507
+ this.logger.error("Compression failed", { error });
4508
+ return glLifeCore.Result.err({
4509
+ type: "COMPRESSION_FAILED",
4510
+ message: `Failed to compress data: ${error}`,
4511
+ cause: error
4512
+ });
4513
+ }
4514
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4515
+ const key = `${this.config.name}-backup-${timestamp}.gz`;
4516
+ const customMetadata = {
4517
+ originalSize: buffer.length.toString(),
4518
+ compressionRatio: (compressedData.length / buffer.length).toString(),
4519
+ compressionLevel: compressionLevel.toString(),
4520
+ backupDate: (/* @__PURE__ */ new Date()).toISOString(),
4521
+ ...options?.metadata || {}
4522
+ };
4523
+ try {
4524
+ const r2Object = await this.bucket.put(key, compressedData, {
4525
+ httpMetadata: {
4526
+ contentType: "application/gzip",
4527
+ contentEncoding: "gzip",
4528
+ contentDisposition: `attachment; filename="${key}"`
4529
+ },
4530
+ customMetadata,
4531
+ storageClass: options?.storageClass || "Standard"
4532
+ });
4533
+ const backupInfo = {
4534
+ key: r2Object.key,
4535
+ size: buffer.length,
4536
+ compressedSize: r2Object.size,
4537
+ compressionRatio: r2Object.size / buffer.length,
4538
+ uploadedAt: r2Object.uploaded,
4539
+ etag: r2Object.etag,
4540
+ metadata: {
4541
+ databaseVersion: customMetadata.databaseVersion,
4542
+ backupType: customMetadata.backupType,
4543
+ originalChecksum: customMetadata.originalChecksum
4544
+ }
4545
+ };
4546
+ if (this.config.enableLogging) {
4547
+ this.logger.debug("Backup completed", {
4548
+ key: backupInfo.key,
4549
+ originalSize: backupInfo.size,
4550
+ compressedSize: backupInfo.compressedSize,
4551
+ compressionRatio: backupInfo.compressionRatio
4552
+ });
4553
+ }
4554
+ return glLifeCore.Result.ok(backupInfo);
4555
+ } catch (error) {
4556
+ this.logger.error("R2 upload failed", { error, key });
4557
+ return glLifeCore.Result.err({
4558
+ type: "UPLOAD_FAILED",
4559
+ message: `Failed to upload backup: ${error}`,
4560
+ key,
4561
+ cause: error
4562
+ });
4563
+ }
4564
+ } catch (error) {
4565
+ this.logger.error("Backup operation failed", { error });
4566
+ return glLifeCore.Result.err({
4567
+ type: "UNKNOWN",
4568
+ message: `Backup failed: ${error}`,
4569
+ cause: error
4570
+ });
4571
+ }
4572
+ }
4573
+ /**
4574
+ * Restore from a backup with decompression
4575
+ */
4576
+ async restore(key, options) {
4577
+ try {
4578
+ if (this.config.enableLogging) {
4579
+ this.logger.debug("R2 Backup operation", { operation: "restore", key });
4580
+ }
4581
+ let r2Object;
4582
+ try {
4583
+ r2Object = await this.bucket.get(key);
4584
+ } catch (error) {
4585
+ this.logger.error("R2 download failed", { error, key });
4586
+ return glLifeCore.Result.err({
4587
+ type: "DOWNLOAD_FAILED",
4588
+ message: `Failed to download backup: ${error}`,
4589
+ key,
4590
+ cause: error
4591
+ });
4592
+ }
4593
+ if (!r2Object) {
4594
+ return glLifeCore.Result.err({
4595
+ type: "BACKUP_NOT_FOUND",
4596
+ message: `Backup not found: ${key}`,
4597
+ key
4598
+ });
4599
+ }
4600
+ const compressedData = await r2Object.arrayBuffer();
4601
+ try {
4602
+ const decompressed = zlib.gunzipSync(Buffer.from(compressedData));
4603
+ if (this.config.enableLogging) {
4604
+ this.logger.debug("Restore completed", {
4605
+ key,
4606
+ compressedSize: compressedData.byteLength,
4607
+ originalSize: decompressed.length
4608
+ });
4609
+ }
4610
+ return glLifeCore.Result.ok(decompressed.buffer);
4611
+ } catch (error) {
4612
+ this.logger.error("Decompression failed", { error, key });
4613
+ return glLifeCore.Result.err({
4614
+ type: "DECOMPRESSION_FAILED",
4615
+ message: `Failed to decompress backup: ${error}`,
4616
+ cause: error
4617
+ });
4618
+ }
4619
+ } catch (error) {
4620
+ this.logger.error("Restore operation failed", { error, key });
4621
+ return glLifeCore.Result.err({
4622
+ type: "RESTORE_FAILED",
4623
+ message: `Restore failed: ${error}`,
4624
+ cause: error
4625
+ });
4626
+ }
4627
+ }
4628
+ /**
4629
+ * List all backups
4630
+ */
4631
+ async listBackups(options) {
4632
+ try {
4633
+ if (this.config.enableLogging) {
4634
+ this.logger.debug("R2 Backup operation", { operation: "listBackups" });
4635
+ }
4636
+ const r2Objects = await this.bucket.list({
4637
+ prefix: `${this.config.name}-backup`,
4638
+ limit: options?.limit,
4639
+ cursor: options?.cursor,
4640
+ include: ["customMetadata"]
4641
+ });
4642
+ const backups = r2Objects.objects.map((obj) => {
4643
+ const originalSize = parseInt(obj.customMetadata?.originalSize || "0", 10);
4644
+ const compressionRatio = parseFloat(
4645
+ obj.customMetadata?.compressionRatio || "1.0"
4646
+ );
4647
+ return {
4648
+ key: obj.key,
4649
+ size: originalSize,
4650
+ compressedSize: obj.size,
4651
+ compressionRatio,
4652
+ uploadedAt: obj.uploaded,
4653
+ etag: obj.etag,
4654
+ metadata: {
4655
+ databaseVersion: obj.customMetadata?.databaseVersion,
4656
+ backupType: obj.customMetadata?.backupType,
4657
+ originalChecksum: obj.customMetadata?.originalChecksum
4658
+ }
4659
+ };
4660
+ });
4661
+ return glLifeCore.Result.ok(backups);
4662
+ } catch (error) {
4663
+ this.logger.error("List backups failed", { error });
4664
+ return glLifeCore.Result.err({
4665
+ type: "UNKNOWN",
4666
+ message: `Failed to list backups: ${error}`,
4667
+ cause: error
4668
+ });
4669
+ }
4670
+ }
4671
+ /**
4672
+ * Delete a single backup
4673
+ */
4674
+ async deleteBackup(key) {
4675
+ try {
4676
+ if (this.config.enableLogging) {
4677
+ this.logger.debug("R2 Backup operation", { operation: "deleteBackup", key });
4678
+ }
4679
+ await this.bucket.delete(key);
4680
+ return glLifeCore.Result.ok(void 0);
4681
+ } catch (error) {
4682
+ this.logger.error("Delete backup failed", { error, key });
4683
+ return glLifeCore.Result.err({
4684
+ type: "UNKNOWN",
4685
+ message: `Failed to delete backup: ${error}`,
4686
+ cause: error
4687
+ });
4688
+ }
4689
+ }
4690
+ /**
4691
+ * Delete multiple backups (batched for efficiency)
4692
+ */
4693
+ async deleteMultipleBackups(keys) {
4694
+ try {
4695
+ if (this.config.enableLogging) {
4696
+ this.logger.debug("R2 Backup operation", {
4697
+ operation: "deleteMultipleBackups",
4698
+ count: keys.length
4699
+ });
4700
+ }
4701
+ const batchSize = 1e3;
4702
+ const batches = [];
4703
+ for (let i = 0; i < keys.length; i += batchSize) {
4704
+ batches.push(keys.slice(i, i + batchSize));
4705
+ }
4706
+ for (const batch of batches) {
4707
+ await this.bucket.delete(batch);
4708
+ }
4709
+ return glLifeCore.Result.ok(void 0);
4710
+ } catch (error) {
4711
+ this.logger.error("Delete multiple backups failed", { error, count: keys.length });
4712
+ return glLifeCore.Result.err({
4713
+ type: "UNKNOWN",
4714
+ message: `Failed to delete backups: ${error}`,
4715
+ cause: error
4716
+ });
4717
+ }
4718
+ }
4719
+ };
4720
+
4721
+ // src/index.ts
4722
+ var version = "1.0.0";
4723
+
4724
+ Object.defineProperty(exports, "Config", {
4725
+ enumerable: true,
4726
+ get: function () { return glLifeCore.Config; }
4727
+ });
4728
+ Object.defineProperty(exports, "EventBus", {
4729
+ enumerable: true,
4730
+ get: function () { return glLifeCore.EventBus; }
4731
+ });
4732
+ Object.defineProperty(exports, "Logger", {
4733
+ enumerable: true,
4734
+ get: function () { return glLifeCore.Logger; }
4735
+ });
4736
+ Object.defineProperty(exports, "Option", {
4737
+ enumerable: true,
4738
+ get: function () { return glLifeCore.Option; }
4739
+ });
4740
+ Object.defineProperty(exports, "Result", {
4741
+ enumerable: true,
4742
+ get: function () { return glLifeCore.Result; }
4743
+ });
4744
+ exports.CacheInvalidator = CacheInvalidator;
4745
+ exports.D1DatabaseAdapter = D1DatabaseAdapter;
4746
+ exports.DataSeeder = DataSeeder;
4747
+ exports.DatabaseConnectionManager = DatabaseConnectionManager;
4748
+ exports.DatabaseTransaction = DatabaseTransaction;
4749
+ exports.DurableObjectStorageAdapter = DurableObjectStorageAdapter;
4750
+ exports.KVCache = KVCache;
4751
+ exports.MemoryCache = MemoryCache;
4752
+ exports.MigrationLoader = MigrationLoader;
4753
+ exports.MigrationRunner = MigrationRunner;
4754
+ exports.R2BackupAdapter = R2BackupAdapter;
4755
+ exports.RawSQLExecutor = RawSQLExecutor;
4756
+ exports.TenantAwareQueryBuilder = TenantAwareQueryBuilder;
4757
+ exports.TenantContextManager = TenantContextManager;
4758
+ exports.TenantSchemaManager = TenantSchemaManager;
4759
+ exports.TypeSafeQueryBuilder = TypeSafeQueryBuilder;
4760
+ exports.version = version;
4761
+ //# sourceMappingURL=index.cjs.map
4762
+ //# sourceMappingURL=index.cjs.map