mustflow 2.115.3 → 2.115.4
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/dist/cli/lib/local-index/constants.js +1 -1
- package/dist/cli/lib/local-index/freshness.js +65 -35
- package/dist/cli/lib/local-index/index.js +26 -9
- package/dist/cli/lib/local-index/populate.js +4 -1
- package/dist/cli/lib/local-index/schema.js +10 -0
- package/dist/cli/lib/local-index/source-index.js +43 -11
- package/package.json +1 -1
- package/templates/default/manifest.toml +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const LOCAL_INDEX_SCHEMA_VERSION = '
|
|
1
|
+
export const LOCAL_INDEX_SCHEMA_VERSION = '21';
|
|
2
2
|
export const LOCAL_INDEX_PARSER_VERSION = '1';
|
|
3
3
|
export const DEFAULT_DATABASE_RELATIVE_PATH = '.mustflow/cache/mustflow.sqlite';
|
|
4
4
|
export const LATEST_RUN_STATE_RELATIVE_PATH = '.mustflow/state/runs/latest.json';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LOCAL_INDEX_SCHEMA_VERSION } from './constants.js';
|
|
1
|
+
import { DEFAULT_DATABASE_RELATIVE_PATH, LOCAL_INDEX_SCHEMA_VERSION } from './constants.js';
|
|
2
2
|
import { hasTable, queryRows, readMetadataValue, toSearchString } from './database-read.js';
|
|
3
|
-
import { normalizeIndexedFileSourceScope, readIndexedFileRecord } from './source-index.js';
|
|
3
|
+
import { collectSourceAnchorCandidatePaths, getSourceScopeHash, normalizeIndexedFileSourceScope, readIndexedFileRecord, readLocalIndexSourceConfig, } from './source-index.js';
|
|
4
4
|
import { collectDocuments } from './workflow-documents.js';
|
|
5
5
|
export function readStoredSchemaVersion(database) {
|
|
6
6
|
return readMetadataValue(database, 'schema_version');
|
|
@@ -11,46 +11,76 @@ export function getStalePaths(projectRoot, database, options = {}) {
|
|
|
11
11
|
if (schemaVersion !== LOCAL_INDEX_SCHEMA_VERSION) {
|
|
12
12
|
return ['.mustflow/cache/mustflow.sqlite'];
|
|
13
13
|
}
|
|
14
|
-
if (hasTable(database, 'indexed_files')
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
if (!hasTable(database, 'indexed_files') ||
|
|
15
|
+
!hasTable(database, 'indexed_source_candidates') ||
|
|
16
|
+
!hasTable(database, 'source_anchors') ||
|
|
17
|
+
!hasTable(database, 'source_anchor_status')) {
|
|
18
|
+
return [DEFAULT_DATABASE_RELATIVE_PATH];
|
|
19
|
+
}
|
|
20
|
+
const stalePaths = new Set();
|
|
21
|
+
const indexedRows = queryRows(database, 'SELECT path, source_scope, content_hash FROM indexed_files');
|
|
22
|
+
const indexedPaths = new Set(indexedRows.map((row) => toSearchString(row.path)));
|
|
23
|
+
const indexedSourcePaths = new Set(queryRows(database, 'SELECT path FROM indexed_source_candidates').map((row) => toSearchString(row.path)));
|
|
24
|
+
for (const sourcePath of indexedSourcePaths) {
|
|
25
|
+
if (!indexedPaths.has(sourcePath)) {
|
|
26
|
+
stalePaths.add(sourcePath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const row of indexedRows) {
|
|
30
|
+
const indexedPath = toSearchString(row.path);
|
|
31
|
+
const sourceScope = normalizeIndexedFileSourceScope(toSearchString(row.source_scope));
|
|
32
|
+
if (!includeState && sourceScope === 'state') {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const current = readIndexedFileRecord(projectRoot, indexedPath, sourceScope);
|
|
37
|
+
if (current.contentHash !== toSearchString(row.content_hash)) {
|
|
31
38
|
stalePaths.add(indexedPath);
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
stalePaths.add(document.path);
|
|
37
|
-
}
|
|
41
|
+
catch {
|
|
42
|
+
stalePaths.add(indexedPath);
|
|
38
43
|
}
|
|
39
|
-
return Array.from(stalePaths).sort((left, right) => left.localeCompare(right));
|
|
40
44
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const currentHashes = new Map(currentDocuments.map((document) => [document.path, document.contentHash]));
|
|
45
|
-
const stalePaths = new Set();
|
|
46
|
-
for (const [indexedPath, indexedHash] of indexedHashes) {
|
|
47
|
-
if (currentHashes.get(indexedPath) !== indexedHash) {
|
|
48
|
-
stalePaths.add(indexedPath);
|
|
45
|
+
for (const document of collectDocuments(projectRoot)) {
|
|
46
|
+
if (!indexedPaths.has(document.path)) {
|
|
47
|
+
stalePaths.add(document.path);
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const sourceIndexEnabledValue = readMetadataValue(database, 'source_index_enabled');
|
|
51
|
+
if (sourceIndexEnabledValue !== 'true' && sourceIndexEnabledValue !== 'false') {
|
|
52
|
+
stalePaths.add(DEFAULT_DATABASE_RELATIVE_PATH);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const sourceIndexEnabled = sourceIndexEnabledValue === 'true';
|
|
56
|
+
const hasIndexedSourceScope = indexedRows.some((row) => normalizeIndexedFileSourceScope(toSearchString(row.source_scope)) === 'source_anchor');
|
|
57
|
+
const hasSourceAnchorRows = hasTable(database, 'source_anchors') && queryRows(database, 'SELECT id FROM source_anchors LIMIT 1').length > 0;
|
|
58
|
+
if (!sourceIndexEnabled && (indexedSourcePaths.size > 0 || hasIndexedSourceScope || hasSourceAnchorRows)) {
|
|
59
|
+
stalePaths.add(DEFAULT_DATABASE_RELATIVE_PATH);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const sourceConfig = readLocalIndexSourceConfig(projectRoot);
|
|
63
|
+
const storedSourceScopeHash = readMetadataValue(database, 'source_scope_hash');
|
|
64
|
+
const currentSourceScopeHash = getSourceScopeHash(sourceIndexEnabled, sourceConfig);
|
|
65
|
+
if (storedSourceScopeHash !== currentSourceScopeHash) {
|
|
66
|
+
stalePaths.add(DEFAULT_DATABASE_RELATIVE_PATH);
|
|
67
|
+
}
|
|
68
|
+
if (sourceIndexEnabled) {
|
|
69
|
+
const currentSourcePaths = new Set(collectSourceAnchorCandidatePaths(projectRoot, sourceConfig));
|
|
70
|
+
for (const sourcePath of currentSourcePaths) {
|
|
71
|
+
if (!indexedSourcePaths.has(sourcePath)) {
|
|
72
|
+
stalePaths.add(sourcePath);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const sourcePath of indexedSourcePaths) {
|
|
76
|
+
if (!currentSourcePaths.has(sourcePath)) {
|
|
77
|
+
stalePaths.add(sourcePath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
stalePaths.add(DEFAULT_DATABASE_RELATIVE_PATH);
|
|
54
84
|
}
|
|
55
85
|
}
|
|
56
86
|
return Array.from(stalePaths).sort((left, right) => left.localeCompare(right));
|
|
@@ -158,6 +158,14 @@ function indexedFilesMatch(database, currentFiles) {
|
|
|
158
158
|
}
|
|
159
159
|
return true;
|
|
160
160
|
}
|
|
161
|
+
function indexedSourceCandidatesMatch(database, currentPaths) {
|
|
162
|
+
if (!hasTable(database, 'indexed_source_candidates')) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
const storedPaths = new Set(queryRows(database, 'SELECT path FROM indexed_source_candidates').map((row) => toSearchString(row.path)));
|
|
166
|
+
const currentPathSet = new Set(currentPaths);
|
|
167
|
+
return storedPaths.size === currentPathSet.size && [...storedPaths].every((storedPath) => currentPathSet.has(storedPath));
|
|
168
|
+
}
|
|
161
169
|
const INDEXED_FILE_MTIME_TOLERANCE_MS = 1;
|
|
162
170
|
function indexedFileMtimeMsEqual(storedMtimeMs, currentMtimeMs) {
|
|
163
171
|
return storedMtimeMs !== null && Math.abs(storedMtimeMs - currentMtimeMs) <= INDEXED_FILE_MTIME_TOLERANCE_MS;
|
|
@@ -183,8 +191,8 @@ function indexedFileMetadataMatch(database, currentFiles) {
|
|
|
183
191
|
}
|
|
184
192
|
return true;
|
|
185
193
|
}
|
|
186
|
-
async function readIncrementalPreflightReuse(SQL, databasePath, projectRoot, currentFiles, sourceScopeHash, dryRun, indexMode) {
|
|
187
|
-
if (!currentFiles) {
|
|
194
|
+
async function readIncrementalPreflightReuse(SQL, databasePath, projectRoot, currentFiles, currentSourceCandidatePaths, sourceScopeHash, dryRun, indexMode) {
|
|
195
|
+
if (!currentFiles || !currentSourceCandidatePaths) {
|
|
188
196
|
return { result: null, rebuildReason: null };
|
|
189
197
|
}
|
|
190
198
|
if (!existsSync(databasePath)) {
|
|
@@ -205,6 +213,9 @@ async function readIncrementalPreflightReuse(SQL, databasePath, projectRoot, cur
|
|
|
205
213
|
if (!hasTable(database, 'indexed_files')) {
|
|
206
214
|
return { result: null, rebuildReason: 'indexed_files_missing' };
|
|
207
215
|
}
|
|
216
|
+
if (!indexedSourceCandidatesMatch(database, currentSourceCandidatePaths)) {
|
|
217
|
+
return { result: null, rebuildReason: 'source_scope_mismatch' };
|
|
218
|
+
}
|
|
208
219
|
if (!indexedFileMetadataMatch(database, currentFiles)) {
|
|
209
220
|
return { result: null, rebuildReason: 'file_fingerprint_mismatch' };
|
|
210
221
|
}
|
|
@@ -225,7 +236,7 @@ async function readIncrementalPreflightReuse(SQL, databasePath, projectRoot, cur
|
|
|
225
236
|
database?.close();
|
|
226
237
|
}
|
|
227
238
|
}
|
|
228
|
-
async function readIncrementalReuseDecision(SQL, databasePath, currentFiles, sourceScopeHash) {
|
|
239
|
+
async function readIncrementalReuseDecision(SQL, databasePath, currentFiles, currentSourceCandidatePaths, sourceScopeHash) {
|
|
229
240
|
if (!existsSync(databasePath)) {
|
|
230
241
|
return { reusable: false, rebuildReason: 'missing_index', capabilities: null };
|
|
231
242
|
}
|
|
@@ -244,6 +255,9 @@ async function readIncrementalReuseDecision(SQL, databasePath, currentFiles, sou
|
|
|
244
255
|
if (!hasTable(database, 'indexed_files')) {
|
|
245
256
|
return { reusable: false, rebuildReason: 'indexed_files_missing', capabilities: null };
|
|
246
257
|
}
|
|
258
|
+
if (!indexedSourceCandidatesMatch(database, currentSourceCandidatePaths)) {
|
|
259
|
+
return { reusable: false, rebuildReason: 'source_scope_mismatch', capabilities: null };
|
|
260
|
+
}
|
|
247
261
|
if (!indexedFilesMatch(database, currentFiles)) {
|
|
248
262
|
return { reusable: false, rebuildReason: 'file_fingerprint_mismatch', capabilities: null };
|
|
249
263
|
}
|
|
@@ -284,7 +298,7 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
284
298
|
capabilityDatabase.close();
|
|
285
299
|
if (incremental) {
|
|
286
300
|
const preflightFiles = collectFastPreflightIndexedFileMetadataRecords(projectRoot, includeSource, sourceConfig);
|
|
287
|
-
const preflightReuse = await readIncrementalPreflightReuse(SQL, databasePath, projectRoot, preflightFiles, sourceScopeHash, dryRun, indexMode);
|
|
301
|
+
const preflightReuse = await readIncrementalPreflightReuse(SQL, databasePath, projectRoot, preflightFiles?.records ?? null, preflightFiles?.sourceCandidatePaths ?? null, sourceScopeHash, dryRun, indexMode);
|
|
288
302
|
if (preflightReuse.result) {
|
|
289
303
|
return preflightReuse.result;
|
|
290
304
|
}
|
|
@@ -307,16 +321,16 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
307
321
|
const verificationEvidence = createVerificationEvidenceIndex(projectRoot);
|
|
308
322
|
const indexedFiles = collectIndexedFileRecords(projectRoot, documents, sourceAnchors, sourceAnchorCandidatePaths);
|
|
309
323
|
if (incremental) {
|
|
310
|
-
const reuseDecision = await readIncrementalReuseDecision(SQL, databasePath, indexedFiles, sourceScopeHash);
|
|
324
|
+
const reuseDecision = await readIncrementalReuseDecision(SQL, databasePath, indexedFiles, sourceAnchorCandidatePaths, sourceScopeHash);
|
|
311
325
|
reusedExisting = reuseDecision.reusable;
|
|
312
|
-
rebuildReason = reuseDecision.rebuildReason ?? rebuildReason;
|
|
326
|
+
rebuildReason = reuseDecision.reusable ? null : (reuseDecision.rebuildReason ?? rebuildReason);
|
|
313
327
|
capabilities = reuseDecision.capabilities ?? capabilities;
|
|
314
328
|
}
|
|
315
329
|
if (!dryRun && !reusedExisting) {
|
|
316
330
|
const database = new SQL.Database();
|
|
317
331
|
try {
|
|
318
332
|
createSchema(database, capabilities);
|
|
319
|
-
populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, verificationEvidence, indexMode, sourceScopeHash, includeSource, new Date().toISOString());
|
|
333
|
+
populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, sourceAnchorCandidatePaths, verificationEvidence, indexMode, sourceScopeHash, includeSource, new Date().toISOString());
|
|
320
334
|
writeFileInsideWithoutSymlinks(projectRoot, databasePath, database.export());
|
|
321
335
|
}
|
|
322
336
|
finally {
|
|
@@ -702,10 +716,13 @@ export async function readLocalSourceAnchorCheckWarnings(projectRoot) {
|
|
|
702
716
|
if (readMetadataValue(database, 'source_index_enabled') !== 'true') {
|
|
703
717
|
return [];
|
|
704
718
|
}
|
|
705
|
-
if (!hasTable(database, 'indexed_files') ||
|
|
719
|
+
if (!hasTable(database, 'indexed_files') ||
|
|
720
|
+
!hasTable(database, 'indexed_source_candidates') ||
|
|
721
|
+
!hasTable(database, 'source_anchors') ||
|
|
722
|
+
!hasTable(database, 'source_anchor_status')) {
|
|
706
723
|
return ['source anchor local index is missing source-anchor tables; refresh with mf index --source'];
|
|
707
724
|
}
|
|
708
|
-
const indexedSourcePaths = new Set(queryRows(database, 'SELECT path FROM
|
|
725
|
+
const indexedSourcePaths = new Set(queryRows(database, 'SELECT path FROM indexed_source_candidates')
|
|
709
726
|
.map((row) => toSearchString(row.path))
|
|
710
727
|
.filter(Boolean));
|
|
711
728
|
const staleSourcePaths = getStalePaths(projectRoot, database, { includeState: false }).filter((stalePath) => indexedSourcePaths.has(stalePath));
|
|
@@ -172,7 +172,7 @@ function rollbackTransaction(database) {
|
|
|
172
172
|
// Keep the original indexing failure as the actionable error.
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
export function populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, verificationEvidence, indexMode, sourceScopeHash, sourceIndexEnabled, indexedAt) {
|
|
175
|
+
export function populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, indexedSourceCandidates, verificationEvidence, indexMode, sourceScopeHash, sourceIndexEnabled, indexedAt) {
|
|
176
176
|
const sourceAnchorRiskSignals = createSourceAnchorRiskSignals(sourceAnchors);
|
|
177
177
|
database.run('BEGIN TRANSACTION');
|
|
178
178
|
try {
|
|
@@ -223,6 +223,9 @@ export function populateDatabase(database, capabilities, documents, skills, skil
|
|
|
223
223
|
LOCAL_INDEX_PARSER_VERSION,
|
|
224
224
|
]);
|
|
225
225
|
}
|
|
226
|
+
for (const sourceCandidatePath of indexedSourceCandidates) {
|
|
227
|
+
database.run('INSERT INTO indexed_source_candidates (path) VALUES (?)', [sourceCandidatePath]);
|
|
228
|
+
}
|
|
226
229
|
for (const document of documents) {
|
|
227
230
|
database.run('INSERT INTO documents (path, type, title, locale, revision, content_hash, content_snippet) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
228
231
|
document.path,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { SEARCH_BACKEND_FTS5 } from './constants.js';
|
|
2
2
|
export function createSchema(database, capabilities) {
|
|
3
|
+
database.run('PRAGMA foreign_keys = ON');
|
|
4
|
+
const [foreignKeyResult] = database.exec('PRAGMA foreign_keys');
|
|
5
|
+
if (foreignKeyResult?.values[0]?.[0] !== 1) {
|
|
6
|
+
throw new Error('SQLite foreign-key enforcement is unavailable for the local index.');
|
|
7
|
+
}
|
|
3
8
|
database.run(`
|
|
4
9
|
CREATE TABLE metadata (
|
|
5
10
|
key TEXT PRIMARY KEY,
|
|
@@ -17,6 +22,11 @@ CREATE TABLE indexed_files (
|
|
|
17
22
|
parser_version TEXT NOT NULL
|
|
18
23
|
);
|
|
19
24
|
|
|
25
|
+
CREATE TABLE indexed_source_candidates (
|
|
26
|
+
path TEXT PRIMARY KEY,
|
|
27
|
+
FOREIGN KEY (path) REFERENCES indexed_files(path) ON UPDATE RESTRICT ON DELETE RESTRICT
|
|
28
|
+
);
|
|
29
|
+
|
|
20
30
|
CREATE TABLE documents (
|
|
21
31
|
path TEXT PRIMARY KEY,
|
|
22
32
|
type TEXT NOT NULL,
|
|
@@ -1,11 +1,34 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, statSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { isRecord, readStringArray } from '../command-contract.js';
|
|
4
|
+
import { MUSTFLOW_TEXT_MAX_BYTES, mustflowProjectPath } from '../mustflow-read.js';
|
|
4
5
|
import { readMustflowTomlFile } from '../toml.js';
|
|
6
|
+
import { ensureInsideWithoutSymlinks, readFileInsideWithoutSymlinks } from '../../../core/safe-filesystem.js';
|
|
5
7
|
import { listSourceAnchorFiles } from '../../../core/source-anchors.js';
|
|
6
8
|
import { INDEX_CONFIG_RELATIVE_PATH, LATEST_RUN_STATE_RELATIVE_PATH, MUSTFLOW_RELATIVE_PATH, SOURCE_INDEX_MAX_FILE_BYTES, } from './constants.js';
|
|
7
9
|
import { sha256Bytes, sha256Text } from './hashing.js';
|
|
8
10
|
import { getExistingIndexablePaths } from './workflow-documents.js';
|
|
11
|
+
function indexedProjectPath(projectRoot, relativePath) {
|
|
12
|
+
if (relativePath.length === 0 ||
|
|
13
|
+
relativePath.includes('\0') ||
|
|
14
|
+
relativePath.includes('\\') ||
|
|
15
|
+
path.posix.isAbsolute(relativePath) ||
|
|
16
|
+
path.win32.isAbsolute(relativePath)) {
|
|
17
|
+
throw new Error(`Indexed file path must be a canonical project-relative path: ${relativePath}`);
|
|
18
|
+
}
|
|
19
|
+
const segments = relativePath.split('/');
|
|
20
|
+
if (segments.some((segment) => segment.length === 0 || segment === '.' || segment === '..')) {
|
|
21
|
+
throw new Error(`Indexed file path must be a canonical project-relative path: ${relativePath}`);
|
|
22
|
+
}
|
|
23
|
+
const fullPath = mustflowProjectPath(projectRoot, relativePath);
|
|
24
|
+
ensureInsideWithoutSymlinks(projectRoot, fullPath);
|
|
25
|
+
return fullPath;
|
|
26
|
+
}
|
|
27
|
+
function readIndexedFileBytes(projectRoot, relativePath) {
|
|
28
|
+
return readFileInsideWithoutSymlinks(projectRoot, indexedProjectPath(projectRoot, relativePath), {
|
|
29
|
+
maxBytes: MUSTFLOW_TEXT_MAX_BYTES,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
9
32
|
function readIndexToml(projectRoot) {
|
|
10
33
|
const indexConfigPath = path.join(projectRoot, ...INDEX_CONFIG_RELATIVE_PATH.split('/'));
|
|
11
34
|
if (!existsSync(indexConfigPath)) {
|
|
@@ -69,13 +92,13 @@ export function readIndexedFileRecord(projectRoot, relativePath, sourceScope, co
|
|
|
69
92
|
const metadata = readIndexedFileMetadataRecord(projectRoot, relativePath, sourceScope);
|
|
70
93
|
return {
|
|
71
94
|
...metadata,
|
|
72
|
-
contentHash: contentHash ?? sha256Bytes(
|
|
95
|
+
contentHash: contentHash ?? sha256Bytes(readIndexedFileBytes(projectRoot, relativePath)),
|
|
73
96
|
};
|
|
74
97
|
}
|
|
75
98
|
function hashIndexedFileMetadataRecord(projectRoot, metadata) {
|
|
76
99
|
return {
|
|
77
100
|
...metadata,
|
|
78
|
-
contentHash: sha256Bytes(
|
|
101
|
+
contentHash: sha256Bytes(readIndexedFileBytes(projectRoot, metadata.path)),
|
|
79
102
|
};
|
|
80
103
|
}
|
|
81
104
|
function tryHashIndexedFileMetadataRecord(projectRoot, metadata) {
|
|
@@ -92,8 +115,11 @@ export function hashIndexedFileMetadataRecords(projectRoot, metadataRecords) {
|
|
|
92
115
|
.filter((record) => Boolean(record));
|
|
93
116
|
}
|
|
94
117
|
export function readIndexedFileMetadataRecord(projectRoot, relativePath, sourceScope) {
|
|
95
|
-
const fullPath =
|
|
118
|
+
const fullPath = indexedProjectPath(projectRoot, relativePath);
|
|
96
119
|
const stats = statSync(fullPath);
|
|
120
|
+
if (!stats.isFile()) {
|
|
121
|
+
throw new Error(`Indexed path must be a regular file: ${relativePath}`);
|
|
122
|
+
}
|
|
97
123
|
return {
|
|
98
124
|
path: relativePath,
|
|
99
125
|
sourceScope,
|
|
@@ -117,10 +143,7 @@ export function collectIndexedFileRecords(projectRoot, documents, sourceAnchors,
|
|
|
117
143
|
const sourcePaths = new Set([...sourceAnchorCandidatePaths, ...sourceAnchors.map((anchor) => anchor.path)]);
|
|
118
144
|
for (const anchorPath of [...sourcePaths].sort((left, right) => left.localeCompare(right))) {
|
|
119
145
|
if (!records.has(anchorPath)) {
|
|
120
|
-
|
|
121
|
-
if (record) {
|
|
122
|
-
records.set(anchorPath, record);
|
|
123
|
-
}
|
|
146
|
+
records.set(anchorPath, readIndexedFileRecord(projectRoot, anchorPath, 'source_anchor'));
|
|
124
147
|
}
|
|
125
148
|
}
|
|
126
149
|
if (existsSync(path.join(projectRoot, ...LATEST_RUN_STATE_RELATIVE_PATH.split('/')))) {
|
|
@@ -132,19 +155,25 @@ export function collectIndexedFileRecords(projectRoot, documents, sourceAnchors,
|
|
|
132
155
|
return [...records.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
133
156
|
}
|
|
134
157
|
export function collectSourceAnchorCandidatePaths(projectRoot, sourceConfig) {
|
|
135
|
-
|
|
158
|
+
const paths = listSourceAnchorFiles(projectRoot, {
|
|
136
159
|
...sourceConfig,
|
|
137
160
|
excludeGeneratedOrVendor: true,
|
|
138
161
|
});
|
|
162
|
+
for (const relativePath of paths) {
|
|
163
|
+
indexedProjectPath(projectRoot, relativePath);
|
|
164
|
+
}
|
|
165
|
+
return paths;
|
|
139
166
|
}
|
|
140
167
|
export function collectFastPreflightIndexedFileMetadataRecords(projectRoot, includeSource, sourceConfig) {
|
|
141
168
|
const records = new Map();
|
|
169
|
+
let sourceCandidatePaths = [];
|
|
142
170
|
for (const relativePath of getExistingIndexablePaths(projectRoot)) {
|
|
143
171
|
records.set(relativePath, readIndexedFileMetadataRecord(projectRoot, relativePath, 'workflow'));
|
|
144
172
|
}
|
|
145
173
|
if (includeSource) {
|
|
146
174
|
try {
|
|
147
|
-
|
|
175
|
+
sourceCandidatePaths = collectSourceAnchorCandidatePaths(projectRoot, sourceConfig);
|
|
176
|
+
for (const sourcePath of sourceCandidatePaths) {
|
|
148
177
|
if (!records.has(sourcePath)) {
|
|
149
178
|
records.set(sourcePath, readIndexedFileMetadataRecord(projectRoot, sourcePath, 'source_anchor'));
|
|
150
179
|
}
|
|
@@ -157,5 +186,8 @@ export function collectFastPreflightIndexedFileMetadataRecords(projectRoot, incl
|
|
|
157
186
|
if (existsSync(path.join(projectRoot, ...LATEST_RUN_STATE_RELATIVE_PATH.split('/')))) {
|
|
158
187
|
records.set(LATEST_RUN_STATE_RELATIVE_PATH, readIndexedFileMetadataRecord(projectRoot, LATEST_RUN_STATE_RELATIVE_PATH, 'state'));
|
|
159
188
|
}
|
|
160
|
-
return
|
|
189
|
+
return {
|
|
190
|
+
records: [...records.values()].sort((left, right) => left.path.localeCompare(right.path)),
|
|
191
|
+
sourceCandidatePaths,
|
|
192
|
+
};
|
|
161
193
|
}
|
package/package.json
CHANGED