@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.
Files changed (257) hide show
  1. package/LICENSE +21 -0
  2. package/MAST_SPEC.md +2926 -0
  3. package/README.md +842 -0
  4. package/assets/skill.md +45 -0
  5. package/dist/ast/extract.d.ts +65 -0
  6. package/dist/ast/extract.d.ts.map +1 -0
  7. package/dist/ast/extract.js +147 -0
  8. package/dist/ast/extract.js.map +1 -0
  9. package/dist/ast/extractor.d.ts +85 -0
  10. package/dist/ast/extractor.d.ts.map +1 -0
  11. package/dist/ast/extractor.js +2 -0
  12. package/dist/ast/extractor.js.map +1 -0
  13. package/dist/ast/extractors/markdown.d.ts +40 -0
  14. package/dist/ast/extractors/markdown.d.ts.map +1 -0
  15. package/dist/ast/extractors/markdown.js +175 -0
  16. package/dist/ast/extractors/markdown.js.map +1 -0
  17. package/dist/ast/extractors/typescript.d.ts +179 -0
  18. package/dist/ast/extractors/typescript.d.ts.map +1 -0
  19. package/dist/ast/extractors/typescript.js +1439 -0
  20. package/dist/ast/extractors/typescript.js.map +1 -0
  21. package/dist/ast/parser.d.ts +5 -0
  22. package/dist/ast/parser.d.ts.map +1 -0
  23. package/dist/ast/parser.js +21 -0
  24. package/dist/ast/parser.js.map +1 -0
  25. package/dist/ast/types.d.ts +586 -0
  26. package/dist/ast/types.d.ts.map +1 -0
  27. package/dist/ast/types.js +3 -0
  28. package/dist/ast/types.js.map +1 -0
  29. package/dist/cli/docs-cmd.d.ts +22 -0
  30. package/dist/cli/docs-cmd.d.ts.map +1 -0
  31. package/dist/cli/docs-cmd.js +100 -0
  32. package/dist/cli/docs-cmd.js.map +1 -0
  33. package/dist/cli/index-cmd.d.ts +72 -0
  34. package/dist/cli/index-cmd.d.ts.map +1 -0
  35. package/dist/cli/index-cmd.js +263 -0
  36. package/dist/cli/index-cmd.js.map +1 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +4 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/cli/init.d.ts +14 -0
  42. package/dist/cli/init.d.ts.map +1 -0
  43. package/dist/cli/init.js +64 -0
  44. package/dist/cli/init.js.map +1 -0
  45. package/dist/cli/install-hooks.d.ts +3 -0
  46. package/dist/cli/install-hooks.d.ts.map +1 -0
  47. package/dist/cli/install-hooks.js +34 -0
  48. package/dist/cli/install-hooks.js.map +1 -0
  49. package/dist/cli/metrics-cmd.d.ts +3 -0
  50. package/dist/cli/metrics-cmd.d.ts.map +1 -0
  51. package/dist/cli/metrics-cmd.js +164 -0
  52. package/dist/cli/metrics-cmd.js.map +1 -0
  53. package/dist/cli/program.d.ts +14 -0
  54. package/dist/cli/program.d.ts.map +1 -0
  55. package/dist/cli/program.js +44 -0
  56. package/dist/cli/program.js.map +1 -0
  57. package/dist/cli/query.d.ts +46 -0
  58. package/dist/cli/query.d.ts.map +1 -0
  59. package/dist/cli/query.js +153 -0
  60. package/dist/cli/query.js.map +1 -0
  61. package/dist/cli/search-cmd.d.ts +33 -0
  62. package/dist/cli/search-cmd.d.ts.map +1 -0
  63. package/dist/cli/search-cmd.js +114 -0
  64. package/dist/cli/search-cmd.js.map +1 -0
  65. package/dist/cli/serve.d.ts +3 -0
  66. package/dist/cli/serve.d.ts.map +1 -0
  67. package/dist/cli/serve.js +19 -0
  68. package/dist/cli/serve.js.map +1 -0
  69. package/dist/cli/skill-install.d.ts +39 -0
  70. package/dist/cli/skill-install.d.ts.map +1 -0
  71. package/dist/cli/skill-install.js +57 -0
  72. package/dist/cli/skill-install.js.map +1 -0
  73. package/dist/cli/status.d.ts +35 -0
  74. package/dist/cli/status.d.ts.map +1 -0
  75. package/dist/cli/status.js +117 -0
  76. package/dist/cli/status.js.map +1 -0
  77. package/dist/cli/upgrade-cmd.d.ts +48 -0
  78. package/dist/cli/upgrade-cmd.d.ts.map +1 -0
  79. package/dist/cli/upgrade-cmd.js +129 -0
  80. package/dist/cli/upgrade-cmd.js.map +1 -0
  81. package/dist/cli/version.d.ts +3 -0
  82. package/dist/cli/version.d.ts.map +1 -0
  83. package/dist/cli/version.js +19 -0
  84. package/dist/cli/version.js.map +1 -0
  85. package/dist/env.d.ts +17 -0
  86. package/dist/env.d.ts.map +1 -0
  87. package/dist/env.js +17 -0
  88. package/dist/env.js.map +1 -0
  89. package/dist/graph/checker-resolver.d.ts +156 -0
  90. package/dist/graph/checker-resolver.d.ts.map +1 -0
  91. package/dist/graph/checker-resolver.js +458 -0
  92. package/dist/graph/checker-resolver.js.map +1 -0
  93. package/dist/graph/db.d.ts +318 -0
  94. package/dist/graph/db.d.ts.map +1 -0
  95. package/dist/graph/db.js +266 -0
  96. package/dist/graph/db.js.map +1 -0
  97. package/dist/graph/index.d.ts +4 -0
  98. package/dist/graph/index.d.ts.map +1 -0
  99. package/dist/graph/index.js +5 -0
  100. package/dist/graph/index.js.map +1 -0
  101. package/dist/graph/local-type-env.d.ts +45 -0
  102. package/dist/graph/local-type-env.d.ts.map +1 -0
  103. package/dist/graph/local-type-env.js +59 -0
  104. package/dist/graph/local-type-env.js.map +1 -0
  105. package/dist/graph/path-range.d.ts +40 -0
  106. package/dist/graph/path-range.d.ts.map +1 -0
  107. package/dist/graph/path-range.js +42 -0
  108. package/dist/graph/path-range.js.map +1 -0
  109. package/dist/graph/populate.d.ts +263 -0
  110. package/dist/graph/populate.d.ts.map +1 -0
  111. package/dist/graph/populate.js +984 -0
  112. package/dist/graph/populate.js.map +1 -0
  113. package/dist/graph/queries.d.ts +114 -0
  114. package/dist/graph/queries.d.ts.map +1 -0
  115. package/dist/graph/queries.js +415 -0
  116. package/dist/graph/queries.js.map +1 -0
  117. package/dist/graph/sqliteBatch.d.ts +48 -0
  118. package/dist/graph/sqliteBatch.d.ts.map +1 -0
  119. package/dist/graph/sqliteBatch.js +93 -0
  120. package/dist/graph/sqliteBatch.js.map +1 -0
  121. package/dist/indexer/freshness.d.ts +48 -0
  122. package/dist/indexer/freshness.d.ts.map +1 -0
  123. package/dist/indexer/freshness.js +72 -0
  124. package/dist/indexer/freshness.js.map +1 -0
  125. package/dist/indexer/import-resolver.d.ts +50 -0
  126. package/dist/indexer/import-resolver.d.ts.map +1 -0
  127. package/dist/indexer/import-resolver.js +318 -0
  128. package/dist/indexer/import-resolver.js.map +1 -0
  129. package/dist/indexer/index.d.ts +249 -0
  130. package/dist/indexer/index.d.ts.map +1 -0
  131. package/dist/indexer/index.js +560 -0
  132. package/dist/indexer/index.js.map +1 -0
  133. package/dist/indexer/walker.d.ts +60 -0
  134. package/dist/indexer/walker.d.ts.map +1 -0
  135. package/dist/indexer/walker.js +128 -0
  136. package/dist/indexer/walker.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +80 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +151 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/context.d.ts +23 -0
  142. package/dist/mcp/context.d.ts.map +1 -0
  143. package/dist/mcp/context.js +2 -0
  144. package/dist/mcp/context.js.map +1 -0
  145. package/dist/mcp/register-tools.d.ts +21 -0
  146. package/dist/mcp/register-tools.d.ts.map +1 -0
  147. package/dist/mcp/register-tools.js +42 -0
  148. package/dist/mcp/register-tools.js.map +1 -0
  149. package/dist/mcp/server.d.ts +58 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/mcp/server.js +177 -0
  152. package/dist/mcp/server.js.map +1 -0
  153. package/dist/mcp/staleness.d.ts +72 -0
  154. package/dist/mcp/staleness.d.ts.map +1 -0
  155. package/dist/mcp/staleness.js +213 -0
  156. package/dist/mcp/staleness.js.map +1 -0
  157. package/dist/mcp/startup.d.ts +40 -0
  158. package/dist/mcp/startup.d.ts.map +1 -0
  159. package/dist/mcp/startup.js +114 -0
  160. package/dist/mcp/startup.js.map +1 -0
  161. package/dist/mcp/tools/_helpers.d.ts +26 -0
  162. package/dist/mcp/tools/_helpers.d.ts.map +1 -0
  163. package/dist/mcp/tools/_helpers.js +54 -0
  164. package/dist/mcp/tools/_helpers.js.map +1 -0
  165. package/dist/mcp/tools/callers.d.ts +4 -0
  166. package/dist/mcp/tools/callers.d.ts.map +1 -0
  167. package/dist/mcp/tools/callers.js +112 -0
  168. package/dist/mcp/tools/callers.js.map +1 -0
  169. package/dist/mcp/tools/dependencies.d.ts +4 -0
  170. package/dist/mcp/tools/dependencies.d.ts.map +1 -0
  171. package/dist/mcp/tools/dependencies.js +36 -0
  172. package/dist/mcp/tools/dependencies.js.map +1 -0
  173. package/dist/mcp/tools/efficiency.d.ts +4 -0
  174. package/dist/mcp/tools/efficiency.d.ts.map +1 -0
  175. package/dist/mcp/tools/efficiency.js +39 -0
  176. package/dist/mcp/tools/efficiency.js.map +1 -0
  177. package/dist/mcp/tools/exports.d.ts +4 -0
  178. package/dist/mcp/tools/exports.d.ts.map +1 -0
  179. package/dist/mcp/tools/exports.js +56 -0
  180. package/dist/mcp/tools/exports.js.map +1 -0
  181. package/dist/mcp/tools/implementors.d.ts +4 -0
  182. package/dist/mcp/tools/implementors.d.ts.map +1 -0
  183. package/dist/mcp/tools/implementors.js +47 -0
  184. package/dist/mcp/tools/implementors.js.map +1 -0
  185. package/dist/mcp/tools/project-skeleton.d.ts +4 -0
  186. package/dist/mcp/tools/project-skeleton.d.ts.map +1 -0
  187. package/dist/mcp/tools/project-skeleton.js +61 -0
  188. package/dist/mcp/tools/project-skeleton.js.map +1 -0
  189. package/dist/mcp/tools/reindex.d.ts +14 -0
  190. package/dist/mcp/tools/reindex.d.ts.map +1 -0
  191. package/dist/mcp/tools/reindex.js +32 -0
  192. package/dist/mcp/tools/reindex.js.map +1 -0
  193. package/dist/mcp/tools/rename-impact.d.ts +4 -0
  194. package/dist/mcp/tools/rename-impact.d.ts.map +1 -0
  195. package/dist/mcp/tools/rename-impact.js +124 -0
  196. package/dist/mcp/tools/rename-impact.js.map +1 -0
  197. package/dist/mcp/tools/search.d.ts +4 -0
  198. package/dist/mcp/tools/search.d.ts.map +1 -0
  199. package/dist/mcp/tools/search.js +69 -0
  200. package/dist/mcp/tools/search.js.map +1 -0
  201. package/dist/mcp/tools/signature.d.ts +4 -0
  202. package/dist/mcp/tools/signature.d.ts.map +1 -0
  203. package/dist/mcp/tools/signature.js +127 -0
  204. package/dist/mcp/tools/signature.js.map +1 -0
  205. package/dist/mcp/tools/status.d.ts +4 -0
  206. package/dist/mcp/tools/status.d.ts.map +1 -0
  207. package/dist/mcp/tools/status.js +30 -0
  208. package/dist/mcp/tools/status.js.map +1 -0
  209. package/dist/search/declex.d.ts +109 -0
  210. package/dist/search/declex.d.ts.map +1 -0
  211. package/dist/search/declex.js +242 -0
  212. package/dist/search/declex.js.map +1 -0
  213. package/dist/search/fts.d.ts +77 -0
  214. package/dist/search/fts.d.ts.map +1 -0
  215. package/dist/search/fts.js +223 -0
  216. package/dist/search/fts.js.map +1 -0
  217. package/dist/search/fused.d.ts +110 -0
  218. package/dist/search/fused.d.ts.map +1 -0
  219. package/dist/search/fused.js +323 -0
  220. package/dist/search/fused.js.map +1 -0
  221. package/dist/search/potential-matches.d.ts +88 -0
  222. package/dist/search/potential-matches.d.ts.map +1 -0
  223. package/dist/search/potential-matches.js +83 -0
  224. package/dist/search/potential-matches.js.map +1 -0
  225. package/dist/search/scope.d.ts +66 -0
  226. package/dist/search/scope.d.ts.map +1 -0
  227. package/dist/search/scope.js +71 -0
  228. package/dist/search/scope.js.map +1 -0
  229. package/dist/store/config.d.ts +57 -0
  230. package/dist/store/config.d.ts.map +1 -0
  231. package/dist/store/config.js +208 -0
  232. package/dist/store/config.js.map +1 -0
  233. package/dist/store/lock.d.ts +101 -0
  234. package/dist/store/lock.d.ts.map +1 -0
  235. package/dist/store/lock.js +182 -0
  236. package/dist/store/lock.js.map +1 -0
  237. package/dist/store/lockMetrics.d.ts +52 -0
  238. package/dist/store/lockMetrics.d.ts.map +1 -0
  239. package/dist/store/lockMetrics.js +29 -0
  240. package/dist/store/lockMetrics.js.map +1 -0
  241. package/dist/store/sqliteChunkStore.d.ts +44 -0
  242. package/dist/store/sqliteChunkStore.d.ts.map +1 -0
  243. package/dist/store/sqliteChunkStore.js +135 -0
  244. package/dist/store/sqliteChunkStore.js.map +1 -0
  245. package/dist/telemetry/lockMetricsSummary.d.ts +42 -0
  246. package/dist/telemetry/lockMetricsSummary.d.ts.map +1 -0
  247. package/dist/telemetry/lockMetricsSummary.js +139 -0
  248. package/dist/telemetry/lockMetricsSummary.js.map +1 -0
  249. package/dist/telemetry/metrics.d.ts +140 -0
  250. package/dist/telemetry/metrics.d.ts.map +1 -0
  251. package/dist/telemetry/metrics.js +312 -0
  252. package/dist/telemetry/metrics.js.map +1 -0
  253. package/dist/telemetry/tokenizer.d.ts +125 -0
  254. package/dist/telemetry/tokenizer.d.ts.map +1 -0
  255. package/dist/telemetry/tokenizer.js +174 -0
  256. package/dist/telemetry/tokenizer.js.map +1 -0
  257. 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"}