@spikedpunch/mast 0.1.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/LICENSE +21 -0
- package/MAST_SPEC.md +2926 -0
- package/README.md +842 -0
- package/assets/skill.md +45 -0
- package/dist/ast/extract.d.ts +65 -0
- package/dist/ast/extract.d.ts.map +1 -0
- package/dist/ast/extract.js +147 -0
- package/dist/ast/extract.js.map +1 -0
- package/dist/ast/extractor.d.ts +85 -0
- package/dist/ast/extractor.d.ts.map +1 -0
- package/dist/ast/extractor.js +2 -0
- package/dist/ast/extractor.js.map +1 -0
- package/dist/ast/extractors/markdown.d.ts +40 -0
- package/dist/ast/extractors/markdown.d.ts.map +1 -0
- package/dist/ast/extractors/markdown.js +175 -0
- package/dist/ast/extractors/markdown.js.map +1 -0
- package/dist/ast/extractors/typescript.d.ts +179 -0
- package/dist/ast/extractors/typescript.d.ts.map +1 -0
- package/dist/ast/extractors/typescript.js +1439 -0
- package/dist/ast/extractors/typescript.js.map +1 -0
- package/dist/ast/parser.d.ts +5 -0
- package/dist/ast/parser.d.ts.map +1 -0
- package/dist/ast/parser.js +21 -0
- package/dist/ast/parser.js.map +1 -0
- package/dist/ast/types.d.ts +586 -0
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/ast/types.js +3 -0
- package/dist/ast/types.js.map +1 -0
- package/dist/cli/docs-cmd.d.ts +22 -0
- package/dist/cli/docs-cmd.d.ts.map +1 -0
- package/dist/cli/docs-cmd.js +100 -0
- package/dist/cli/docs-cmd.js.map +1 -0
- package/dist/cli/index-cmd.d.ts +72 -0
- package/dist/cli/index-cmd.d.ts.map +1 -0
- package/dist/cli/index-cmd.js +263 -0
- package/dist/cli/index-cmd.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +14 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +64 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/install-hooks.d.ts +3 -0
- package/dist/cli/install-hooks.d.ts.map +1 -0
- package/dist/cli/install-hooks.js +34 -0
- package/dist/cli/install-hooks.js.map +1 -0
- package/dist/cli/metrics-cmd.d.ts +3 -0
- package/dist/cli/metrics-cmd.d.ts.map +1 -0
- package/dist/cli/metrics-cmd.js +164 -0
- package/dist/cli/metrics-cmd.js.map +1 -0
- package/dist/cli/program.d.ts +14 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +44 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/query.d.ts +46 -0
- package/dist/cli/query.d.ts.map +1 -0
- package/dist/cli/query.js +153 -0
- package/dist/cli/query.js.map +1 -0
- package/dist/cli/search-cmd.d.ts +33 -0
- package/dist/cli/search-cmd.d.ts.map +1 -0
- package/dist/cli/search-cmd.js +114 -0
- package/dist/cli/search-cmd.js.map +1 -0
- package/dist/cli/serve.d.ts +3 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +19 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/skill-install.d.ts +39 -0
- package/dist/cli/skill-install.d.ts.map +1 -0
- package/dist/cli/skill-install.js +57 -0
- package/dist/cli/skill-install.js.map +1 -0
- package/dist/cli/status.d.ts +35 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +117 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/upgrade-cmd.d.ts +48 -0
- package/dist/cli/upgrade-cmd.d.ts.map +1 -0
- package/dist/cli/upgrade-cmd.js +129 -0
- package/dist/cli/upgrade-cmd.js.map +1 -0
- package/dist/cli/version.d.ts +3 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +19 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/env.d.ts +17 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +17 -0
- package/dist/env.js.map +1 -0
- package/dist/graph/checker-resolver.d.ts +156 -0
- package/dist/graph/checker-resolver.d.ts.map +1 -0
- package/dist/graph/checker-resolver.js +458 -0
- package/dist/graph/checker-resolver.js.map +1 -0
- package/dist/graph/db.d.ts +318 -0
- package/dist/graph/db.d.ts.map +1 -0
- package/dist/graph/db.js +266 -0
- package/dist/graph/db.js.map +1 -0
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +5 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/local-type-env.d.ts +45 -0
- package/dist/graph/local-type-env.d.ts.map +1 -0
- package/dist/graph/local-type-env.js +59 -0
- package/dist/graph/local-type-env.js.map +1 -0
- package/dist/graph/path-range.d.ts +40 -0
- package/dist/graph/path-range.d.ts.map +1 -0
- package/dist/graph/path-range.js +42 -0
- package/dist/graph/path-range.js.map +1 -0
- package/dist/graph/populate.d.ts +263 -0
- package/dist/graph/populate.d.ts.map +1 -0
- package/dist/graph/populate.js +984 -0
- package/dist/graph/populate.js.map +1 -0
- package/dist/graph/queries.d.ts +114 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +415 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/sqliteBatch.d.ts +48 -0
- package/dist/graph/sqliteBatch.d.ts.map +1 -0
- package/dist/graph/sqliteBatch.js +93 -0
- package/dist/graph/sqliteBatch.js.map +1 -0
- package/dist/indexer/freshness.d.ts +48 -0
- package/dist/indexer/freshness.d.ts.map +1 -0
- package/dist/indexer/freshness.js +72 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/import-resolver.d.ts +50 -0
- package/dist/indexer/import-resolver.d.ts.map +1 -0
- package/dist/indexer/import-resolver.js +318 -0
- package/dist/indexer/import-resolver.js.map +1 -0
- package/dist/indexer/index.d.ts +249 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +560 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/walker.d.ts +60 -0
- package/dist/indexer/walker.d.ts.map +1 -0
- package/dist/indexer/walker.js +128 -0
- package/dist/indexer/walker.js.map +1 -0
- package/dist/indexer/watcher.d.ts +80 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +151 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/mcp/context.d.ts +23 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +2 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/register-tools.d.ts +21 -0
- package/dist/mcp/register-tools.d.ts.map +1 -0
- package/dist/mcp/register-tools.js +42 -0
- package/dist/mcp/register-tools.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +177 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/staleness.d.ts +72 -0
- package/dist/mcp/staleness.d.ts.map +1 -0
- package/dist/mcp/staleness.js +213 -0
- package/dist/mcp/staleness.js.map +1 -0
- package/dist/mcp/startup.d.ts +40 -0
- package/dist/mcp/startup.d.ts.map +1 -0
- package/dist/mcp/startup.js +114 -0
- package/dist/mcp/startup.js.map +1 -0
- package/dist/mcp/tools/_helpers.d.ts +26 -0
- package/dist/mcp/tools/_helpers.d.ts.map +1 -0
- package/dist/mcp/tools/_helpers.js +54 -0
- package/dist/mcp/tools/_helpers.js.map +1 -0
- package/dist/mcp/tools/callers.d.ts +4 -0
- package/dist/mcp/tools/callers.d.ts.map +1 -0
- package/dist/mcp/tools/callers.js +112 -0
- package/dist/mcp/tools/callers.js.map +1 -0
- package/dist/mcp/tools/dependencies.d.ts +4 -0
- package/dist/mcp/tools/dependencies.d.ts.map +1 -0
- package/dist/mcp/tools/dependencies.js +36 -0
- package/dist/mcp/tools/dependencies.js.map +1 -0
- package/dist/mcp/tools/efficiency.d.ts +4 -0
- package/dist/mcp/tools/efficiency.d.ts.map +1 -0
- package/dist/mcp/tools/efficiency.js +39 -0
- package/dist/mcp/tools/efficiency.js.map +1 -0
- package/dist/mcp/tools/exports.d.ts +4 -0
- package/dist/mcp/tools/exports.d.ts.map +1 -0
- package/dist/mcp/tools/exports.js +56 -0
- package/dist/mcp/tools/exports.js.map +1 -0
- package/dist/mcp/tools/implementors.d.ts +4 -0
- package/dist/mcp/tools/implementors.d.ts.map +1 -0
- package/dist/mcp/tools/implementors.js +47 -0
- package/dist/mcp/tools/implementors.js.map +1 -0
- package/dist/mcp/tools/project-skeleton.d.ts +4 -0
- package/dist/mcp/tools/project-skeleton.d.ts.map +1 -0
- package/dist/mcp/tools/project-skeleton.js +61 -0
- package/dist/mcp/tools/project-skeleton.js.map +1 -0
- package/dist/mcp/tools/reindex.d.ts +14 -0
- package/dist/mcp/tools/reindex.d.ts.map +1 -0
- package/dist/mcp/tools/reindex.js +32 -0
- package/dist/mcp/tools/reindex.js.map +1 -0
- package/dist/mcp/tools/rename-impact.d.ts +4 -0
- package/dist/mcp/tools/rename-impact.d.ts.map +1 -0
- package/dist/mcp/tools/rename-impact.js +124 -0
- package/dist/mcp/tools/rename-impact.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +4 -0
- package/dist/mcp/tools/search.d.ts.map +1 -0
- package/dist/mcp/tools/search.js +69 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/mcp/tools/signature.d.ts +4 -0
- package/dist/mcp/tools/signature.d.ts.map +1 -0
- package/dist/mcp/tools/signature.js +127 -0
- package/dist/mcp/tools/signature.js.map +1 -0
- package/dist/mcp/tools/status.d.ts +4 -0
- package/dist/mcp/tools/status.d.ts.map +1 -0
- package/dist/mcp/tools/status.js +30 -0
- package/dist/mcp/tools/status.js.map +1 -0
- package/dist/search/declex.d.ts +109 -0
- package/dist/search/declex.d.ts.map +1 -0
- package/dist/search/declex.js +242 -0
- package/dist/search/declex.js.map +1 -0
- package/dist/search/fts.d.ts +77 -0
- package/dist/search/fts.d.ts.map +1 -0
- package/dist/search/fts.js +223 -0
- package/dist/search/fts.js.map +1 -0
- package/dist/search/fused.d.ts +110 -0
- package/dist/search/fused.d.ts.map +1 -0
- package/dist/search/fused.js +323 -0
- package/dist/search/fused.js.map +1 -0
- package/dist/search/potential-matches.d.ts +88 -0
- package/dist/search/potential-matches.d.ts.map +1 -0
- package/dist/search/potential-matches.js +83 -0
- package/dist/search/potential-matches.js.map +1 -0
- package/dist/search/scope.d.ts +66 -0
- package/dist/search/scope.d.ts.map +1 -0
- package/dist/search/scope.js +71 -0
- package/dist/search/scope.js.map +1 -0
- package/dist/store/config.d.ts +57 -0
- package/dist/store/config.d.ts.map +1 -0
- package/dist/store/config.js +208 -0
- package/dist/store/config.js.map +1 -0
- package/dist/store/lock.d.ts +101 -0
- package/dist/store/lock.d.ts.map +1 -0
- package/dist/store/lock.js +182 -0
- package/dist/store/lock.js.map +1 -0
- package/dist/store/lockMetrics.d.ts +52 -0
- package/dist/store/lockMetrics.d.ts.map +1 -0
- package/dist/store/lockMetrics.js +29 -0
- package/dist/store/lockMetrics.js.map +1 -0
- package/dist/store/sqliteChunkStore.d.ts +44 -0
- package/dist/store/sqliteChunkStore.d.ts.map +1 -0
- package/dist/store/sqliteChunkStore.js +135 -0
- package/dist/store/sqliteChunkStore.js.map +1 -0
- package/dist/telemetry/lockMetricsSummary.d.ts +42 -0
- package/dist/telemetry/lockMetricsSummary.d.ts.map +1 -0
- package/dist/telemetry/lockMetricsSummary.js +139 -0
- package/dist/telemetry/lockMetricsSummary.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +140 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +312 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/telemetry/tokenizer.d.ts +125 -0
- package/dist/telemetry/tokenizer.d.ts.map +1 -0
- package/dist/telemetry/tokenizer.js +174 -0
- package/dist/telemetry/tokenizer.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scope a `mast_search` call is restricted to — `file_pattern` and
|
|
3
|
+
* `language` — compiled once and applied identically by every ranker.
|
|
4
|
+
*
|
|
5
|
+
* This module exists because the two filters used to be implemented in exactly
|
|
6
|
+
* one of the two rankers `fusedSearch` unions together: `searchFts` pushed
|
|
7
|
+
* them into SQL, `searchRankerD` applied none, and RRF then merged D's
|
|
8
|
+
* unscoped declaration hits into a scoped result set. That is this package's
|
|
9
|
+
* severity-zero shape (`docs/defects/LEDGER.md`) reached through the filter a
|
|
10
|
+
* caller supplied precisely to be sure of the answer's boundaries — so scope
|
|
11
|
+
* is defined here, once, rather than at each ranker's own SQL.
|
|
12
|
+
*
|
|
13
|
+
* Glob semantics come from {@link globToRegex}, the walker's own translation —
|
|
14
|
+
* the same function `mast_project_skeleton` already uses for its `file_pattern`
|
|
15
|
+
* and the same one that decides which files get indexed at all. A pattern
|
|
16
|
+
* therefore selects the same files at query time as it does at index time,
|
|
17
|
+
* which was not true of the SQL `LIKE` translation this replaces: under `LIKE`,
|
|
18
|
+
* `*` crossed `/`, a literal `_` acted as a single-character wildcard, and
|
|
19
|
+
* matching was case-insensitive against a case-sensitive index (the same three
|
|
20
|
+
* faults recorded in `docs/defects/D004-like-prefix-match.md`, whose four-site
|
|
21
|
+
* sweep did not reach here).
|
|
22
|
+
*/
|
|
23
|
+
import type { Db } from '../graph/db.js';
|
|
24
|
+
/**
|
|
25
|
+
* The SQL-level filters `SearchInput` carries, in this module's own naming.
|
|
26
|
+
*
|
|
27
|
+
* `undefined` is spelled out alongside `null` because `exactOptionalPropertyTypes`
|
|
28
|
+
* is on: callers forward `SearchInput.file_pattern` (itself `string | null |
|
|
29
|
+
* undefined`) straight through, and an optional-only property would reject the
|
|
30
|
+
* explicit `undefined` that forwarding produces.
|
|
31
|
+
*/
|
|
32
|
+
export interface SearchScope {
|
|
33
|
+
readonly filePattern?: string | null | undefined;
|
|
34
|
+
readonly language?: string | null | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Predicate over one indexed row's `(file_path, language)` pair. */
|
|
37
|
+
export type ScopeMatcher = (filePath: string, language: string) => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Compile a scope into a single predicate, or `null` when the scope restricts
|
|
40
|
+
* nothing — callers branch on `null` to skip filtering entirely rather than
|
|
41
|
+
* running an always-true predicate over every candidate.
|
|
42
|
+
*
|
|
43
|
+
* The `RegExp` is built once here, not per row: `globToRegex` compiles a
|
|
44
|
+
* pattern, and doing that inside a filter callback would recompile it for
|
|
45
|
+
* every chunk in the candidate pool.
|
|
46
|
+
*/
|
|
47
|
+
export declare function compileScopeMatcher(scope: SearchScope): ScopeMatcher | null;
|
|
48
|
+
/**
|
|
49
|
+
* The indexed paths inside `scope`, or `null` when the scope restricts nothing.
|
|
50
|
+
*
|
|
51
|
+
* Used by the callers that can only filter by path — FTS5 (whose `MATCH` query
|
|
52
|
+
* applies its `LIMIT` in SQL, so the restriction has to be an `IN` list rather
|
|
53
|
+
* than a post-filter) and the zero-result assist (two of whose three passes
|
|
54
|
+
* return only a symbol and a file). An empty array is a real answer, not an
|
|
55
|
+
* absent one: the scope matched no indexed file, so the search has nothing to
|
|
56
|
+
* return.
|
|
57
|
+
*
|
|
58
|
+
* `files` is scanned in full rather than filtered in SQL because the glob is
|
|
59
|
+
* not expressible as a SQL predicate without reintroducing `LIKE`. The table
|
|
60
|
+
* holds one row per indexed file, and the query it replaced —
|
|
61
|
+
* `path LIKE ?` against a `BINARY` index with `case_sensitive_like` OFF —
|
|
62
|
+
* could not use the index either (`graph/path-range.ts`), so this is not a
|
|
63
|
+
* plan regression.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveScopedPaths(db: Db, scope: SearchScope): Promise<string[] | null>;
|
|
66
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/search/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC/C;AAED,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAE3E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,IAAI,CAW3E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAM7F"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scope a `mast_search` call is restricted to — `file_pattern` and
|
|
3
|
+
* `language` — compiled once and applied identically by every ranker.
|
|
4
|
+
*
|
|
5
|
+
* This module exists because the two filters used to be implemented in exactly
|
|
6
|
+
* one of the two rankers `fusedSearch` unions together: `searchFts` pushed
|
|
7
|
+
* them into SQL, `searchRankerD` applied none, and RRF then merged D's
|
|
8
|
+
* unscoped declaration hits into a scoped result set. That is this package's
|
|
9
|
+
* severity-zero shape (`docs/defects/LEDGER.md`) reached through the filter a
|
|
10
|
+
* caller supplied precisely to be sure of the answer's boundaries — so scope
|
|
11
|
+
* is defined here, once, rather than at each ranker's own SQL.
|
|
12
|
+
*
|
|
13
|
+
* Glob semantics come from {@link globToRegex}, the walker's own translation —
|
|
14
|
+
* the same function `mast_project_skeleton` already uses for its `file_pattern`
|
|
15
|
+
* and the same one that decides which files get indexed at all. A pattern
|
|
16
|
+
* therefore selects the same files at query time as it does at index time,
|
|
17
|
+
* which was not true of the SQL `LIKE` translation this replaces: under `LIKE`,
|
|
18
|
+
* `*` crossed `/`, a literal `_` acted as a single-character wildcard, and
|
|
19
|
+
* matching was case-insensitive against a case-sensitive index (the same three
|
|
20
|
+
* faults recorded in `docs/defects/D004-like-prefix-match.md`, whose four-site
|
|
21
|
+
* sweep did not reach here).
|
|
22
|
+
*/
|
|
23
|
+
import { globToRegex } from '../indexer/walker.js';
|
|
24
|
+
/**
|
|
25
|
+
* Compile a scope into a single predicate, or `null` when the scope restricts
|
|
26
|
+
* nothing — callers branch on `null` to skip filtering entirely rather than
|
|
27
|
+
* running an always-true predicate over every candidate.
|
|
28
|
+
*
|
|
29
|
+
* The `RegExp` is built once here, not per row: `globToRegex` compiles a
|
|
30
|
+
* pattern, and doing that inside a filter callback would recompile it for
|
|
31
|
+
* every chunk in the candidate pool.
|
|
32
|
+
*/
|
|
33
|
+
export function compileScopeMatcher(scope) {
|
|
34
|
+
const pattern = scope.filePattern ?? null;
|
|
35
|
+
const language = scope.language ?? null;
|
|
36
|
+
if (pattern === null && language === null)
|
|
37
|
+
return null;
|
|
38
|
+
const rx = pattern === null ? null : globToRegex(pattern);
|
|
39
|
+
return (filePath, rowLanguage) => {
|
|
40
|
+
if (rx !== null && !rx.test(filePath))
|
|
41
|
+
return false;
|
|
42
|
+
if (language !== null && rowLanguage !== language)
|
|
43
|
+
return false;
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The indexed paths inside `scope`, or `null` when the scope restricts nothing.
|
|
49
|
+
*
|
|
50
|
+
* Used by the callers that can only filter by path — FTS5 (whose `MATCH` query
|
|
51
|
+
* applies its `LIMIT` in SQL, so the restriction has to be an `IN` list rather
|
|
52
|
+
* than a post-filter) and the zero-result assist (two of whose three passes
|
|
53
|
+
* return only a symbol and a file). An empty array is a real answer, not an
|
|
54
|
+
* absent one: the scope matched no indexed file, so the search has nothing to
|
|
55
|
+
* return.
|
|
56
|
+
*
|
|
57
|
+
* `files` is scanned in full rather than filtered in SQL because the glob is
|
|
58
|
+
* not expressible as a SQL predicate without reintroducing `LIKE`. The table
|
|
59
|
+
* holds one row per indexed file, and the query it replaced —
|
|
60
|
+
* `path LIKE ?` against a `BINARY` index with `case_sensitive_like` OFF —
|
|
61
|
+
* could not use the index either (`graph/path-range.ts`), so this is not a
|
|
62
|
+
* plan regression.
|
|
63
|
+
*/
|
|
64
|
+
export async function resolveScopedPaths(db, scope) {
|
|
65
|
+
const matcher = compileScopeMatcher(scope);
|
|
66
|
+
if (matcher === null)
|
|
67
|
+
return null;
|
|
68
|
+
const rows = await db.selectFrom('files').select(['path', 'language']).execute();
|
|
69
|
+
return rows.filter((r) => matcher(r.path, r.language)).map((r) => r.path);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/search/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAmBnD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC;IACxC,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,EAAE,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC1D,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE;QAC/B,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QACpD,IAAI,QAAQ,KAAK,IAAI,IAAI,WAAW,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAM,EAAE,KAAkB;IACjE,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAElC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACjF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { MastConfig } from '../ast/types.js';
|
|
2
|
+
export declare const CURRENT_SCHEMA_VERSION = "1.3.0";
|
|
3
|
+
export interface ResolvedConfig extends MastConfig {
|
|
4
|
+
/** Absolute path to the state directory. */
|
|
5
|
+
readonly resolved_state_dir: string;
|
|
6
|
+
/** Absolute path to the project root. */
|
|
7
|
+
readonly resolved_project_root: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolveConfigOptions {
|
|
10
|
+
/** Absolute or relative path to project root (default: cwd). */
|
|
11
|
+
projectRoot?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Overrides state_dir from config file.
|
|
14
|
+
* Falls back to MAST_STATE_DIR env var, then mast.config.json, then default.
|
|
15
|
+
*/
|
|
16
|
+
stateDirOverride?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Explicit override for `file_extensions` (F9, `mast init --extensions`).
|
|
19
|
+
* Highest priority in the config merge — wins over `mast.config.json` and
|
|
20
|
+
* any persisted `<state_dir>/config.json`.
|
|
21
|
+
*/
|
|
22
|
+
extensions?: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* Explicit override for `exclude_patterns` (F9, `mast init --exclude`).
|
|
25
|
+
* Same priority as `extensions`.
|
|
26
|
+
*/
|
|
27
|
+
excludePatterns?: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the active MAST configuration.
|
|
31
|
+
*
|
|
32
|
+
* State-directory priority order (highest to lowest) — unchanged by F9,
|
|
33
|
+
* because the state dir must be resolved BEFORE the persisted state config
|
|
34
|
+
* can be loaded from inside it:
|
|
35
|
+
* 1. `stateDirOverride` (CLI `--state-dir` flag)
|
|
36
|
+
* 2. `MAST_STATE_DIR` environment variable
|
|
37
|
+
* 3. `state_dir` key in `mast.config.json`
|
|
38
|
+
* 4. Built-in default (`.mast`)
|
|
39
|
+
*
|
|
40
|
+
* Priority order for every other config key (highest to lowest):
|
|
41
|
+
* 1. Explicit overrides passed to this function (`extensions`/`excludePatterns`
|
|
42
|
+
* — CLI `mast init --extensions`/`--exclude`, F9)
|
|
43
|
+
* 2. `mast.config.json` in `projectRoot`
|
|
44
|
+
* 3. Persisted `<resolved_state_dir>/config.json` (F9 — previously write-only
|
|
45
|
+
* dead state; see `pickStateConfigCustomization` for why only the
|
|
46
|
+
* customisation keys are taken from it, never the path keys)
|
|
47
|
+
* 4. Built-in defaults
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveConfig(options?: ResolveConfigOptions): ResolvedConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Load a previously-written config.json from the state directory.
|
|
52
|
+
* Returns null if the file does not exist.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loadStateConfig(stateDir: string): Partial<MastConfig> | null;
|
|
55
|
+
/** Write the resolved config to `<stateDir>/config.json`. */
|
|
56
|
+
export declare function writeStateConfig(stateDir: string, config: ResolvedConfig): void;
|
|
57
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/store/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA4BlD,eAAO,MAAM,sBAAsB,UAAU,CAAC;AA+B9C,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,4CAA4C;IAC5C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,yCAAyC;IACzC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAgFD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,cAAc,CA0ChF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAI5E;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAG/E"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { ConfigEnvSchema } from '../env.js';
|
|
4
|
+
// 1.3.0 (F5, Stage 3): identifier_fts rows now carry QUALIFIED compound
|
|
5
|
+
// strings ("Class.method") appended after the bare-identifier bag — see
|
|
6
|
+
// ast/extractors/typescript.ts's `appendQualifiedCompounds` and MAST_SPEC.md
|
|
7
|
+
// §6.3. This is a CONTENT-format change to an already-existing column, not a
|
|
8
|
+
// new column, so a schema-diff check would not catch it: an old index's
|
|
9
|
+
// identifier_fts rows lack the compounds entirely, and `searchIdentifiers`'
|
|
10
|
+
// phrase query for a qualified method name would silently keep returning []
|
|
11
|
+
// against that stale state — exactly the class of wrong-version hazard §7.4's
|
|
12
|
+
// migration guard exists for (a confidently-empty answer, not an error).
|
|
13
|
+
// Bumping forces the §7.4 Step 2 wipe-and-full-reindex path, verified by
|
|
14
|
+
// mcp/__tests__/startup.test.ts's schema-mismatch coverage; the Docker seed
|
|
15
|
+
// (§7.4) picks up the new format automatically on its next build since it
|
|
16
|
+
// reindexes from a version-tagged image rather than reusing a mounted volume.
|
|
17
|
+
//
|
|
18
|
+
// 1.2.0: chunks moved from chunks.lance to a `chunks` table inside graph.db
|
|
19
|
+
// (M1, eval/GITNEXUS_COMPARISON.md §15.1) — the on-disk shape changed (a new
|
|
20
|
+
// SQLite table plus the retired Lance chunk table) so old state must not be
|
|
21
|
+
// read by code expecting the new layout. Per the never-shipped constraint
|
|
22
|
+
// (no customers, no migration data to preserve) the §7.4 Step 2 wipe-and-full-
|
|
23
|
+
// reindex path is the correct and sufficient handling, verified by
|
|
24
|
+
// mcp/__tests__/startup.test.ts's schema-mismatch coverage.
|
|
25
|
+
//
|
|
26
|
+
// 1.1.0: vectors.lance gained a `content_hash` column so re-embedding is keyed
|
|
27
|
+
// on chunk content, not just chunk_id (H1). A bump forces the §7.4 Step 2 wipe
|
|
28
|
+
// so an old vectors table (without the column) is rebuilt rather than read.
|
|
29
|
+
export const CURRENT_SCHEMA_VERSION = '1.3.0';
|
|
30
|
+
const DEFAULTS = {
|
|
31
|
+
state_dir: '.mast',
|
|
32
|
+
project_root: '.',
|
|
33
|
+
// `.md` rides the existing exclude_patterns for vendored noise — dependency
|
|
34
|
+
// READMEs live under node_modules/** which is already excluded; no
|
|
35
|
+
// markdown-specific exclusion logic is applied.
|
|
36
|
+
file_extensions: ['.ts', '.tsx', '.js', '.jsx', '.md'],
|
|
37
|
+
// Build output, dependencies, and tests. `.mast/**` excludes this tool's own
|
|
38
|
+
// state directory, which is the one path guaranteed to exist in every project
|
|
39
|
+
// that runs it. (`.kluster/**` sat here until 2026-08-19 — a leftover from the
|
|
40
|
+
// repository mast was extracted from, meaningless to any other consumer.)
|
|
41
|
+
exclude_patterns: [
|
|
42
|
+
'**/node_modules/**',
|
|
43
|
+
'**/dist/**',
|
|
44
|
+
'**/build/**',
|
|
45
|
+
'**/coverage/**',
|
|
46
|
+
'**/.next/**',
|
|
47
|
+
'**/.turbo/**',
|
|
48
|
+
'.mast/**',
|
|
49
|
+
'**/*.test.ts',
|
|
50
|
+
'**/*.spec.ts',
|
|
51
|
+
],
|
|
52
|
+
rrf_k: 60,
|
|
53
|
+
declaration_exact_ranker: true,
|
|
54
|
+
chunk_split_threshold: 100,
|
|
55
|
+
context_lines: 3,
|
|
56
|
+
markdown_heading_depth: 2,
|
|
57
|
+
};
|
|
58
|
+
// F9 (Stage 3.5, eval/GITNEXUS_COMPARISON.md M3): the keys a caller may
|
|
59
|
+
// customise, as opposed to the path keys (`state_dir`, `project_root`,
|
|
60
|
+
// `resolved_state_dir`, `resolved_project_root`) that describe WHERE this
|
|
61
|
+
// resolution is running, not WHAT it configures. Used by
|
|
62
|
+
// `pickStateConfigCustomization` below to build an explicit picked-keys
|
|
63
|
+
// merge — never a spread-minus-deletes — so a path key can never leak in
|
|
64
|
+
// through this list by omission.
|
|
65
|
+
const CUSTOMIZATION_KEYS = [
|
|
66
|
+
'file_extensions',
|
|
67
|
+
'exclude_patterns',
|
|
68
|
+
'rrf_k',
|
|
69
|
+
'declaration_exact_ranker',
|
|
70
|
+
'chunk_split_threshold',
|
|
71
|
+
'context_lines',
|
|
72
|
+
'markdown_heading_depth',
|
|
73
|
+
];
|
|
74
|
+
function isStringArray(value) {
|
|
75
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Picks ONLY the customisation keys out of a persisted `<state_dir>/config.json`
|
|
79
|
+
* — never `state_dir`/`project_root`/`resolved_state_dir`/`resolved_project_root`.
|
|
80
|
+
*
|
|
81
|
+
* WHY this matters (path-portability hazard): `writeStateConfig` persists a
|
|
82
|
+
* full `ResolvedConfig`, including ABSOLUTE paths resolved in a PREVIOUS
|
|
83
|
+
* process. The SDD pipeline mounts the same workspace volume at different
|
|
84
|
+
* container paths across runs, so an absolute path loaded back from a
|
|
85
|
+
* previous container would silently resolve `resolved_state_dir` /
|
|
86
|
+
* `resolved_project_root` to a location that doesn't exist (or worse, exists
|
|
87
|
+
* but belongs to an unrelated project) in THIS container. Every path field
|
|
88
|
+
* in the returned `ResolvedConfig` must always come from the CURRENT
|
|
89
|
+
* resolution — never from disk.
|
|
90
|
+
*
|
|
91
|
+
* Unlike `mast.config.json` (see the precedent comment on `resolveConfig`
|
|
92
|
+
* below, which trusts a plain `JSON.parse` cast because that file is
|
|
93
|
+
* developer-authored and colocated with source control), a state
|
|
94
|
+
* config.json is machine-written but crosses a trust boundary of its own —
|
|
95
|
+
* an arbitrary file on disk that could be stale, foreign, or hand-edited.
|
|
96
|
+
* Each picked key is minimally validated by shape (string array / number /
|
|
97
|
+
* boolean) rather than trusted on cast; a key that fails validation is
|
|
98
|
+
* dropped so DEFAULTS (or a lower-priority layer) fills it in instead of a
|
|
99
|
+
* malformed value silently propagating.
|
|
100
|
+
*/
|
|
101
|
+
function pickStateConfigCustomization(source) {
|
|
102
|
+
if (source === null)
|
|
103
|
+
return {};
|
|
104
|
+
// `MastConfig`'s fields are `readonly` (immutable once constructed), but
|
|
105
|
+
// this function BUILDS one field at a time — a mutable local view keeps
|
|
106
|
+
// the assignments below straightforward. The returned value still exposes
|
|
107
|
+
// the standard `Partial<MastConfig>` (readonly) shape to callers.
|
|
108
|
+
const picked = {};
|
|
109
|
+
if (isStringArray(source.file_extensions))
|
|
110
|
+
picked.file_extensions = source.file_extensions;
|
|
111
|
+
if (isStringArray(source.exclude_patterns))
|
|
112
|
+
picked.exclude_patterns = source.exclude_patterns;
|
|
113
|
+
if (typeof source.rrf_k === 'number')
|
|
114
|
+
picked.rrf_k = source.rrf_k;
|
|
115
|
+
if (typeof source.declaration_exact_ranker === 'boolean') {
|
|
116
|
+
picked.declaration_exact_ranker = source.declaration_exact_ranker;
|
|
117
|
+
}
|
|
118
|
+
if (typeof source.chunk_split_threshold === 'number')
|
|
119
|
+
picked.chunk_split_threshold = source.chunk_split_threshold;
|
|
120
|
+
if (typeof source.context_lines === 'number')
|
|
121
|
+
picked.context_lines = source.context_lines;
|
|
122
|
+
if (typeof source.markdown_heading_depth === 'number') {
|
|
123
|
+
picked.markdown_heading_depth = source.markdown_heading_depth;
|
|
124
|
+
}
|
|
125
|
+
// Safety net for the path-portability rule above: fails loudly if a future
|
|
126
|
+
// edit to this function ever assigns a key outside CUSTOMIZATION_KEYS (e.g.
|
|
127
|
+
// a path key re-added by accident) instead of silently reintroducing the
|
|
128
|
+
// shared-volume hazard this function exists to prevent.
|
|
129
|
+
for (const key of Object.keys(picked)) {
|
|
130
|
+
if (!CUSTOMIZATION_KEYS.includes(key)) {
|
|
131
|
+
throw new Error(`pickStateConfigCustomization: unexpected key "${key}" outside CUSTOMIZATION_KEYS`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return picked;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve the active MAST configuration.
|
|
138
|
+
*
|
|
139
|
+
* State-directory priority order (highest to lowest) — unchanged by F9,
|
|
140
|
+
* because the state dir must be resolved BEFORE the persisted state config
|
|
141
|
+
* can be loaded from inside it:
|
|
142
|
+
* 1. `stateDirOverride` (CLI `--state-dir` flag)
|
|
143
|
+
* 2. `MAST_STATE_DIR` environment variable
|
|
144
|
+
* 3. `state_dir` key in `mast.config.json`
|
|
145
|
+
* 4. Built-in default (`.mast`)
|
|
146
|
+
*
|
|
147
|
+
* Priority order for every other config key (highest to lowest):
|
|
148
|
+
* 1. Explicit overrides passed to this function (`extensions`/`excludePatterns`
|
|
149
|
+
* — CLI `mast init --extensions`/`--exclude`, F9)
|
|
150
|
+
* 2. `mast.config.json` in `projectRoot`
|
|
151
|
+
* 3. Persisted `<resolved_state_dir>/config.json` (F9 — previously write-only
|
|
152
|
+
* dead state; see `pickStateConfigCustomization` for why only the
|
|
153
|
+
* customisation keys are taken from it, never the path keys)
|
|
154
|
+
* 4. Built-in defaults
|
|
155
|
+
*/
|
|
156
|
+
export function resolveConfig(options = {}) {
|
|
157
|
+
const resolvedProjectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
158
|
+
const configFile = join(resolvedProjectRoot, 'mast.config.json');
|
|
159
|
+
// Never-shipped ⇒ no back-compat (IMPLEMENTATION_PLAN.md Stage 7 decision
|
|
160
|
+
// 2): a `mast.config.json` written before Stage 7.2 may still carry the
|
|
161
|
+
// removed embedding-model / Transformers.js cache-dir config keys. Nothing
|
|
162
|
+
// here validates the parsed shape (plain JSON.parse + cast, no zod) — this
|
|
163
|
+
// file is developer-authored and colocated with source control, unlike the
|
|
164
|
+
// machine-written state config.json below which DOES get minimal shape
|
|
165
|
+
// validation (see `pickStateConfigCustomization`). Those extra keys just
|
|
166
|
+
// ride along on `fileConfig`/`merged` unread — the spread below never looks
|
|
167
|
+
// them up, and `writeStateConfig` re-persisting `merged` via
|
|
168
|
+
// `JSON.stringify` carries them along harmlessly too.
|
|
169
|
+
let fileConfig = {};
|
|
170
|
+
if (existsSync(configFile)) {
|
|
171
|
+
const raw = readFileSync(configFile, 'utf-8');
|
|
172
|
+
fileConfig = JSON.parse(raw);
|
|
173
|
+
}
|
|
174
|
+
// State dir chain is resolved first and independently of the customisation
|
|
175
|
+
// merge below — see the priority-order doc comment above.
|
|
176
|
+
const { MAST_STATE_DIR: envStateDir } = ConfigEnvSchema.parse(process.env);
|
|
177
|
+
const stateDir = options.stateDirOverride ?? envStateDir ?? fileConfig.state_dir ?? DEFAULTS.state_dir;
|
|
178
|
+
const resolvedStateDir = resolve(resolvedProjectRoot, stateDir);
|
|
179
|
+
const stateConfig = pickStateConfigCustomization(loadStateConfig(resolvedStateDir));
|
|
180
|
+
const cliOverrides = {
|
|
181
|
+
...(options.extensions !== undefined ? { file_extensions: options.extensions } : {}),
|
|
182
|
+
...(options.excludePatterns !== undefined ? { exclude_patterns: options.excludePatterns } : {}),
|
|
183
|
+
};
|
|
184
|
+
const merged = { ...DEFAULTS, ...stateConfig, ...fileConfig, ...cliOverrides };
|
|
185
|
+
return {
|
|
186
|
+
...merged,
|
|
187
|
+
state_dir: stateDir,
|
|
188
|
+
project_root: resolvedProjectRoot,
|
|
189
|
+
resolved_state_dir: resolvedStateDir,
|
|
190
|
+
resolved_project_root: resolvedProjectRoot,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Load a previously-written config.json from the state directory.
|
|
195
|
+
* Returns null if the file does not exist.
|
|
196
|
+
*/
|
|
197
|
+
export function loadStateConfig(stateDir) {
|
|
198
|
+
const configPath = join(stateDir, 'config.json');
|
|
199
|
+
if (!existsSync(configPath))
|
|
200
|
+
return null;
|
|
201
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
202
|
+
}
|
|
203
|
+
/** Write the resolved config to `<stateDir>/config.json`. */
|
|
204
|
+
export function writeStateConfig(stateDir, config) {
|
|
205
|
+
mkdirSync(stateDir, { recursive: true });
|
|
206
|
+
writeFileSync(join(stateDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/store/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type LockMetricsSink } from './lockMetrics.js';
|
|
2
|
+
export type LockType = 'structure';
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when the lock was taken away while this process still believed it
|
|
5
|
+
* held it — another process judged it stale and stole it, or the lock
|
|
6
|
+
* directory was removed underneath us.
|
|
7
|
+
*
|
|
8
|
+
* It is thrown at RELEASE, not when the compromise is detected, because
|
|
9
|
+
* proper-lockfile detects it inside an `fs.stat` callback on a timer: there is
|
|
10
|
+
* no caller on the stack to receive it there, which is exactly why the
|
|
11
|
+
* library's default handler (`onCompromised: (err) => { throw err; }`,
|
|
12
|
+
* `lib/lockfile.js`) reached the process as an uncaught exception and killed
|
|
13
|
+
* it — measured: `ECOMPROMISED ENOENT`, exit code 7, with no
|
|
14
|
+
* `uncaughtException` handler anywhere in `src`.
|
|
15
|
+
*
|
|
16
|
+
* **What this does and does not fix.** It converts a process kill into a
|
|
17
|
+
* failed operation the caller can retry, and it stops a run that was not
|
|
18
|
+
* exclusive from reporting success. It does NOT stop the work already in
|
|
19
|
+
* flight: by the time the compromise is detected, `fn` is mid-execution and
|
|
20
|
+
* nothing here can interrupt it. Doing that needs an `AbortSignal` threaded
|
|
21
|
+
* through `withLock` and honoured by every write phase, which is a design
|
|
22
|
+
* change, not a patch. The exposure is bounded: SQLite writes go through
|
|
23
|
+
* `populateFile`'s `BEGIN IMMEDIATE` transaction and stay serialised whatever
|
|
24
|
+
* this advisory lock believes, so what is actually at risk is the plain-JSON
|
|
25
|
+
* `file_manifest.json` / `index.json` writes this lock exists to coordinate —
|
|
26
|
+
* and a run that fails here is re-run, which rewrites both.
|
|
27
|
+
*/
|
|
28
|
+
export declare class LockCompromisedError extends Error {
|
|
29
|
+
/** The underlying proper-lockfile error (`ECOMPROMISED`). */
|
|
30
|
+
readonly reason: unknown;
|
|
31
|
+
constructor(type: LockType, reason: unknown);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Ensure the marker files required by proper-lockfile exist in `stateDir`.
|
|
35
|
+
* Must be called during `mast init` before any lock can be acquired.
|
|
36
|
+
*/
|
|
37
|
+
export declare function initLockMarkers(stateDir: string): void;
|
|
38
|
+
export interface AcquireOptions {
|
|
39
|
+
/** Number of additional retries after the first attempt (default: 5). */
|
|
40
|
+
maxRetries?: number;
|
|
41
|
+
/** Milliseconds between retries (default: 1000). */
|
|
42
|
+
retryIntervalMs?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Label identifying the calling workflow in lock metrics (e.g.
|
|
45
|
+
* `'index-run'`, `'jit-staleness'`) — the signal that distinguishes a
|
|
46
|
+
* whole-run hold from a per-file JIT re-parse. Defaults to `'unknown'`.
|
|
47
|
+
*/
|
|
48
|
+
caller?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Injection seam (§4.4) for lock metrics — tests pass an in-memory fake so
|
|
51
|
+
* they can assert on recorded events without touching the filesystem.
|
|
52
|
+
* Production call sites omit this and get the default JSONL sink for
|
|
53
|
+
* `stateDir` (see {@link createFileLockMetricsSink}).
|
|
54
|
+
*/
|
|
55
|
+
sink?: LockMetricsSink;
|
|
56
|
+
/**
|
|
57
|
+
* Override the staleness window (default {@link STALE_MS}). proper-lockfile
|
|
58
|
+
* derives its refresh interval as `stale / 2`, so this is the only way to
|
|
59
|
+
* make a compromise observable inside a test's patience — production call
|
|
60
|
+
* sites never set it.
|
|
61
|
+
*/
|
|
62
|
+
staleMs?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Acquire an advisory write lock for the given lock type.
|
|
66
|
+
*
|
|
67
|
+
* Returns a release function. Always `await release()` in a finally block.
|
|
68
|
+
* The returned function is wrapped to record hold-duration lock metrics on
|
|
69
|
+
* release — this covers direct callers as well as callers that go through
|
|
70
|
+
* {@link withLock}.
|
|
71
|
+
*
|
|
72
|
+
* Per spec §7.6:
|
|
73
|
+
* - CLI commands: maxRetries=1, retryIntervalMs=2000 (2s total)
|
|
74
|
+
* - mast_reindex / mast serve startup: maxRetries=5, retryIntervalMs=1000
|
|
75
|
+
*
|
|
76
|
+
* **F11 (`IMPLEMENTATION_PLAN.md` "Replace fail-fast advisory locking")
|
|
77
|
+
* narrowed this lock's role**: the JIT re-parse path (`mcp/staleness.ts`'s
|
|
78
|
+
* `checkAndRefreshIfStale`) no longer calls `acquireLock` at all — it relies
|
|
79
|
+
* on `populateFile`'s own `BEGIN IMMEDIATE` transaction
|
|
80
|
+
* (`graph/populate.ts`) plus a dedicated short `busy_timeout` instead. This
|
|
81
|
+
* lock is now acquired only by coarse writers: `mast index` / the startup
|
|
82
|
+
* reindex, `mast_reindex`, and the manifest/`index.json` phase (plain-JSON
|
|
83
|
+
* `writeFileSync`, which SQLite can never coordinate) — see `populateFile`'s
|
|
84
|
+
* doc comment for the full design rationale.
|
|
85
|
+
*
|
|
86
|
+
* @throws if the lock cannot be acquired after all retries.
|
|
87
|
+
*/
|
|
88
|
+
export declare function acquireLock(stateDir: string, type: LockType, options?: AcquireOptions): Promise<() => Promise<void>>;
|
|
89
|
+
/**
|
|
90
|
+
* Run `fn` while holding the given lock.
|
|
91
|
+
*
|
|
92
|
+
* The lock's directory is tracked in a process-wide registry that a single
|
|
93
|
+
* SIGTERM/SIGINT handler cleans up on shutdown, so an interrupted process does
|
|
94
|
+
* not leave `<type>.lock` behind for every lock it held.
|
|
95
|
+
*
|
|
96
|
+
* @throws {LockCompromisedError} if the lock was taken away while `fn` ran.
|
|
97
|
+
* `fn` still completed — see that error's doc for what is and is not
|
|
98
|
+
* guaranteed. If `fn` itself threw, that error is thrown instead.
|
|
99
|
+
*/
|
|
100
|
+
export declare function withLock<T>(stateDir: string, type: LockType, options: AcquireOptions, fn: () => Promise<T>): Promise<T>;
|
|
101
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/store/lock.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMnF,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC;AAInC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBAEb,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO;CAQ5C;AAOD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAKtD;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAiD9B;AAyBD;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CA4BZ"}
|