@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,217 @@
1
+ // Markdown format adapter — wraps existing parse/render/extract modules.
2
+ import { AdapterCapability } from "./adapter.js";
3
+ import { parseTree, assertFullCoverage } from "../core/parse/tree.js";
4
+ import { render } from "../core/parse/render.js";
5
+ import { extractFromBlock, extractFromFrontmatter, maskCode } from "../graph/extract.js";
6
+ import { parse as parseYaml } from "yaml";
7
+ export const MARKDOWN_FORMAT = "markdown";
8
+ function toAdapterEdge(e) {
9
+ return {
10
+ srcBlock: e.srcBlock,
11
+ srcField: e.srcField,
12
+ predicate: e.predicate,
13
+ dstKind: e.dstKind,
14
+ target: e.target,
15
+ anchor: e.anchor,
16
+ provenance: e.provenance,
17
+ };
18
+ }
19
+ export const markdownAdapter = {
20
+ format: MARKDOWN_FORMAT,
21
+ extensions: [".md", ".markdown"],
22
+ capabilities: new Set([
23
+ AdapterCapability.Parse,
24
+ AdapterCapability.Render,
25
+ AdapterCapability.ExtractEdges,
26
+ AdapterCapability.ExtractMetadata,
27
+ AdapterCapability.ComputeProperties,
28
+ AdapterCapability.StructuralMutation,
29
+ AdapterCapability.ProjectNodes,
30
+ ]),
31
+ parse(source) {
32
+ return parseTree(source);
33
+ },
34
+ render(tree) {
35
+ return render(tree);
36
+ },
37
+ extractEdges(blocks, metadata) {
38
+ const edges = [];
39
+ // Walk every block (recursing into list/blockquote/table nesting) and scan
40
+ // its raw for links. srcBlock is the block's OWN assigned id — the caller
41
+ // passes id-assigned blocks (blockId zipped on), mirroring projectNodes; a
42
+ // block-grain edge without it is useless, so fall back to "" only for the
43
+ // pre-identity callers. extractFromBlock returns [] for link-free blocks, so
44
+ // no per-block gate is needed (matches the non-adapter fallback path).
45
+ const walk = (list) => {
46
+ for (const block of list) {
47
+ for (const e of extractFromBlock(block.blockId ?? "", block.type, block.raw)) {
48
+ edges.push(toAdapterEdge(e));
49
+ }
50
+ if (block.children.length > 0)
51
+ walk(block.children);
52
+ }
53
+ };
54
+ walk(blocks);
55
+ if (metadata) {
56
+ for (const e of extractFromFrontmatter(metadata))
57
+ edges.push(toAdapterEdge(e));
58
+ }
59
+ return edges;
60
+ },
61
+ extractMetadata(source) {
62
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source);
63
+ if (!match)
64
+ return null;
65
+ try {
66
+ const parsed = parseYaml(match[1]);
67
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
68
+ ? parsed
69
+ : null;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ },
75
+ projectNodes(blocks) {
76
+ const nodes = [];
77
+ // Byte span of a regex match within the block's raw bytes (node-locates the
78
+ // feature inside its block; disambiguates multiple same-kind nodes — 13/nodes).
79
+ const span = (m) => ({
80
+ spanStart: m.index ?? 0,
81
+ spanEnd: (m.index ?? 0) + m[0].length,
82
+ });
83
+ const walk = (list, parentId) => {
84
+ for (const b of list) {
85
+ // Anchor to the block's OWN assigned id (zipped on by ingest); fall back
86
+ // to the parent's id, then "root", for pre-identity/parse-time callers.
87
+ const id = b.blockId || parentId || "root";
88
+ // Code is not prose (extraction_version x2): a code_fence block projects
89
+ // no link/anchor/field nodes, and inline code spans are masked before the
90
+ // regex scans below. Same rule as graph/extract.ts so the md:link /
91
+ // md:wikilink nodes and the `references` edges agree. maskCode is
92
+ // length-preserving, so spans still index the ORIGINAL raw.
93
+ const scan = b.type === "code_fence" ? "" : maskCode(b.raw);
94
+ // Links: [text](target)
95
+ for (const m of scan.matchAll(/\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g)) {
96
+ nodes.push({ kind: "md:link", name: m[1], value: m[2], blockId: id, ...span(m) });
97
+ }
98
+ // Wikilinks: [[target]]
99
+ for (const m of scan.matchAll(/\[\[([^\]]+)\]\]/g)) {
100
+ nodes.push({ kind: "md:wikilink", value: m[1], blockId: id, ...span(m) });
101
+ }
102
+ // Tasks: checkbox items (the whole block; span covers its raw)
103
+ if (b.type === "task") {
104
+ const checked = b.attrs.checked === true;
105
+ nodes.push({ kind: "md:task", value: b.text, blockId: id, attrs: { checked }, spanStart: 0, spanEnd: b.raw.length });
106
+ }
107
+ // Anchors: ^ref
108
+ const anchorRe = /\^([a-zA-Z0-9_-]+)/g;
109
+ for (const m of scan.matchAll(anchorRe)) {
110
+ nodes.push({ kind: "md:anchor", name: m[1], blockId: id, ...span(m) });
111
+ }
112
+ // Inline fields (dataview forms). Two shapes, so a multi-word value is
113
+ // captured whole instead of truncated at the first space:
114
+ // line form `key:: value` — key at line start, value to end of line
115
+ // bracketed `[key:: value]` / `(key:: value)` — value ends at closer
116
+ // Bracketed matches first; the line form is anchored to a line start
117
+ // (leading whitespace only), so a `[key:: …]`/`(key:: …)` sitting on its
118
+ // own line is not double-counted (a bracket is not `[a-z]`).
119
+ for (const m of scan.matchAll(/[[(]([a-z][a-z0-9_]*)::[ \t]*([^\]\n)]*?)[ \t]*[\])]/gi)) {
120
+ nodes.push({ kind: "md:inline_field", name: m[1], value: m[2], blockId: id, ...span(m) });
121
+ }
122
+ for (const m of scan.matchAll(/^[ \t]*([a-z][a-z0-9_]*)::[ \t]*([^\n]*?)[ \t]*$/gim)) {
123
+ const start = (m.index ?? 0) + (m[0].match(/^[ \t]*/)?.[0].length ?? 0);
124
+ const end = (m.index ?? 0) + m[0].replace(/[ \t]+$/, "").length;
125
+ nodes.push({ kind: "md:inline_field", name: m[1], value: m[2], blockId: id, spanStart: start, spanEnd: end });
126
+ }
127
+ if (b.children.length > 0)
128
+ walk(b.children, id);
129
+ }
130
+ };
131
+ walk(blocks, "");
132
+ return nodes;
133
+ },
134
+ // Editable node properties (node-editability). Each editor rewrites ONLY the
135
+ // node's own span within the block's raw bytes, then returns a block `update`
136
+ // (markdown or attrs). The span pins the exact occurrence, so editing the
137
+ // first of several same-kind nodes in one block is unambiguous.
138
+ nodeEditors: {
139
+ "md:link": {
140
+ // Retype the link text: [OLD](target) → [NEW](target), this occurrence only.
141
+ name: ({ blockRaw, span, node }, newValue) => {
142
+ if (!span)
143
+ throw new Error("md:link.name requires a recorded span");
144
+ const seg = blockRaw.slice(span.start, span.end);
145
+ const rebuilt = seg.replace(/^\[[^\]]*\]/, `[${newValue}]`);
146
+ if (rebuilt === seg)
147
+ throw new Error(`could not locate link text in ${JSON.stringify(seg)}`);
148
+ void node;
149
+ return { markdown: blockRaw.slice(0, span.start) + rebuilt + blockRaw.slice(span.end) };
150
+ },
151
+ // Retarget the link: [text](OLD) → [text](NEW), this occurrence only.
152
+ value: ({ blockRaw, span }, newValue) => {
153
+ if (!span)
154
+ throw new Error("md:link.value requires a recorded span");
155
+ const seg = blockRaw.slice(span.start, span.end);
156
+ const rebuilt = seg.replace(/\]\(([^)\s]+)(\s+"[^"]*")?\)$/, (_m, _url, title) => `](${newValue}${title ?? ""})`);
157
+ if (rebuilt === seg)
158
+ throw new Error(`could not locate link target in ${JSON.stringify(seg)}`);
159
+ return { markdown: blockRaw.slice(0, span.start) + rebuilt + blockRaw.slice(span.end) };
160
+ },
161
+ },
162
+ "md:task": {
163
+ // A task's checkbox is a typed block attribute, not a byte edit.
164
+ checked: (_ctx, newValue) => ({ attrs: { checked: newValue === "true" || newValue === "1" } }),
165
+ },
166
+ },
167
+ // Computed properties surfaced as $-intrinsics (12 §4). These are engine-
168
+ // derived and never claim the authored `title`/`tags` keys.
169
+ // $title — text of the first level-1 heading
170
+ // $tags — distinct #hashtags found in body text (order-preserving)
171
+ computeProperties(blocks) {
172
+ const out = {};
173
+ const findH1 = (list) => {
174
+ for (const b of list) {
175
+ if (b.type === "heading" && b.attrs.level === 1 && b.text.trim())
176
+ return b.text.trim();
177
+ const nested = findH1(b.children);
178
+ if (nested)
179
+ return nested;
180
+ }
181
+ return undefined;
182
+ };
183
+ const title = findH1(blocks);
184
+ if (title)
185
+ out.$title = title;
186
+ const tags = [];
187
+ const seen = new Set();
188
+ const scan = (list) => {
189
+ for (const b of list) {
190
+ // #tag — a hash followed by a word char, not inside a heading marker or
191
+ // a code fence. Matches Obsidian-style inline tags in prose.
192
+ if (b.type !== "heading" && b.type !== "code_fence") {
193
+ for (const m of b.raw.matchAll(/(?:^|\s)#([a-zA-Z][\w/-]*)/g)) {
194
+ const t = m[1];
195
+ if (!seen.has(t)) {
196
+ seen.add(t);
197
+ tags.push(t);
198
+ }
199
+ }
200
+ }
201
+ scan(b.children);
202
+ }
203
+ };
204
+ scan(blocks);
205
+ if (tags.length > 0)
206
+ out.$tags = tags;
207
+ return out;
208
+ },
209
+ reconcileHints() {
210
+ return {
211
+ anchorEvidenceKeys: ["lang", "info"],
212
+ smallBlockTokens: 8,
213
+ };
214
+ },
215
+ };
216
+ export { assertFullCoverage };
217
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1,6 @@
1
+ import type { FormatAdapter } from "./adapter.js";
2
+ export declare function registerAdapter(adapter: FormatAdapter): void;
3
+ export declare function adapterForFormat(format: string): FormatAdapter | undefined;
4
+ export declare function adapterForPath(filePath: string): FormatAdapter | undefined;
5
+ export declare function registeredFormats(): string[];
6
+ export declare function detectFormat(filePath: string): string | undefined;
@@ -0,0 +1,24 @@
1
+ // Format adapter registry. Maps file extensions and format names to adapters.
2
+ import { extname } from "node:path";
3
+ const byFormat = new Map();
4
+ const byExtension = new Map();
5
+ export function registerAdapter(adapter) {
6
+ byFormat.set(adapter.format, adapter);
7
+ for (const ext of adapter.extensions) {
8
+ byExtension.set(ext.toLowerCase(), adapter);
9
+ }
10
+ }
11
+ export function adapterForFormat(format) {
12
+ return byFormat.get(format);
13
+ }
14
+ export function adapterForPath(filePath) {
15
+ const ext = extname(filePath).toLowerCase();
16
+ return byExtension.get(ext);
17
+ }
18
+ export function registeredFormats() {
19
+ return [...byFormat.keys()];
20
+ }
21
+ export function detectFormat(filePath) {
22
+ return adapterForPath(filePath)?.format;
23
+ }
24
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,5 @@
1
+ import { type FormatAdapter, type AdapterEdge } from "./adapter.js";
2
+ export declare const YAML_FORMAT = "yaml";
3
+ declare function extractYamlEdges(source: string): AdapterEdge[];
4
+ export declare const yamlAdapter: FormatAdapter;
5
+ export { extractYamlEdges };
@@ -0,0 +1,413 @@
1
+ // YAML format adapter. Decomposes YAML files into blocks with yaml:-prefixed
2
+ // kinds, extracts $ref/extends/$schema edges, and projects top-level mapping
3
+ // entries as metadata.
4
+ import { parseDocument, isMap, isSeq, isScalar, } from "yaml";
5
+ import { AdapterCapability } from "./adapter.js";
6
+ import { normalizeText } from "../core/hash.js";
7
+ export const YAML_FORMAT = "yaml";
8
+ // Block kinds for YAML structures.
9
+ const K = {
10
+ mapping: "yaml:mapping",
11
+ mapping_entry: "yaml:mapping_entry",
12
+ sequence: "yaml:sequence",
13
+ sequence_item: "yaml:sequence_item",
14
+ scalar: "yaml:scalar",
15
+ };
16
+ function span(start, end) {
17
+ return { start, end };
18
+ }
19
+ function emptyBlock(kind, s, raw) {
20
+ return {
21
+ type: kind,
22
+ span: s,
23
+ raw,
24
+ text: normalizeText(raw),
25
+ attrs: {},
26
+ children: [],
27
+ trivia: "",
28
+ anchors: [],
29
+ outLinks: [],
30
+ };
31
+ }
32
+ // Build a block from a Pair (key: value mapping entry).
33
+ function blockFromPair(pair, source, depth) {
34
+ const keyNode = pair.key;
35
+ const valNode = pair.value;
36
+ // Determine the span covering the full pair (key + value).
37
+ let start = Infinity;
38
+ let end = 0;
39
+ for (const n of [keyNode, valNode]) {
40
+ if (n && isScalar(n) && n.range) {
41
+ start = Math.min(start, n.range[0]);
42
+ end = Math.max(end, n.range[2] ?? n.range[1]);
43
+ }
44
+ else if (n && (isMap(n) || isSeq(n)) && n.range) {
45
+ start = Math.min(start, n.range[0]);
46
+ end = Math.max(end, n.range[2] ?? n.range[1]);
47
+ }
48
+ }
49
+ // If we can't determine span (shouldn't happen with parsed YAML), skip.
50
+ if (!isFinite(start) || end === 0)
51
+ return null;
52
+ const raw = source.slice(start, end);
53
+ const keyStr = isScalar(keyNode) ? String(keyNode.value) : "";
54
+ const attrs = { key: keyStr };
55
+ const children = [];
56
+ // Recurse into nested maps/seqs as child blocks.
57
+ if (isMap(valNode)) {
58
+ for (const item of valNode.items) {
59
+ const child = blockFromPair(item, source, depth + 1);
60
+ if (child)
61
+ children.push(child);
62
+ }
63
+ }
64
+ else if (isSeq(valNode)) {
65
+ for (let i = 0; i < valNode.items.length; i++) {
66
+ const item = valNode.items[i];
67
+ if (isScalar(item) && item.range) {
68
+ const iStart = item.range[0];
69
+ const iEnd = item.range[2] ?? item.range[1];
70
+ const iRaw = source.slice(iStart, iEnd);
71
+ const child = emptyBlock(K.sequence_item, span(iStart, iEnd), iRaw);
72
+ child.attrs = { index: i, value: item.value };
73
+ children.push(child);
74
+ }
75
+ else if (isMap(item) && item.range) {
76
+ const iStart = item.range[0];
77
+ const iEnd = item.range[2] ?? item.range[1];
78
+ const iRaw = source.slice(iStart, iEnd);
79
+ const child = emptyBlock(K.mapping, span(iStart, iEnd), iRaw);
80
+ for (const sub of item.items) {
81
+ const subBlock = blockFromPair(sub, source, depth + 2);
82
+ if (subBlock)
83
+ child.children.push(subBlock);
84
+ }
85
+ children.push(child);
86
+ }
87
+ else if (isSeq(item) && item.range) {
88
+ const iStart = item.range[0];
89
+ const iEnd = item.range[2] ?? item.range[1];
90
+ const iRaw = source.slice(iStart, iEnd);
91
+ const child = emptyBlock(K.sequence, span(iStart, iEnd), iRaw);
92
+ children.push(child);
93
+ }
94
+ }
95
+ }
96
+ const block = emptyBlock(K.mapping_entry, span(start, end), raw);
97
+ block.attrs = attrs;
98
+ block.children = children;
99
+ return block;
100
+ }
101
+ function parseYamlBlocks(source) {
102
+ let doc;
103
+ try {
104
+ doc = parseDocument(source, { keepSourceTokens: true });
105
+ }
106
+ catch {
107
+ // Malformed YAML: return a single opaque block.
108
+ return [emptyBlock("yaml:opaque", span(0, source.length), source)];
109
+ }
110
+ const contents = doc.contents;
111
+ if (!contents) {
112
+ return source.trim().length > 0
113
+ ? [emptyBlock(K.scalar, span(0, source.length), source)]
114
+ : [];
115
+ }
116
+ const blocks = [];
117
+ if (isMap(contents)) {
118
+ for (const pair of contents.items) {
119
+ const block = blockFromPair(pair, source, 0);
120
+ if (block)
121
+ blocks.push(block);
122
+ }
123
+ }
124
+ else if (isSeq(contents)) {
125
+ for (let i = 0; i < contents.items.length; i++) {
126
+ const item = contents.items[i];
127
+ if (isScalar(item) && item.range) {
128
+ const start = item.range[0];
129
+ const end = item.range[2] ?? item.range[1];
130
+ const block = emptyBlock(K.sequence_item, span(start, end), source.slice(start, end));
131
+ block.attrs = { index: i, value: item.value };
132
+ blocks.push(block);
133
+ }
134
+ else if (isMap(item) && item.range) {
135
+ const start = item.range[0];
136
+ const end = item.range[2] ?? item.range[1];
137
+ const block = emptyBlock(K.mapping, span(start, end), source.slice(start, end));
138
+ for (const sub of item.items) {
139
+ const child = blockFromPair(sub, source, 1);
140
+ if (child)
141
+ block.children.push(child);
142
+ }
143
+ blocks.push(block);
144
+ }
145
+ }
146
+ }
147
+ else if (isScalar(contents) && contents.range) {
148
+ const start = contents.range[0];
149
+ const end = contents.range[2] ?? contents.range[1];
150
+ blocks.push(emptyBlock(K.scalar, span(start, end), source.slice(start, end)));
151
+ }
152
+ return blocks;
153
+ }
154
+ // Attach trivia (inter-block whitespace) to blocks, same algorithm as markdown.
155
+ function buildTree(source) {
156
+ const children = parseYamlBlocks(source);
157
+ const leadingTrivia = children.length === 0 ? source : source.slice(0, children[0].span.start);
158
+ for (let i = 0; i < children.length; i++) {
159
+ const block = children[i];
160
+ const next = children[i + 1];
161
+ const triviaEnd = next ? next.span.start : source.length;
162
+ block.trivia = source.slice(block.span.end, triviaEnd);
163
+ }
164
+ return { source, leadingTrivia, children };
165
+ }
166
+ // Edge extraction: $ref, extends, $schema, and path-like string values.
167
+ function extractYamlEdges(source) {
168
+ let doc;
169
+ try {
170
+ doc = parseDocument(source);
171
+ }
172
+ catch {
173
+ return [];
174
+ }
175
+ const edges = [];
176
+ const seen = new Set();
177
+ function push(e) {
178
+ const k = `${e.predicate}|${e.target}|${e.provenance}`;
179
+ if (seen.has(k))
180
+ return;
181
+ seen.add(k);
182
+ edges.push(e);
183
+ }
184
+ function walkValue(key, value) {
185
+ if (typeof value !== "string")
186
+ return;
187
+ if (key === "$ref" || key === "\\$ref") {
188
+ const target = value.startsWith("#") ? "" : value.split("#")[0];
189
+ if (target) {
190
+ push({
191
+ srcBlock: null,
192
+ srcField: "$ref",
193
+ predicate: "references",
194
+ dstKind: isExternalUri(target) ? "external" : "document",
195
+ target,
196
+ anchor: value.includes("#") ? value.split("#")[1] : null,
197
+ provenance: "yaml_ref",
198
+ });
199
+ }
200
+ return;
201
+ }
202
+ if (key === "$schema") {
203
+ push({
204
+ srcBlock: null,
205
+ srcField: "$schema",
206
+ predicate: "schema",
207
+ dstKind: isExternalUri(value) ? "external" : "document",
208
+ target: value,
209
+ anchor: null,
210
+ provenance: "yaml_schema",
211
+ });
212
+ return;
213
+ }
214
+ if (key === "extends") {
215
+ push({
216
+ srcBlock: null,
217
+ srcField: "extends",
218
+ predicate: "extends",
219
+ dstKind: isExternalUri(value) ? "external" : "document",
220
+ target: value,
221
+ anchor: null,
222
+ provenance: "yaml_extends",
223
+ });
224
+ return;
225
+ }
226
+ // Path-like string values: ./path or /path (not URLs, not bare words).
227
+ if ((value.startsWith("./") || value.startsWith("../") || value.startsWith("/")) && !value.includes(" ")) {
228
+ push({
229
+ srcBlock: null,
230
+ srcField: key,
231
+ predicate: key,
232
+ dstKind: "document",
233
+ target: value,
234
+ anchor: null,
235
+ provenance: "yaml_ref",
236
+ });
237
+ }
238
+ }
239
+ function walkNode(node, parentKey) {
240
+ if (isMap(node)) {
241
+ for (const pair of node.items) {
242
+ const key = isScalar(pair.key) ? String(pair.key.value) : parentKey;
243
+ if (isScalar(pair.value)) {
244
+ walkValue(key, pair.value.value);
245
+ }
246
+ else {
247
+ walkNode(pair.value, key);
248
+ }
249
+ }
250
+ }
251
+ else if (isSeq(node)) {
252
+ for (const item of node.items) {
253
+ if (isScalar(item)) {
254
+ walkValue(parentKey, item.value);
255
+ }
256
+ else {
257
+ walkNode(item, parentKey);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ walkNode(doc.contents, "");
263
+ return edges;
264
+ }
265
+ function isExternalUri(s) {
266
+ return /^https?:\/\//.test(s);
267
+ }
268
+ export const yamlAdapter = {
269
+ format: YAML_FORMAT,
270
+ extensions: [".yaml", ".yml"],
271
+ capabilities: new Set([
272
+ AdapterCapability.Parse,
273
+ AdapterCapability.Render,
274
+ AdapterCapability.ExtractEdges,
275
+ AdapterCapability.ExtractMetadata,
276
+ AdapterCapability.ProjectNodes,
277
+ AdapterCapability.StructuralMutation,
278
+ ]),
279
+ parse(source) {
280
+ return buildTree(source);
281
+ },
282
+ render(tree) {
283
+ // Splice renderer: untouched blocks emit raw verbatim, preserving comments
284
+ // and formatting. Same strategy as the markdown adapter.
285
+ const out = [tree.leadingTrivia];
286
+ for (const block of tree.children) {
287
+ out.push(block.raw);
288
+ out.push(block.trivia);
289
+ }
290
+ return out.join("");
291
+ },
292
+ extractEdges(_blocks, metadata) {
293
+ // YAML edge extraction works from the parsed document, not individual blocks,
294
+ // since references can appear at any nesting depth. We re-extract from the
295
+ // full source stored in blocks[0]'s parent tree, but since we receive
296
+ // metadata (the parsed top-level mapping), we walk that instead.
297
+ if (!metadata)
298
+ return [];
299
+ // Walk the metadata object for edges. For full extraction we'd need the
300
+ // source, so we implement a lightweight object-walking extractor here.
301
+ const edges = [];
302
+ const seen = new Set();
303
+ function push(e) {
304
+ const k = `${e.predicate}|${e.target}|${e.provenance}`;
305
+ if (seen.has(k))
306
+ return;
307
+ seen.add(k);
308
+ edges.push(e);
309
+ }
310
+ function walk(obj, parentKey) {
311
+ if (typeof obj === "string") {
312
+ walkStringValue(parentKey, obj, push);
313
+ }
314
+ else if (Array.isArray(obj)) {
315
+ for (const item of obj)
316
+ walk(item, parentKey);
317
+ }
318
+ else if (obj && typeof obj === "object") {
319
+ for (const [k, v] of Object.entries(obj)) {
320
+ walk(v, k);
321
+ }
322
+ }
323
+ }
324
+ walk(metadata, "");
325
+ return edges;
326
+ },
327
+ projectNodes(blocks) {
328
+ const nodes = [];
329
+ const walk = (list) => {
330
+ for (const b of list) {
331
+ const id = b.blockId ?? "";
332
+ // $ref values
333
+ if (b.type === "yaml:mapping_entry" && b.attrs.key === "$ref") {
334
+ const val = b.raw.replace(/^[^:]*:\s*/, "").trim();
335
+ nodes.push({ kind: "yaml:ref", name: "$ref", value: val, blockId: id });
336
+ }
337
+ // $schema values
338
+ if (b.type === "yaml:mapping_entry" && b.attrs.key === "$schema") {
339
+ const val = b.raw.replace(/^[^:]*:\s*/, "").trim();
340
+ nodes.push({ kind: "yaml:schema", name: "$schema", value: val, blockId: id });
341
+ }
342
+ // YAML anchors &name
343
+ for (const m of b.raw.matchAll(/&([a-zA-Z_][a-zA-Z0-9_]*)/g)) {
344
+ nodes.push({ kind: "yaml:anchor", name: m[1], blockId: id });
345
+ }
346
+ // YAML aliases *name
347
+ for (const m of b.raw.matchAll(/\*([a-zA-Z_][a-zA-Z0-9_]*)/g)) {
348
+ nodes.push({ kind: "yaml:alias", name: m[1], blockId: id });
349
+ }
350
+ // Environment variable references ${VAR}
351
+ for (const m of b.raw.matchAll(/\$\{([A-Z_][A-Z0-9_]*)\}/g)) {
352
+ nodes.push({ kind: "yaml:env_var", name: m[1], blockId: id });
353
+ }
354
+ if (b.children.length > 0)
355
+ walk(b.children);
356
+ }
357
+ };
358
+ walk(blocks);
359
+ return nodes;
360
+ },
361
+ extractMetadata(source) {
362
+ try {
363
+ const doc = parseDocument(source);
364
+ const obj = doc.toJS();
365
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
366
+ return obj;
367
+ }
368
+ return null;
369
+ }
370
+ catch {
371
+ return null;
372
+ }
373
+ },
374
+ };
375
+ function walkStringValue(key, value, push) {
376
+ if (key === "$ref") {
377
+ const target = value.startsWith("#") ? "" : value.split("#")[0];
378
+ if (target) {
379
+ push({
380
+ srcBlock: null, srcField: "$ref", predicate: "references",
381
+ dstKind: isExternalUri(target) ? "external" : "document",
382
+ target, anchor: value.includes("#") ? value.split("#")[1] : null,
383
+ provenance: "yaml_ref",
384
+ });
385
+ }
386
+ return;
387
+ }
388
+ if (key === "$schema") {
389
+ push({
390
+ srcBlock: null, srcField: "$schema", predicate: "schema",
391
+ dstKind: isExternalUri(value) ? "external" : "document",
392
+ target: value, anchor: null, provenance: "yaml_schema",
393
+ });
394
+ return;
395
+ }
396
+ if (key === "extends") {
397
+ push({
398
+ srcBlock: null, srcField: "extends", predicate: "extends",
399
+ dstKind: isExternalUri(value) ? "external" : "document",
400
+ target: value, anchor: null, provenance: "yaml_extends",
401
+ });
402
+ return;
403
+ }
404
+ if ((value.startsWith("./") || value.startsWith("../") || value.startsWith("/")) && !value.includes(" ")) {
405
+ push({
406
+ srcBlock: null, srcField: key, predicate: key,
407
+ dstKind: "document", target: value, anchor: null, provenance: "yaml_ref",
408
+ });
409
+ }
410
+ }
411
+ // Also export the standalone source-based extractor for direct use.
412
+ export { extractYamlEdges };
413
+ //# sourceMappingURL=yaml.js.map