@reqlan/analytical 0.8.2 → 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 +27 -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 +56 -4
  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 +8 -2
  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 +12 -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 -1
  27. package/out/core/workspace-paths.js +8 -44
  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/schema.d.ts +1 -1
  53. package/out/index-store/schema.js +6 -2
  54. package/out/index-store/schema.js.map +1 -1
  55. package/out/index-store/sqlite-store.d.ts +10 -4
  56. package/out/index-store/sqlite-store.js +81 -606
  57. package/out/index-store/sqlite-store.js.map +1 -1
  58. package/out/index-store/webview-graph-queries.d.ts +7 -0
  59. package/out/index-store/webview-graph-queries.js.map +1 -1
  60. package/out/index-store/webview-table-queries.d.ts +1 -1
  61. package/out/index-store/webview-table-queries.js +9 -0
  62. package/out/index-store/webview-table-queries.js.map +1 -1
  63. package/out/index-store/workspace-index-file.d.ts +3 -14
  64. package/out/index-store/workspace-index-file.js +1 -118
  65. package/out/index-store/workspace-index-file.js.map +1 -1
  66. package/out/index-store/workspace-index-sync.d.ts +4 -17
  67. package/out/index-store/workspace-index-sync.js +29 -11
  68. package/out/index-store/workspace-index-sync.js.map +1 -1
  69. package/out/index-store/workspace-index.d.ts +36 -9
  70. package/out/index-store/workspace-index.js +223 -120
  71. package/out/index-store/workspace-index.js.map +1 -1
  72. package/out/index.d.ts +16 -39
  73. package/out/index.js +9 -23
  74. package/out/index.js.map +1 -1
  75. package/out/native/generated.d.ts +102 -0
  76. package/out/native/generated.js +4 -0
  77. package/out/native/generated.js.map +1 -0
  78. package/out/native/index.d.ts +17 -0
  79. package/out/native/index.js +12 -0
  80. package/out/native/index.js.map +1 -0
  81. package/out/native/load-native.d.ts +50 -0
  82. package/out/native/load-native.js +152 -0
  83. package/out/native/load-native.js.map +1 -0
  84. package/out/{analysis-api.d.ts → native/native-analysis-api.d.ts} +24 -20
  85. package/out/native/native-analysis-api.js +212 -0
  86. package/out/native/native-analysis-api.js.map +1 -0
  87. package/out/native/native-index-store.d.ts +5 -0
  88. package/out/native/native-index-store.js +6 -0
  89. package/out/native/native-index-store.js.map +1 -0
  90. package/out/native/native-sql-db.d.ts +135 -0
  91. package/out/native/native-sql-db.js +217 -0
  92. package/out/native/native-sql-db.js.map +1 -0
  93. package/out/native/native-workspace-index.d.ts +164 -0
  94. package/out/native/native-workspace-index.js +283 -0
  95. package/out/native/native-workspace-index.js.map +1 -0
  96. package/out/native/open-analysis-api.d.ts +16 -0
  97. package/out/native/open-analysis-api.js +21 -0
  98. package/out/native/open-analysis-api.js.map +1 -0
  99. package/out/native/parse-source.d.ts +24 -0
  100. package/out/native/parse-source.js +26 -0
  101. package/out/native/parse-source.js.map +1 -0
  102. package/package.json +18 -22
  103. package/out/analysis/analyser-registry.d.ts +0 -21
  104. package/out/analysis/analyser-registry.js +0 -33
  105. package/out/analysis/analyser-registry.js.map +0 -1
  106. package/out/analysis/completion-tracking-analyser.d.ts +0 -3
  107. package/out/analysis/completion-tracking-analyser.js +0 -37
  108. package/out/analysis/completion-tracking-analyser.js.map +0 -1
  109. package/out/analysis/deprecation-impact-analyser.d.ts +0 -3
  110. package/out/analysis/deprecation-impact-analyser.js +0 -21
  111. package/out/analysis/deprecation-impact-analyser.js.map +0 -1
  112. package/out/analysis/file-related-analyser.d.ts +0 -5
  113. package/out/analysis/file-related-analyser.js +0 -41
  114. package/out/analysis/file-related-analyser.js.map +0 -1
  115. package/out/analysis/fuzzy-search-analyser.d.ts +0 -17
  116. package/out/analysis/fuzzy-search-analyser.js +0 -25
  117. package/out/analysis/fuzzy-search-analyser.js.map +0 -1
  118. package/out/analysis/fuzzy-search-worker-client.d.ts +0 -39
  119. package/out/analysis/fuzzy-search-worker-client.js +0 -178
  120. package/out/analysis/fuzzy-search-worker-client.js.map +0 -1
  121. package/out/analysis/fuzzy-search-worker.d.ts +0 -32
  122. package/out/analysis/fuzzy-search-worker.js +0 -81
  123. package/out/analysis/fuzzy-search-worker.js.map +0 -1
  124. package/out/analysis/fuzzy-search.d.ts +0 -38
  125. package/out/analysis/fuzzy-search.js +0 -217
  126. package/out/analysis/fuzzy-search.js.map +0 -1
  127. package/out/analysis/git-dates-analyser.d.ts +0 -9
  128. package/out/analysis/git-dates-analyser.js +0 -77
  129. package/out/analysis/git-dates-analyser.js.map +0 -1
  130. package/out/analysis/list-ideas-analyser.d.ts +0 -3
  131. package/out/analysis/list-ideas-analyser.js +0 -7
  132. package/out/analysis/list-ideas-analyser.js.map +0 -1
  133. package/out/analysis/local-graph-analyser.d.ts +0 -6
  134. package/out/analysis/local-graph-analyser.js +0 -42
  135. package/out/analysis/local-graph-analyser.js.map +0 -1
  136. package/out/analysis/semantic-search-analyser.d.ts +0 -8
  137. package/out/analysis/semantic-search-analyser.js +0 -51
  138. package/out/analysis/semantic-search-analyser.js.map +0 -1
  139. package/out/analysis-api.js +0 -190
  140. package/out/analysis-api.js.map +0 -1
  141. package/out/core/analytical-store.d.ts +0 -85
  142. package/out/core/analytical-store.js +0 -143
  143. package/out/core/analytical-store.js.map +0 -1
  144. package/out/core/context-model.d.ts +0 -204
  145. package/out/core/context-model.js +0 -36
  146. package/out/core/context-model.js.map +0 -1
  147. package/out/core/context-signals.d.ts +0 -149
  148. package/out/core/context-signals.js +0 -397
  149. package/out/core/context-signals.js.map +0 -1
  150. package/out/create-runtime.d.ts +0 -47
  151. package/out/create-runtime.js +0 -79
  152. package/out/create-runtime.js.map +0 -1
  153. package/out/export/export-html.d.ts +0 -4
  154. package/out/export/export-html.js +0 -12
  155. package/out/export/export-html.js.map +0 -1
  156. package/out/export/html-export-assets.d.ts +0 -3
  157. package/out/export/html-export-assets.js +0 -1926
  158. package/out/export/html-export-assets.js.map +0 -1
  159. package/out/export/html-export-template.d.ts +0 -18
  160. package/out/export/html-export-template.js +0 -1139
  161. package/out/export/html-export-template.js.map +0 -1
  162. package/out/export/html-export-utils.d.ts +0 -86
  163. package/out/export/html-export-utils.js +0 -485
  164. package/out/export/html-export-utils.js.map +0 -1
  165. package/out/export/write-html-export.d.ts +0 -2
  166. package/out/export/write-html-export.js +0 -181
  167. package/out/export/write-html-export.js.map +0 -1
  168. package/out/graph/physics-core-source.d.ts +0 -5
  169. package/out/graph/physics-core-source.js +0 -8
  170. package/out/graph/physics-core-source.js.map +0 -1
  171. package/out/graph/physics-core.d.ts +0 -141
  172. package/out/graph/physics-core.js +0 -237
  173. package/out/graph/physics-core.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 across delete+reinsert reindex.
65
- const existingGit = await all(this.db, `
66
- SELECT id, git_created_at, git_modified_at
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
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);
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
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
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
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
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
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,47 +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
- async updateGitDates(id, createdAt, modifiedAt) {
408
- await run(this.db, `
409
- UPDATE ideas SET git_created_at = ?, git_modified_at = ?
410
- WHERE id = ?
411
- `, createdAt ?? null, modifiedAt ?? null, id);
254
+ async updateGitDates(id, createdAt, modifiedAt, changeCount) {
255
+ this.db.conn.updateGitDates(id, createdAt, modifiedAt, changeCount);
412
256
  }
413
257
  async counts() {
414
- const ideas = (await get(this.db, 'SELECT COUNT(*) as count FROM ideas')).count;
415
- const edges = (await get(this.db, 'SELECT COUNT(*) as count FROM edges')).count;
416
- return { ideas, edges };
258
+ return this.db.conn.counts();
417
259
  }
418
260
  async getAttributeCatalog() {
419
261
  const ideas = await this.getAllIdeasRaw();
@@ -445,13 +287,7 @@ export class SqliteIndexStore {
445
287
  };
446
288
  }
447
289
  async searchByNameOrSummary(query) {
448
- const pattern = `%${query}%`;
449
- const rows = await all(this.db, `
450
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
451
- FROM ideas
452
- WHERE name LIKE ? OR summary LIKE ?
453
- ORDER BY name
454
- `, pattern, pattern);
290
+ const rows = this.db.conn.searchIdeaRows(query);
455
291
  return rows.map(row => this.toSummary(row));
456
292
  }
457
293
  /**
@@ -461,13 +297,7 @@ export class SqliteIndexStore {
461
297
  async listTodoIdeas(limit = ACTIVITY_BAR_TODO_LIMIT) {
462
298
  const cappedLimit = Math.max(0, Math.min(Math.floor(limit), 500));
463
299
  // Coarse prefilter: `"todo":` matches the attribute key, not `@status todo`.
464
- const rows = await all(this.db, `
465
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
466
- FROM ideas
467
- WHERE kind != 'ideaset'
468
- AND attributes_json LIKE '%"todo":%'
469
- ORDER BY name ASC, file_uri ASC, line_start ASC
470
- `);
300
+ const rows = this.db.conn.listTodoIdeaRows();
471
301
  const ideas = [];
472
302
  for (const row of rows) {
473
303
  const attributes = parseAttributes(row.attributes_json);
@@ -491,57 +321,17 @@ export class SqliteIndexStore {
491
321
  };
492
322
  }
493
323
  async listIdeasForGraphQuery(query, limit) {
494
- const { sql, params } = buildGraphFilterWhereClause(query);
495
- const orderSql = buildGraphTruncationOrderClause(query.truncationBasis);
496
- const totalMatching = (await get(this.db, `SELECT COUNT(*) as count FROM ideas i WHERE ${sql}`, ...params)).count;
497
- const rows = await all(this.db, `
498
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json
499
- FROM ideas i
500
- WHERE ${sql}
501
- ORDER BY ${orderSql}
502
- LIMIT ?
503
- `, ...params, limit);
324
+ const { rows, totalMatching } = this.db.conn.listIdeasForGraphQuery(query, limit);
504
325
  return {
505
326
  candidates: rows.map(row => this.toSummary(row)),
506
327
  totalMatching
507
328
  };
508
329
  }
509
330
  async countIdeas(query = { page: 0, pageSize: 50, attributeColumns: [], referenceFilters: [] }) {
510
- const { sql, params } = buildIdeasWhereClause(query);
511
- return (await get(this.db, `SELECT COUNT(*) as count FROM ideas i WHERE ${sql}`, ...params)).count;
331
+ return this.db.conn.countIdeas(query);
512
332
  }
513
333
  async listIdeasPage(query) {
514
- const { sql: whereSql, params: whereParams } = buildIdeasWhereClause(query);
515
- const orderSql = buildIdeasOrderClause(query);
516
- const rows = await all(this.db, `
517
- SELECT
518
- i.id,
519
- i.name,
520
- i.kind,
521
- i.file_uri,
522
- i.line_start,
523
- i.summary,
524
- i.attributes_json,
525
- (
526
- SELECT COUNT(*)
527
- FROM edges e
528
- WHERE e.source_id = i.id
529
- ) AS outbound_count,
530
- (
531
- SELECT COUNT(*)
532
- FROM edges e
533
- WHERE e.target_id = i.id
534
- ) AS inbound_count,
535
- (
536
- SELECT COUNT(*)
537
- FROM edges e
538
- WHERE e.source_id = i.id OR e.target_id = i.id
539
- ) AS reference_count
540
- FROM ideas i
541
- WHERE ${whereSql}
542
- ORDER BY ${orderSql}
543
- LIMIT ? OFFSET ?
544
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
334
+ const rows = this.db.conn.listIdeasPageRows(query);
545
335
  const ideaIds = rows.map(row => row.id);
546
336
  const referencesByIdea = await this.listReferenceChipsForIdeas(ideaIds);
547
337
  return rows.map(row => {
@@ -554,27 +344,7 @@ export class SqliteIndexStore {
554
344
  if (ideaIds.length === 0) {
555
345
  return result;
556
346
  }
557
- const placeholders = ideaIds.map(() => '?').join(', ');
558
- const rows = await all(this.db, `
559
- SELECT
560
- e.id,
561
- e.kind,
562
- e.source_id,
563
- e.target_id,
564
- e.target_file,
565
- e.label,
566
- si.name AS source_name,
567
- si.file_uri AS source_uri,
568
- si.line_start AS source_line,
569
- ti.name AS target_name,
570
- ti.file_uri AS target_uri,
571
- ti.line_start AS target_line
572
- FROM edges e
573
- JOIN ideas si ON si.id = e.source_id
574
- LEFT JOIN ideas ti ON ti.id = e.target_id
575
- WHERE e.source_id IN (${placeholders}) OR e.target_id IN (${placeholders})
576
- ORDER BY e.id
577
- `, ...ideaIds, ...ideaIds);
347
+ const rows = this.db.conn.listReferenceChipRows(ideaIds);
578
348
  for (const ideaId of ideaIds) {
579
349
  result.set(ideaId, { outbound: [], inbound: [] });
580
350
  }
@@ -589,78 +359,10 @@ export class SqliteIndexStore {
589
359
  return result;
590
360
  }
591
361
  async countIdeasets(query = { page: 0, pageSize: 50 }) {
592
- const { sql, params } = buildIdeasetsWhereClause(query);
593
- const row = await get(this.db, `
594
- SELECT COUNT(*) AS count
595
- FROM (
596
- SELECT
597
- d.file_uri AS id,
598
- 'file' AS kind,
599
- d.file_uri AS file_uri,
600
- 0 AS line_start,
601
- NULL AS name,
602
- (
603
- SELECT COUNT(*)
604
- FROM ideas i
605
- WHERE i.file_uri = d.file_uri
606
- ) AS member_count
607
- FROM documents d
608
- UNION ALL
609
- SELECT
610
- i.id,
611
- 'explicit' AS kind,
612
- i.file_uri,
613
- i.line_start,
614
- i.name,
615
- (
616
- SELECT COUNT(*)
617
- FROM edges e
618
- WHERE e.source_id = i.id AND e.kind = 'ideaset_member'
619
- ) AS member_count
620
- FROM ideas i
621
- WHERE i.kind = 'ideaset'
622
- ) ideasets
623
- WHERE ${sql}
624
- `, ...params);
625
- return row?.count ?? 0;
362
+ return this.db.conn.countIdeasets(query);
626
363
  }
627
364
  async listIdeasetsPage(query) {
628
- const { sql: whereSql, params: whereParams } = buildIdeasetsWhereClause(query);
629
- const orderSql = buildIdeasetsOrderClause(query);
630
- const rows = await all(this.db, `
631
- SELECT *
632
- FROM (
633
- SELECT
634
- d.file_uri AS id,
635
- 'file' AS kind,
636
- d.file_uri AS file_uri,
637
- 0 AS line_start,
638
- NULL AS name,
639
- (
640
- SELECT COUNT(*)
641
- FROM ideas i
642
- WHERE i.file_uri = d.file_uri
643
- ) AS member_count
644
- FROM documents d
645
- UNION ALL
646
- SELECT
647
- i.id,
648
- 'explicit' AS kind,
649
- i.file_uri,
650
- i.line_start,
651
- i.name,
652
- (
653
- SELECT COUNT(*)
654
- FROM edges e
655
- WHERE e.source_id = i.id AND e.kind = 'ideaset_member'
656
- ) AS member_count
657
- FROM ideas i
658
- WHERE i.kind = 'ideaset'
659
- ) ideasets
660
- WHERE ${whereSql}
661
- ORDER BY ${orderSql}
662
- LIMIT ? OFFSET ?
663
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
365
+ const rows = this.db.conn.listIdeasetsPageRows(query);
664
366
  const ideasets = rows.map(row => toIdeasetTableRow(row));
665
367
  return Promise.all(ideasets.map(async (ideaset) => ({
666
368
  ...ideaset,
@@ -668,20 +370,7 @@ export class SqliteIndexStore {
668
370
  })));
669
371
  }
670
372
  async listIdeasetMembers(ideasetId, kind, fileUri) {
671
- const rows = kind === 'file'
672
- ? await all(this.db, `
673
- SELECT name, file_uri, line_start
674
- FROM ideas
675
- WHERE file_uri = ? AND kind != 'ideaset'
676
- ORDER BY line_start
677
- `, fileUri)
678
- : await all(this.db, `
679
- SELECT i.name, i.file_uri, i.line_start
680
- FROM edges e
681
- JOIN ideas i ON i.id = e.target_id
682
- WHERE e.source_id = ? AND e.kind = 'ideaset_member'
683
- ORDER BY i.line_start
684
- `, ideasetId);
373
+ const rows = this.db.conn.listIdeasetMemberRows(ideasetId, kind, fileUri);
685
374
  return rows.map(row => ({
686
375
  name: row.name,
687
376
  fileUri: row.file_uri,
@@ -689,37 +378,10 @@ export class SqliteIndexStore {
689
378
  }));
690
379
  }
691
380
  async countReferences(query = { page: 0, pageSize: 50 }) {
692
- const { sql, params } = buildReferencesWhereClause(query);
693
- return (await get(this.db, `
694
- SELECT COUNT(*) as count
695
- FROM edges e
696
- JOIN ideas si ON si.id = e.source_id
697
- LEFT JOIN ideas ti ON ti.id = e.target_id
698
- WHERE ${sql}
699
- `, ...params)).count;
381
+ return this.db.conn.countReferences(query);
700
382
  }
701
383
  async listReferencesPage(query) {
702
- const { sql: whereSql, params: whereParams } = buildReferencesWhereClause(query);
703
- const orderSql = buildReferencesOrderClause(query);
704
- const rows = await all(this.db, `
705
- SELECT
706
- e.kind,
707
- e.source_id,
708
- e.target_id,
709
- e.target_file,
710
- e.label,
711
- si.name AS source_name,
712
- si.file_uri AS source_uri,
713
- si.line_start AS source_line,
714
- ti.name AS target_name,
715
- ti.file_uri AS target_uri
716
- FROM edges e
717
- JOIN ideas si ON si.id = e.source_id
718
- LEFT JOIN ideas ti ON ti.id = e.target_id
719
- WHERE ${whereSql}
720
- ORDER BY ${orderSql}
721
- LIMIT ? OFFSET ?
722
- `, ...whereParams, query.pageSize, query.page * query.pageSize);
384
+ const rows = this.db.conn.listReferencesPageRows(query);
723
385
  return rows.map(row => toReferenceTableRow(row));
724
386
  }
725
387
  /**
@@ -735,62 +397,22 @@ export class SqliteIndexStore {
735
397
  return rows;
736
398
  }
737
399
  async queryAttributesPage(query) {
738
- const rows = await all(this.db, `
739
- SELECT id, attributes_json
740
- FROM ideas
741
- WHERE kind != 'ideaset'
742
- `);
400
+ const rows = this.db.conn.listAttributeIdeaRows();
743
401
  return filterAndPageAttributes(aggregateAttributesFromRows(rows), query);
744
402
  }
745
403
  /**
746
- * Idea ids that still need git date backfill for the Timeline tab.
404
+ * Idea ids that still need git date / change-count backfill.
405
+ * Missing when both dates are null, or when change count has not been indexed yet.
747
406
  * - `fileUri`: only ideas in that file (active-editor priority queue)
748
407
  * - `preferFileUri`: list that file's ideas first, then the rest of the backlog
749
408
  * rq:["../../../../reqlan rq/extension/git-codelens.rq".git_dates_background_indexing]
750
409
  * rq:["../../../../reqlan rq/extension/git-codelens.rq".git_idea_timeline_analysis]
751
410
  * rq:["../../../../reqlan rq/extension/features-graph-analysers.rq".git_dates]
752
411
  * rq:["../../../../reqlan rq/extension/module/ideas_summary/webview.rq".timeline_page]
412
+ * rq:["../../../../reqlan rq/extension/module/ideas_summary/webview.rq".ideas_list]
753
413
  */
754
414
  async listIdeaIdsMissingGitDates(limit = 40, options) {
755
- const capped = Math.max(1, Math.min(limit, 200));
756
- const fileUri = options?.fileUri?.trim();
757
- if (fileUri) {
758
- const rows = await all(this.db, `
759
- SELECT id
760
- FROM ideas
761
- WHERE kind != 'ideaset'
762
- AND git_created_at IS NULL
763
- AND git_modified_at IS NULL
764
- AND file_uri = ?
765
- ORDER BY line_start ASC
766
- LIMIT ?
767
- `, fileUri, capped);
768
- return rows.map(row => row.id);
769
- }
770
- const preferFileUri = options?.preferFileUri?.trim();
771
- if (preferFileUri) {
772
- const rows = await all(this.db, `
773
- SELECT id
774
- FROM ideas
775
- WHERE kind != 'ideaset'
776
- AND git_created_at IS NULL
777
- AND git_modified_at IS NULL
778
- ORDER BY CASE WHEN file_uri = ? THEN 0 ELSE 1 END,
779
- file_uri ASC, line_start ASC
780
- LIMIT ?
781
- `, preferFileUri, capped);
782
- return rows.map(row => row.id);
783
- }
784
- const rows = await all(this.db, `
785
- SELECT id
786
- FROM ideas
787
- WHERE kind != 'ideaset'
788
- AND git_created_at IS NULL
789
- AND git_modified_at IS NULL
790
- ORDER BY file_uri ASC, line_start ASC
791
- LIMIT ?
792
- `, capped);
793
- return rows.map(row => row.id);
415
+ return this.db.conn.listIdeaIdsMissingGitDates(limit, options?.fileUri?.trim() || undefined, options?.preferFileUri?.trim() || undefined);
794
416
  }
795
417
  /**
796
418
  * Recent idea evolution events from indexed git dates for the Timeline tab.
@@ -798,15 +420,7 @@ export class SqliteIndexStore {
798
420
  * per ["../../../../reqlan rq/extension/module/ideas_summary/webview.rq".timeline_page]
799
421
  */
800
422
  async listRecentGitIdeaEvents(limit = 50) {
801
- const rows = await all(this.db, `
802
- SELECT id, name, kind, file_uri, line_start, summary, attributes_json,
803
- git_created_at, git_modified_at
804
- FROM ideas
805
- WHERE kind != 'ideaset'
806
- AND (git_modified_at IS NOT NULL OR git_created_at IS NOT NULL)
807
- ORDER BY COALESCE(git_modified_at, git_created_at) DESC
808
- LIMIT ?
809
- `, Math.max(1, Math.min(limit * 2, 400)));
423
+ const rows = this.db.conn.listRecentGitIdeaRows(limit);
810
424
  const events = [];
811
425
  for (const row of rows) {
812
426
  const attributes = parseAttributes(row.attributes_json);
@@ -854,157 +468,14 @@ export class SqliteIndexStore {
854
468
  tags: ideaTags(attributes),
855
469
  tagsKeys: tagsFilterKeysFromAttributes(attributes),
856
470
  gitCreatedAt: row.git_created_at ?? undefined,
857
- gitModifiedAt: row.git_modified_at ?? undefined
471
+ gitModifiedAt: row.git_modified_at ?? undefined,
472
+ gitChangeCount: row.git_change_count ?? undefined
858
473
  };
859
474
  }
860
475
  async migrate() {
861
- for (const statement of BASE_MIGRATIONS) {
862
- await exec(this.db, statement);
863
- }
864
- const currentVersion = Number((await get(this.db, `SELECT value FROM meta WHERE key = 'schema_version'`))?.value ?? '1');
865
- for (let version = currentVersion + 1; version <= SCHEMA_VERSION; version++) {
866
- const steps = VERSION_MIGRATIONS[version] ?? [];
867
- for (const statement of steps) {
868
- try {
869
- await exec(this.db, statement);
870
- }
871
- catch (error) {
872
- const message = error instanceof Error ? error.message : String(error);
873
- if (!message.includes('duplicate column')) {
874
- throw error;
875
- }
876
- }
877
- }
878
- }
879
- await run(this.db, `
880
- INSERT INTO meta (key, value) VALUES (?, ?)
881
- ON CONFLICT(key) DO UPDATE SET value = excluded.value
882
- `, 'schema_version', String(SCHEMA_VERSION));
883
- }
884
- }
885
- async function openDatabase(path) {
886
- await mkdir(dirname(path), { recursive: true });
887
- const SQL = await getSqlJsModule();
888
- const bytes = await readDatabaseBytes(path);
889
- return {
890
- db: bytes ? new SQL.Database(bytes) : new SQL.Database(),
891
- dbPath: path,
892
- inTransaction: false,
893
- dirty: false
894
- };
895
- }
896
- async function closeDatabase(db) {
897
- await persistDatabase(db);
898
- db.db.close();
899
- }
900
- async function run(db, sql, ...params) {
901
- db.db.run(sql, params);
902
- const statementKind = classifyStatement(sql);
903
- if (statementKind === 'begin') {
904
- db.inTransaction = true;
905
- return;
906
- }
907
- if (statementKind === 'commit') {
908
- db.inTransaction = false;
909
- db.dirty = true;
910
- await persistDatabase(db);
911
- return;
912
- }
913
- if (statementKind === 'rollback') {
914
- db.inTransaction = false;
915
- db.dirty = false;
916
- return;
917
- }
918
- if (statementKind === 'write') {
919
- db.dirty = true;
920
- if (!db.inTransaction) {
921
- await persistDatabase(db);
922
- }
923
- }
924
- }
925
- async function get(db, sql, ...params) {
926
- const rows = await all(db, sql, ...params);
927
- return rows[0];
928
- }
929
- async function all(db, sql, ...params) {
930
- const statement = db.db.prepare(sql, params);
931
- const rows = [];
932
- try {
933
- while (statement.step()) {
934
- rows.push(statement.getAsObject());
935
- }
936
- return rows;
937
- }
938
- finally {
939
- statement.free();
940
- }
941
- }
942
- /** Like `all`, but yields to the event loop every chunk so interactive work can run. */
943
- async function allYielding(db, sql, ...params) {
944
- const statement = db.db.prepare(sql, params);
945
- const rows = [];
946
- try {
947
- let stepped = 0;
948
- while (statement.step()) {
949
- rows.push(statement.getAsObject());
950
- stepped += 1;
951
- if (stepped % 250 === 0) {
952
- await new Promise(resolve => setTimeout(resolve, 0));
953
- }
954
- }
955
- return rows;
956
- }
957
- finally {
958
- statement.free();
476
+ this.db.conn.migrateIdeasSchema();
959
477
  }
960
478
  }
961
- async function exec(db, sql) {
962
- db.db.exec(sql);
963
- db.dirty = true;
964
- if (!db.inTransaction) {
965
- await persistDatabase(db);
966
- }
967
- }
968
- let sqlJsModulePromise;
969
- async function getSqlJsModule() {
970
- sqlJsModulePromise ?? (sqlJsModulePromise = import('sql.js/dist/sql-asm.js').then(({ default: initSqlJs }) => initSqlJs({})));
971
- return sqlJsModulePromise;
972
- }
973
- async function readDatabaseBytes(path) {
974
- try {
975
- const file = await readFile(path);
976
- return new Uint8Array(file);
977
- }
978
- catch (error) {
979
- if (isMissingFileError(error)) {
980
- return undefined;
981
- }
982
- throw error;
983
- }
984
- }
985
- async function persistDatabase(db) {
986
- if (!db.dirty) {
987
- return;
988
- }
989
- await writeFile(db.dbPath, db.db.export());
990
- db.dirty = false;
991
- }
992
- function classifyStatement(sql) {
993
- const keyword = sql.trimStart().match(/^[A-Za-z]+/)?.[0]?.toUpperCase();
994
- if (keyword === 'BEGIN') {
995
- return 'begin';
996
- }
997
- if (keyword === 'COMMIT') {
998
- return 'commit';
999
- }
1000
- if (keyword === 'ROLLBACK') {
1001
- return 'rollback';
1002
- }
1003
- if (keyword && ['INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'CREATE', 'DROP', 'ALTER', 'PRAGMA'].includes(keyword)) {
1004
- return 'write';
1005
- }
1006
- return 'read';
1007
- }
1008
479
  function isMissingFileError(error) {
1009
480
  return typeof error === 'object'
1010
481
  && error !== null
@@ -1033,6 +504,9 @@ function toIdeaTableRow(row, attributeColumns, outboundReferences, inboundRefere
1033
504
  inboundReferences,
1034
505
  fileUri: row.file_uri,
1035
506
  lineStart: row.line_start,
507
+ gitCreatedAt: row.git_created_at ?? undefined,
508
+ gitModifiedAt: row.git_modified_at ?? undefined,
509
+ gitChangeCount: row.git_change_count ?? undefined,
1036
510
  stabilityCue,
1037
511
  stabilityLabel
1038
512
  };
@@ -1195,7 +669,8 @@ function mapIdeaRow(row) {
1195
669
  attributesJson: row.attributes_json,
1196
670
  contentHash: row.content_hash,
1197
671
  gitCreatedAt: row.git_created_at ?? undefined,
1198
- gitModifiedAt: row.git_modified_at ?? undefined
672
+ gitModifiedAt: row.git_modified_at ?? undefined,
673
+ gitChangeCount: row.git_change_count ?? undefined
1199
674
  };
1200
675
  }
1201
676
  //# sourceMappingURL=sqlite-store.js.map