@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.
- package/README.md +27 -2
- package/README.template.md +1 -1
- package/out/core/barrel-page.d.ts +2 -1
- package/out/core/barrel-page.js +18 -83
- package/out/core/barrel-page.js.map +1 -1
- package/out/core/base-discovery.d.ts +6 -0
- package/out/core/base-discovery.js +56 -4
- package/out/core/base-discovery.js.map +1 -1
- package/out/core/create-base.js +10 -14
- package/out/core/create-base.js.map +1 -1
- package/out/core/file-reference-resolve.js +8 -2
- package/out/core/file-reference-resolve.js.map +1 -1
- package/out/core/index-error.d.ts +3 -3
- package/out/core/index-error.js.map +1 -1
- package/out/core/index-state-types.d.ts +47 -0
- package/out/core/index-state-types.js +11 -0
- package/out/core/index-state-types.js.map +1 -0
- package/out/core/path-relative.d.ts +15 -0
- package/out/core/path-relative.js +84 -0
- package/out/core/path-relative.js.map +1 -0
- package/out/core/rqignore.js +62 -62
- package/out/core/rqignore.js.map +1 -1
- package/out/core/types.d.ts +12 -3
- package/out/core/types.js +4 -1
- package/out/core/types.js.map +1 -1
- package/out/core/workspace-paths.d.ts +1 -1
- package/out/core/workspace-paths.js +8 -44
- package/out/core/workspace-paths.js.map +1 -1
- package/out/export/build-export-snapshot.js +1 -1
- package/out/export/build-export-snapshot.js.map +1 -1
- package/out/export/export-utils.d.ts +5 -0
- package/out/export/export-utils.js +38 -0
- package/out/export/export-utils.js.map +1 -0
- package/out/export/write-csv-export.js +1 -1
- package/out/export/write-csv-export.js.map +1 -1
- package/out/export/write-markdown-export.js +1 -1
- package/out/export/write-markdown-export.js.map +1 -1
- package/out/index-store/base-registry.d.ts +2 -3
- package/out/index-store/base-registry.js +8 -4
- package/out/index-store/base-registry.js.map +1 -1
- package/out/index-store/fuzzy-search-hit.d.ts +10 -0
- package/out/index-store/fuzzy-search-hit.js +2 -0
- package/out/index-store/fuzzy-search-hit.js.map +1 -0
- package/out/index-store/index-diagnostics-store.d.ts +4 -0
- package/out/index-store/index-diagnostics-store.js +44 -88
- package/out/index-store/index-diagnostics-store.js.map +1 -1
- package/out/index-store/index-file-error.d.ts +1 -1
- package/out/index-store/index-parse-issues.d.ts +4 -3
- package/out/index-store/index-parse-issues.js +0 -38
- package/out/index-store/index-parse-issues.js.map +1 -1
- package/out/index-store/index-status.d.ts +1 -1
- package/out/index-store/schema.d.ts +1 -1
- package/out/index-store/schema.js +6 -2
- package/out/index-store/schema.js.map +1 -1
- package/out/index-store/sqlite-store.d.ts +10 -4
- package/out/index-store/sqlite-store.js +81 -606
- package/out/index-store/sqlite-store.js.map +1 -1
- package/out/index-store/webview-graph-queries.d.ts +7 -0
- package/out/index-store/webview-graph-queries.js.map +1 -1
- package/out/index-store/webview-table-queries.d.ts +1 -1
- package/out/index-store/webview-table-queries.js +9 -0
- package/out/index-store/webview-table-queries.js.map +1 -1
- package/out/index-store/workspace-index-file.d.ts +3 -14
- package/out/index-store/workspace-index-file.js +1 -118
- package/out/index-store/workspace-index-file.js.map +1 -1
- package/out/index-store/workspace-index-sync.d.ts +4 -17
- package/out/index-store/workspace-index-sync.js +29 -11
- package/out/index-store/workspace-index-sync.js.map +1 -1
- package/out/index-store/workspace-index.d.ts +36 -9
- package/out/index-store/workspace-index.js +223 -120
- package/out/index-store/workspace-index.js.map +1 -1
- package/out/index.d.ts +16 -39
- package/out/index.js +9 -23
- package/out/index.js.map +1 -1
- package/out/native/generated.d.ts +102 -0
- package/out/native/generated.js +4 -0
- package/out/native/generated.js.map +1 -0
- package/out/native/index.d.ts +17 -0
- package/out/native/index.js +12 -0
- package/out/native/index.js.map +1 -0
- package/out/native/load-native.d.ts +50 -0
- package/out/native/load-native.js +152 -0
- package/out/native/load-native.js.map +1 -0
- package/out/{analysis-api.d.ts → native/native-analysis-api.d.ts} +24 -20
- package/out/native/native-analysis-api.js +212 -0
- package/out/native/native-analysis-api.js.map +1 -0
- package/out/native/native-index-store.d.ts +5 -0
- package/out/native/native-index-store.js +6 -0
- package/out/native/native-index-store.js.map +1 -0
- package/out/native/native-sql-db.d.ts +135 -0
- package/out/native/native-sql-db.js +217 -0
- package/out/native/native-sql-db.js.map +1 -0
- package/out/native/native-workspace-index.d.ts +164 -0
- package/out/native/native-workspace-index.js +283 -0
- package/out/native/native-workspace-index.js.map +1 -0
- package/out/native/open-analysis-api.d.ts +16 -0
- package/out/native/open-analysis-api.js +21 -0
- package/out/native/open-analysis-api.js.map +1 -0
- package/out/native/parse-source.d.ts +24 -0
- package/out/native/parse-source.js +26 -0
- package/out/native/parse-source.js.map +1 -0
- package/package.json +18 -22
- package/out/analysis/analyser-registry.d.ts +0 -21
- package/out/analysis/analyser-registry.js +0 -33
- package/out/analysis/analyser-registry.js.map +0 -1
- package/out/analysis/completion-tracking-analyser.d.ts +0 -3
- package/out/analysis/completion-tracking-analyser.js +0 -37
- package/out/analysis/completion-tracking-analyser.js.map +0 -1
- package/out/analysis/deprecation-impact-analyser.d.ts +0 -3
- package/out/analysis/deprecation-impact-analyser.js +0 -21
- package/out/analysis/deprecation-impact-analyser.js.map +0 -1
- package/out/analysis/file-related-analyser.d.ts +0 -5
- package/out/analysis/file-related-analyser.js +0 -41
- package/out/analysis/file-related-analyser.js.map +0 -1
- package/out/analysis/fuzzy-search-analyser.d.ts +0 -17
- package/out/analysis/fuzzy-search-analyser.js +0 -25
- package/out/analysis/fuzzy-search-analyser.js.map +0 -1
- package/out/analysis/fuzzy-search-worker-client.d.ts +0 -39
- package/out/analysis/fuzzy-search-worker-client.js +0 -178
- package/out/analysis/fuzzy-search-worker-client.js.map +0 -1
- package/out/analysis/fuzzy-search-worker.d.ts +0 -32
- package/out/analysis/fuzzy-search-worker.js +0 -81
- package/out/analysis/fuzzy-search-worker.js.map +0 -1
- package/out/analysis/fuzzy-search.d.ts +0 -38
- package/out/analysis/fuzzy-search.js +0 -217
- package/out/analysis/fuzzy-search.js.map +0 -1
- package/out/analysis/git-dates-analyser.d.ts +0 -9
- package/out/analysis/git-dates-analyser.js +0 -77
- package/out/analysis/git-dates-analyser.js.map +0 -1
- package/out/analysis/list-ideas-analyser.d.ts +0 -3
- package/out/analysis/list-ideas-analyser.js +0 -7
- package/out/analysis/list-ideas-analyser.js.map +0 -1
- package/out/analysis/local-graph-analyser.d.ts +0 -6
- package/out/analysis/local-graph-analyser.js +0 -42
- package/out/analysis/local-graph-analyser.js.map +0 -1
- package/out/analysis/semantic-search-analyser.d.ts +0 -8
- package/out/analysis/semantic-search-analyser.js +0 -51
- package/out/analysis/semantic-search-analyser.js.map +0 -1
- package/out/analysis-api.js +0 -190
- package/out/analysis-api.js.map +0 -1
- package/out/core/analytical-store.d.ts +0 -85
- package/out/core/analytical-store.js +0 -143
- package/out/core/analytical-store.js.map +0 -1
- package/out/core/context-model.d.ts +0 -204
- package/out/core/context-model.js +0 -36
- package/out/core/context-model.js.map +0 -1
- package/out/core/context-signals.d.ts +0 -149
- package/out/core/context-signals.js +0 -397
- package/out/core/context-signals.js.map +0 -1
- package/out/create-runtime.d.ts +0 -47
- package/out/create-runtime.js +0 -79
- package/out/create-runtime.js.map +0 -1
- package/out/export/export-html.d.ts +0 -4
- package/out/export/export-html.js +0 -12
- package/out/export/export-html.js.map +0 -1
- package/out/export/html-export-assets.d.ts +0 -3
- package/out/export/html-export-assets.js +0 -1926
- package/out/export/html-export-assets.js.map +0 -1
- package/out/export/html-export-template.d.ts +0 -18
- package/out/export/html-export-template.js +0 -1139
- package/out/export/html-export-template.js.map +0 -1
- package/out/export/html-export-utils.d.ts +0 -86
- package/out/export/html-export-utils.js +0 -485
- package/out/export/html-export-utils.js.map +0 -1
- package/out/export/write-html-export.d.ts +0 -2
- package/out/export/write-html-export.js +0 -181
- package/out/export/write-html-export.js.map +0 -1
- package/out/graph/physics-core-source.d.ts +0 -5
- package/out/graph/physics-core-source.js +0 -8
- package/out/graph/physics-core-source.js.map +0 -1
- package/out/graph/physics-core.d.ts +0 -141
- package/out/graph/physics-core.js +0 -237
- package/out/graph/physics-core.js.map +0 -1
- package/out/index-store/idea-extractor.d.ts +0 -11
- package/out/index-store/idea-extractor.js +0 -351
- package/out/index-store/idea-extractor.js.map +0 -1
- package/out/index-store/overview-coverage.d.ts +0 -23
- package/out/index-store/overview-coverage.js +0 -254
- 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
|
-
*
|
|
3
|
+
* Backed by rusqlite via the core native engine (NativeSqlDb).
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { basename
|
|
7
|
-
import {
|
|
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 {
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
27
|
+
this.db.conn.close();
|
|
27
28
|
}
|
|
28
|
-
/** Close
|
|
29
|
+
/** Close without extra flush work (file-backed rusqlite already durable). */
|
|
29
30
|
closeWithoutPersist() {
|
|
30
|
-
this.db.
|
|
31
|
+
this.db.conn.close();
|
|
31
32
|
}
|
|
32
33
|
static async deleteDatabaseFile(dbPath) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
return row?.content_hash;
|
|
46
|
+
return this.db.conn.getDocumentHash(fileUri) ?? undefined;
|
|
45
47
|
}
|
|
46
48
|
async getDocumentMtimeMs(fileUri) {
|
|
47
|
-
const
|
|
48
|
-
return
|
|
49
|
+
const value = this.db.conn.getDocumentMtimeMs(fileUri);
|
|
50
|
+
return value == null ? undefined : value;
|
|
49
51
|
}
|
|
50
52
|
async updateDocumentMtime(fileUri, mtimeMs) {
|
|
51
|
-
|
|
53
|
+
this.db.conn.updateDocumentMtime(fileUri, mtimeMs);
|
|
52
54
|
}
|
|
53
55
|
async listDocumentMtimes() {
|
|
54
|
-
const rows =
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
211
|
+
const rows = this.db.conn.getEdgesFromRows(sourceId);
|
|
356
212
|
return rows.map(mapEdgeRow);
|
|
357
213
|
}
|
|
358
214
|
async getEdgesTo(targetId) {
|
|
359
|
-
const rows =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
251
|
+
const rows = this.db.conn.allIdeaRawRows();
|
|
405
252
|
return rows.map(mapIdeaRow);
|
|
406
253
|
}
|
|
407
|
-
async updateGitDates(id, createdAt, modifiedAt) {
|
|
408
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|