@omgbase/core 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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/cli/eval-matcher.d.ts +13 -0
  3. package/dist/src/cli/eval-matcher.js +36 -0
  4. package/dist/src/core/attach.d.ts +5 -0
  5. package/dist/src/core/attach.js +38 -0
  6. package/dist/src/core/cursor.d.ts +11 -0
  7. package/dist/src/core/cursor.js +35 -0
  8. package/dist/src/core/hash.d.ts +23 -0
  9. package/dist/src/core/hash.js +82 -0
  10. package/dist/src/core/ids.d.ts +6 -0
  11. package/dist/src/core/ids.js +26 -0
  12. package/dist/src/core/index.d.ts +1 -0
  13. package/dist/src/core/index.js +2 -0
  14. package/dist/src/core/ingest.d.ts +43 -0
  15. package/dist/src/core/ingest.js +287 -0
  16. package/dist/src/core/order-key.d.ts +8 -0
  17. package/dist/src/core/order-key.js +72 -0
  18. package/dist/src/core/parse/parse.d.ts +3 -0
  19. package/dist/src/core/parse/parse.js +104 -0
  20. package/dist/src/core/parse/render.d.ts +3 -0
  21. package/dist/src/core/parse/render.js +20 -0
  22. package/dist/src/core/parse/tree.d.ts +8 -0
  23. package/dist/src/core/parse/tree.js +43 -0
  24. package/dist/src/core/parse/types.d.ts +52 -0
  25. package/dist/src/core/parse/types.js +9 -0
  26. package/dist/src/core/read/document.d.ts +107 -0
  27. package/dist/src/core/read/document.js +191 -0
  28. package/dist/src/core/read/nodes.d.ts +47 -0
  29. package/dist/src/core/read/nodes.js +102 -0
  30. package/dist/src/core/read/outline.d.ts +12 -0
  31. package/dist/src/core/read/outline.js +76 -0
  32. package/dist/src/core/read/reader.d.ts +125 -0
  33. package/dist/src/core/read/reader.js +206 -0
  34. package/dist/src/core/read/refs.d.ts +15 -0
  35. package/dist/src/core/read/refs.js +51 -0
  36. package/dist/src/core/store/edges.d.ts +26 -0
  37. package/dist/src/core/store/edges.js +101 -0
  38. package/dist/src/core/store/fts.d.ts +10 -0
  39. package/dist/src/core/store/fts.js +22 -0
  40. package/dist/src/core/store/gc.d.ts +18 -0
  41. package/dist/src/core/store/gc.js +69 -0
  42. package/dist/src/core/store/nodes.d.ts +5 -0
  43. package/dist/src/core/store/nodes.js +57 -0
  44. package/dist/src/core/store/properties.d.ts +38 -0
  45. package/dist/src/core/store/properties.js +177 -0
  46. package/dist/src/core/store/rebuild.d.ts +3 -0
  47. package/dist/src/core/store/rebuild.js +29 -0
  48. package/dist/src/core/store/schema.d.ts +9 -0
  49. package/dist/src/core/store/schema.js +386 -0
  50. package/dist/src/core/store/sections.d.ts +2 -0
  51. package/dist/src/core/store/sections.js +29 -0
  52. package/dist/src/core/store/store.d.ts +24 -0
  53. package/dist/src/core/store/store.js +164 -0
  54. package/dist/src/core/store/writers.d.ts +52 -0
  55. package/dist/src/core/store/writers.js +89 -0
  56. package/dist/src/core/vec.d.ts +10 -0
  57. package/dist/src/core/vec.js +33 -0
  58. package/dist/src/format/adapter.d.ts +97 -0
  59. package/dist/src/format/adapter.js +14 -0
  60. package/dist/src/format/index.d.ts +6 -0
  61. package/dist/src/format/index.js +15 -0
  62. package/dist/src/format/json.d.ts +3 -0
  63. package/dist/src/format/json.js +324 -0
  64. package/dist/src/format/markdown.d.ts +5 -0
  65. package/dist/src/format/markdown.js +217 -0
  66. package/dist/src/format/registry.d.ts +6 -0
  67. package/dist/src/format/registry.js +24 -0
  68. package/dist/src/format/yaml.d.ts +5 -0
  69. package/dist/src/format/yaml.js +413 -0
  70. package/dist/src/graph/extract.d.ts +38 -0
  71. package/dist/src/graph/extract.js +242 -0
  72. package/dist/src/graph/history.d.ts +103 -0
  73. package/dist/src/graph/history.js +210 -0
  74. package/dist/src/graph/inbound-links.d.ts +33 -0
  75. package/dist/src/graph/inbound-links.js +130 -0
  76. package/dist/src/graph/link-destinations.d.ts +22 -0
  77. package/dist/src/graph/link-destinations.js +84 -0
  78. package/dist/src/graph/link-health.d.ts +79 -0
  79. package/dist/src/graph/link-health.js +146 -0
  80. package/dist/src/graph/links.d.ts +23 -0
  81. package/dist/src/graph/links.js +42 -0
  82. package/dist/src/index.d.ts +60 -0
  83. package/dist/src/index.js +70 -0
  84. package/dist/src/mcp/errors.d.ts +17 -0
  85. package/dist/src/mcp/errors.js +18 -0
  86. package/dist/src/mcp/graph.d.ts +63 -0
  87. package/dist/src/mcp/graph.js +212 -0
  88. package/dist/src/mcp/reference.d.ts +1 -0
  89. package/dist/src/mcp/reference.js +227 -0
  90. package/dist/src/mcp/server.d.ts +30 -0
  91. package/dist/src/mcp/server.js +1076 -0
  92. package/dist/src/mcp/stdio.d.ts +13 -0
  93. package/dist/src/mcp/stdio.js +24 -0
  94. package/dist/src/migrate/mrplex.d.ts +27 -0
  95. package/dist/src/migrate/mrplex.js +29 -0
  96. package/dist/src/mutate/apply.d.ts +83 -0
  97. package/dist/src/mutate/apply.js +302 -0
  98. package/dist/src/mutate/doc-store.d.ts +49 -0
  99. package/dist/src/mutate/doc-store.js +71 -0
  100. package/dist/src/mutate/docs.d.ts +60 -0
  101. package/dist/src/mutate/docs.js +232 -0
  102. package/dist/src/mutate/known-ids.d.ts +12 -0
  103. package/dist/src/mutate/known-ids.js +154 -0
  104. package/dist/src/mutate/load.d.ts +3 -0
  105. package/dist/src/mutate/load.js +66 -0
  106. package/dist/src/mutate/lower.d.ts +32 -0
  107. package/dist/src/mutate/lower.js +232 -0
  108. package/dist/src/mutate/macros.d.ts +91 -0
  109. package/dist/src/mutate/macros.js +230 -0
  110. package/dist/src/mutate/ops.d.ts +40 -0
  111. package/dist/src/mutate/ops.js +513 -0
  112. package/dist/src/mutate/opset.d.ts +92 -0
  113. package/dist/src/mutate/opset.js +79 -0
  114. package/dist/src/mutate/plan-update.d.ts +51 -0
  115. package/dist/src/mutate/plan-update.js +187 -0
  116. package/dist/src/mutate/tree.d.ts +46 -0
  117. package/dist/src/mutate/tree.js +167 -0
  118. package/dist/src/oqx/run.d.ts +1 -0
  119. package/dist/src/oqx/run.js +6 -0
  120. package/dist/src/oqx-js/context.d.ts +23 -0
  121. package/dist/src/oqx-js/context.js +564 -0
  122. package/dist/src/oqx-js/planner.d.ts +10 -0
  123. package/dist/src/oqx-js/planner.js +91 -0
  124. package/dist/src/oqx-js/run.d.ts +41 -0
  125. package/dist/src/oqx-js/run.js +332 -0
  126. package/dist/src/oqx-js/sql/translate.d.ts +27 -0
  127. package/dist/src/oqx-js/sql/translate.js +290 -0
  128. package/dist/src/reconcile/crossdoc.d.ts +34 -0
  129. package/dist/src/reconcile/crossdoc.js +76 -0
  130. package/dist/src/reconcile/eval/generator.d.ts +21 -0
  131. package/dist/src/reconcile/eval/generator.js +129 -0
  132. package/dist/src/reconcile/eval/metrics.d.ts +32 -0
  133. package/dist/src/reconcile/eval/metrics.js +74 -0
  134. package/dist/src/reconcile/flatten.d.ts +12 -0
  135. package/dist/src/reconcile/flatten.js +37 -0
  136. package/dist/src/reconcile/phase5.d.ts +8 -0
  137. package/dist/src/reconcile/phase5.js +167 -0
  138. package/dist/src/reconcile/phase6a.d.ts +2 -0
  139. package/dist/src/reconcile/phase6a.js +159 -0
  140. package/dist/src/reconcile/phases.d.ts +28 -0
  141. package/dist/src/reconcile/phases.js +0 -0
  142. package/dist/src/reconcile/reconcile.d.ts +21 -0
  143. package/dist/src/reconcile/reconcile.js +115 -0
  144. package/dist/src/reconcile/similarity.d.ts +7 -0
  145. package/dist/src/reconcile/similarity.js +41 -0
  146. package/dist/src/reconcile/types.d.ts +46 -0
  147. package/dist/src/reconcile/types.js +19 -0
  148. package/dist/src/search/cel/ast.d.ts +74 -0
  149. package/dist/src/search/cel/ast.js +3 -0
  150. package/dist/src/search/cel/compile.d.ts +60 -0
  151. package/dist/src/search/cel/compile.js +836 -0
  152. package/dist/src/search/cel/lexer.d.ts +9 -0
  153. package/dist/src/search/cel/lexer.js +139 -0
  154. package/dist/src/search/cel/parser.d.ts +7 -0
  155. package/dist/src/search/cel/parser.js +219 -0
  156. package/dist/src/search/drain.d.ts +39 -0
  157. package/dist/src/search/drain.js +97 -0
  158. package/dist/src/search/embeddings.d.ts +131 -0
  159. package/dist/src/search/embeddings.js +263 -0
  160. package/dist/src/search/external.d.ts +13 -0
  161. package/dist/src/search/external.js +177 -0
  162. package/dist/src/search/fts-query.d.ts +1 -0
  163. package/dist/src/search/fts-query.js +64 -0
  164. package/dist/src/search/provider.d.ts +17 -0
  165. package/dist/src/search/provider.js +13 -0
  166. package/dist/src/search/resolve.d.ts +18 -0
  167. package/dist/src/search/resolve.js +23 -0
  168. package/dist/src/search/rrf.d.ts +36 -0
  169. package/dist/src/search/rrf.js +84 -0
  170. package/dist/src/search/tasks.d.ts +12 -0
  171. package/dist/src/search/tasks.js +131 -0
  172. package/dist/src/search/text.d.ts +16 -0
  173. package/dist/src/search/text.js +21 -0
  174. package/dist/src/search/vector.d.ts +18 -0
  175. package/dist/src/search/vector.js +49 -0
  176. package/dist/src/sync/admin.d.ts +59 -0
  177. package/dist/src/sync/admin.js +53 -0
  178. package/dist/src/sync/attach.d.ts +13 -0
  179. package/dist/src/sync/attach.js +29 -0
  180. package/dist/src/sync/checkpoint.d.ts +21 -0
  181. package/dist/src/sync/checkpoint.js +52 -0
  182. package/dist/src/sync/driver.d.ts +25 -0
  183. package/dist/src/sync/driver.js +80 -0
  184. package/dist/src/sync/external-source.d.ts +14 -0
  185. package/dist/src/sync/external-source.js +137 -0
  186. package/dist/src/sync/freshness.d.ts +42 -0
  187. package/dist/src/sync/freshness.js +152 -0
  188. package/dist/src/sync/fs-util.d.ts +12 -0
  189. package/dist/src/sync/fs-util.js +55 -0
  190. package/dist/src/sync/git-heuristics.d.ts +6 -0
  191. package/dist/src/sync/git-heuristics.js +16 -0
  192. package/dist/src/sync/observe.d.ts +63 -0
  193. package/dist/src/sync/observe.js +84 -0
  194. package/dist/src/sync/plugin.d.ts +54 -0
  195. package/dist/src/sync/plugin.js +12 -0
  196. package/dist/src/sync/reconciling-ingest.d.ts +12 -0
  197. package/dist/src/sync/reconciling-ingest.js +209 -0
  198. package/dist/src/sync/recovery.d.ts +6 -0
  199. package/dist/src/sync/recovery.js +34 -0
  200. package/dist/src/sync/settings.d.ts +18 -0
  201. package/dist/src/sync/settings.js +58 -0
  202. package/dist/src/sync/sources.d.ts +37 -0
  203. package/dist/src/sync/sources.js +75 -0
  204. package/dist/src/sync/tombstone.d.ts +9 -0
  205. package/dist/src/sync/tombstone.js +55 -0
  206. package/dist/src/sync/watch-lease.d.ts +14 -0
  207. package/dist/src/sync/watch-lease.js +91 -0
  208. package/dist/src/sync/watcher.d.ts +19 -0
  209. package/dist/src/sync/watcher.js +34 -0
  210. package/dist/src/sync/workspace.d.ts +34 -0
  211. package/dist/src/sync/workspace.js +120 -0
  212. package/dist/src/sync/writer-lock.d.ts +16 -0
  213. package/dist/src/sync/writer-lock.js +121 -0
  214. package/package.json +80 -0
@@ -0,0 +1,130 @@
1
+ import { extractFromBlock, maskCode, resolveRelativePath } from "./extract.js";
2
+ function canonical(path) {
3
+ return path.replace(/^\/+/, "");
4
+ }
5
+ function dirOf(path) {
6
+ return path.replace(/[^/]*$/, "");
7
+ }
8
+ /** Does a destination written in a doc under `srcDir` resolve to `docPath`? */
9
+ function resolvesTo(dest, srcDir, docPath) {
10
+ if (dest === "" || /^[a-z][a-z0-9+.-]*:/i.test(dest))
11
+ return false;
12
+ return canonical(resolveRelativePath(dest, srcDir)) === docPath;
13
+ }
14
+ /**
15
+ * Every link occurrence in the repo whose destination resolves to `docPath`,
16
+ * found via the open edges that point at `docId`. Block-level edges are
17
+ * re-scanned from the block raw so `target`/`anchor` are as written (and so a
18
+ * self-doc pure-fragment link `#H`, which also has dst_node == docId, is NOT
19
+ * reported — it does not depend on the path). Frontmatter edges are reported
20
+ * with `block: null`, `target: docPath`, and their `field`.
21
+ */
22
+ export function inboundLinksTo(store, repoId, docId, docPath) {
23
+ const rows = store.db
24
+ .prepare(`SELECT DISTINCT e.src_doc AS srcDoc, d.path AS srcPath, e.src_block AS srcBlock, e.src_field AS srcField
25
+ FROM edges e JOIN docs d ON d.doc_id = e.src_doc
26
+ WHERE e.repo_id = ? AND e.dst_node = ? AND e.to_commit IS NULL AND d.deleted_commit IS NULL
27
+ ORDER BY d.path, e.src_block`)
28
+ .all(repoId, docId);
29
+ const out = [];
30
+ const seenBlocks = new Set();
31
+ for (const r of rows) {
32
+ if (r.srcBlock === null) {
33
+ out.push({ doc: r.srcDoc, path: r.srcPath, block: null, target: docPath, anchor: null, ...(r.srcField ? { field: r.srcField } : {}) });
34
+ continue;
35
+ }
36
+ if (seenBlocks.has(r.srcBlock))
37
+ continue;
38
+ seenBlocks.add(r.srcBlock);
39
+ const blk = store.db
40
+ .prepare("SELECT bl.type, b.bytes FROM blocks bl JOIN blobs b ON b.hash = bl.raw_hash WHERE bl.block_id = ? AND bl.deleted_commit IS NULL")
41
+ .get(r.srcBlock);
42
+ if (!blk)
43
+ continue;
44
+ const srcDir = dirOf(r.srcPath);
45
+ for (const e of extractFromBlock(r.srcBlock, blk.type, blk.bytes.toString("utf8"))) {
46
+ if (e.dstKind === "external" || !resolvesTo(e.target, srcDir, docPath))
47
+ continue;
48
+ out.push({
49
+ doc: r.srcDoc,
50
+ path: r.srcPath,
51
+ block: r.srcBlock,
52
+ target: e.target,
53
+ anchor: e.anchor,
54
+ ...(e.srcField ? { field: e.srcField } : {}),
55
+ });
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ // Relative path from directory `fromDir` ("a/b/" or "") to file `to`.
61
+ function relativePath(fromDir, to) {
62
+ const from = fromDir.split("/").filter(Boolean);
63
+ const target = to.split("/").filter(Boolean);
64
+ let i = 0;
65
+ while (i < from.length && i < target.length && from[i] === target[i])
66
+ i++;
67
+ const ups = from.length - i;
68
+ const rest = target.slice(i).join("/");
69
+ return ups === 0 ? `./${rest}` : `${"../".repeat(ups)}${rest}`;
70
+ }
71
+ // Rewrite one destination (path + optional fragment) if its path resolves to
72
+ // `fromPath`, keeping the author's style: absolute stays absolute, `./`/`../`
73
+ // stays relative (recomputed from `writeDir`), bare root-relative stays bare.
74
+ function rewriteDest(dest, matchDir, writeDir, fromPath, toPath) {
75
+ const hashIdx = dest.indexOf("#");
76
+ const caretIdx = dest.indexOf("^");
77
+ let cut = dest.length;
78
+ if (caretIdx >= 0 && (hashIdx < 0 || caretIdx < hashIdx))
79
+ cut = caretIdx;
80
+ else if (hashIdx >= 0)
81
+ cut = hashIdx;
82
+ const pathPart = dest.slice(0, cut);
83
+ const fragment = dest.slice(cut);
84
+ if (!resolvesTo(pathPart, matchDir, fromPath))
85
+ return null;
86
+ let newPath;
87
+ if (pathPart.startsWith("/"))
88
+ newPath = "/" + toPath;
89
+ else if (pathPart.startsWith("./") || pathPart.startsWith("../"))
90
+ newPath = relativePath(writeDir, toPath);
91
+ else
92
+ newPath = toPath;
93
+ return newPath + fragment;
94
+ }
95
+ // Destination-bearing syntaxes, with the destination isolated in group 2 so we
96
+ // can splice it in place. Scanned over the code-MASKED raw (positions carry
97
+ // over 1:1 to the original), so links inside code are never touched.
98
+ const MD_LINK_DEST = /(!?\[[^\]]*\]\()([^)\s]+)((?:\s+"[^"]*")?\))/g;
99
+ const WIKILINK_DEST = /(!?\[\[)([^\]]+)(\]\])/g;
100
+ const INLINE_FIELD_PATH = /((?:^|\s)[a-z][a-z0-9_]*::\s*)(\/[^\s]+)/gi;
101
+ /**
102
+ * Rewrite every link destination in `raw` that resolves (relative to `matchDir`,
103
+ * the source doc's directory at extraction time) to `fromPath` so it points at
104
+ * `toPath`, writing relative forms against `writeDir` (the source doc's current
105
+ * directory — differs from `matchDir` only for links inside the moved doc
106
+ * itself). Anchors, link text, titles, and code spans are preserved. Returns
107
+ * null when nothing changed.
108
+ */
109
+ export function retargetLinksInRaw(raw, matchDir, writeDir, fromPath, toPath) {
110
+ const masked = maskCode(raw);
111
+ const edits = [];
112
+ for (const re of [MD_LINK_DEST, WIKILINK_DEST, INLINE_FIELD_PATH]) {
113
+ for (const m of masked.matchAll(re)) {
114
+ const dest = m[2];
115
+ const replaced = rewriteDest(dest, matchDir, writeDir, fromPath, toPath);
116
+ if (replaced === null)
117
+ continue;
118
+ const start = (m.index ?? 0) + m[1].length;
119
+ edits.push({ start, end: start + dest.length, text: replaced });
120
+ }
121
+ }
122
+ if (edits.length === 0)
123
+ return null;
124
+ edits.sort((a, b) => b.start - a.start);
125
+ let out = raw;
126
+ for (const e of edits)
127
+ out = out.slice(0, e.start) + e.text + out.slice(e.end);
128
+ return out === raw ? null : out;
129
+ }
130
+ //# sourceMappingURL=inbound-links.js.map
@@ -0,0 +1,22 @@
1
+ /** Split an authored destination into its path part and its trailing fragment
2
+ * (`#Heading` / `^ref`, fragment includes the marker). Mirrors extract.ts
3
+ * splitAnchor: a `^` ref wins over a `#` heading when both appear. */
4
+ export declare function splitDestination(dest: string): {
5
+ path: string;
6
+ fragment: string;
7
+ };
8
+ /** Canonical repo-relative path for a link path as authored in a doc living in
9
+ * `docDir` ("" for the root, else "dir/sub/"): `./`/`../` resolve against the
10
+ * doc's directory (mirroring sync/reconciling-ingest resolveRelativePath), and
11
+ * the leading `/` (repo-root anchor) is dropped — this is the path the edge
12
+ * index keys phantoms by. */
13
+ export declare function canonicalLinkPath(path: string, docDir?: string): string;
14
+ /** Directory prefix ("" or "a/b/") of a repo-relative doc path. */
15
+ export declare function docDirOf(docPath: string): string;
16
+ /**
17
+ * Rewrite link destinations in a block's raw markdown. `replace(dest)` is called
18
+ * once per link feature found outside inline code; it returns the new
19
+ * destination text, or null to leave that link untouched. Bytes outside link
20
+ * destinations are never changed.
21
+ */
22
+ export declare function rewriteLinkDestinations(raw: string, replace: (dest: string) => string | null): string;
@@ -0,0 +1,84 @@
1
+ // Link-destination awareness shared by the stale detector (link-health.ts) and
2
+ // the repair macro (mutate/macros.ts linksRepair). A "destination" is the exact
3
+ // authored target text of a link feature in a block's raw markdown:
4
+ //
5
+ // [text](dest) ![alt](dest) Markdown link / image → `dest`
6
+ // [[dest]] [[dest|alias]] wikilink → `dest` (alias kept aside)
7
+ // key:: /dest inline field, bare repo path → `/dest`
8
+ //
9
+ // Inline code spans (`…`) are opaque: a link-shaped string inside backticks is
10
+ // prose about a link, not a link, so scanners skip them. Whole `code_fence`
11
+ // blocks are the caller's job to exclude (they are their own block type).
12
+ //
13
+ // The same regexes the edge extractor (extract.ts) and node projector
14
+ // (format/markdown.ts) use, so what this module rewrites is exactly what the
15
+ // edge index reports as a link.
16
+ /** Split an authored destination into its path part and its trailing fragment
17
+ * (`#Heading` / `^ref`, fragment includes the marker). Mirrors extract.ts
18
+ * splitAnchor: a `^` ref wins over a `#` heading when both appear. */
19
+ export function splitDestination(dest) {
20
+ const caret = dest.indexOf("^");
21
+ if (caret >= 0)
22
+ return { path: dest.slice(0, caret), fragment: dest.slice(caret) };
23
+ const hash = dest.indexOf("#");
24
+ if (hash >= 0)
25
+ return { path: dest.slice(0, hash), fragment: dest.slice(hash) };
26
+ return { path: dest, fragment: "" };
27
+ }
28
+ /** Canonical repo-relative path for a link path as authored in a doc living in
29
+ * `docDir` ("" for the root, else "dir/sub/"): `./`/`../` resolve against the
30
+ * doc's directory (mirroring sync/reconciling-ingest resolveRelativePath), and
31
+ * the leading `/` (repo-root anchor) is dropped — this is the path the edge
32
+ * index keys phantoms by. */
33
+ export function canonicalLinkPath(path, docDir = "") {
34
+ if (path.startsWith("./") || path.startsWith("../")) {
35
+ const out = [];
36
+ for (const p of (docDir + path).split("/")) {
37
+ if (p === "." || p === "")
38
+ continue;
39
+ if (p === "..") {
40
+ out.pop();
41
+ continue;
42
+ }
43
+ out.push(p);
44
+ }
45
+ return out.join("/");
46
+ }
47
+ return path.replace(/^\//, "");
48
+ }
49
+ /** Directory prefix ("" or "a/b/") of a repo-relative doc path. */
50
+ export function docDirOf(docPath) {
51
+ const i = docPath.lastIndexOf("/");
52
+ return i < 0 ? "" : docPath.slice(0, i + 1);
53
+ }
54
+ // Link-feature scanners. Each captures (prefix, dest, suffix) so a rewrite can
55
+ // splice a new destination in place and leave everything else byte-identical.
56
+ const MD_LINK = /(!?\[[^\]]*\]\()([^)\s]+)((?:\s+"[^"]*")?\))/g;
57
+ const WIKILINK = /(!?\[\[)([^\]|]+)((?:\|[^\]]*)?\]\])/g;
58
+ const INLINE_FIELD_PATH = /((?:^|\s)[a-z][a-z0-9_]*::[ \t]*)(\/[^\s]+)()/gi;
59
+ // Inline code: a backtick run closed by an identical run (CommonMark code span).
60
+ const CODE_SPAN = /(`+)[^`][\s\S]*?\1/g;
61
+ /**
62
+ * Rewrite link destinations in a block's raw markdown. `replace(dest)` is called
63
+ * once per link feature found outside inline code; it returns the new
64
+ * destination text, or null to leave that link untouched. Bytes outside link
65
+ * destinations are never changed.
66
+ */
67
+ export function rewriteLinkDestinations(raw, replace) {
68
+ const rewriteSegment = (seg) => {
69
+ const sub = (re) => (s) => s.replace(re, (whole, pre, dest, post) => {
70
+ const next = replace(dest);
71
+ return next === null ? whole : pre + next + post;
72
+ });
73
+ return sub(INLINE_FIELD_PATH)(sub(WIKILINK)(sub(MD_LINK)(seg)));
74
+ };
75
+ let out = "";
76
+ let last = 0;
77
+ for (const m of raw.matchAll(CODE_SPAN)) {
78
+ const start = m.index ?? 0;
79
+ out += rewriteSegment(raw.slice(last, start)) + m[0];
80
+ last = start + m[0].length;
81
+ }
82
+ return out + rewriteSegment(raw.slice(last));
83
+ }
84
+ //# sourceMappingURL=link-destinations.js.map
@@ -0,0 +1,79 @@
1
+ import type { Store } from "../core/store/store.js";
2
+ export type StaleReason = "dangling_doc" | "dangling_block" | "broken_anchor";
3
+ export interface StaleLink {
4
+ /** source document id */
5
+ srcDoc: string;
6
+ /** source document repo-relative path */
7
+ srcPath: string;
8
+ /** source block id (null for frontmatter-level edges) */
9
+ srcBlock: string | null;
10
+ predicate: string;
11
+ provenance: string;
12
+ dstKind: string;
13
+ /** canonical target PATH (phantom prefix stripped, no leading `/`), e.g. "guides/old.md" */
14
+ target: string;
15
+ /** the destination exactly as authored in the source block, e.g. "/guides/old.md#Setup"
16
+ * — paste-ready as `links_repair.from`. null when it can't be recovered (frontmatter
17
+ * edges have no block; a projected node no longer matches). */
18
+ authored: string | null;
19
+ anchor: string | null;
20
+ reason: StaleReason;
21
+ }
22
+ /** links_stale summary mode: counts only, no per-row output. */
23
+ export interface LinkHealthSummary {
24
+ /** number of dangling internal links (rows `linksStale` would return, uncapped) */
25
+ staleCount: number;
26
+ /** distinct missing targets, most-referenced first (ties by target) */
27
+ byTarget: {
28
+ target: string;
29
+ count: number;
30
+ }[];
31
+ /** source documents carrying dangling links, most first (ties by path) */
32
+ bySource: {
33
+ srcPath: string;
34
+ count: number;
35
+ }[];
36
+ externalCount: number;
37
+ totalOpenEdges: number;
38
+ }
39
+ export interface LinkHealth {
40
+ /** dangling internal links, ordered by srcPath then target */
41
+ stale: StaleLink[];
42
+ /** open external (http(s)) edges — unverifiable, not counted as stale */
43
+ externalCount: number;
44
+ /** total open edges scanned (after src-doc glob scoping) */
45
+ totalOpenEdges: number;
46
+ /** true when `stale` was capped by `limit` */
47
+ truncated: boolean;
48
+ }
49
+ export interface LinkHealthOptions {
50
+ /** scope the SOURCE docs by a path glob (`journal/*`); `*` maps to SQL `%`. */
51
+ pathGlob?: string;
52
+ /** cap the number of stale links returned (default 500). */
53
+ limit?: number;
54
+ }
55
+ export declare function globClause(column: string, glob: string): {
56
+ clause: string;
57
+ param: string;
58
+ };
59
+ /**
60
+ * linksStale: scan open edges repo-wide and return the dangling internal ones,
61
+ * plus a count of unverifiable external links. Read-only; pure DB read over the
62
+ * edge index (no FS/network).
63
+ *
64
+ * Dangling detection: an open edge whose `dst_node LIKE 'phantom:%'` points at a
65
+ * path with no live doc. `target` is the path with the `phantom:` prefix
66
+ * stripped so callers see `guides/old.md`, not the internal id.
67
+ *
68
+ * Scoping: `pathGlob` filters the SOURCE docs (joined edges.src_doc → docs).
69
+ * Edges whose src_doc is tombstoned (docs.deleted_commit IS NOT NULL) are
70
+ * skipped — a deleted doc's dangling links are not actionable.
71
+ */
72
+ export declare function linksStale(store: Store, repoId: string, opts?: LinkHealthOptions): LinkHealth;
73
+ /**
74
+ * linksStaleSummary: the same scan as linksStale, reduced to counts grouped by
75
+ * missing target and by source document — a repo-wide audit in one small call
76
+ * (no per-row output, no limit). Scoping (`pathGlob` over SOURCE docs) and the
77
+ * tombstone rule match linksStale exactly, so the totals agree.
78
+ */
79
+ export declare function linksStaleSummary(store: Store, repoId: string, opts?: Pick<LinkHealthOptions, "pathGlob">): LinkHealthSummary;
@@ -0,0 +1,146 @@
1
+ import { canonicalLinkPath, docDirOf, splitDestination } from "./link-destinations.js";
2
+ // Convert a path glob to a SQL LIKE clause + param, mirroring compileWithin
3
+ // (search/cel/compile.ts) and docHistory (graph/history.ts): escape %/_ then
4
+ // map `*`→`%`. A glob without `*` is an exact-path match.
5
+ export function globClause(column, glob) {
6
+ if (glob.includes("*")) {
7
+ const like = glob.replace(/[%_]/g, "\\$&").replace(/\*/g, "%");
8
+ return { clause: `${column} LIKE ? ESCAPE '\\'`, param: like };
9
+ }
10
+ return { clause: `${column} = ?`, param: glob };
11
+ }
12
+ /**
13
+ * linksStale: scan open edges repo-wide and return the dangling internal ones,
14
+ * plus a count of unverifiable external links. Read-only; pure DB read over the
15
+ * edge index (no FS/network).
16
+ *
17
+ * Dangling detection: an open edge whose `dst_node LIKE 'phantom:%'` points at a
18
+ * path with no live doc. `target` is the path with the `phantom:` prefix
19
+ * stripped so callers see `guides/old.md`, not the internal id.
20
+ *
21
+ * Scoping: `pathGlob` filters the SOURCE docs (joined edges.src_doc → docs).
22
+ * Edges whose src_doc is tombstoned (docs.deleted_commit IS NOT NULL) are
23
+ * skipped — a deleted doc's dangling links are not actionable.
24
+ */
25
+ export function linksStale(store, repoId, opts = {}) {
26
+ const limit = opts.limit ?? 500;
27
+ const params = [repoId];
28
+ let globSql = "";
29
+ if (opts.pathGlob) {
30
+ const { clause, param } = globClause("d.path", opts.pathGlob);
31
+ globSql = `AND ${clause}`;
32
+ params.push(param);
33
+ }
34
+ // Total open edges (scoped) and external count in one pass over the join.
35
+ const totals = store.db
36
+ .prepare(`SELECT count(*) AS total,
37
+ sum(CASE WHEN e.dst_kind = 'external' THEN 1 ELSE 0 END) AS external
38
+ FROM edges e
39
+ JOIN docs d ON d.doc_id = e.src_doc
40
+ WHERE e.repo_id = ? AND e.to_commit IS NULL AND d.deleted_commit IS NULL ${globSql}`)
41
+ .get(...params);
42
+ // Dangling internal links: open, source not tombstoned, dst is a phantom.
43
+ const rows = store.db
44
+ .prepare(`SELECT e.src_doc AS srcDoc, d.path AS srcPath, e.src_block AS srcBlock,
45
+ e.predicate AS predicate, e.provenance AS provenance, e.dst_kind AS dstKind,
46
+ e.dst_node AS dstNode, e.anchor AS anchor
47
+ FROM edges e
48
+ JOIN docs d ON d.doc_id = e.src_doc
49
+ WHERE e.repo_id = ? AND e.to_commit IS NULL AND d.deleted_commit IS NULL
50
+ AND e.dst_node LIKE 'phantom:%' ${globSql}
51
+ ORDER BY d.path, e.dst_node, e.src_block
52
+ LIMIT ?`)
53
+ .all(...params, limit + 1);
54
+ const truncated = rows.length > limit;
55
+ const page = rows.slice(0, limit);
56
+ // Recover the destination AS AUTHORED from the block's projected link nodes:
57
+ // the edge index keeps only the canonical target, but md:link / md:wikilink /
58
+ // md:inline_field nodes carry the exact destination text. Pick the node whose
59
+ // canonical (path, fragment) equals this edge's (target, anchor).
60
+ const nodeStmt = store.db.prepare("SELECT kind, value FROM nodes WHERE block_id = ? AND kind IN ('md:link','md:wikilink','md:inline_field') AND value IS NOT NULL");
61
+ const authoredFor = (srcBlock, srcPath, target, anchor) => {
62
+ if (srcBlock === null)
63
+ return null;
64
+ const docDir = docDirOf(srcPath);
65
+ for (const n of nodeStmt.all(srcBlock)) {
66
+ let dest = n.value;
67
+ if (n.kind === "md:inline_field") {
68
+ const wl = /^\[\[([^\]]+)\]\]$/.exec(dest);
69
+ if (wl)
70
+ dest = wl[1];
71
+ else if (!dest.startsWith("/"))
72
+ continue;
73
+ }
74
+ if (n.kind === "md:wikilink")
75
+ dest = dest.split("|")[0];
76
+ const { path, fragment } = splitDestination(dest);
77
+ if (canonicalLinkPath(path, docDir) !== target)
78
+ continue;
79
+ if ((fragment === "" ? null : fragment.slice(1)) !== anchor)
80
+ continue;
81
+ return dest;
82
+ }
83
+ return null;
84
+ };
85
+ const stale = page.map((r) => {
86
+ // Strip the "phantom:" prefix to expose the canonical path.
87
+ const target = r.dstNode.slice("phantom:".length);
88
+ return {
89
+ srcDoc: r.srcDoc,
90
+ srcPath: r.srcPath,
91
+ srcBlock: r.srcBlock,
92
+ predicate: r.predicate,
93
+ provenance: r.provenance,
94
+ dstKind: r.dstKind,
95
+ target,
96
+ authored: authoredFor(r.srcBlock, r.srcPath, target, r.anchor),
97
+ anchor: r.anchor,
98
+ // All phantom targets are dangling DOCUMENTS in this data model (cross-doc
99
+ // block refs collapse to document phantoms carrying an anchor). We tag rows
100
+ // that also carry an anchor as such, but the miss is still a missing doc.
101
+ reason: "dangling_doc",
102
+ };
103
+ });
104
+ return {
105
+ stale,
106
+ externalCount: totals.external ?? 0,
107
+ totalOpenEdges: totals.total,
108
+ truncated,
109
+ };
110
+ }
111
+ /**
112
+ * linksStaleSummary: the same scan as linksStale, reduced to counts grouped by
113
+ * missing target and by source document — a repo-wide audit in one small call
114
+ * (no per-row output, no limit). Scoping (`pathGlob` over SOURCE docs) and the
115
+ * tombstone rule match linksStale exactly, so the totals agree.
116
+ */
117
+ export function linksStaleSummary(store, repoId, opts = {}) {
118
+ const params = [repoId];
119
+ let globSql = "";
120
+ if (opts.pathGlob) {
121
+ const { clause, param } = globClause("d.path", opts.pathGlob);
122
+ globSql = `AND ${clause}`;
123
+ params.push(param);
124
+ }
125
+ const base = `FROM edges e JOIN docs d ON d.doc_id = e.src_doc
126
+ WHERE e.repo_id = ? AND e.to_commit IS NULL AND d.deleted_commit IS NULL ${globSql}`;
127
+ const totals = store.db
128
+ .prepare(`SELECT count(*) AS total, sum(CASE WHEN e.dst_kind = 'external' THEN 1 ELSE 0 END) AS external ${base}`)
129
+ .get(...params);
130
+ const phantom = `${base} AND e.dst_node LIKE 'phantom:%'`;
131
+ const byTarget = store.db
132
+ .prepare(`SELECT e.dst_node AS dstNode, count(*) AS count ${phantom} GROUP BY e.dst_node ORDER BY count DESC, e.dst_node`)
133
+ .all(...params)
134
+ .map((r) => ({ target: r.dstNode.slice("phantom:".length), count: r.count }));
135
+ const bySource = store.db
136
+ .prepare(`SELECT d.path AS srcPath, count(*) AS count ${phantom} GROUP BY d.path ORDER BY count DESC, d.path`)
137
+ .all(...params);
138
+ return {
139
+ staleCount: byTarget.reduce((n, t) => n + t.count, 0),
140
+ byTarget,
141
+ bySource,
142
+ externalCount: totals.external ?? 0,
143
+ totalOpenEdges: totals.total,
144
+ };
145
+ }
146
+ //# sourceMappingURL=link-health.js.map
@@ -0,0 +1,23 @@
1
+ import type { Store } from "../core/store/store.js";
2
+ export interface LinkEdge {
3
+ predicate: string;
4
+ /** the other endpoint (dst for out-edges, src for in-edges) */
5
+ node: string;
6
+ kind: string;
7
+ /** how many underlying block-grain edges this doc-grain row rolls up */
8
+ count: number;
9
+ /** sample source block ids (out-edges only) */
10
+ samples?: string[];
11
+ }
12
+ export interface LinksResult {
13
+ out: LinkEdge[];
14
+ in: LinkEdge[];
15
+ }
16
+ export interface LinksOptions {
17
+ direction?: "out" | "in" | "both";
18
+ predicates?: string[];
19
+ /** block-grain instead of the doc_edges rollup */
20
+ blocks?: boolean;
21
+ }
22
+ /** Open edges touching a document, grouped by direction (11 §5.4). */
23
+ export declare function docLinks(store: Store, docId: string, opts?: LinksOptions): LinksResult;
@@ -0,0 +1,42 @@
1
+ /** Open edges touching a document, grouped by direction (11 §5.4). */
2
+ export function docLinks(store, docId, opts = {}) {
3
+ const dir = opts.direction ?? "both";
4
+ const predFilter = opts.predicates && opts.predicates.length > 0 ? opts.predicates : null;
5
+ const inPred = (p) => !predFilter || predFilter.includes(p);
6
+ const out = [];
7
+ const inbound = [];
8
+ if (dir === "out" || dir === "both") {
9
+ if (opts.blocks) {
10
+ const rows = store.db
11
+ .prepare(`SELECT predicate, dst_node, dst_kind FROM edges
12
+ WHERE src_doc = ? AND to_commit IS NULL ORDER BY predicate, dst_node`)
13
+ .all(docId);
14
+ for (const r of rows)
15
+ if (inPred(r.predicate))
16
+ out.push({ predicate: r.predicate, node: r.dst_node, kind: r.dst_kind, count: 1 });
17
+ }
18
+ else {
19
+ const rows = store.db
20
+ .prepare(`SELECT predicate, dst_node, dst_kind, count, samples FROM doc_edges
21
+ WHERE src_doc = ? ORDER BY predicate, dst_node`)
22
+ .all(docId);
23
+ for (const r of rows) {
24
+ if (!inPred(r.predicate))
25
+ continue;
26
+ out.push({ predicate: r.predicate, node: r.dst_node, kind: r.dst_kind, count: r.count, samples: JSON.parse(r.samples) });
27
+ }
28
+ }
29
+ }
30
+ if (dir === "in" || dir === "both") {
31
+ const rows = store.db
32
+ .prepare(`SELECT predicate, src_doc, count(*) AS cnt FROM edges
33
+ WHERE dst_node = ? AND to_commit IS NULL
34
+ GROUP BY predicate, src_doc ORDER BY predicate, src_doc`)
35
+ .all(docId);
36
+ for (const r of rows)
37
+ if (inPred(r.predicate))
38
+ inbound.push({ predicate: r.predicate, node: r.src_doc, kind: "document", count: r.cnt });
39
+ }
40
+ return { out, in: inbound };
41
+ }
42
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1,60 @@
1
+ export { VERSION } from "./core/index.js";
2
+ export type { FormatAdapter, AdapterEdge, ProjectedNode, EmbeddingChunk, ReconcileHints } from "./format/adapter.js";
3
+ export { AdapterCapability } from "./format/adapter.js";
4
+ export { registerAdapter, adapterForFormat, adapterForPath, registeredFormats, detectFormat } from "./format/registry.js";
5
+ export { markdownAdapter, MARKDOWN_FORMAT } from "./format/markdown.js";
6
+ export { yamlAdapter, YAML_FORMAT } from "./format/yaml.js";
7
+ export { jsonAdapter, JSON_FORMAT } from "./format/json.js";
8
+ export { writeDocNodes, deleteDocNodes } from "./core/store/nodes.js";
9
+ export { docPropertiesMerged, docPropertiesGrouped, writeDocProperties, flattenFrontmatter } from "./core/store/properties.js";
10
+ import "./format/index.js";
11
+ export { Store, type StoreOptions } from "./core/store/store.js";
12
+ export { Workspace, RepoSelectionError, type RepoRow } from "./sync/workspace.js";
13
+ export { resolveSettings, workspaceSettings, repoOwnSettings, writeWorkspaceSettings, writeRepoSettings, deepMerge, type Settings, } from "./sync/settings.js";
14
+ export { freshnessSweep, rebuildFileStats, recordFileStat, detectDiskDrift, type SweepResult, type DiskDrift } from "./sync/freshness.js";
15
+ export { withWriterLock, writerLockFree, WriterLockTimeout } from "./sync/writer-lock.js";
16
+ export { WatchLease, watchLeaseLive } from "./sync/watch-lease.js";
17
+ export { ingestDirectory, type IngestDirResult } from "./sync/attach.js";
18
+ export { ensureRepo } from "./core/attach.js";
19
+ export { walkMarkdown, walkMarkdownAsync } from "./sync/fs-util.js";
20
+ export { Watcher, type WatcherOptions } from "./sync/watcher.js";
21
+ export type { SyncSource, SourceCapabilities, SourceIdentity, SourceEntry, SourceItem, SourceWatch, WatchListener, } from "./sync/plugin.js";
22
+ export { createExternalSource, type ExternalSourceSpec } from "./sync/external-source.js";
23
+ export { ensureAdapter, listAdapters, createSource, deleteSource, listSources, getSourceByName, attachSourceToRepo, detachSourceFromRepo, sourcesForRepo, renderConfigFlags, type AdapterRow, type SourceRow, } from "./sync/sources.js";
24
+ export { reconcileChanges, attachSource } from "./sync/driver.js";
25
+ export { observeFile, observeMany, observeOne, observeDelete, type ObserveResult, type ObserveOneResult, type ObserveDeleteResult } from "./sync/observe.js";
26
+ export { buildServer, type ServerContext } from "./mcp/server.js";
27
+ export { serveStdio, type ServeStdioHandle } from "./mcp/stdio.js";
28
+ export { processCheckpoint, type CheckpointResult } from "./sync/checkpoint.js";
29
+ export { reposStatus, syncStatus, type RepoStatus, type SyncStatus, type DiskStatus } from "./sync/admin.js";
30
+ export { docsOutline, type OutlineResult, type OutlineOptions } from "./core/read/outline.js";
31
+ export { docsRead, reconstructContent, type DocsReadResult, type DocsReadOptions } from "./core/read/document.js";
32
+ export { nodesGet, nodesGetMany, type GetNode, type Resolution } from "./core/read/nodes.js";
33
+ export { findDoc, loadDocBlocks, blockRaw, docsList, docsTree, normalizeTreePrefix, DOCS_LIST_DEFAULT_LIMIT, DOCS_TREE_DEFAULT_LIMIT, type DocInfo, type BlockNode, type DocListRow, type DocListPage, type DocListOptions, type DocTreeEntry, type DocTreePage, type DocTreeOptions, } from "./core/read/reader.js";
34
+ export { CursorInvalid, encodeCursor, decodeCursor } from "./core/cursor.js";
35
+ export { resolveRef, type ResolvedRef } from "./core/read/refs.js";
36
+ export { resolve, type ResolveHit, type ResolveInput } from "./search/resolve.js";
37
+ export { oqxRun, oqxRunAsync, collectSemanticPhrases, parseOqx, type OqxResult, type OqxHit, type OqxOptions, type EmbedQuery } from "./oqx/run.js";
38
+ export { EmbeddingWorker, SemanticUnavailable, contextPrefix, shouldEmbed, estimateTokens, type EmbeddingProvider, type EmbedTask, type DocEmbedTask, type DocEmbedBlockRef, type DocEmbedMethod, type DocVectorRow, } from "./search/embeddings.js";
39
+ export { buildEmbedTasks, buildDocEmbedTasks } from "./search/tasks.js";
40
+ export { EmbedDrainer, type DrainerOptions } from "./search/drain.js";
41
+ export { embeddingSettings, type EmbeddingSettings } from "./search/provider.js";
42
+ export { createExternalProvider, type ExternalProvider } from "./search/external.js";
43
+ export { hybridSearch, type HybridHit, type HybridInput } from "./search/rrf.js";
44
+ export { vectorSearch, docVectorSearch, type VectorHit, type DocVectorHit } from "./search/vector.js";
45
+ export { historyNode, diffUnified, changesSince, type NodeChange, type CommitDigest } from "./graph/history.js";
46
+ export { docLinks, type LinksResult, type LinkEdge, type LinksOptions } from "./graph/links.js";
47
+ export { FilterInvalid } from "./search/cel/parser.js";
48
+ export { EngineError, type ErrorCode } from "./mcp/errors.js";
49
+ export { MutationError } from "./mutate/tree.js";
50
+ export { isValidId, prefixOf } from "./core/ids.js";
51
+ export { apply, type ApplyRequest, type ApplyResult, type Op } from "./mutate/apply.js";
52
+ export { type DocStore, FsDocStore, NullDocStore, resolveDocStore } from "./mutate/doc-store.js";
53
+ export { type To, type At, type Expect } from "./mutate/ops.js";
54
+ export { tasksComplete, sectionsAppend, sectionsRename, sectionsMove, listsInsertItem, linksRetarget, nodeSet, editablePropsFor, type RetargetHit, } from "./mutate/macros.js";
55
+ export { docsCreate, docsMove, docsDelete, docsSetMeta, type DocOpContext, type DocOpResult, type DocMoveOptions, type DocMoveResult, } from "./mutate/docs.js";
56
+ export { inboundLinksTo, retargetLinksInRaw, type InboundLink } from "./graph/inbound-links.js";
57
+ export { planUpdate, applyOpset, docsUpdate, type PlanUpdateOptions, type ApplyOpsetRequest, type DocsUpdateContext, type DocsUpdateResult, } from "./mutate/plan-update.js";
58
+ export { opsetKernelOps, renderOpsetPlan, serializeOpset, parseOpset, type Opset, type PlanOp, type OpsetSummary, type OpsetPrecondition, type PlanDisposition, } from "./mutate/opset.js";
59
+ export { rebuildIndex, type RebuildTarget } from "./core/store/rebuild.js";
60
+ export { runGc, type GcResult } from "./core/store/gc.js";