sqlew 3.5.3 → 3.6.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 (227) hide show
  1. package/CHANGELOG.md +247 -1772
  2. package/README.md +70 -304
  3. package/assets/config.example.toml +97 -0
  4. package/dist/adapters/index.d.ts +11 -0
  5. package/dist/adapters/index.d.ts.map +1 -0
  6. package/dist/adapters/index.js +21 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/mysql-adapter.d.ts +31 -0
  9. package/dist/adapters/mysql-adapter.d.ts.map +1 -0
  10. package/dist/adapters/mysql-adapter.js +63 -0
  11. package/dist/adapters/mysql-adapter.js.map +1 -0
  12. package/dist/adapters/postgresql-adapter.d.ts +31 -0
  13. package/dist/adapters/postgresql-adapter.d.ts.map +1 -0
  14. package/dist/adapters/postgresql-adapter.js +63 -0
  15. package/dist/adapters/postgresql-adapter.js.map +1 -0
  16. package/dist/adapters/sqlite-adapter.d.ts +37 -0
  17. package/dist/adapters/sqlite-adapter.d.ts.map +1 -0
  18. package/dist/adapters/sqlite-adapter.js +129 -0
  19. package/dist/adapters/sqlite-adapter.js.map +1 -0
  20. package/dist/adapters/types.d.ts +33 -0
  21. package/dist/adapters/types.d.ts.map +1 -0
  22. package/dist/adapters/types.js +2 -0
  23. package/dist/adapters/types.js.map +1 -0
  24. package/dist/cli.js +55 -54
  25. package/dist/cli.js.map +1 -1
  26. package/dist/config/example-generator.d.ts +11 -0
  27. package/dist/config/example-generator.d.ts.map +1 -0
  28. package/dist/config/example-generator.js +48 -0
  29. package/dist/config/example-generator.js.map +1 -0
  30. package/dist/config/loader.d.ts.map +1 -1
  31. package/dist/config/loader.js +4 -0
  32. package/dist/config/loader.js.map +1 -1
  33. package/dist/config/types.d.ts +9 -0
  34. package/dist/config/types.d.ts.map +1 -1
  35. package/dist/config/types.js.map +1 -1
  36. package/dist/database.d.ts +44 -122
  37. package/dist/database.d.ts.map +1 -1
  38. package/dist/database.js +145 -416
  39. package/dist/database.js.map +1 -1
  40. package/dist/index.js +215 -185
  41. package/dist/index.js.map +1 -1
  42. package/dist/knexfile.d.ts +6 -0
  43. package/dist/knexfile.d.ts.map +1 -0
  44. package/dist/knexfile.js +85 -0
  45. package/dist/knexfile.js.map +1 -0
  46. package/dist/migrations/add-help-system-tables.d.ts +35 -0
  47. package/dist/migrations/add-help-system-tables.d.ts.map +1 -0
  48. package/dist/migrations/add-help-system-tables.js +206 -0
  49. package/dist/migrations/add-help-system-tables.js.map +1 -0
  50. package/dist/migrations/add-token-tracking.d.ts +28 -0
  51. package/dist/migrations/add-token-tracking.d.ts.map +1 -0
  52. package/dist/migrations/add-token-tracking.js +108 -0
  53. package/dist/migrations/add-token-tracking.js.map +1 -0
  54. package/dist/migrations/index.d.ts +25 -12
  55. package/dist/migrations/index.d.ts.map +1 -1
  56. package/dist/migrations/index.js +147 -20
  57. package/dist/migrations/index.js.map +1 -1
  58. package/dist/migrations/knex/20251025020452_create_master_tables.d.ts +4 -0
  59. package/dist/migrations/knex/20251025020452_create_master_tables.d.ts.map +1 -0
  60. package/dist/migrations/knex/20251025020452_create_master_tables.js +65 -0
  61. package/dist/migrations/knex/20251025020452_create_master_tables.js.map +1 -0
  62. package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts +4 -0
  63. package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts.map +1 -0
  64. package/dist/migrations/knex/20251025021152_create_transaction_tables.js +235 -0
  65. package/dist/migrations/knex/20251025021152_create_transaction_tables.js.map +1 -0
  66. package/dist/migrations/knex/20251025021351_create_indexes.d.ts +4 -0
  67. package/dist/migrations/knex/20251025021351_create_indexes.d.ts.map +1 -0
  68. package/dist/migrations/knex/20251025021351_create_indexes.js +62 -0
  69. package/dist/migrations/knex/20251025021351_create_indexes.js.map +1 -0
  70. package/dist/migrations/knex/20251025021416_seed_master_data.d.ts +4 -0
  71. package/dist/migrations/knex/20251025021416_seed_master_data.d.ts.map +1 -0
  72. package/dist/migrations/knex/20251025021416_seed_master_data.js +58 -0
  73. package/dist/migrations/knex/20251025021416_seed_master_data.js.map +1 -0
  74. package/dist/migrations/knex/20251025070349_create_views.d.ts +4 -0
  75. package/dist/migrations/knex/20251025070349_create_views.d.ts.map +1 -0
  76. package/dist/migrations/knex/20251025070349_create_views.js +143 -0
  77. package/dist/migrations/knex/20251025070349_create_views.js.map +1 -0
  78. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts +4 -0
  79. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts.map +1 -0
  80. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js +15 -0
  81. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js.map +1 -0
  82. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts +8 -0
  83. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts.map +1 -0
  84. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js +12 -0
  85. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js.map +1 -0
  86. package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts +19 -0
  87. package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts.map +1 -0
  88. package/dist/migrations/knex/20251025090000_create_help_system_tables.js +115 -0
  89. package/dist/migrations/knex/20251025090000_create_help_system_tables.js.map +1 -0
  90. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts +13 -0
  91. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts.map +1 -0
  92. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js +377 -0
  93. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js.map +1 -0
  94. package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts +15 -0
  95. package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts.map +1 -0
  96. package/dist/migrations/knex/20251025100000_seed_help_metadata.js +253 -0
  97. package/dist/migrations/knex/20251025100000_seed_help_metadata.js.map +1 -0
  98. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts +16 -0
  99. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts.map +1 -0
  100. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js +276 -0
  101. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js.map +1 -0
  102. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts +8 -0
  103. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts.map +1 -0
  104. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js +64 -0
  105. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js.map +1 -0
  106. package/dist/migrations/seed-help-data.d.ts +48 -0
  107. package/dist/migrations/seed-help-data.d.ts.map +1 -0
  108. package/dist/migrations/seed-help-data.js +1466 -0
  109. package/dist/migrations/seed-help-data.js.map +1 -0
  110. package/dist/migrations/seed-tool-metadata.d.ts +24 -0
  111. package/dist/migrations/seed-tool-metadata.d.ts.map +1 -0
  112. package/dist/migrations/seed-tool-metadata.js +392 -0
  113. package/dist/migrations/seed-tool-metadata.js.map +1 -0
  114. package/dist/migrations/v3.6.0-help-system-refactor.d.ts +46 -0
  115. package/dist/migrations/v3.6.0-help-system-refactor.d.ts.map +1 -0
  116. package/dist/migrations/v3.6.0-help-system-refactor.js +223 -0
  117. package/dist/migrations/v3.6.0-help-system-refactor.js.map +1 -0
  118. package/dist/schema.d.ts.map +1 -1
  119. package/dist/schema.js +2 -0
  120. package/dist/schema.js.map +1 -1
  121. package/dist/tests/git-aware-completion.test.js +89 -70
  122. package/dist/tests/git-aware-completion.test.js.map +1 -1
  123. package/dist/tests/help-system.test.d.ts +23 -0
  124. package/dist/tests/help-system.test.d.ts.map +1 -0
  125. package/dist/tests/help-system.test.js +374 -0
  126. package/dist/tests/help-system.test.js.map +1 -0
  127. package/dist/tests/tasks.auto-pruning-decision-link.test.js +92 -78
  128. package/dist/tests/tasks.auto-pruning-decision-link.test.js.map +1 -1
  129. package/dist/tests/tasks.auto-pruning-partial.test.js +106 -95
  130. package/dist/tests/tasks.auto-pruning-partial.test.js.map +1 -1
  131. package/dist/tests/tasks.auto-pruning-persistence.test.js +115 -97
  132. package/dist/tests/tasks.auto-pruning-persistence.test.js.map +1 -1
  133. package/dist/tests/tasks.auto-pruning-safety.test.js +124 -103
  134. package/dist/tests/tasks.auto-pruning-safety.test.js.map +1 -1
  135. package/dist/tests/tasks.dependencies.test.js +338 -307
  136. package/dist/tests/tasks.dependencies.test.js.map +1 -1
  137. package/dist/tests/tasks.link-file-backward-compat.test.js +116 -104
  138. package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -1
  139. package/dist/tests/tasks.watch-files-action.test.js +122 -101
  140. package/dist/tests/tasks.watch-files-action.test.js.map +1 -1
  141. package/dist/tests/tasks.watch-files-parameter.test.js +105 -94
  142. package/dist/tests/tasks.watch-files-parameter.test.js.map +1 -1
  143. package/dist/tests/two-step-git-completion.test.js +176 -133
  144. package/dist/tests/two-step-git-completion.test.js.map +1 -1
  145. package/dist/tests/vcs-staging.test.js +1 -1
  146. package/dist/tests/vcs-staging.test.js.map +1 -1
  147. package/dist/tools/config.d.ts +9 -6
  148. package/dist/tools/config.d.ts.map +1 -1
  149. package/dist/tools/config.js +16 -14
  150. package/dist/tools/config.js.map +1 -1
  151. package/dist/tools/constraints.d.ts +10 -7
  152. package/dist/tools/constraints.d.ts.map +1 -1
  153. package/dist/tools/constraints.js +66 -48
  154. package/dist/tools/constraints.js.map +1 -1
  155. package/dist/tools/context.d.ts +36 -33
  156. package/dist/tools/context.d.ts.map +1 -1
  157. package/dist/tools/context.js +374 -330
  158. package/dist/tools/context.js.map +1 -1
  159. package/dist/tools/files.d.ts +12 -9
  160. package/dist/tools/files.d.ts.map +1 -1
  161. package/dist/tools/files.js +173 -95
  162. package/dist/tools/files.js.map +1 -1
  163. package/dist/tools/help-queries.d.ts +130 -0
  164. package/dist/tools/help-queries.d.ts.map +1 -0
  165. package/dist/tools/help-queries.js +393 -0
  166. package/dist/tools/help-queries.js.map +1 -0
  167. package/dist/tools/messaging.d.ts +14 -11
  168. package/dist/tools/messaging.d.ts.map +1 -1
  169. package/dist/tools/messaging.js +217 -133
  170. package/dist/tools/messaging.js.map +1 -1
  171. package/dist/tools/tasks.d.ts +18 -16
  172. package/dist/tools/tasks.d.ts.map +1 -1
  173. package/dist/tools/tasks.js +513 -439
  174. package/dist/tools/tasks.js.map +1 -1
  175. package/dist/tools/utils.d.ts +14 -11
  176. package/dist/tools/utils.d.ts.map +1 -1
  177. package/dist/tools/utils.js +86 -121
  178. package/dist/tools/utils.js.map +1 -1
  179. package/dist/utils/activity-logging.d.ts +114 -0
  180. package/dist/utils/activity-logging.d.ts.map +1 -0
  181. package/dist/utils/activity-logging.js +162 -0
  182. package/dist/utils/activity-logging.js.map +1 -0
  183. package/dist/utils/batch.d.ts +2 -2
  184. package/dist/utils/batch.d.ts.map +1 -1
  185. package/dist/utils/batch.js +8 -8
  186. package/dist/utils/batch.js.map +1 -1
  187. package/dist/utils/cleanup.d.ts +21 -13
  188. package/dist/utils/cleanup.d.ts.map +1 -1
  189. package/dist/utils/cleanup.js +31 -24
  190. package/dist/utils/cleanup.js.map +1 -1
  191. package/dist/utils/debug-logger.d.ts +44 -0
  192. package/dist/utils/debug-logger.d.ts.map +1 -0
  193. package/dist/utils/debug-logger.js +116 -0
  194. package/dist/utils/debug-logger.js.map +1 -0
  195. package/dist/utils/help-tracking.d.ts +55 -0
  196. package/dist/utils/help-tracking.d.ts.map +1 -0
  197. package/dist/utils/help-tracking.js +88 -0
  198. package/dist/utils/help-tracking.js.map +1 -0
  199. package/dist/utils/retention.d.ts +7 -7
  200. package/dist/utils/retention.d.ts.map +1 -1
  201. package/dist/utils/retention.js +12 -12
  202. package/dist/utils/retention.js.map +1 -1
  203. package/dist/utils/task-stale-detection.d.ts +15 -13
  204. package/dist/utils/task-stale-detection.d.ts.map +1 -1
  205. package/dist/utils/task-stale-detection.js +100 -302
  206. package/dist/utils/task-stale-detection.js.map +1 -1
  207. package/dist/utils/token-estimation.d.ts +72 -0
  208. package/dist/utils/token-estimation.d.ts.map +1 -0
  209. package/dist/utils/token-estimation.js +71 -0
  210. package/dist/utils/token-estimation.js.map +1 -0
  211. package/dist/utils/token-logging.d.ts +48 -0
  212. package/dist/utils/token-logging.d.ts.map +1 -0
  213. package/dist/utils/token-logging.js +112 -0
  214. package/dist/utils/token-logging.js.map +1 -0
  215. package/dist/utils/view-queries.d.ts +34 -0
  216. package/dist/utils/view-queries.d.ts.map +1 -0
  217. package/dist/utils/view-queries.js +192 -0
  218. package/dist/utils/view-queries.js.map +1 -0
  219. package/dist/watcher/file-watcher.d.ts.map +1 -1
  220. package/dist/watcher/file-watcher.js +25 -11
  221. package/dist/watcher/file-watcher.js.map +1 -1
  222. package/docs/BEST_PRACTICES.md +56 -448
  223. package/docs/MIGRATION_v3.6.0.md +170 -0
  224. package/docs/SHARED_CONCEPTS.md +63 -208
  225. package/docs/TASK_OVERVIEW.md +2 -2
  226. package/docs/TOOL_SELECTION.md +41 -248
  227. package/package.json +16 -4
package/dist/database.js CHANGED
@@ -1,230 +1,67 @@
1
1
  /**
2
2
  * Database connection and initialization module
3
- * Handles SQLite database setup with configurable path
3
+ * Handles database setup with Knex.js and DatabaseAdapter pattern
4
4
  */
5
- import Database from 'better-sqlite3';
6
- import { mkdirSync, existsSync } from 'fs';
7
- import { dirname, resolve, isAbsolute } from 'path';
8
- import { initializeSchema, isSchemaInitialized, verifySchemaIntegrity } from './schema.js';
9
- import { DEFAULT_DB_PATH, DB_BUSY_TIMEOUT } from './constants.js';
10
- import { performAutoCleanup } from './utils/cleanup.js';
11
- import { runAllMigrations, needsAnyMigrations } from './migrations/index.js';
12
- import { detectAndTransitionStaleTasks, autoArchiveOldDoneTasks, detectAndTransitionToReview } from './utils/task-stale-detection.js';
13
- import { loadConfigFile, loadAndFlattenConfig, validateConfig } from './config/loader.js';
14
- let dbInstance = null;
5
+ import knexConfig from './knexfile.js';
6
+ import { createDatabaseAdapter } from './adapters/index.js';
7
+ // Global adapter instance
8
+ let adapterInstance = null;
15
9
  /**
16
- * Initialize database connection
17
- * Creates database file and folder if they don't exist
18
- * Initializes schema on first run
19
- *
20
- * @param dbPath - Optional database path (defaults to .sqlew/sqlew.db)
21
- * @returns SQLite database instance
10
+ * Initialize database with adapter pattern
22
11
  */
23
- export function initializeDatabase(dbPath, configPath) {
24
- // If already initialized, return existing instance
25
- if (dbInstance) {
26
- return dbInstance;
27
- }
28
- try {
29
- // Load config file to get database path and other settings
30
- const config = loadConfigFile(configPath);
31
- // Validate config
32
- const validation = validateConfig(config);
33
- if (!validation.valid) {
34
- console.warn('⚠️ Configuration validation warnings:');
35
- validation.errors.forEach(err => console.warn(` - ${err}`));
36
- }
37
- // Determine database path with priority:
38
- // 1. CLI argument (dbPath parameter)
39
- // 2. Config file (config.database.path)
40
- // 3. Default path (DEFAULT_DB_PATH)
41
- const finalPath = dbPath || config.database?.path || DEFAULT_DB_PATH;
42
- // Convert to absolute path if relative
43
- const absolutePath = isAbsolute(finalPath)
44
- ? finalPath
45
- : resolve(process.cwd(), finalPath);
46
- // Create directory if it doesn't exist
47
- const dbDir = dirname(absolutePath);
48
- if (!existsSync(dbDir)) {
49
- mkdirSync(dbDir, { recursive: true });
50
- console.log(`✓ Created database directory: ${dbDir}`);
51
- }
52
- // Open database connection
53
- const db = new Database(absolutePath, {
54
- verbose: process.env.DEBUG_SQL ? console.log : undefined,
55
- });
56
- // Configure database
57
- db.pragma('journal_mode = WAL'); // Write-Ahead Logging for better concurrency
58
- db.pragma('foreign_keys = ON'); // Enforce foreign key constraints
59
- db.pragma('synchronous = NORMAL'); // Balance between safety and performance
60
- db.pragma(`busy_timeout = ${DB_BUSY_TIMEOUT}`); // Set busy timeout
61
- console.log(`✓ Connected to database: ${absolutePath}`);
62
- // Check if database has existing schema
63
- const schemaExists = isSchemaInitialized(db);
64
- if (schemaExists) {
65
- // Run all pending migrations using orchestrator
66
- // This handles upgrades from any version (v1.0.0, v1.1.x, v2.0.0, v2.1.x) to latest
67
- if (needsAnyMigrations(db)) {
68
- const migrationResults = runAllMigrations(db);
69
- // Check if any migration failed
70
- const failed = migrationResults.find(r => !r.success);
71
- if (failed) {
72
- console.error('\n❌ ERROR: Migration failed!');
73
- console.error(failed.message);
74
- db.close();
75
- process.exit(1);
76
- }
77
- // After table prefix migration, run schema initialization to create views/triggers
78
- // (tables already exist, CREATE TABLE IF NOT EXISTS will skip them)
79
- const hadPrefixMigration = migrationResults.some(r => r.message.toLowerCase().includes('table prefix') ||
80
- r.message.toLowerCase().includes('prefix'));
81
- if (hadPrefixMigration) {
82
- console.log('\n→ Creating views and triggers for new schema...');
83
- initializeSchema(db);
84
- }
85
- }
86
- // Validate existing schema integrity (after migrations)
87
- console.log('→ Validating existing database schema...');
88
- const validation = verifySchemaIntegrity(db);
89
- if (!validation.valid) {
90
- // Schema is invalid - display error and exit
91
- console.error('\n❌ ERROR: Database schema validation failed!');
92
- console.error('\nThe existing database file has an incompatible schema.');
93
- console.error(`Database location: ${absolutePath}`);
94
- if (validation.missing.length > 0) {
95
- console.error('\n📋 Missing components:');
96
- validation.missing.forEach(item => console.error(` - ${item}`));
97
- }
98
- if (validation.errors.length > 0) {
99
- console.error('\n⚠️ Validation errors:');
100
- validation.errors.forEach(error => console.error(` - ${error}`));
101
- }
102
- console.error('\n💡 Possible solutions:');
103
- console.error(' 1. Backup and delete the existing database file to start fresh');
104
- console.error(' 2. Use a different database path with --db-path option');
105
- console.error(' 3. Restore from a backup if available\n');
106
- // Close database and exit
107
- db.close();
108
- process.exit(1);
109
- }
110
- console.log('✓ Database schema validation passed');
111
- }
112
- else {
113
- // Initialize new schema
114
- console.log('→ Initializing database schema...');
115
- initializeSchema(db);
116
- }
117
- // Populate database with config file values (if any)
118
- try {
119
- const flatConfig = loadAndFlattenConfig(configPath);
120
- let configUpdates = 0;
121
- for (const [key, value] of Object.entries(flatConfig)) {
122
- if (value !== undefined) {
123
- const stringValue = typeof value === 'boolean' ? (value ? '1' : '0') : String(value);
124
- db.prepare('INSERT OR REPLACE INTO m_config (key, value) VALUES (?, ?)').run(key, stringValue);
125
- configUpdates++;
126
- }
127
- }
128
- if (configUpdates > 0) {
129
- console.log(`✓ Loaded ${configUpdates} config values from file`);
130
- }
131
- }
132
- catch (error) {
133
- console.warn('⚠️ Failed to load config file values:', error instanceof Error ? error.message : String(error));
134
- }
135
- // Initialize v3.4.0 config keys (git-aware auto-complete)
136
- // Use INSERT OR IGNORE to avoid overwriting existing values
137
- const v340Configs = [
138
- { key: 'git_auto_complete_enabled', value: '1' },
139
- { key: 'require_all_files_committed', value: '1' },
140
- { key: 'stale_review_notification_hours', value: '48' },
141
- ];
142
- const configInsert = db.prepare('INSERT OR IGNORE INTO m_config (key, value) VALUES (?, ?)');
143
- for (const config of v340Configs) {
144
- configInsert.run(config.key, config.value);
145
- }
146
- // Initialize v3.5.2 config keys (two-step git-aware workflow)
147
- // Use INSERT OR IGNORE to avoid overwriting existing values
148
- const v352Configs = [
149
- { key: 'git_auto_complete_on_stage', value: '1' }, // Auto-complete on git add
150
- { key: 'git_auto_archive_on_commit', value: '1' }, // Auto-archive on git commit
151
- { key: 'require_all_files_staged', value: '1' }, // Require ALL files staged (vs ANY)
152
- { key: 'require_all_files_committed_for_archive', value: '1' }, // Require ALL files committed for archiving
153
- ];
154
- for (const config of v352Configs) {
155
- configInsert.run(config.key, config.value);
156
- }
157
- // Store instance
158
- dbInstance = db;
159
- // Perform initial cleanup and task maintenance
160
- // Run synchronous tasks first
161
- const staleTransitions = detectAndTransitionStaleTasks(db);
162
- const archivedTasks = autoArchiveOldDoneTasks(db);
163
- const cleanupResult = performAutoCleanup(db);
164
- // Run async smart review detection in background (non-blocking)
165
- detectAndTransitionToReview(db).then(reviewTransitions => {
166
- const taskMaintenance = [];
167
- if (staleTransitions > 0)
168
- taskMaintenance.push(`${staleTransitions} stale task transitions`);
169
- if (archivedTasks > 0)
170
- taskMaintenance.push(`${archivedTasks} done tasks archived`);
171
- if (reviewTransitions > 0)
172
- taskMaintenance.push(`${reviewTransitions} quality-based review transitions`);
173
- if (cleanupResult.messagesDeleted > 0)
174
- taskMaintenance.push(`${cleanupResult.messagesDeleted} messages`);
175
- if (cleanupResult.fileChangesDeleted > 0)
176
- taskMaintenance.push(`${cleanupResult.fileChangesDeleted} file changes`);
177
- if (taskMaintenance.length > 0) {
178
- console.log(`✓ Cleanup: ${taskMaintenance.join(', ')}`);
179
- }
180
- }).catch(error => {
181
- console.warn('⚠️ Initial cleanup failed:', error instanceof Error ? error.message : String(error));
182
- });
183
- return db;
184
- }
185
- catch (error) {
186
- const message = error instanceof Error ? error.message : String(error);
187
- throw new Error(`Failed to initialize database: ${message}`);
12
+ export async function initializeDatabase(config) {
13
+ if (adapterInstance) {
14
+ return adapterInstance;
188
15
  }
16
+ const dbType = config?.databaseType || 'sqlite';
17
+ const adapter = createDatabaseAdapter(dbType);
18
+ // Determine if running from compiled code (dist/) or source (src/)
19
+ const isCompiledCode = import.meta.url.includes('/dist/');
20
+ const environment = isCompiledCode ? 'production' : 'development';
21
+ // Use config from knexfile or provided config
22
+ const baseConfig = knexConfig[environment] || knexConfig.development;
23
+ const knexConnConfig = config?.connection
24
+ ? { ...baseConfig, connection: config.connection }
25
+ : baseConfig;
26
+ await adapter.connect(knexConnConfig);
27
+ // Run migrations if needed
28
+ const knex = adapter.getKnex();
29
+ await knex.migrate.latest();
30
+ console.log(`✓ Database initialized with Knex adapter (${environment})`);
31
+ adapterInstance = adapter;
32
+ return adapter;
189
33
  }
190
34
  /**
191
- * Close database connection
35
+ * Get current database adapter
192
36
  */
193
- export function closeDatabase() {
194
- if (dbInstance) {
195
- dbInstance.close();
196
- dbInstance = null;
197
- console.log('✓ Database connection closed');
37
+ export function getAdapter() {
38
+ if (!adapterInstance) {
39
+ throw new Error('Database not initialized. Call initializeDatabase() first.');
198
40
  }
41
+ return adapterInstance;
199
42
  }
200
43
  /**
201
- * Get current database instance
202
- * Throws error if not initialized
203
- *
204
- * @returns Current database instance
44
+ * Close database connection
205
45
  */
206
- export function getDatabase() {
207
- if (!dbInstance) {
208
- throw new Error('Database not initialized. Call initializeDatabase() first.');
46
+ export async function closeDatabase() {
47
+ if (adapterInstance) {
48
+ await adapterInstance.disconnect();
49
+ adapterInstance = null;
50
+ console.log('✓ Database connection closed');
209
51
  }
210
- return dbInstance;
211
52
  }
212
53
  // ============================================================================
213
- // Helper Functions for Master Table Management
54
+ // Helper Functions for Master Table Management (Async)
214
55
  // ============================================================================
215
56
  /**
216
57
  * Get or create agent by name
217
- * Uses INSERT OR IGNORE for idempotent operation
218
- *
219
- * @param db - Database instance
220
- * @param name - Agent name
221
- * @returns Agent ID
222
58
  */
223
- export function getOrCreateAgent(db, name) {
224
- // Try to insert
225
- db.prepare('INSERT OR IGNORE INTO m_agents (name) VALUES (?)').run(name);
59
+ export async function getOrCreateAgent(adapter, name, trx) {
60
+ const knex = trx || adapter.getKnex();
61
+ // Try to insert (will be ignored if exists)
62
+ await knex('m_agents').insert({ name }).onConflict('name').ignore();
226
63
  // Get the ID
227
- const result = db.prepare('SELECT id FROM m_agents WHERE name = ?').get(name);
64
+ const result = await knex('m_agents').where({ name }).first('id');
228
65
  if (!result) {
229
66
  throw new Error(`Failed to get or create agent: ${name}`);
230
67
  }
@@ -232,14 +69,11 @@ export function getOrCreateAgent(db, name) {
232
69
  }
233
70
  /**
234
71
  * Get or create context key by name
235
- *
236
- * @param db - Database instance
237
- * @param key - Context key name
238
- * @returns Context key ID
239
72
  */
240
- export function getOrCreateContextKey(db, key) {
241
- db.prepare('INSERT OR IGNORE INTO m_context_keys (key) VALUES (?)').run(key);
242
- const result = db.prepare('SELECT id FROM m_context_keys WHERE key = ?').get(key);
73
+ export async function getOrCreateContextKey(adapter, key, trx) {
74
+ const knex = trx || adapter.getKnex();
75
+ await knex('m_context_keys').insert({ key }).onConflict('key').ignore();
76
+ const result = await knex('m_context_keys').where({ key }).first('id');
243
77
  if (!result) {
244
78
  throw new Error(`Failed to get or create context key: ${key}`);
245
79
  }
@@ -247,14 +81,11 @@ export function getOrCreateContextKey(db, key) {
247
81
  }
248
82
  /**
249
83
  * Get or create file by path
250
- *
251
- * @param db - Database instance
252
- * @param path - File path
253
- * @returns File ID
254
84
  */
255
- export function getOrCreateFile(db, path) {
256
- db.prepare('INSERT OR IGNORE INTO m_files (path) VALUES (?)').run(path);
257
- const result = db.prepare('SELECT id FROM m_files WHERE path = ?').get(path);
85
+ export async function getOrCreateFile(adapter, path, trx) {
86
+ const knex = trx || adapter.getKnex();
87
+ await knex('m_files').insert({ path }).onConflict('path').ignore();
88
+ const result = await knex('m_files').where({ path }).first('id');
258
89
  if (!result) {
259
90
  throw new Error(`Failed to get or create file: ${path}`);
260
91
  }
@@ -262,14 +93,11 @@ export function getOrCreateFile(db, path) {
262
93
  }
263
94
  /**
264
95
  * Get or create tag by name
265
- *
266
- * @param db - Database instance
267
- * @param name - Tag name
268
- * @returns Tag ID
269
96
  */
270
- export function getOrCreateTag(db, name) {
271
- db.prepare('INSERT OR IGNORE INTO m_tags (name) VALUES (?)').run(name);
272
- const result = db.prepare('SELECT id FROM m_tags WHERE name = ?').get(name);
97
+ export async function getOrCreateTag(adapter, name, trx) {
98
+ const knex = trx || adapter.getKnex();
99
+ await knex('m_tags').insert({ name }).onConflict('name').ignore();
100
+ const result = await knex('m_tags').where({ name }).first('id');
273
101
  if (!result) {
274
102
  throw new Error(`Failed to get or create tag: ${name}`);
275
103
  }
@@ -277,14 +105,11 @@ export function getOrCreateTag(db, name) {
277
105
  }
278
106
  /**
279
107
  * Get or create scope by name
280
- *
281
- * @param db - Database instance
282
- * @param name - Scope name
283
- * @returns Scope ID
284
108
  */
285
- export function getOrCreateScope(db, name) {
286
- db.prepare('INSERT OR IGNORE INTO m_scopes (name) VALUES (?)').run(name);
287
- const result = db.prepare('SELECT id FROM m_scopes WHERE name = ?').get(name);
109
+ export async function getOrCreateScope(adapter, name, trx) {
110
+ const knex = trx || adapter.getKnex();
111
+ await knex('m_scopes').insert({ name }).onConflict('name').ignore();
112
+ const result = await knex('m_scopes').where({ name }).first('id');
288
113
  if (!result) {
289
114
  throw new Error(`Failed to get or create scope: ${name}`);
290
115
  }
@@ -292,17 +117,11 @@ export function getOrCreateScope(db, name) {
292
117
  }
293
118
  /**
294
119
  * Get or create category ID
295
- * Uses INSERT to create if doesn't exist
296
- *
297
- * @param db - Database instance
298
- * @param category - Category name
299
- * @returns Category ID
300
120
  */
301
- export function getOrCreateCategoryId(db, category) {
302
- // Use INSERT OR IGNORE for idempotent operation
303
- db.prepare('INSERT OR IGNORE INTO m_constraint_categories (name) VALUES (?)').run(category);
304
- // Get the ID
305
- const result = db.prepare('SELECT id FROM m_constraint_categories WHERE name = ?').get(category);
121
+ export async function getOrCreateCategoryId(adapter, category, trx) {
122
+ const knex = trx || adapter.getKnex();
123
+ await knex('m_constraint_categories').insert({ name: category }).onConflict('name').ignore();
124
+ const result = await knex('m_constraint_categories').where({ name: category }).first('id');
306
125
  if (!result) {
307
126
  throw new Error(`Failed to get or create category: ${category}`);
308
127
  }
@@ -310,77 +129,56 @@ export function getOrCreateCategoryId(db, category) {
310
129
  }
311
130
  /**
312
131
  * Get layer ID by name
313
- * Does not auto-create (layers are predefined)
314
- *
315
- * @param db - Database instance
316
- * @param name - Layer name
317
- * @returns Layer ID or null if not found
318
132
  */
319
- export function getLayerId(db, name) {
320
- const result = db.prepare('SELECT id FROM m_layers WHERE name = ?').get(name);
133
+ export async function getLayerId(adapter, name, trx) {
134
+ const knex = trx || adapter.getKnex();
135
+ const result = await knex('m_layers').where({ name }).first('id');
321
136
  return result ? result.id : null;
322
137
  }
323
138
  /**
324
139
  * Get constraint category ID by name
325
- * Does not auto-create (categories are predefined)
326
- *
327
- * @param db - Database instance
328
- * @param name - Category name
329
- * @returns Category ID or null if not found
330
140
  */
331
- export function getCategoryId(db, name) {
332
- const result = db.prepare('SELECT id FROM m_constraint_categories WHERE name = ?').get(name);
141
+ export async function getCategoryId(adapter, name, trx) {
142
+ const knex = trx || adapter.getKnex();
143
+ const result = await knex('m_constraint_categories').where({ name }).first('id');
333
144
  return result ? result.id : null;
334
145
  }
335
146
  // ============================================================================
336
- // Configuration Management
147
+ // Configuration Management (Async)
337
148
  // ============================================================================
338
149
  /**
339
150
  * Get configuration value from m_config table
340
- *
341
- * @param db - Database instance
342
- * @param key - Config key
343
- * @returns Config value as string or null if not found
344
151
  */
345
- export function getConfigValue(db, key) {
346
- const result = db.prepare('SELECT value FROM m_config WHERE key = ?').get(key);
152
+ export async function getConfigValue(adapter, key) {
153
+ const knex = adapter.getKnex();
154
+ const result = await knex('m_config').where({ key }).first('value');
347
155
  return result ? result.value : null;
348
156
  }
349
157
  /**
350
158
  * Set configuration value in m_config table
351
- *
352
- * @param db - Database instance
353
- * @param key - Config key
354
- * @param value - Config value (will be converted to string)
355
159
  */
356
- export function setConfigValue(db, key, value) {
160
+ export async function setConfigValue(adapter, key, value) {
161
+ const knex = adapter.getKnex();
357
162
  const stringValue = String(value);
358
- db.prepare('INSERT OR REPLACE INTO m_config (key, value) VALUES (?, ?)').run(key, stringValue);
163
+ await knex('m_config')
164
+ .insert({ key, value: stringValue })
165
+ .onConflict('key')
166
+ .merge({ value: stringValue });
359
167
  }
360
168
  /**
361
169
  * Get configuration value as boolean
362
- *
363
- * @param db - Database instance
364
- * @param key - Config key
365
- * @param defaultValue - Default value if key not found
366
- * @returns Boolean value
367
170
  */
368
- export function getConfigBool(db, key, defaultValue = false) {
369
- const value = getConfigValue(db, key);
171
+ export async function getConfigBool(adapter, key, defaultValue = false) {
172
+ const value = await getConfigValue(adapter, key);
370
173
  if (value === null)
371
174
  return defaultValue;
372
175
  return value === '1' || value.toLowerCase() === 'true';
373
176
  }
374
177
  /**
375
178
  * Get configuration value as integer
376
- *
377
- * @param db - Database instance
378
- * @param key - Config key
379
- * @param defaultValue - Default value if key not found
380
- * @returns Integer value
381
179
  */
382
- export function getConfigInt(db, key, defaultValue = 0) {
383
- const value = getConfigValue(db, key);
180
+ export async function getConfigInt(adapter, key, defaultValue = 0) {
181
+ const value = await getConfigValue(adapter, key);
384
182
  if (value === null)
385
183
  return defaultValue;
386
184
  const parsed = parseInt(value, 10);
@@ -388,12 +186,10 @@ export function getConfigInt(db, key, defaultValue = 0) {
388
186
  }
389
187
  /**
390
188
  * Get all configuration as an object
391
- *
392
- * @param db - Database instance
393
- * @returns Object with all m_config key-value pairs
394
189
  */
395
- export function getAllConfig(db) {
396
- const rows = db.prepare('SELECT key, value FROM m_config').all();
190
+ export async function getAllConfig(adapter) {
191
+ const knex = adapter.getKnex();
192
+ const rows = await knex('m_config').select('key', 'value');
397
193
  const config = {};
398
194
  for (const row of rows) {
399
195
  config[row.key] = row.value;
@@ -401,35 +197,10 @@ export function getAllConfig(db) {
401
197
  return config;
402
198
  }
403
199
  // ============================================================================
404
- // Transaction Helpers
405
- // ============================================================================
406
- /**
407
- * Execute a function within a transaction
408
- * Automatically handles commit/rollback
409
- *
410
- * @param db - Database instance
411
- * @param fn - Function to execute in transaction
412
- * @returns Result from function
413
- */
414
- export function transaction(db, fn) {
415
- db.exec('BEGIN TRANSACTION');
416
- try {
417
- const result = fn();
418
- db.exec('COMMIT');
419
- return result;
420
- }
421
- catch (error) {
422
- db.exec('ROLLBACK');
423
- throw error;
424
- }
425
- }
426
- // ============================================================================
427
- // Decision Context Management (v3.2.2)
200
+ // Decision Context Management (Async - v3.2.2)
428
201
  // ============================================================================
429
202
  /**
430
203
  * Validate JSON structure for alternatives array
431
- * @param alternatives - JSON string or null
432
- * @throws Error if JSON is invalid or not an array
433
204
  */
434
205
  function validateAlternativesJson(alternatives) {
435
206
  if (alternatives === null || alternatives === undefined)
@@ -449,8 +220,6 @@ function validateAlternativesJson(alternatives) {
449
220
  }
450
221
  /**
451
222
  * Validate JSON structure for tradeoffs object
452
- * @param tradeoffs - JSON string or null
453
- * @throws Error if JSON is invalid or doesn't have pros/cons structure
454
223
  */
455
224
  function validateTradeoffsJson(tradeoffs) {
456
225
  if (tradeoffs === null || tradeoffs === undefined)
@@ -460,7 +229,6 @@ function validateTradeoffsJson(tradeoffs) {
460
229
  if (typeof parsed !== 'object' || parsed === null) {
461
230
  throw new Error('tradeoffs must be a JSON object');
462
231
  }
463
- // Optional: Check for pros/cons keys if provided
464
232
  if (parsed.pros !== undefined && !Array.isArray(parsed.pros)) {
465
233
  throw new Error('tradeoffs.pros must be an array');
466
234
  }
@@ -477,85 +245,54 @@ function validateTradeoffsJson(tradeoffs) {
477
245
  }
478
246
  /**
479
247
  * Add decision context to a decision
480
- *
481
- * @param db - Database instance
482
- * @param decisionKey - Decision key to attach context to
483
- * @param rationale - Rationale for the decision (required)
484
- * @param alternatives - JSON array of alternatives considered (optional)
485
- * @param tradeoffs - JSON object with pros/cons (optional)
486
- * @param decidedBy - Agent name who decided (optional)
487
- * @param relatedTaskId - Related task ID (optional)
488
- * @param relatedConstraintId - Related constraint ID (optional)
489
- * @returns Context ID
490
248
  */
491
- export function addDecisionContext(db, decisionKey, rationale, alternatives = null, tradeoffs = null, decidedBy = null, relatedTaskId = null, relatedConstraintId = null) {
249
+ export async function addDecisionContext(adapter, decisionKey, rationale, alternatives = null, tradeoffs = null, decidedBy = null, relatedTaskId = null, relatedConstraintId = null) {
492
250
  // Validate JSON inputs
493
251
  validateAlternativesJson(alternatives);
494
252
  validateTradeoffsJson(tradeoffs);
253
+ const knex = adapter.getKnex();
495
254
  // Get decision key ID
496
- const keyId = getOrCreateContextKey(db, decisionKey);
255
+ const keyId = await getOrCreateContextKey(adapter, decisionKey);
497
256
  // Get agent ID if provided
498
257
  let agentId = null;
499
258
  if (decidedBy) {
500
- agentId = getOrCreateAgent(db, decidedBy);
259
+ agentId = await getOrCreateAgent(adapter, decidedBy);
501
260
  }
502
261
  // Insert context
503
- const result = db.prepare(`
504
- INSERT INTO t_decision_context (
505
- decision_key_id,
506
- rationale,
507
- alternatives_considered,
508
- tradeoffs,
509
- decided_by_agent_id,
510
- related_task_id,
511
- related_constraint_id
512
- ) VALUES (?, ?, ?, ?, ?, ?, ?)
513
- `).run(keyId, rationale, alternatives, tradeoffs, agentId, relatedTaskId, relatedConstraintId);
514
- return result.lastInsertRowid;
262
+ const [id] = await knex('t_decision_context').insert({
263
+ decision_key_id: keyId,
264
+ rationale,
265
+ alternatives_considered: alternatives,
266
+ tradeoffs,
267
+ decided_by_agent_id: agentId,
268
+ related_task_id: relatedTaskId,
269
+ related_constraint_id: relatedConstraintId,
270
+ decision_date: Math.floor(Date.now() / 1000),
271
+ });
272
+ return id;
515
273
  }
516
274
  /**
517
275
  * Get decision with context
518
- *
519
- * @param db - Database instance
520
- * @param decisionKey - Decision key
521
- * @returns Decision with context or null if not found
522
276
  */
523
- export function getDecisionWithContext(db, decisionKey) {
277
+ export async function getDecisionWithContext(adapter, decisionKey) {
278
+ const knex = adapter.getKnex();
524
279
  // First get the decision
525
- const decision = db.prepare(`
526
- SELECT
527
- k.key,
528
- d.value,
529
- d.version,
530
- CASE d.status WHEN 1 THEN 'active' WHEN 2 THEN 'deprecated' ELSE 'draft' END as status,
531
- l.name as layer,
532
- a.name as decided_by,
533
- datetime(d.ts, 'unixepoch') as updated
534
- FROM t_decisions d
535
- JOIN m_context_keys k ON d.key_id = k.id
536
- LEFT JOIN m_layers l ON d.layer_id = l.id
537
- LEFT JOIN m_agents a ON d.agent_id = a.id
538
- WHERE k.key = ?
539
- `).get(decisionKey);
280
+ const decision = await knex('t_decisions as d')
281
+ .join('m_context_keys as k', 'd.key_id', 'k.id')
282
+ .leftJoin('m_layers as l', 'd.layer_id', 'l.id')
283
+ .leftJoin('m_agents as a', 'd.agent_id', 'a.id')
284
+ .where('k.key', decisionKey)
285
+ .select('k.key', 'd.value', 'd.version', knex.raw(`CASE d.status WHEN 1 THEN 'active' WHEN 2 THEN 'deprecated' ELSE 'draft' END as status`), 'l.name as layer', 'a.name as decided_by', knex.raw(`datetime(d.ts, 'unixepoch') as updated`))
286
+ .first();
540
287
  if (!decision)
541
288
  return null;
542
289
  // Get all contexts for this decision
543
- const contexts = db.prepare(`
544
- SELECT
545
- dc.id,
546
- dc.rationale,
547
- dc.alternatives_considered,
548
- dc.tradeoffs,
549
- a.name as decided_by,
550
- datetime(dc.decision_date, 'unixepoch') as decision_date,
551
- dc.related_task_id,
552
- dc.related_constraint_id
553
- FROM t_decision_context dc
554
- JOIN m_context_keys k ON dc.decision_key_id = k.id
555
- LEFT JOIN m_agents a ON dc.decided_by_agent_id = a.id
556
- WHERE k.key = ?
557
- ORDER BY dc.decision_date DESC
558
- `).all(decisionKey);
290
+ const contexts = await knex('t_decision_context as dc')
291
+ .join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
292
+ .leftJoin('m_agents as a', 'dc.decided_by_agent_id', 'a.id')
293
+ .where('k.key', decisionKey)
294
+ .select('dc.id', 'dc.rationale', 'dc.alternatives_considered', 'dc.tradeoffs', 'a.name as decided_by', knex.raw(`datetime(dc.decision_date, 'unixepoch') as decision_date`), 'dc.related_task_id', 'dc.related_constraint_id')
295
+ .orderBy('dc.decision_date', 'desc');
559
296
  return {
560
297
  ...decision,
561
298
  context: contexts,
@@ -563,54 +300,46 @@ export function getDecisionWithContext(db, decisionKey) {
563
300
  }
564
301
  /**
565
302
  * List decision contexts with optional filters
566
- *
567
- * @param db - Database instance
568
- * @param filters - Optional filters
569
- * @returns Array of decision contexts
570
303
  */
571
- export function listDecisionContexts(db, filters) {
572
- let query = `
573
- SELECT
574
- dc.id,
575
- k.key as decision_key,
576
- dc.rationale,
577
- dc.alternatives_considered,
578
- dc.tradeoffs,
579
- a.name as decided_by,
580
- datetime(dc.decision_date, 'unixepoch') as decision_date,
581
- dc.related_task_id,
582
- dc.related_constraint_id
583
- FROM t_decision_context dc
584
- JOIN m_context_keys k ON dc.decision_key_id = k.id
585
- LEFT JOIN m_agents a ON dc.decided_by_agent_id = a.id
586
- WHERE 1=1
587
- `;
588
- const params = [];
304
+ export async function listDecisionContexts(adapter, filters) {
305
+ const knex = adapter.getKnex();
306
+ let query = knex('t_decision_context as dc')
307
+ .join('m_context_keys as k', 'dc.decision_key_id', 'k.id')
308
+ .leftJoin('m_agents as a', 'dc.decided_by_agent_id', 'a.id')
309
+ .select('dc.id', 'k.key as decision_key', 'dc.rationale', 'dc.alternatives_considered', 'dc.tradeoffs', 'a.name as decided_by', knex.raw(`datetime(dc.decision_date, 'unixepoch') as decision_date`), 'dc.related_task_id', 'dc.related_constraint_id');
589
310
  if (filters?.decisionKey) {
590
- query += ' AND k.key = ?';
591
- params.push(filters.decisionKey);
311
+ query = query.where('k.key', filters.decisionKey);
592
312
  }
593
313
  if (filters?.relatedTaskId !== undefined) {
594
- query += ' AND dc.related_task_id = ?';
595
- params.push(filters.relatedTaskId);
314
+ query = query.where('dc.related_task_id', filters.relatedTaskId);
596
315
  }
597
316
  if (filters?.relatedConstraintId !== undefined) {
598
- query += ' AND dc.related_constraint_id = ?';
599
- params.push(filters.relatedConstraintId);
317
+ query = query.where('dc.related_constraint_id', filters.relatedConstraintId);
600
318
  }
601
319
  if (filters?.decidedBy) {
602
- query += ' AND a.name = ?';
603
- params.push(filters.decidedBy);
320
+ query = query.where('a.name', filters.decidedBy);
604
321
  }
605
- query += ' ORDER BY dc.decision_date DESC';
322
+ query = query.orderBy('dc.decision_date', 'desc');
606
323
  if (filters?.limit) {
607
- query += ' LIMIT ?';
608
- params.push(filters.limit);
324
+ query = query.limit(filters.limit);
609
325
  }
610
326
  if (filters?.offset) {
611
- query += ' OFFSET ?';
612
- params.push(filters.offset);
327
+ query = query.offset(filters.offset);
613
328
  }
614
- return db.prepare(query).all(...params);
329
+ return await query;
330
+ }
331
+ /**
332
+ * Backwards compatibility alias for getAdapter
333
+ */
334
+ export function getDatabase() {
335
+ return getAdapter();
336
+ }
337
+ /**
338
+ * Execute a function within a database transaction
339
+ */
340
+ export async function transaction(callback) {
341
+ const adapter = getAdapter();
342
+ const knex = adapter.getKnex();
343
+ return await knex.transaction(callback);
615
344
  }
616
345
  //# sourceMappingURL=database.js.map