Sign In

@geml/geml

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@geml/geml - npm Package Compare versions

Comparing version
1.7.8
to
1.8.1
+7
-4
codemap/mcp-server.mjs

@@ -35,6 +35,2 @@ #!/usr/bin/env node

import { fileURLToPath } from "node:url";
// Where the sources live is serve.mjs's rule (the recipe's `root`, else the
// graph dir's parent). Imported, not restated: two copies would drift and the
// source panel and this tool would disagree about which file a symbol is in.
import { resolveSrcRoot } from "./serve.mjs";

@@ -49,2 +45,9 @@ // blockSpans from the reference parser (its CLI entry is guarded, so importing

const { blockSpans } = await import(`file://${parserPath.replace(/\\/g, "/")}`);
// Where the sources live is serve.mjs's rule (the recipe's `root`, else the
// graph dir's parent). Imported, not restated: two copies would drift and the
// source panel and this tool would disagree about which file a symbol is in.
// Imported DYNAMICALLY, after the guard above: serve.mjs itself imports the
// built parser, so a static import here would die with a bare
// ERR_MODULE_NOT_FOUND before the guard could say "build first".
const { resolveSrcRoot } = await import("./serve.mjs");
const splitLines = (s) => s.split(/(?<=\n)/);

@@ -51,0 +54,0 @@

@@ -1,2 +0,2 @@

export type DiagnosticCode = "unterminated-block" | "unknown-block-type" | "unknown-attribute" | "block-nesting-too-deep" | "list-nesting-too-deep" | "inline-nesting-too-deep" | "stray-labeled-fence" | "fence-like-line" | "unresolvable-code-source" | "bad-code-source" | "bad-source-range" | "stale-code-snapshot" | "duplicate-id" | "unresolved-reference" | "unresolved-footnote" | "unresolved-cross-document-reference" | "unresolvable-document" | "unchecked-cross-document-reference" | "embed-missing-src" | "ignored-embed-body" | "transclusion-cycle" | "embed-target-not-geml" | "media-target-is-document" | "inline-transclusion-not-inline" | "unsafe-embed-scheme" | "unresolvable-table-source" | "table-source-not-a-table" | "unknown-metadata-reference" | "table-src-and-body" | "unknown-table-format" | "bad-table-delimiter" | "ignored-table-delimiter" | "bad-compute-formula" | "unlexable-compute-formula" | "compute-error" | "compute-non-numeric-cell" | "compute-not-a-number" | "bad-summary-entry" | "summary-unknown-column" | "unlexable-summary-expression" | "summary-error" | "unknown-diagram-format" | "ignored-diagram-body" | "code-graph-missing-src" | "code-graph-unresolvable-document" | "chart-missing-data" | "chart-data-not-a-table" | "chart-missing-type" | "chart-unknown-type" | "chart-unknown-rows-scope" | "chart-missing-channel" | "chart-empty-channel" | "chart-unknown-column" | "chart-unused-channel" | "chart-missing-summary-row" | "chart-summary-row-unavailable" | "chart-non-numeric-value" | "chart-data-not-records" | "data-parse" | "unknown-data-format" | "data-format-no-engine" | "bad-data-schema" | "data-src-and-body" | "bad-data-source" | "unresolvable-data-source";
export type DiagnosticCode = "unterminated-block" | "unknown-block-type" | "unknown-attribute" | "block-nesting-too-deep" | "list-nesting-too-deep" | "inline-nesting-too-deep" | "stray-labeled-fence" | "fence-like-line" | "unresolvable-code-source" | "bad-code-source" | "bad-source-range" | "code-src-and-body" | "duplicate-id" | "unresolved-reference" | "unresolved-footnote" | "unresolved-cross-document-reference" | "unresolvable-document" | "unchecked-cross-document-reference" | "embed-missing-src" | "ignored-embed-body" | "transclusion-cycle" | "embed-target-not-geml" | "media-target-is-document" | "inline-transclusion-not-inline" | "unsafe-embed-scheme" | "unresolvable-table-source" | "table-source-not-a-table" | "unknown-metadata-reference" | "duplicate-meta-key" | "table-src-and-body" | "unknown-table-format" | "bad-table-delimiter" | "ignored-table-delimiter" | "bad-compute-formula" | "unlexable-compute-formula" | "compute-error" | "compute-non-numeric-cell" | "compute-not-a-number" | "bad-summary-entry" | "summary-unknown-column" | "unlexable-summary-expression" | "summary-error" | "unknown-diagram-format" | "ignored-diagram-body" | "code-graph-missing-src" | "code-graph-unresolvable-document" | "chart-missing-data" | "chart-data-not-a-table" | "chart-missing-type" | "chart-unknown-type" | "chart-unknown-rows-scope" | "chart-missing-channel" | "chart-empty-channel" | "chart-unknown-column" | "chart-unused-channel" | "chart-missing-summary-row" | "chart-summary-row-unavailable" | "chart-non-numeric-value" | "chart-data-not-records" | "data-parse" | "unknown-data-format" | "data-format-no-engine" | "bad-data-schema" | "data-src-and-body" | "bad-data-source" | "unresolvable-data-source";
export interface Diagnostic {

@@ -3,0 +3,0 @@ severity: "error" | "warning";

@@ -27,3 +27,3 @@ // GEML reference parser — the diagnostic catalogue (spec Appendix A).

"bad-source-range": "error",
"stale-code-snapshot": "warning",
"code-src-and-body": "error",
"duplicate-id": "error",

@@ -45,2 +45,3 @@ "unresolved-reference": "error",

"unknown-metadata-reference": "error",
"duplicate-meta-key": "warning",
"table-src-and-body": "error",

@@ -47,0 +48,0 @@ "unknown-table-format": "warning",

@@ -73,2 +73,8 @@ import { type Value } from "./attrs.js";

export declare function isSafeUrl(url: string, allowDataImage?: boolean): boolean;
export declare function parseInline(s: string, line: number, sink: RefSink, depth?: number): Inline[];
interface Pairs {
br: Int32Array;
pa: Int32Array;
off: number;
}
export declare function parseInline(s: string, line: number, sink: RefSink, depth?: number, pairs?: Pairs): Inline[];
export {};

@@ -75,37 +75,53 @@ // GEML reference parser — Milestone 2: inline content (§5).

}
function pairsOf(s) {
const br = new Int32Array(s.length).fill(-1);
const pa = new Int32Array(s.length).fill(-1);
const bs = [], ps = [];
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c === "[")
bs.push(i);
else if (c === "]") {
const j = bs.pop();
if (j !== undefined)
br[j] = i;
}
else if (c === "(")
ps.push(i);
else if (c === ")") {
const j = ps.pop();
if (j !== undefined)
pa[j] = i;
}
}
return { br, pa, off: 0 };
}
// A link label is a bracket-balanced span, so the map restricted to it IS the
// map of the substring — the maps are built ONCE for the whole inline and read
// through an offset by every nesting level, rather than rebuilt per level (which
// at the 100-deep cap would have multiplied a 1 MB line's map memory by 100).
// A partner falling outside the window means "unbalanced here", which is what
// the substring-local scan reported.
function pairEnd(m, p, s, i) {
const j = m[p.off + i];
if (j === undefined || j < 0)
return -1;
const end = j - p.off;
return end < s.length ? end : -1;
}
// Read a balanced `(...)` starting at s[i]==='('. Returns content and index
// just past the closing ')', or null if unbalanced.
function readParen(s, i) {
function readParen(s, i, p) {
if (s[i] !== "(")
return null;
let depth = 0;
for (let j = i; j < s.length; j++) {
const c = s[j];
if (c === "(")
depth++;
else if (c === ")") {
depth--;
if (depth === 0)
return { content: s.slice(i + 1, j), end: j + 1 };
}
}
return null;
const j = pairEnd(p.pa, p, s, i);
return j < 0 ? null : { content: s.slice(i + 1, j), end: j + 1 };
}
// Read a balanced `[...]` starting at s[i]==='['. Returns content and index
// just past the closing ']', or null if unbalanced.
function readBracket(s, i) {
function readBracket(s, i, p) {
if (s[i] !== "[")
return null;
let depth = 0;
for (let j = i; j < s.length; j++) {
const c = s[j];
if (c === "[")
depth++;
else if (c === "]") {
depth--;
if (depth === 0)
return { content: s.slice(i + 1, j), end: j + 1 };
}
}
return null;
const j = pairEnd(p.br, p, s, i);
return j < 0 ? null : { content: s.slice(i + 1, j), end: j + 1 };
}

@@ -124,3 +140,3 @@ // Optional `{…}` attribute object immediately following a construct.

// phase B (emphasis). Children of links are fully re-parsed.
function scanAtoms(s, line, sink, depth = 0) {
function scanAtoms(s, line, sink, depth, p) {
const out = [];

@@ -132,2 +148,7 @@ let buf = "";

} };
// Emit `node` as the atom occupying source span [start, end).
const atom = (node, start, end) => {
flush();
out.push({ node, first: s[start], last: s[end - 1] });
};
let i = 0;

@@ -140,5 +161,5 @@ while (i < s.length) {

if (next === undefined || next === "\n") { // line-final backslash
flush();
out.push({ type: "break" });
i += next === undefined ? 1 : 2;
const end = i + (next === undefined ? 1 : 2);
atom({ type: "break" }, i, end);
i = end;
continue;

@@ -149,4 +170,3 @@ }

// (emphasis) cannot mistake an escaped `*`/`~` for a delimiter (§5.3(1)).
flush();
out.push({ type: "text", value: next });
atom({ type: "text", value: next }, i, i + 2);
i += 2;

@@ -167,4 +187,3 @@ continue;

if (close >= 0) {
flush();
out.push({ type: "code", value: s.slice(i + n, close) });
atom({ type: "code", value: s.slice(i + n, close) }, i, close + n);
i = close + n;

@@ -181,4 +200,3 @@ continue;

if (close > i + 1) {
flush();
out.push({ type: "math", value: s.slice(i + 1, close) });
atom({ type: "math", value: s.slice(i + 1, close) }, i, close + 1);
i = close + 1;

@@ -197,11 +215,10 @@ continue;

if (c === "!" && s[i + 1] === "[" && s[i + 2] === "[") {
const inner = readBracket(s, i + 2); // the inner [...] after `![`
const inner = readBracket(s, i + 2, p); // the inner [...] after `![`
if (inner && s[inner.end] === "]") {
const { doc, anchor } = classifyDest(inner.content.trim());
if (anchor) {
flush();
const node = { type: "project", anchor };
if (doc)
node.doc = doc;
out.push(node);
atom(node, i, inner.end + 1);
// Validated by the same §8 resolver as any reference; the target's TYPE

@@ -217,4 +234,4 @@ // is checked separately, since only inline content can be projected.

if (c === "!" && s[i + 1] === "[") {
const label = readBracket(s, i + 1);
const paren = label ? readParen(s, label.end) : null;
const label = readBracket(s, i + 1, p);
const paren = label ? readParen(s, label.end, p) : null;
if (label && paren) {

@@ -244,4 +261,3 @@ const a = readAttrs(s, paren.end);

}
flush();
out.push(node);
atom(node, i, a ? a.end : paren.end);
i = a ? a.end : paren.end;

@@ -253,3 +269,3 @@ continue;

if (c === "[" && s[i + 1] === "[") {
const inner = readBracket(s, i + 1); // inner [...] after the first [
const inner = readBracket(s, i + 1, p); // inner [...] after the first [
if (inner && s[inner.end] === "]") {

@@ -259,7 +275,6 @@ const target = inner.content.trim();

if (anchor) {
flush();
const node = { type: "autoref", anchor };
if (doc)
node.doc = doc;
out.push(node);
atom(node, i, inner.end + 1);
sink.refs.push({ kind: doc ? "cross" : "autoref", doc, anchor, line });

@@ -273,7 +288,6 @@ i = inner.end + 1;

if (c === "[" && s[i + 1] === "^") {
const br = readBracket(s, i);
const br = readBracket(s, i, p);
if (br && br.content.startsWith("^")) {
const ref = br.content.slice(1).trim();
flush();
out.push({ type: "footnote", ref });
atom({ type: "footnote", ref }, i, br.end);
sink.refs.push({ kind: "footnote", anchor: ref, line });

@@ -286,4 +300,4 @@ i = br.end;

if (c === "[") {
const label = readBracket(s, i);
const paren = label ? readParen(s, label.end) : null;
const label = readBracket(s, i, p);
const paren = label ? readParen(s, label.end, p) : null;
if (label && paren) {

@@ -295,3 +309,5 @@ const a = readAttrs(s, paren.end);

type: "link",
children: parseInline(label.content, line, sink, depth + 1),
// The label window starts one character past this `[`, so the shared
// maps are read at that offset instead of being rebuilt for it.
children: parseInline(label.content, line, sink, depth + 1, { br: p.br, pa: p.pa, off: p.off + i + 1 }),
attrs: attrObj.attrs,

@@ -308,4 +324,3 @@ };

}
flush();
out.push(node);
atom(node, i, a ? a.end : paren.end);
i = a ? a.end : paren.end;

@@ -321,15 +336,19 @@ continue;

}
// Phase B: emphasis / strong / strikethrough on a plain text run (§5.3).
// Phase B: emphasis / strong / strikethrough over the whole inline sequence
// (§5.3, GEP-0007).
//
// A maximal run of `*` is an emphasis delimiter (one `*` -> emphasis, two ->
// strong, longer runs pair greedily); a maximal run of two or more `~` is a
// strikethrough delimiter (a lone `~` is literal). Whether a run may *open*
// and/or *close* is fixed by flanking: it must hug a non-space character, and on
// the side facing a punctuation character it must also have whitespace or
// punctuation on the far side (the CommonMark left/right-flanking rule). Runs are
// then paired by a single left-to-right stack scan with the rule of three, so
// nested and adjacent delimiters resolve to exactly one tree — no leftmost-regex
// guesswork. Delimiters pair only *within* one text run: they never reach across
// a code span, inline math, a link or image (atoms from phase A), or a block
// boundary. Any delimiter left unpaired is literal text.
// A maximal run of `*` in literal text is an emphasis delimiter (one `*` ->
// emphasis, two -> strong, longer runs pair greedily); a maximal run of two or
// more `~` is a strikethrough delimiter (a lone `~` is literal). Whether a run
// may *open* and/or *close* is fixed by flanking: it must hug a non-space
// character, and on the side facing a punctuation character it must also have
// whitespace or punctuation on the far side (the CommonMark left/right-flanking
// rule). Runs are then paired by a single left-to-right stack scan with the
// rule of three, so nested and adjacent delimiters resolve to exactly one tree
// — no leftmost-regex guesswork. Delimiters pair across phase-A atoms — a pair
// may wrap a code span, math, a link or image — but the atoms themselves are
// opaque: characters inside one are never delimiters, and at an atom boundary
// the flanking test reads the atom's edge source characters (AtomPart). A
// delimiter run never pairs across a block boundary, and any run left unpaired
// is literal text.
// Unicode punctuation, not just ASCII (§5.3). With an ASCII-only test, `“` and

@@ -348,4 +367,8 @@ // `,` count as ordinary letters, and a run hugged by CJK punctuation on the

}
// Split a text run into a doubly-linked list of text and delimiter-run nodes.
function tokenizeRuns(s) {
// Split the mixed phase-A sequence into a doubly-linked list of text,
// delimiter-run, and atom nodes. A delimiter run at the edge of a text part
// flanks against the neighboring part's edge character — an atom's recorded
// source edge, or the adjacent char of a neighboring text part — and against
// nothing (whitespace) at the ends of the sequence.
function tokenizeRuns(parts) {
let head = null, tail = null;

@@ -356,34 +379,53 @@ const push = (node) => { node.prev = tail; if (tail)

head = node; tail = node; };
let i = 0;
while (i < s.length) {
const c = s[i];
if (c === "*" || c === "~") {
let j = i;
while (s[j] === c)
j++;
const n = j - i;
if (c === "~" && n < 2)
push({ t: "text", v: "~", prev: null, next: null });
// …and thread every delimiter onto the delimiter-only chain as it is pushed.
let dhead = null, dtail = null, dn = 0;
const pushDelim = (d) => {
d.idx = dn++;
d.dprev = dtail;
if (dtail)
dtail.dnext = d;
else
dhead = d;
dtail = d;
push(d);
};
for (let k = 0; k < parts.length; k++) {
const part = parts[k];
if (typeof part !== "string") {
push({ t: "atom", node: part.node, prev: null, next: null });
continue;
}
const s = part;
// A text part's neighbors are always atoms (or the sequence edge):
// scanAtoms flushes buffered text exactly when it emits an atom, so two
// text parts are never adjacent.
const before0 = k > 0 ? parts[k - 1].last : undefined;
const after0 = k + 1 < parts.length ? parts[k + 1].first : undefined;
let i = 0;
while (i < s.length) {
const c = s[i];
if (c === "*" || c === "~") {
let j = i;
while (s[j] === c)
j++;
const n = j - i;
if (c === "~" && n < 2)
push({ t: "text", v: "~", prev: null, next: null });
else {
const f = flank(i > 0 ? s[i - 1] : before0, j < s.length ? s[j] : after0);
pushDelim({ t: "delim", ch: c, n, open: f.open, close: f.close, idx: 0, dprev: null, dnext: null, prev: null, next: null });
}
i = j;
}
else {
const f = flank(i > 0 ? s[i - 1] : undefined, j < s.length ? s[j] : undefined);
push({ t: "delim", ch: c, n, open: f.open, close: f.close, prev: null, next: null });
let j = i;
while (j < s.length && s[j] !== "*" && s[j] !== "~")
j++;
push({ t: "text", v: s.slice(i, j), prev: null, next: null });
i = j;
}
i = j;
}
else {
let j = i;
while (j < s.length && s[j] !== "*" && s[j] !== "~")
j++;
push({ t: "text", v: s.slice(i, j), prev: null, next: null });
i = j;
}
}
return head;
return { head, first: dhead };
}
const nextDelim = (n) => { for (; n; n = n.next)
if (n.t === "delim")
return n; return null; };
const prevDelim = (n) => { for (; n; n = n.prev)
if (n.t === "delim")
return n; return null; };
// Rule of three: when either side can also play the other role, a combined

@@ -396,2 +438,14 @@ // length that is a multiple of three is only allowed if both lengths are.

}
// Drop a delimiter from the delimiter-only chain. Called for a spent delimiter
// and for every delimiter that a new wrap swallows: the chain must hold exactly
// the delimiters still pairable AT THIS LEVEL, or an opener search would reach
// inside a finished span and splice its own list.
function unlinkDelim(d) {
if (d.dprev)
d.dprev.dnext = d.dnext;
if (d.dnext)
d.dnext.dprev = d.dprev;
d.dprev = null;
d.dnext = null;
}
function unlink(node, head) {

@@ -404,2 +458,4 @@ if (node.prev)

node.next.prev = node.prev;
if (node.t === "delim")
unlinkDelim(node);
return head;

@@ -410,8 +466,20 @@ }

// future searches with `bottom` so the scan stays linear and deterministic.
function processEmphasis(head) {
function processEmphasis(head, first) {
// The cut-off is a delimiter POSITION, not a node: the delimiter it names can
// be consumed and unlinked later on, and a stale node reference would then
// never be reached — the search would run to the head of the list every time,
// which is the O(n^2) blowup this map exists to prevent. -1 means "no bound
// yet" (the whole prefix is searchable), matching an unset entry.
const bottom = new Map();
let closer = nextDelim(head);
let closer = first;
while (closer) {
if (closer.t !== "delim" || !closer.close) {
closer = nextDelim(closer.next);
// A `~` run pairs TWO characters at a time, so one leftover character is no
// longer a delimiter — exactly as a lone `~` was never one (tokenizeRuns).
// Skipping it here is what keeps `n` from going negative: pairing a spent run
// again drove `n` past 0, and since the loop only advances on `n === 0` the
// same closer was re-paired forever, allocating a wrap each time (a hang on
// `~~~a~~~`) or reaching finalize with n = -1 (`"~".repeat(-1)` threw a
// RangeError on `~~~~a~~~`).
if (!closer.close || (closer.ch === "~" && closer.n < 2)) {
closer = closer.dnext;
continue;

@@ -421,11 +489,13 @@ }

const key = `${ch}${closer.open ? 1 : 0}${closer.n % 3}`;
const stop = bottom.has(key) ? bottom.get(key) : null;
let opener = prevDelim(closer.prev);
const stop = bottom.get(key) ?? -1;
let opener = closer.dprev;
let found = null;
while (opener && opener !== stop) {
if (opener.t === "delim" && opener.open && opener.ch === ch && rule3(opener, closer)) {
while (opener && opener.idx > stop) {
// …and the same for the opener side: a `~` run down to one character can
// no longer open, so `use = 2` never takes more than a side has left.
if (opener.open && opener.ch === ch && (ch !== "~" || opener.n >= 2) && rule3(opener, closer)) {
found = opener;
break;
}
opener = prevDelim(opener.prev);
opener = opener.dprev;
}

@@ -435,6 +505,10 @@ if (found) {

const kind = ch === "~" ? "strike" : use === 2 ? "strong" : "emph";
// Gather and detach the nodes strictly between opener and closer.
// Gather and detach the nodes strictly between opener and closer. Any
// delimiter among them is now inside the new span — unpaired and literal —
// so it leaves the delimiter chain with them.
let kidsHead = null, kidsTail = null;
for (let p = found.next; p && p !== closer;) {
const q = p.next;
if (p.t === "delim")
unlinkDelim(p);
p.prev = kidsTail;

@@ -457,5 +531,5 @@ p.next = null;

if (closer.n === 0) {
const after = closer.next;
const after = closer.dnext;
head = unlink(closer, head);
closer = nextDelim(after);
closer = after;
}

@@ -465,4 +539,6 @@ // else: keep the same closer (it still has delimiter characters left).

else {
bottom.set(key, closer.prev);
closer = nextDelim(closer.next);
// Nothing before this closer can open for this key, so no later closer
// with the same key need look past the delimiter just before it either.
bottom.set(key, closer.dprev ? closer.dprev.idx : -1);
closer = closer.dnext;
}

@@ -473,3 +549,5 @@ }

// Linked list of (possibly nested) nodes -> Inline[]; unpaired delimiters and
// empty text vanish into literal text, with adjacent text runs merged.
// empty text vanish into literal text, with adjacent text runs merged. An
// escaped-punctuation atom is a text Inline, so it folds into its neighbors —
// the emitted sequence is canonical with no adjacent text nodes at any level.
function finalize(head) {

@@ -489,2 +567,8 @@ const out = [];

pushText(n.ch.repeat(n.n));
else if (n.t === "atom") {
if (n.node.type === "text")
pushText(n.node.value);
else
out.push(n.node);
}
else

@@ -495,20 +579,10 @@ out.push({ type: n.kind, children: finalize(n.kids) });

}
function emphasize(text) {
const head = tokenizeRuns(text);
return head ? finalize(processEmphasis(head)) : [];
function emphasize(parts) {
const { head, first } = tokenizeRuns(parts);
return head ? finalize(processEmphasis(head, first)) : [];
}
// Coalesce adjacent literal text nodes (e.g. an escaped `*` atom sitting between
// two text runs) so the inline sequence is canonical.
function mergeText(ns) {
const out = [];
for (const n of ns) {
const last = out[out.length - 1];
if (n.type === "text" && last && last.type === "text")
last.value += n.value;
else
out.push(n);
}
return out;
}
export function parseInline(s, line, sink, depth = 0) {
// `pairs` is internal: the bracket/paren partner maps of the WHOLE inline, with
// the offset of this call's window into them. Only the recursive link-label call
// supplies it; every external caller omits it and gets the maps built here.
export function parseInline(s, line, sink, depth = 0, pairs) {
if (depth > MAX_INLINE_NESTING) {

@@ -521,13 +595,5 @@ // Pathological nesting (thousands of nested link labels) would overflow the

diags.push({ severity: "error", code: "inline-nesting-too-deep", message: `inline nesting too deep (max ${MAX_INLINE_NESTING})`, line });
return mergeText(emphasize(s));
return emphasize([s]);
}
const atoms = scanAtoms(s, line, sink, depth);
const out = [];
for (const a of atoms) {
if (typeof a === "string")
out.push(...emphasize(a));
else
out.push(a);
}
return mergeText(out);
return emphasize(scanAtoms(s, line, sink, depth, pairs ?? pairsOf(s)));
}

@@ -301,11 +301,17 @@ #!/usr/bin/env node

throw new Error(run.stderr);
// The CLI prints the path it was given, which here is absolute. Every
// other tool in this server speaks paths relative to the root, and a
// model is meant to paste a row's file straight into geml_get — so put
// the rows in those coordinates, and keep the server's own layout out of
// the client's view while we are at it.
const base = realpathSync(OPTS.root) + sep;
// The CLI prints the path it was given. Every other tool in this server
// speaks paths relative to the root, and a model is meant to paste a
// row's file straight into geml_get — so put the rows in those
// coordinates, and keep the server's own layout out of the client's
// view while we are at it. The given path carries the RAW root spelling
// when `path` was omitted but resolveInRoot's realpath-anchored one when
// it was not, and on a symlinked root (macOS /var -> /private/var) those
// two differ — so strip whichever spelling a row actually carries.
const bases = [...new Set([resolve(OPTS.root) + sep, realpathSync(OPTS.root) + sep])];
return run.stdout
.split("\n")
.map((line) => (line.startsWith(base) ? line.slice(base.length).replace(/\\/g, "/") : line))
.map((line) => {
const b = bases.find((x) => line.startsWith(x));
return b ? line.slice(b.length).replace(/\\/g, "/") : line;
})
.join("\n")

@@ -312,0 +318,0 @@ .trim();

{
"name": "@geml/geml",
"version": "1.7.8",
"version": "1.8.1",
"mcpName": "io.github.geml-spec/geml",

@@ -5,0 +5,0 @@ "publishConfig": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet