@reqlan/analytical 0.9.1 → 0.10.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 (179) hide show
  1. package/README.md +13 -2
  2. package/README.template.md +1 -1
  3. package/out/core/barrel-page.d.ts +2 -1
  4. package/out/core/barrel-page.js +18 -83
  5. package/out/core/barrel-page.js.map +1 -1
  6. package/out/core/base-discovery.d.ts +6 -0
  7. package/out/core/base-discovery.js +47 -2
  8. package/out/core/base-discovery.js.map +1 -1
  9. package/out/core/create-base.js +10 -14
  10. package/out/core/create-base.js.map +1 -1
  11. package/out/core/file-reference-resolve.js +3 -1
  12. package/out/core/file-reference-resolve.js.map +1 -1
  13. package/out/core/index-error.d.ts +3 -3
  14. package/out/core/index-error.js.map +1 -1
  15. package/out/core/index-state-types.d.ts +47 -0
  16. package/out/core/index-state-types.js +11 -0
  17. package/out/core/index-state-types.js.map +1 -0
  18. package/out/core/path-relative.d.ts +15 -0
  19. package/out/core/path-relative.js +84 -0
  20. package/out/core/path-relative.js.map +1 -0
  21. package/out/core/rqignore.js +62 -62
  22. package/out/core/rqignore.js.map +1 -1
  23. package/out/core/types.d.ts +4 -3
  24. package/out/core/types.js +4 -1
  25. package/out/core/types.js.map +1 -1
  26. package/out/core/workspace-paths.d.ts +1 -2
  27. package/out/core/workspace-paths.js +6 -46
  28. package/out/core/workspace-paths.js.map +1 -1
  29. package/out/export/build-export-snapshot.js +1 -1
  30. package/out/export/build-export-snapshot.js.map +1 -1
  31. package/out/export/export-utils.d.ts +5 -0
  32. package/out/export/export-utils.js +38 -0
  33. package/out/export/export-utils.js.map +1 -0
  34. package/out/export/write-csv-export.js +1 -1
  35. package/out/export/write-csv-export.js.map +1 -1
  36. package/out/export/write-markdown-export.js +1 -1
  37. package/out/export/write-markdown-export.js.map +1 -1
  38. package/out/index-store/base-registry.d.ts +2 -3
  39. package/out/index-store/base-registry.js +8 -4
  40. package/out/index-store/base-registry.js.map +1 -1
  41. package/out/index-store/fuzzy-search-hit.d.ts +10 -0
  42. package/out/index-store/fuzzy-search-hit.js +2 -0
  43. package/out/index-store/fuzzy-search-hit.js.map +1 -0
  44. package/out/index-store/index-diagnostics-store.d.ts +4 -0
  45. package/out/index-store/index-diagnostics-store.js +44 -88
  46. package/out/index-store/index-diagnostics-store.js.map +1 -1
  47. package/out/index-store/index-file-error.d.ts +1 -1
  48. package/out/index-store/index-parse-issues.d.ts +4 -3
  49. package/out/index-store/index-parse-issues.js +0 -38
  50. package/out/index-store/index-parse-issues.js.map +1 -1
  51. package/out/index-store/index-status.d.ts +1 -1
  52. package/out/index-store/sqlite-store.d.ts +6 -2
  53. package/out/index-store/sqlite-store.js +70 -613
  54. package/out/index-store/sqlite-store.js.map +1 -1
  55. package/out/index-store/webview-graph-queries.d.ts +5 -0
  56. package/out/index-store/webview-graph-queries.js.map +1 -1
  57. package/out/index-store/workspace-index-file.d.ts +3 -14
  58. package/out/index-store/workspace-index-file.js +1 -114
  59. package/out/index-store/workspace-index-file.js.map +1 -1
  60. package/out/index-store/workspace-index-sync.d.ts +4 -17
  61. package/out/index-store/workspace-index-sync.js +29 -11
  62. package/out/index-store/workspace-index-sync.js.map +1 -1
  63. package/out/index-store/workspace-index.d.ts +36 -9
  64. package/out/index-store/workspace-index.js +223 -120
  65. package/out/index-store/workspace-index.js.map +1 -1
  66. package/out/index.d.ts +16 -43
  67. package/out/index.js +9 -25
  68. package/out/index.js.map +1 -1
  69. package/out/native/generated.d.ts +102 -0
  70. package/out/native/generated.js +4 -0
  71. package/out/native/generated.js.map +1 -0
  72. package/out/native/index.d.ts +17 -0
  73. package/out/native/index.js +12 -0
  74. package/out/native/index.js.map +1 -0
  75. package/out/native/load-native.d.ts +50 -0
  76. package/out/native/load-native.js +152 -0
  77. package/out/native/load-native.js.map +1 -0
  78. package/out/{analysis-api.d.ts → native/native-analysis-api.d.ts} +19 -19
  79. package/out/native/native-analysis-api.js +212 -0
  80. package/out/native/native-analysis-api.js.map +1 -0
  81. package/out/native/native-index-store.d.ts +5 -0
  82. package/out/native/native-index-store.js +6 -0
  83. package/out/native/native-index-store.js.map +1 -0
  84. package/out/native/native-sql-db.d.ts +135 -0
  85. package/out/native/native-sql-db.js +217 -0
  86. package/out/native/native-sql-db.js.map +1 -0
  87. package/out/native/native-workspace-index.d.ts +164 -0
  88. package/out/native/native-workspace-index.js +283 -0
  89. package/out/native/native-workspace-index.js.map +1 -0
  90. package/out/native/open-analysis-api.d.ts +16 -0
  91. package/out/native/open-analysis-api.js +21 -0
  92. package/out/native/open-analysis-api.js.map +1 -0
  93. package/out/native/parse-source.d.ts +24 -0
  94. package/out/native/parse-source.js +26 -0
  95. package/out/native/parse-source.js.map +1 -0
  96. package/package.json +18 -30
  97. package/out/analysis/analyser-registry.d.ts +0 -21
  98. package/out/analysis/analyser-registry.js +0 -33
  99. package/out/analysis/analyser-registry.js.map +0 -1
  100. package/out/analysis/completion-tracking-analyser.d.ts +0 -3
  101. package/out/analysis/completion-tracking-analyser.js +0 -37
  102. package/out/analysis/completion-tracking-analyser.js.map +0 -1
  103. package/out/analysis/contextual-search.d.ts +0 -26
  104. package/out/analysis/contextual-search.js +0 -94
  105. package/out/analysis/contextual-search.js.map +0 -1
  106. package/out/analysis/deprecation-impact-analyser.d.ts +0 -3
  107. package/out/analysis/deprecation-impact-analyser.js +0 -21
  108. package/out/analysis/deprecation-impact-analyser.js.map +0 -1
  109. package/out/analysis/file-related-analyser.d.ts +0 -5
  110. package/out/analysis/file-related-analyser.js +0 -41
  111. package/out/analysis/file-related-analyser.js.map +0 -1
  112. package/out/analysis/fuzzy-search-analyser.d.ts +0 -17
  113. package/out/analysis/fuzzy-search-analyser.js +0 -25
  114. package/out/analysis/fuzzy-search-analyser.js.map +0 -1
  115. package/out/analysis/fuzzy-search-worker-client.d.ts +0 -39
  116. package/out/analysis/fuzzy-search-worker-client.js +0 -178
  117. package/out/analysis/fuzzy-search-worker-client.js.map +0 -1
  118. package/out/analysis/fuzzy-search-worker.d.ts +0 -32
  119. package/out/analysis/fuzzy-search-worker.js +0 -81
  120. package/out/analysis/fuzzy-search-worker.js.map +0 -1
  121. package/out/analysis/fuzzy-search.d.ts +0 -62
  122. package/out/analysis/fuzzy-search.js +0 -471
  123. package/out/analysis/fuzzy-search.js.map +0 -1
  124. package/out/analysis/git-dates-analyser.d.ts +0 -12
  125. package/out/analysis/git-dates-analyser.js +0 -96
  126. package/out/analysis/git-dates-analyser.js.map +0 -1
  127. package/out/analysis/list-ideas-analyser.d.ts +0 -3
  128. package/out/analysis/list-ideas-analyser.js +0 -7
  129. package/out/analysis/list-ideas-analyser.js.map +0 -1
  130. package/out/analysis/local-graph-analyser.d.ts +0 -6
  131. package/out/analysis/local-graph-analyser.js +0 -42
  132. package/out/analysis/local-graph-analyser.js.map +0 -1
  133. package/out/analysis/semantic-search-analyser.d.ts +0 -8
  134. package/out/analysis/semantic-search-analyser.js +0 -51
  135. package/out/analysis/semantic-search-analyser.js.map +0 -1
  136. package/out/analysis-api.js +0 -199
  137. package/out/analysis-api.js.map +0 -1
  138. package/out/core/analytical-store.d.ts +0 -85
  139. package/out/core/analytical-store.js +0 -143
  140. package/out/core/analytical-store.js.map +0 -1
  141. package/out/core/context-model.d.ts +0 -204
  142. package/out/core/context-model.js +0 -36
  143. package/out/core/context-model.js.map +0 -1
  144. package/out/core/context-signals.d.ts +0 -154
  145. package/out/core/context-signals.js +0 -406
  146. package/out/core/context-signals.js.map +0 -1
  147. package/out/create-runtime.d.ts +0 -47
  148. package/out/create-runtime.js +0 -79
  149. package/out/create-runtime.js.map +0 -1
  150. package/out/export/export-html.d.ts +0 -4
  151. package/out/export/export-html.js +0 -12
  152. package/out/export/export-html.js.map +0 -1
  153. package/out/export/html-export-assets.d.ts +0 -3
  154. package/out/export/html-export-assets.js +0 -2294
  155. package/out/export/html-export-assets.js.map +0 -1
  156. package/out/export/html-export-template.d.ts +0 -18
  157. package/out/export/html-export-template.js +0 -1154
  158. package/out/export/html-export-template.js.map +0 -1
  159. package/out/export/html-export-utils.d.ts +0 -86
  160. package/out/export/html-export-utils.js +0 -485
  161. package/out/export/html-export-utils.js.map +0 -1
  162. package/out/export/write-html-export.d.ts +0 -2
  163. package/out/export/write-html-export.js +0 -181
  164. package/out/export/write-html-export.js.map +0 -1
  165. package/out/graph/physics-core-source.d.ts +0 -5
  166. package/out/graph/physics-core-source.js +0 -8
  167. package/out/graph/physics-core-source.js.map +0 -1
  168. package/out/graph/physics-core.d.ts +0 -141
  169. package/out/graph/physics-core.js +0 -237
  170. package/out/graph/physics-core.js.map +0 -1
  171. package/out/index-store/file-treatment.d.ts +0 -40
  172. package/out/index-store/file-treatment.js +0 -144
  173. package/out/index-store/file-treatment.js.map +0 -1
  174. package/out/index-store/idea-extractor.d.ts +0 -11
  175. package/out/index-store/idea-extractor.js +0 -351
  176. package/out/index-store/idea-extractor.js.map +0 -1
  177. package/out/index-store/overview-coverage.d.ts +0 -23
  178. package/out/index-store/overview-coverage.js +0 -254
  179. package/out/index-store/overview-coverage.js.map +0 -1
@@ -1,112 +1,72 @@
1
1
  /**
2
2
  * SQLite-backed persistence for the workspace idea graph index.
3
- * Uses a bundled asm.js build so VSIX installs do not depend on native modules.
3
+ * Backed by rusqlite via the core native engine (NativeSqlDb).
4
4
  */
5
- import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
6
- import { basename, dirname } from 'node:path';
7
- import { BASE_MIGRATIONS, SCHEMA_VERSION, VERSION_MIGRATIONS } from './schema.js';
5
+ import { unlink } from 'node:fs/promises';
6
+ import { basename } from 'node:path';
7
+ import { NativeSqlConnection } from '../native/native-sql-db.js';
8
8
  import { ACTIVITY_BAR_TODO_LIMIT, BLOCKING_STATUSES, ideaStatus, ideaTags, parseAttributes } from '../core/types.js';
9
9
  import { statusFilterKeyFromAttributes, tagsFilterKeysFromAttributes, todoNoteFromAttributes } from '../core/filter-specials.js';
10
10
  import { resolveReferencedFilePath } from '../core/file-reference-resolve.js';
11
- import { buildGraphTruncationOrderClause } from './webview-graph-queries.js';
12
- import { aggregateAttributesFromRows, attributeValuesForKeys, buildIdeasetsOrderClause, buildIdeasetsWhereClause, buildIdeasOrderClause, buildIdeasWhereClause, buildReferencesOrderClause, buildReferencesWhereClause, filterAndPageAttributes } from './webview-table-queries.js';
13
- import { buildGraphFilterWhereClause } from './webview-graph-queries.js';
11
+ import { aggregateAttributesFromRows, attributeValuesForKeys, filterAndPageAttributes } from './webview-table-queries.js';
14
12
  export class SqliteIndexStore {
15
13
  constructor(db) {
16
14
  this.db = db;
17
15
  }
18
16
  static async open(dbPath) {
19
- const db = await openDatabase(dbPath);
20
- await run(db, 'PRAGMA foreign_keys = ON');
21
- const store = new SqliteIndexStore(db);
17
+ const conn = await NativeSqlConnection.open(dbPath);
18
+ return SqliteIndexStore.fromConnection(conn);
19
+ }
20
+ /** Wrap an already-open native connection (e.g. NativeWorkspaceIndex ideas DB). */
21
+ static async fromConnection(conn) {
22
+ const store = new SqliteIndexStore({ conn });
22
23
  await store.migrate();
23
24
  return store;
24
25
  }
25
26
  async close() {
26
- await closeDatabase(this.db);
27
+ this.db.conn.close();
27
28
  }
28
- /** Close the in-memory database without writing back to disk. */
29
+ /** Close without extra flush work (file-backed rusqlite already durable). */
29
30
  closeWithoutPersist() {
30
- this.db.db.close();
31
+ this.db.conn.close();
31
32
  }
32
33
  static async deleteDatabaseFile(dbPath) {
33
- try {
34
- await unlink(dbPath);
35
- }
36
- catch (error) {
37
- if (!isMissingFileError(error)) {
38
- throw error;
34
+ for (const suffix of ['', '-wal', '-shm']) {
35
+ try {
36
+ await unlink(`${dbPath}${suffix}`);
37
+ }
38
+ catch (error) {
39
+ if (!isMissingFileError(error)) {
40
+ throw error;
41
+ }
39
42
  }
40
43
  }
41
44
  }
42
45
  async getDocumentHash(fileUri) {
43
- const row = await get(this.db, 'SELECT content_hash FROM documents WHERE file_uri = ?', fileUri);
44
- return row?.content_hash;
46
+ return this.db.conn.getDocumentHash(fileUri) ?? undefined;
45
47
  }
46
48
  async getDocumentMtimeMs(fileUri) {
47
- const row = await get(this.db, 'SELECT mtime_ms FROM documents WHERE file_uri = ?', fileUri);
48
- return row?.mtime_ms == null ? undefined : row.mtime_ms;
49
+ const value = this.db.conn.getDocumentMtimeMs(fileUri);
50
+ return value == null ? undefined : value;
49
51
  }
50
52
  async updateDocumentMtime(fileUri, mtimeMs) {
51
- await run(this.db, `UPDATE documents SET mtime_ms = ?, indexed_at = datetime('now') WHERE file_uri = ?`, mtimeMs, fileUri);
53
+ this.db.conn.updateDocumentMtime(fileUri, mtimeMs);
52
54
  }
53
55
  async listDocumentMtimes() {
54
- const rows = await all(this.db, 'SELECT file_uri, mtime_ms FROM documents');
56
+ const rows = this.db.conn.listDocumentMtimeRows();
55
57
  const result = new Map();
56
58
  for (const row of rows) {
57
59
  result.set(row.file_uri, row.mtime_ms == null ? undefined : row.mtime_ms);
58
60
  }
59
61
  return result;
60
62
  }
63
+ async listDocumentUris() {
64
+ // rq:["../../../reqlan rq/core_analysis/search.rq".file_search]
65
+ return this.db.conn.listDocumentUris();
66
+ }
61
67
  async upsertDocument(fileUri, contentHash, ideas, edges, mtimeMs) {
62
- await run(this.db, 'BEGIN');
63
- try {
64
- // Preserve analyser-populated git dates/counts across delete+reinsert reindex.
65
- const existingGit = await all(this.db, `
66
- SELECT id, git_created_at, git_modified_at, git_change_count
67
- FROM ideas
68
- WHERE file_uri = ?
69
- `, fileUri);
70
- const gitById = new Map(existingGit.map(row => [row.id, row]));
71
- await run(this.db, 'DELETE FROM edges WHERE source_id IN (SELECT id FROM ideas WHERE file_uri = ?)', fileUri);
72
- await run(this.db, 'DELETE FROM ideas WHERE file_uri = ?', fileUri);
73
- const insertIdeaSql = `
74
- INSERT INTO ideas (
75
- id, name, kind, file_uri, line_start, line_end, summary,
76
- attributes_json, content_hash, git_created_at, git_modified_at, git_change_count
77
- ) VALUES (
78
- ?, ?, ?, ?, ?, ?, ?,
79
- ?, ?, ?, ?, ?
80
- )
81
- `;
82
- for (const idea of ideas) {
83
- const previous = gitById.get(idea.id);
84
- await run(this.db, insertIdeaSql, idea.id, idea.name, idea.kind, idea.fileUri, idea.lineStart, idea.lineEnd, idea.summary, idea.attributesJson, idea.contentHash, idea.gitCreatedAt ?? previous?.git_created_at ?? null, idea.gitModifiedAt ?? previous?.git_modified_at ?? null, idea.gitChangeCount ?? previous?.git_change_count ?? null);
85
- }
86
- const insertEdgeSql = `
87
- INSERT OR IGNORE INTO edges (
88
- id, source_id, target_id, target_file, kind, label,
89
- source_line, snippet, is_resolved
90
- )
91
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
92
- `;
93
- for (const edge of edges) {
94
- await run(this.db, insertEdgeSql, edge.id, edge.sourceId, edge.targetId ?? null, edge.targetFile ?? null, edge.kind, edge.label ?? null, edge.sourceLine ?? null, edge.snippet ?? null, edge.isResolved === false ? 0 : 1);
95
- }
96
- await run(this.db, `
97
- INSERT INTO documents (file_uri, content_hash, indexed_at, mtime_ms)
98
- VALUES (?, ?, datetime('now'), ?)
99
- ON CONFLICT(file_uri) DO UPDATE SET
100
- content_hash = excluded.content_hash,
101
- indexed_at = excluded.indexed_at,
102
- mtime_ms = excluded.mtime_ms
103
- `, fileUri, contentHash, mtimeMs ?? null);
104
- await run(this.db, 'COMMIT');
105
- }
106
- catch (error) {
107
- await run(this.db, 'ROLLBACK');
108
- throw error;
109
- }
68
+ // Native transaction preserves analyser-populated git dates across the reindex.
69
+ this.db.conn.upsertDocument(fileUri, contentHash, ideas, edges, mtimeMs);
110
70
  }
111
71
  async removeDocument(fileUri) {
112
72
  await this.removeDocuments([fileUri]);
@@ -116,39 +76,13 @@ export class SqliteIndexStore {
116
76
  if (fileUris.length === 0) {
117
77
  return;
118
78
  }
119
- await run(this.db, 'BEGIN');
120
- try {
121
- for (const fileUri of fileUris) {
122
- await run(this.db, 'DELETE FROM edges WHERE source_id IN (SELECT id FROM ideas WHERE file_uri = ?)', fileUri);
123
- await run(this.db, 'DELETE FROM ideas WHERE file_uri = ?', fileUri);
124
- await run(this.db, 'DELETE FROM documents WHERE file_uri = ?', fileUri);
125
- }
126
- await run(this.db, 'COMMIT');
127
- }
128
- catch (error) {
129
- await run(this.db, 'ROLLBACK');
130
- throw error;
131
- }
79
+ this.db.conn.removeDocuments(fileUris);
132
80
  }
133
81
  async clearAll() {
134
- await run(this.db, 'BEGIN');
135
- try {
136
- await run(this.db, 'DELETE FROM edges');
137
- await run(this.db, 'DELETE FROM ideas');
138
- await run(this.db, 'DELETE FROM documents');
139
- await run(this.db, 'COMMIT');
140
- }
141
- catch (error) {
142
- await run(this.db, 'ROLLBACK');
143
- throw error;
144
- }
82
+ this.db.conn.clearAll();
145
83
  }
146
84
  async listAllIdeas() {
147
- const rows = await allYielding(this.db, `
148
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
149
- FROM ideas
150
- ORDER BY file_uri, line_start
151
- `);
85
+ const rows = this.db.conn.listAllIdeaRows();
152
86
  const ideas = [];
153
87
  for (let index = 0; index < rows.length; index += 1) {
154
88
  if (index > 0 && index % 250 === 0) {
@@ -159,109 +93,38 @@ export class SqliteIndexStore {
159
93
  return ideas;
160
94
  }
161
95
  async getIdea(id) {
162
- const row = await get(this.db, `
163
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json,
164
- git_created_at, git_modified_at, git_change_count
165
- FROM ideas WHERE id = ?
166
- `, id);
96
+ const row = this.db.conn.getIdeaRow(id);
167
97
  return row ? this.toSummary(row) : undefined;
168
98
  }
169
99
  async getIdeasInFile(fileUri) {
170
- const rows = await all(this.db, `
171
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
172
- FROM ideas WHERE file_uri = ?
173
- ORDER BY line_start
174
- `, fileUri);
100
+ const rows = this.db.conn.getIdeasInFileRows(fileUri);
175
101
  return rows.map(row => this.toSummary(row));
176
102
  }
177
103
  async getIdeaAtLine(fileUri, line) {
178
- const row = await get(this.db, `
179
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json,
180
- git_created_at, git_modified_at, git_change_count
181
- FROM ideas
182
- WHERE file_uri = ? AND line_start <= ? AND ? <= line_end AND kind != 'ideaset'
183
- ORDER BY line_start DESC
184
- LIMIT 1
185
- `, fileUri, line, line);
104
+ const row = this.db.conn.getIdeaAtLineRow(fileUri, line);
186
105
  return row ? this.toSummary(row) : undefined;
187
106
  }
188
107
  /** Innermost ideaset whose line range contains `line` (0-based). */
189
108
  async getIdeasetAtLine(fileUri, line) {
190
- const row = await get(this.db, `
191
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json,
192
- git_created_at, git_modified_at, git_change_count
193
- FROM ideas
194
- WHERE file_uri = ? AND line_start <= ? AND ? <= line_end AND kind = 'ideaset'
195
- ORDER BY line_start DESC
196
- LIMIT 1
197
- `, fileUri, line, line);
109
+ const row = this.db.conn.getIdeasetAtLineRow(fileUri, line);
198
110
  return row ? this.toSummary(row) : undefined;
199
111
  }
200
112
  async listIdeasInFileWithRanges(fileUri) {
201
- const rows = await all(this.db, `
202
- SELECT id, name, kind, file_uri, line_start, line_end, summary, attributes_json,
203
- git_created_at, git_modified_at, git_change_count
204
- FROM ideas
205
- WHERE file_uri = ? AND kind != 'ideaset'
206
- ORDER BY line_start ASC
207
- `, fileUri);
113
+ const rows = this.db.conn.listIdeasInFileWithRangeRows(fileUri);
208
114
  return rows.map(row => ({
209
115
  ...this.toSummary(row),
210
116
  lineEnd: row.line_end
211
117
  }));
212
118
  }
213
119
  async listIdeasetsInFileWithRanges(fileUri) {
214
- const rows = await all(this.db, `
215
- SELECT id, name, kind, file_uri, line_start, line_end, summary, attributes_json,
216
- git_created_at, git_modified_at, git_change_count
217
- FROM ideas
218
- WHERE file_uri = ? AND kind = 'ideaset'
219
- ORDER BY line_start ASC
220
- `, fileUri);
120
+ const rows = this.db.conn.listIdeasetsInFileWithRangeRows(fileUri);
221
121
  return rows.map(row => ({
222
122
  ...this.toSummary(row),
223
123
  lineEnd: row.line_end
224
124
  }));
225
125
  }
226
126
  async listReferencesForIdea(ideaId) {
227
- const outbound = await all(this.db, `
228
- SELECT
229
- e.id AS edge_id,
230
- e.kind,
231
- e.source_id,
232
- e.target_id,
233
- e.target_file,
234
- e.label,
235
- e.source_line,
236
- e.snippet,
237
- e.is_resolved,
238
- ti.name AS target_name,
239
- ti.file_uri AS target_uri,
240
- ti.line_start AS target_line
241
- FROM edges e
242
- LEFT JOIN ideas ti ON ti.id = e.target_id
243
- WHERE e.source_id = ?
244
- ORDER BY e.kind, e.id
245
- `, ideaId);
246
- const inbound = await all(this.db, `
247
- SELECT
248
- e.id AS edge_id,
249
- e.kind,
250
- e.source_id,
251
- e.target_id,
252
- e.target_file,
253
- e.label,
254
- e.source_line,
255
- e.snippet,
256
- e.is_resolved,
257
- si.name AS source_name,
258
- si.file_uri AS source_uri,
259
- si.line_start AS source_line_idea
260
- FROM edges e
261
- JOIN ideas si ON si.id = e.source_id
262
- WHERE e.target_id = ?
263
- ORDER BY e.kind, e.id
264
- `, ideaId);
127
+ const { outbound, inbound } = this.db.conn.listReferencesForIdea(ideaId);
265
128
  const rows = [];
266
129
  for (const row of outbound) {
267
130
  rows.push(toOutboundReferenceListRow(row));
@@ -300,17 +163,10 @@ export class SqliteIndexStore {
300
163
  return rows;
301
164
  }
302
165
  async countUnresolvedForIdea(ideaId) {
303
- return (await get(this.db, `
304
- SELECT COUNT(*) AS count FROM edges
305
- WHERE source_id = ? AND is_resolved = 0
306
- `, ideaId)).count;
166
+ return this.db.conn.countUnresolvedForIdea(ideaId);
307
167
  }
308
168
  async countEdgesFromFile(fileUri) {
309
- return (await get(this.db, `
310
- SELECT COUNT(*) AS count
311
- FROM edges
312
- WHERE source_id IN (SELECT id FROM ideas WHERE file_uri = ?)
313
- `, fileUri)).count;
169
+ return this.db.conn.countEdgesFromFile(fileUri);
314
170
  }
315
171
  async listAncestorChain(ideaId, maxDepth = 8) {
316
172
  const ancestors = [];
@@ -352,11 +208,11 @@ export class SqliteIndexStore {
352
208
  return { ideaId, ancestors, statusRollup, blocking };
353
209
  }
354
210
  async getEdgesFrom(sourceId) {
355
- const rows = await all(this.db, 'SELECT * FROM edges WHERE source_id = ?', sourceId);
211
+ const rows = this.db.conn.getEdgesFromRows(sourceId);
356
212
  return rows.map(mapEdgeRow);
357
213
  }
358
214
  async getEdgesTo(targetId) {
359
- const rows = await all(this.db, 'SELECT * FROM edges WHERE target_id = ?', targetId);
215
+ const rows = this.db.conn.getEdgesToRows(targetId);
360
216
  return rows.map(mapEdgeRow);
361
217
  }
362
218
  /** Batched fetch of every edge touching any of the given node ids (source or target). */
@@ -364,8 +220,7 @@ export class SqliteIndexStore {
364
220
  if (nodeIds.length === 0) {
365
221
  return [];
366
222
  }
367
- const placeholders = nodeIds.map(() => '?').join(',');
368
- const rows = await all(this.db, `SELECT * FROM edges WHERE source_id IN (${placeholders}) OR target_id IN (${placeholders})`, ...nodeIds, ...nodeIds);
223
+ const rows = this.db.conn.getEdgesForNodesRows([...nodeIds]);
369
224
  return rows.map(mapEdgeRow);
370
225
  }
371
226
  /** Batched fetch of idea summaries by id, preserving no particular order. */
@@ -373,54 +228,34 @@ export class SqliteIndexStore {
373
228
  if (ids.length === 0) {
374
229
  return [];
375
230
  }
376
- const placeholders = ids.map(() => '?').join(',');
377
- const rows = await all(this.db, `SELECT id, name, kind, file_uri, line_start, summary, attributes_json
378
- FROM ideas WHERE id IN (${placeholders})`, ...ids);
231
+ const rows = this.db.conn.getIdeasByIdsRows([...ids]);
379
232
  return rows.map(row => this.toSummary(row));
380
233
  }
381
234
  async getEdgesReferencingFile(filePath) {
382
- const rows = await all(this.db, `
383
- SELECT * FROM edges
384
- WHERE target_file LIKE ? OR target_file LIKE ?
385
- `, `%${filePath}%`, filePath);
235
+ const rows = this.db.conn.getEdgesReferencingFileRows(filePath);
386
236
  return rows.map(mapEdgeRow);
387
237
  }
388
238
  async getAllEdges() {
389
- const rows = await all(this.db, 'SELECT * FROM edges');
239
+ const rows = this.db.conn.getAllEdgeRows();
390
240
  return rows.map(mapEdgeRow);
391
241
  }
392
242
  /** Outbound file_reference targets for coverage metrics (source id + authored path). */
393
243
  async listFileReferenceTargets() {
394
- const rows = await all(this.db, `SELECT source_id, target_file
395
- FROM edges
396
- WHERE target_file IS NOT NULL AND target_file != ''
397
- AND (kind = 'file_reference' OR target_id IS NULL)`);
244
+ const rows = this.db.conn.listFileReferenceTargetRows();
398
245
  return rows.map(row => ({
399
246
  sourceId: row.source_id,
400
247
  targetFile: row.target_file
401
248
  }));
402
249
  }
403
250
  async getAllIdeasRaw() {
404
- const rows = await all(this.db, 'SELECT * FROM ideas');
251
+ const rows = this.db.conn.allIdeaRawRows();
405
252
  return rows.map(mapIdeaRow);
406
253
  }
407
254
  async updateGitDates(id, createdAt, modifiedAt, changeCount) {
408
- if (changeCount !== undefined) {
409
- await run(this.db, `
410
- UPDATE ideas SET git_created_at = ?, git_modified_at = ?, git_change_count = ?
411
- WHERE id = ?
412
- `, createdAt ?? null, modifiedAt ?? null, changeCount, id);
413
- return;
414
- }
415
- await run(this.db, `
416
- UPDATE ideas SET git_created_at = ?, git_modified_at = ?
417
- WHERE id = ?
418
- `, createdAt ?? null, modifiedAt ?? null, id);
255
+ this.db.conn.updateGitDates(id, createdAt, modifiedAt, changeCount);
419
256
  }
420
257
  async counts() {
421
- const ideas = (await get(this.db, 'SELECT COUNT(*) as count FROM ideas')).count;
422
- const edges = (await get(this.db, 'SELECT COUNT(*) as count FROM edges')).count;
423
- return { ideas, edges };
258
+ return this.db.conn.counts();
424
259
  }
425
260
  async getAttributeCatalog() {
426
261
  const ideas = await this.getAllIdeasRaw();
@@ -452,13 +287,7 @@ export class SqliteIndexStore {
452
287
  };
453
288
  }
454
289
  async searchByNameOrSummary(query) {
455
- const pattern = `%${query}%`;
456
- const rows = await all(this.db, `
457
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
458
- FROM ideas
459
- WHERE name LIKE ? OR summary LIKE ?
460
- ORDER BY name
461
- `, pattern, pattern);
290
+ const rows = this.db.conn.searchIdeaRows(query);
462
291
  return rows.map(row => this.toSummary(row));
463
292
  }
464
293
  /**
@@ -468,13 +297,7 @@ export class SqliteIndexStore {
468
297
  async listTodoIdeas(limit = ACTIVITY_BAR_TODO_LIMIT) {
469
298
  const cappedLimit = Math.max(0, Math.min(Math.floor(limit), 500));
470
299
  // Coarse prefilter: `"todo":` matches the attribute key, not `@status todo`.
471
- const rows = await all(this.db, `
472
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
473
- FROM ideas
474
- WHERE kind != 'ideaset'
475
- AND attributes_json LIKE '%"todo":%'
476
- ORDER BY name ASC, file_uri ASC, line_start ASC
477
- `);
300
+ const rows = this.db.conn.listTodoIdeaRows();
478
301
  const ideas = [];
479
302
  for (const row of rows) {
480
303
  const attributes = parseAttributes(row.attributes_json);
@@ -498,60 +321,17 @@ export class SqliteIndexStore {
498
321
  };
499
322
  }
500
323
  async listIdeasForGraphQuery(query, limit) {
501
- const { sql, params } = buildGraphFilterWhereClause(query);
502
- const orderSql = buildGraphTruncationOrderClause(query.truncationBasis);
503
- const totalMatching = (await get(this.db, `SELECT COUNT(*) as count FROM ideas i WHERE ${sql}`, ...params)).count;
504
- const rows = await all(this.db, `
505
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
506
- FROM ideas i
507
- WHERE ${sql}
508
- ORDER BY ${orderSql}
509
- LIMIT ?
510
- `, ...params, limit);
324
+ const { rows, totalMatching } = this.db.conn.listIdeasForGraphQuery(query, limit);
511
325
  return {
512
326
  candidates: rows.map(row => this.toSummary(row)),
513
327
  totalMatching
514
328
  };
515
329
  }
516
330
  async countIdeas(query = { page: 0, pageSize: 50, attributeColumns: [], referenceFilters: [] }) {
517
- const { sql, params } = buildIdeasWhereClause(query);
518
- return (await get(this.db, `SELECT COUNT(*) as count FROM ideas i WHERE ${sql}`, ...params)).count;
331
+ return this.db.conn.countIdeas(query);
519
332
  }
520
333
  async listIdeasPage(query) {
521
- const { sql: whereSql, params: whereParams } = buildIdeasWhereClause(query);
522
- const orderSql = buildIdeasOrderClause(query);
523
- const rows = await all(this.db, `
524
- SELECT
525
- i.id,
526
- i.name,
527
- i.kind,
528
- i.file_uri,
529
- i.line_start,
530
- i.summary,
531
- i.attributes_json,
532
- i.git_created_at,
533
- i.git_modified_at,
534
- i.git_change_count,
535
- (
536
- SELECT COUNT(*)
537
- FROM edges e
538
- WHERE e.source_id = i.id
539
- ) AS outbound_count,
540
- (
541
- SELECT COUNT(*)
542
- FROM edges e
543
- WHERE e.target_id = i.id
544
- ) AS inbound_count,
545
- (
546
- SELECT COUNT(*)
547
- FROM edges e
548
- WHERE e.source_id = i.id OR e.target_id = i.id
549
- ) AS reference_count
550
- FROM ideas i
551
- WHERE ${whereSql}
552
- ORDER BY ${orderSql}
553
- LIMIT ? OFFSET ?
554
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
334
+ const rows = this.db.conn.listIdeasPageRows(query);
555
335
  const ideaIds = rows.map(row => row.id);
556
336
  const referencesByIdea = await this.listReferenceChipsForIdeas(ideaIds);
557
337
  return rows.map(row => {
@@ -564,27 +344,7 @@ export class SqliteIndexStore {
564
344
  if (ideaIds.length === 0) {
565
345
  return result;
566
346
  }
567
- const placeholders = ideaIds.map(() => '?').join(', ');
568
- const rows = await all(this.db, `
569
- SELECT
570
- e.id,
571
- e.kind,
572
- e.source_id,
573
- e.target_id,
574
- e.target_file,
575
- e.label,
576
- si.name AS source_name,
577
- si.file_uri AS source_uri,
578
- si.line_start AS source_line,
579
- ti.name AS target_name,
580
- ti.file_uri AS target_uri,
581
- ti.line_start AS target_line
582
- FROM edges e
583
- JOIN ideas si ON si.id = e.source_id
584
- LEFT JOIN ideas ti ON ti.id = e.target_id
585
- WHERE e.source_id IN (${placeholders}) OR e.target_id IN (${placeholders})
586
- ORDER BY e.id
587
- `, ...ideaIds, ...ideaIds);
347
+ const rows = this.db.conn.listReferenceChipRows(ideaIds);
588
348
  for (const ideaId of ideaIds) {
589
349
  result.set(ideaId, { outbound: [], inbound: [] });
590
350
  }
@@ -599,78 +359,10 @@ export class SqliteIndexStore {
599
359
  return result;
600
360
  }
601
361
  async countIdeasets(query = { page: 0, pageSize: 50 }) {
602
- const { sql, params } = buildIdeasetsWhereClause(query);
603
- const row = await get(this.db, `
604
- SELECT COUNT(*) AS count
605
- FROM (
606
- SELECT
607
- d.file_uri AS id,
608
- 'file' AS kind,
609
- d.file_uri AS file_uri,
610
- 0 AS line_start,
611
- NULL AS name,
612
- (
613
- SELECT COUNT(*)
614
- FROM ideas i
615
- WHERE i.file_uri = d.file_uri
616
- ) AS member_count
617
- FROM documents d
618
- UNION ALL
619
- SELECT
620
- i.id,
621
- 'explicit' AS kind,
622
- i.file_uri,
623
- i.line_start,
624
- i.name,
625
- (
626
- SELECT COUNT(*)
627
- FROM edges e
628
- WHERE e.source_id = i.id AND e.kind = 'ideaset_member'
629
- ) AS member_count
630
- FROM ideas i
631
- WHERE i.kind = 'ideaset'
632
- ) ideasets
633
- WHERE ${sql}
634
- `, ...params);
635
- return row?.count ?? 0;
362
+ return this.db.conn.countIdeasets(query);
636
363
  }
637
364
  async listIdeasetsPage(query) {
638
- const { sql: whereSql, params: whereParams } = buildIdeasetsWhereClause(query);
639
- const orderSql = buildIdeasetsOrderClause(query);
640
- const rows = await all(this.db, `
641
- SELECT *
642
- FROM (
643
- SELECT
644
- d.file_uri AS id,
645
- 'file' AS kind,
646
- d.file_uri AS file_uri,
647
- 0 AS line_start,
648
- NULL AS name,
649
- (
650
- SELECT COUNT(*)
651
- FROM ideas i
652
- WHERE i.file_uri = d.file_uri
653
- ) AS member_count
654
- FROM documents d
655
- UNION ALL
656
- SELECT
657
- i.id,
658
- 'explicit' AS kind,
659
- i.file_uri,
660
- i.line_start,
661
- i.name,
662
- (
663
- SELECT COUNT(*)
664
- FROM edges e
665
- WHERE e.source_id = i.id AND e.kind = 'ideaset_member'
666
- ) AS member_count
667
- FROM ideas i
668
- WHERE i.kind = 'ideaset'
669
- ) ideasets
670
- WHERE ${whereSql}
671
- ORDER BY ${orderSql}
672
- LIMIT ? OFFSET ?
673
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
365
+ const rows = this.db.conn.listIdeasetsPageRows(query);
674
366
  const ideasets = rows.map(row => toIdeasetTableRow(row));
675
367
  return Promise.all(ideasets.map(async (ideaset) => ({
676
368
  ...ideaset,
@@ -678,20 +370,7 @@ export class SqliteIndexStore {
678
370
  })));
679
371
  }
680
372
  async listIdeasetMembers(ideasetId, kind, fileUri) {
681
- const rows = kind === 'file'
682
- ? await all(this.db, `
683
- SELECT name, file_uri, line_start
684
- FROM ideas
685
- WHERE file_uri = ? AND kind != 'ideaset'
686
- ORDER BY line_start
687
- `, fileUri)
688
- : await all(this.db, `
689
- SELECT i.name, i.file_uri, i.line_start
690
- FROM edges e
691
- JOIN ideas i ON i.id = e.target_id
692
- WHERE e.source_id = ? AND e.kind = 'ideaset_member'
693
- ORDER BY i.line_start
694
- `, ideasetId);
373
+ const rows = this.db.conn.listIdeasetMemberRows(ideasetId, kind, fileUri);
695
374
  return rows.map(row => ({
696
375
  name: row.name,
697
376
  fileUri: row.file_uri,
@@ -699,37 +378,10 @@ export class SqliteIndexStore {
699
378
  }));
700
379
  }
701
380
  async countReferences(query = { page: 0, pageSize: 50 }) {
702
- const { sql, params } = buildReferencesWhereClause(query);
703
- return (await get(this.db, `
704
- SELECT COUNT(*) as count
705
- FROM edges e
706
- JOIN ideas si ON si.id = e.source_id
707
- LEFT JOIN ideas ti ON ti.id = e.target_id
708
- WHERE ${sql}
709
- `, ...params)).count;
381
+ return this.db.conn.countReferences(query);
710
382
  }
711
383
  async listReferencesPage(query) {
712
- const { sql: whereSql, params: whereParams } = buildReferencesWhereClause(query);
713
- const orderSql = buildReferencesOrderClause(query);
714
- const rows = await all(this.db, `
715
- SELECT
716
- e.kind,
717
- e.source_id,
718
- e.target_id,
719
- e.target_file,
720
- e.label,
721
- si.name AS source_name,
722
- si.file_uri AS source_uri,
723
- si.line_start AS source_line,
724
- ti.name AS target_name,
725
- ti.file_uri AS target_uri
726
- FROM edges e
727
- JOIN ideas si ON si.id = e.source_id
728
- LEFT JOIN ideas ti ON ti.id = e.target_id
729
- WHERE ${whereSql}
730
- ORDER BY ${orderSql}
731
- LIMIT ? OFFSET ?
732
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
384
+ const rows = this.db.conn.listReferencesPageRows(query);
733
385
  return rows.map(row => toReferenceTableRow(row));
734
386
  }
735
387
  /**
@@ -745,11 +397,7 @@ export class SqliteIndexStore {
745
397
  return rows;
746
398
  }
747
399
  async queryAttributesPage(query) {
748
- const rows = await all(this.db, `
749
- SELECT id, attributes_json
750
- FROM ideas
751
- WHERE kind != 'ideaset'
752
- `);
400
+ const rows = this.db.conn.listAttributeIdeaRows();
753
401
  return filterAndPageAttributes(aggregateAttributesFromRows(rows), query);
754
402
  }
755
403
  /**
@@ -764,46 +412,7 @@ export class SqliteIndexStore {
764
412
  * rq:["../../../../reqlan rq/extension/module/ideas_summary/webview.rq".ideas_list]
765
413
  */
766
414
  async listIdeaIdsMissingGitDates(limit = 40, options) {
767
- const capped = Math.max(1, Math.min(limit, 200));
768
- const missingSql = `(
769
- (git_created_at IS NULL AND git_modified_at IS NULL)
770
- OR git_change_count IS NULL
771
- )`;
772
- const fileUri = options?.fileUri?.trim();
773
- if (fileUri) {
774
- const rows = await all(this.db, `
775
- SELECT id
776
- FROM ideas
777
- WHERE kind != 'ideaset'
778
- AND ${missingSql}
779
- AND file_uri = ?
780
- ORDER BY line_start ASC
781
- LIMIT ?
782
- `, fileUri, capped);
783
- return rows.map(row => row.id);
784
- }
785
- const preferFileUri = options?.preferFileUri?.trim();
786
- if (preferFileUri) {
787
- const rows = await all(this.db, `
788
- SELECT id
789
- FROM ideas
790
- WHERE kind != 'ideaset'
791
- AND ${missingSql}
792
- ORDER BY CASE WHEN file_uri = ? THEN 0 ELSE 1 END,
793
- file_uri ASC, line_start ASC
794
- LIMIT ?
795
- `, preferFileUri, capped);
796
- return rows.map(row => row.id);
797
- }
798
- const rows = await all(this.db, `
799
- SELECT id
800
- FROM ideas
801
- WHERE kind != 'ideaset'
802
- AND ${missingSql}
803
- ORDER BY file_uri ASC, line_start ASC
804
- LIMIT ?
805
- `, capped);
806
- return rows.map(row => row.id);
415
+ return this.db.conn.listIdeaIdsMissingGitDates(limit, options?.fileUri?.trim() || undefined, options?.preferFileUri?.trim() || undefined);
807
416
  }
808
417
  /**
809
418
  * Recent idea evolution events from indexed git dates for the Timeline tab.
@@ -811,15 +420,7 @@ export class SqliteIndexStore {
811
420
  * per ["../../../../reqlan rq/extension/module/ideas_summary/webview.rq".timeline_page]
812
421
  */
813
422
  async listRecentGitIdeaEvents(limit = 50) {
814
- const rows = await all(this.db, `
815
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json,
816
- git_created_at, git_modified_at, git_change_count
817
- FROM ideas
818
- WHERE kind != 'ideaset'
819
- AND (git_modified_at IS NOT NULL OR git_created_at IS NOT NULL)
820
- ORDER BY COALESCE(git_modified_at, git_created_at) DESC
821
- LIMIT ?
822
- `, Math.max(1, Math.min(limit * 2, 400)));
423
+ const rows = this.db.conn.listRecentGitIdeaRows(limit);
823
424
  const events = [];
824
425
  for (const row of rows) {
825
426
  const attributes = parseAttributes(row.attributes_json);
@@ -872,152 +473,8 @@ export class SqliteIndexStore {
872
473
  };
873
474
  }
874
475
  async migrate() {
875
- for (const statement of BASE_MIGRATIONS) {
876
- await exec(this.db, statement);
877
- }
878
- const currentVersion = Number((await get(this.db, `SELECT value FROM meta WHERE key = 'schema_version'`))?.value ?? '1');
879
- for (let version = currentVersion + 1; version <= SCHEMA_VERSION; version++) {
880
- const steps = VERSION_MIGRATIONS[version] ?? [];
881
- for (const statement of steps) {
882
- try {
883
- await exec(this.db, statement);
884
- }
885
- catch (error) {
886
- const message = error instanceof Error ? error.message : String(error);
887
- if (!message.includes('duplicate column')) {
888
- throw error;
889
- }
890
- }
891
- }
892
- }
893
- await run(this.db, `
894
- INSERT INTO meta (key, value) VALUES (?, ?)
895
- ON CONFLICT(key) DO UPDATE SET value = excluded.value
896
- `, 'schema_version', String(SCHEMA_VERSION));
897
- }
898
- }
899
- async function openDatabase(path) {
900
- await mkdir(dirname(path), { recursive: true });
901
- const SQL = await getSqlJsModule();
902
- const bytes = await readDatabaseBytes(path);
903
- return {
904
- db: bytes ? new SQL.Database(bytes) : new SQL.Database(),
905
- dbPath: path,
906
- inTransaction: false,
907
- dirty: false
908
- };
909
- }
910
- async function closeDatabase(db) {
911
- await persistDatabase(db);
912
- db.db.close();
913
- }
914
- async function run(db, sql, ...params) {
915
- db.db.run(sql, params);
916
- const statementKind = classifyStatement(sql);
917
- if (statementKind === 'begin') {
918
- db.inTransaction = true;
919
- return;
920
- }
921
- if (statementKind === 'commit') {
922
- db.inTransaction = false;
923
- db.dirty = true;
924
- await persistDatabase(db);
925
- return;
926
- }
927
- if (statementKind === 'rollback') {
928
- db.inTransaction = false;
929
- db.dirty = false;
930
- return;
931
- }
932
- if (statementKind === 'write') {
933
- db.dirty = true;
934
- if (!db.inTransaction) {
935
- await persistDatabase(db);
936
- }
937
- }
938
- }
939
- async function get(db, sql, ...params) {
940
- const rows = await all(db, sql, ...params);
941
- return rows[0];
942
- }
943
- async function all(db, sql, ...params) {
944
- const statement = db.db.prepare(sql, params);
945
- const rows = [];
946
- try {
947
- while (statement.step()) {
948
- rows.push(statement.getAsObject());
949
- }
950
- return rows;
951
- }
952
- finally {
953
- statement.free();
954
- }
955
- }
956
- /** Like `all`, but yields to the event loop every chunk so interactive work can run. */
957
- async function allYielding(db, sql, ...params) {
958
- const statement = db.db.prepare(sql, params);
959
- const rows = [];
960
- try {
961
- let stepped = 0;
962
- while (statement.step()) {
963
- rows.push(statement.getAsObject());
964
- stepped += 1;
965
- if (stepped % 250 === 0) {
966
- await new Promise(resolve => setTimeout(resolve, 0));
967
- }
968
- }
969
- return rows;
970
- }
971
- finally {
972
- statement.free();
973
- }
974
- }
975
- async function exec(db, sql) {
976
- db.db.exec(sql);
977
- db.dirty = true;
978
- if (!db.inTransaction) {
979
- await persistDatabase(db);
980
- }
981
- }
982
- let sqlJsModulePromise;
983
- async function getSqlJsModule() {
984
- sqlJsModulePromise ?? (sqlJsModulePromise = import('sql.js/dist/sql-asm.js').then(({ default: initSqlJs }) => initSqlJs({})));
985
- return sqlJsModulePromise;
986
- }
987
- async function readDatabaseBytes(path) {
988
- try {
989
- const file = await readFile(path);
990
- return new Uint8Array(file);
991
- }
992
- catch (error) {
993
- if (isMissingFileError(error)) {
994
- return undefined;
995
- }
996
- throw error;
997
- }
998
- }
999
- async function persistDatabase(db) {
1000
- if (!db.dirty) {
1001
- return;
1002
- }
1003
- await writeFile(db.dbPath, db.db.export());
1004
- db.dirty = false;
1005
- }
1006
- function classifyStatement(sql) {
1007
- const keyword = sql.trimStart().match(/^[A-Za-z]+/)?.[0]?.toUpperCase();
1008
- if (keyword === 'BEGIN') {
1009
- return 'begin';
1010
- }
1011
- if (keyword === 'COMMIT') {
1012
- return 'commit';
1013
- }
1014
- if (keyword === 'ROLLBACK') {
1015
- return 'rollback';
1016
- }
1017
- if (keyword && ['INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'CREATE', 'DROP', 'ALTER', 'PRAGMA'].includes(keyword)) {
1018
- return 'write';
476
+ this.db.conn.migrateIdeasSchema();
1019
477
  }
1020
- return 'read';
1021
478
  }
1022
479
  function isMissingFileError(error) {
1023
480
  return typeof error === 'object'