@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.
- package/LICENSE +21 -0
- package/dist/src/cli/eval-matcher.d.ts +13 -0
- package/dist/src/cli/eval-matcher.js +36 -0
- package/dist/src/core/attach.d.ts +5 -0
- package/dist/src/core/attach.js +38 -0
- package/dist/src/core/cursor.d.ts +11 -0
- package/dist/src/core/cursor.js +35 -0
- package/dist/src/core/hash.d.ts +23 -0
- package/dist/src/core/hash.js +82 -0
- package/dist/src/core/ids.d.ts +6 -0
- package/dist/src/core/ids.js +26 -0
- package/dist/src/core/index.d.ts +1 -0
- package/dist/src/core/index.js +2 -0
- package/dist/src/core/ingest.d.ts +43 -0
- package/dist/src/core/ingest.js +287 -0
- package/dist/src/core/order-key.d.ts +8 -0
- package/dist/src/core/order-key.js +72 -0
- package/dist/src/core/parse/parse.d.ts +3 -0
- package/dist/src/core/parse/parse.js +104 -0
- package/dist/src/core/parse/render.d.ts +3 -0
- package/dist/src/core/parse/render.js +20 -0
- package/dist/src/core/parse/tree.d.ts +8 -0
- package/dist/src/core/parse/tree.js +43 -0
- package/dist/src/core/parse/types.d.ts +52 -0
- package/dist/src/core/parse/types.js +9 -0
- package/dist/src/core/read/document.d.ts +107 -0
- package/dist/src/core/read/document.js +191 -0
- package/dist/src/core/read/nodes.d.ts +47 -0
- package/dist/src/core/read/nodes.js +102 -0
- package/dist/src/core/read/outline.d.ts +12 -0
- package/dist/src/core/read/outline.js +76 -0
- package/dist/src/core/read/reader.d.ts +125 -0
- package/dist/src/core/read/reader.js +206 -0
- package/dist/src/core/read/refs.d.ts +15 -0
- package/dist/src/core/read/refs.js +51 -0
- package/dist/src/core/store/edges.d.ts +26 -0
- package/dist/src/core/store/edges.js +101 -0
- package/dist/src/core/store/fts.d.ts +10 -0
- package/dist/src/core/store/fts.js +22 -0
- package/dist/src/core/store/gc.d.ts +18 -0
- package/dist/src/core/store/gc.js +69 -0
- package/dist/src/core/store/nodes.d.ts +5 -0
- package/dist/src/core/store/nodes.js +57 -0
- package/dist/src/core/store/properties.d.ts +38 -0
- package/dist/src/core/store/properties.js +177 -0
- package/dist/src/core/store/rebuild.d.ts +3 -0
- package/dist/src/core/store/rebuild.js +29 -0
- package/dist/src/core/store/schema.d.ts +9 -0
- package/dist/src/core/store/schema.js +386 -0
- package/dist/src/core/store/sections.d.ts +2 -0
- package/dist/src/core/store/sections.js +29 -0
- package/dist/src/core/store/store.d.ts +24 -0
- package/dist/src/core/store/store.js +164 -0
- package/dist/src/core/store/writers.d.ts +52 -0
- package/dist/src/core/store/writers.js +89 -0
- package/dist/src/core/vec.d.ts +10 -0
- package/dist/src/core/vec.js +33 -0
- package/dist/src/format/adapter.d.ts +97 -0
- package/dist/src/format/adapter.js +14 -0
- package/dist/src/format/index.d.ts +6 -0
- package/dist/src/format/index.js +15 -0
- package/dist/src/format/json.d.ts +3 -0
- package/dist/src/format/json.js +324 -0
- package/dist/src/format/markdown.d.ts +5 -0
- package/dist/src/format/markdown.js +217 -0
- package/dist/src/format/registry.d.ts +6 -0
- package/dist/src/format/registry.js +24 -0
- package/dist/src/format/yaml.d.ts +5 -0
- package/dist/src/format/yaml.js +413 -0
- package/dist/src/graph/extract.d.ts +38 -0
- package/dist/src/graph/extract.js +242 -0
- package/dist/src/graph/history.d.ts +103 -0
- package/dist/src/graph/history.js +210 -0
- package/dist/src/graph/inbound-links.d.ts +33 -0
- package/dist/src/graph/inbound-links.js +130 -0
- package/dist/src/graph/link-destinations.d.ts +22 -0
- package/dist/src/graph/link-destinations.js +84 -0
- package/dist/src/graph/link-health.d.ts +79 -0
- package/dist/src/graph/link-health.js +146 -0
- package/dist/src/graph/links.d.ts +23 -0
- package/dist/src/graph/links.js +42 -0
- package/dist/src/index.d.ts +60 -0
- package/dist/src/index.js +70 -0
- package/dist/src/mcp/errors.d.ts +17 -0
- package/dist/src/mcp/errors.js +18 -0
- package/dist/src/mcp/graph.d.ts +63 -0
- package/dist/src/mcp/graph.js +212 -0
- package/dist/src/mcp/reference.d.ts +1 -0
- package/dist/src/mcp/reference.js +227 -0
- package/dist/src/mcp/server.d.ts +30 -0
- package/dist/src/mcp/server.js +1076 -0
- package/dist/src/mcp/stdio.d.ts +13 -0
- package/dist/src/mcp/stdio.js +24 -0
- package/dist/src/migrate/mrplex.d.ts +27 -0
- package/dist/src/migrate/mrplex.js +29 -0
- package/dist/src/mutate/apply.d.ts +83 -0
- package/dist/src/mutate/apply.js +302 -0
- package/dist/src/mutate/doc-store.d.ts +49 -0
- package/dist/src/mutate/doc-store.js +71 -0
- package/dist/src/mutate/docs.d.ts +60 -0
- package/dist/src/mutate/docs.js +232 -0
- package/dist/src/mutate/known-ids.d.ts +12 -0
- package/dist/src/mutate/known-ids.js +154 -0
- package/dist/src/mutate/load.d.ts +3 -0
- package/dist/src/mutate/load.js +66 -0
- package/dist/src/mutate/lower.d.ts +32 -0
- package/dist/src/mutate/lower.js +232 -0
- package/dist/src/mutate/macros.d.ts +91 -0
- package/dist/src/mutate/macros.js +230 -0
- package/dist/src/mutate/ops.d.ts +40 -0
- package/dist/src/mutate/ops.js +513 -0
- package/dist/src/mutate/opset.d.ts +92 -0
- package/dist/src/mutate/opset.js +79 -0
- package/dist/src/mutate/plan-update.d.ts +51 -0
- package/dist/src/mutate/plan-update.js +187 -0
- package/dist/src/mutate/tree.d.ts +46 -0
- package/dist/src/mutate/tree.js +167 -0
- package/dist/src/oqx/run.d.ts +1 -0
- package/dist/src/oqx/run.js +6 -0
- package/dist/src/oqx-js/context.d.ts +23 -0
- package/dist/src/oqx-js/context.js +564 -0
- package/dist/src/oqx-js/planner.d.ts +10 -0
- package/dist/src/oqx-js/planner.js +91 -0
- package/dist/src/oqx-js/run.d.ts +41 -0
- package/dist/src/oqx-js/run.js +332 -0
- package/dist/src/oqx-js/sql/translate.d.ts +27 -0
- package/dist/src/oqx-js/sql/translate.js +290 -0
- package/dist/src/reconcile/crossdoc.d.ts +34 -0
- package/dist/src/reconcile/crossdoc.js +76 -0
- package/dist/src/reconcile/eval/generator.d.ts +21 -0
- package/dist/src/reconcile/eval/generator.js +129 -0
- package/dist/src/reconcile/eval/metrics.d.ts +32 -0
- package/dist/src/reconcile/eval/metrics.js +74 -0
- package/dist/src/reconcile/flatten.d.ts +12 -0
- package/dist/src/reconcile/flatten.js +37 -0
- package/dist/src/reconcile/phase5.d.ts +8 -0
- package/dist/src/reconcile/phase5.js +167 -0
- package/dist/src/reconcile/phase6a.d.ts +2 -0
- package/dist/src/reconcile/phase6a.js +159 -0
- package/dist/src/reconcile/phases.d.ts +28 -0
- package/dist/src/reconcile/phases.js +0 -0
- package/dist/src/reconcile/reconcile.d.ts +21 -0
- package/dist/src/reconcile/reconcile.js +115 -0
- package/dist/src/reconcile/similarity.d.ts +7 -0
- package/dist/src/reconcile/similarity.js +41 -0
- package/dist/src/reconcile/types.d.ts +46 -0
- package/dist/src/reconcile/types.js +19 -0
- package/dist/src/search/cel/ast.d.ts +74 -0
- package/dist/src/search/cel/ast.js +3 -0
- package/dist/src/search/cel/compile.d.ts +60 -0
- package/dist/src/search/cel/compile.js +836 -0
- package/dist/src/search/cel/lexer.d.ts +9 -0
- package/dist/src/search/cel/lexer.js +139 -0
- package/dist/src/search/cel/parser.d.ts +7 -0
- package/dist/src/search/cel/parser.js +219 -0
- package/dist/src/search/drain.d.ts +39 -0
- package/dist/src/search/drain.js +97 -0
- package/dist/src/search/embeddings.d.ts +131 -0
- package/dist/src/search/embeddings.js +263 -0
- package/dist/src/search/external.d.ts +13 -0
- package/dist/src/search/external.js +177 -0
- package/dist/src/search/fts-query.d.ts +1 -0
- package/dist/src/search/fts-query.js +64 -0
- package/dist/src/search/provider.d.ts +17 -0
- package/dist/src/search/provider.js +13 -0
- package/dist/src/search/resolve.d.ts +18 -0
- package/dist/src/search/resolve.js +23 -0
- package/dist/src/search/rrf.d.ts +36 -0
- package/dist/src/search/rrf.js +84 -0
- package/dist/src/search/tasks.d.ts +12 -0
- package/dist/src/search/tasks.js +131 -0
- package/dist/src/search/text.d.ts +16 -0
- package/dist/src/search/text.js +21 -0
- package/dist/src/search/vector.d.ts +18 -0
- package/dist/src/search/vector.js +49 -0
- package/dist/src/sync/admin.d.ts +59 -0
- package/dist/src/sync/admin.js +53 -0
- package/dist/src/sync/attach.d.ts +13 -0
- package/dist/src/sync/attach.js +29 -0
- package/dist/src/sync/checkpoint.d.ts +21 -0
- package/dist/src/sync/checkpoint.js +52 -0
- package/dist/src/sync/driver.d.ts +25 -0
- package/dist/src/sync/driver.js +80 -0
- package/dist/src/sync/external-source.d.ts +14 -0
- package/dist/src/sync/external-source.js +137 -0
- package/dist/src/sync/freshness.d.ts +42 -0
- package/dist/src/sync/freshness.js +152 -0
- package/dist/src/sync/fs-util.d.ts +12 -0
- package/dist/src/sync/fs-util.js +55 -0
- package/dist/src/sync/git-heuristics.d.ts +6 -0
- package/dist/src/sync/git-heuristics.js +16 -0
- package/dist/src/sync/observe.d.ts +63 -0
- package/dist/src/sync/observe.js +84 -0
- package/dist/src/sync/plugin.d.ts +54 -0
- package/dist/src/sync/plugin.js +12 -0
- package/dist/src/sync/reconciling-ingest.d.ts +12 -0
- package/dist/src/sync/reconciling-ingest.js +209 -0
- package/dist/src/sync/recovery.d.ts +6 -0
- package/dist/src/sync/recovery.js +34 -0
- package/dist/src/sync/settings.d.ts +18 -0
- package/dist/src/sync/settings.js +58 -0
- package/dist/src/sync/sources.d.ts +37 -0
- package/dist/src/sync/sources.js +75 -0
- package/dist/src/sync/tombstone.d.ts +9 -0
- package/dist/src/sync/tombstone.js +55 -0
- package/dist/src/sync/watch-lease.d.ts +14 -0
- package/dist/src/sync/watch-lease.js +91 -0
- package/dist/src/sync/watcher.d.ts +19 -0
- package/dist/src/sync/watcher.js +34 -0
- package/dist/src/sync/workspace.d.ts +34 -0
- package/dist/src/sync/workspace.js +120 -0
- package/dist/src/sync/writer-lock.d.ts +16 -0
- package/dist/src/sync/writer-lock.js +121 -0
- 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,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
|