🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@geml/geml

Package Overview
Dependencies
Maintainers
1
Versions
20
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.5.0
to
1.5.1
+1
-1
dist/diagnostics.d.ts

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

export type DiagnosticCode = "unterminated-block" | "unknown-block-type" | "block-nesting-too-deep" | "list-nesting-too-deep" | "inline-nesting-too-deep" | "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" | "source-attr-conflict" | "unresolvable-table-source" | "table-source-not-a-table" | "unknown-metadata-reference" | "table-src-and-body" | "unknown-table-format" | "bad-compute-formula" | "unlexable-compute-formula" | "compute-error" | "bad-summary-entry" | "summary-unknown-column" | "unlexable-summary-expression" | "summary-error" | "bad-span" | "span-outside-table" | "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";
export type DiagnosticCode = "unterminated-block" | "unknown-block-type" | "unknown-attribute" | "block-nesting-too-deep" | "list-nesting-too-deep" | "inline-nesting-too-deep" | "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-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";
export interface Diagnostic {

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

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

"unknown-block-type": "warning",
"unknown-attribute": "warning",
"block-nesting-too-deep": "error",

@@ -35,3 +36,2 @@ "list-nesting-too-deep": "error",

"unsafe-embed-scheme": "error",
"source-attr-conflict": "error",
"unresolvable-table-source": "error",

@@ -45,2 +45,4 @@ "table-source-not-a-table": "error",

"compute-error": "error",
"compute-non-numeric-cell": "warning",
"compute-not-a-number": "warning",
"bad-summary-entry": "error",

@@ -50,4 +52,2 @@ "summary-unknown-column": "error",

"summary-error": "error",
"bad-span": "error",
"span-outside-table": "warning",
"unknown-diagram-format": "warning",

@@ -54,0 +54,0 @@ "ignored-diagram-body": "warning",

@@ -318,4 +318,9 @@ // GEML reference parser — Milestone 2: inline content (§5).

// boundary. Any delimiter left unpaired is literal text.
const ASCII_PUNCT = /[!-\/:-@\[-`{-~]/;
const isPunct = (c) => c !== undefined && ASCII_PUNCT.test(c);
// Unicode punctuation, not just ASCII (§5.3). With an ASCII-only test, `“` and
// `,` count as ordinary letters, and a run hugged by CJK punctuation on the
// outside and ASCII punctuation on the inside stops flanking: `“*(foo)*”` loses
// its emphasis. CommonMark's rule is Unicode-wide, and the algorithm here is
// meant to be that rule restricted to `*` and `~~` — not a narrower one.
const PUNCT = /[\p{P}\p{S}]/u;
const isPunct = (c) => c !== undefined && PUNCT.test(c);
const isWS = (c) => c === undefined || /\s/.test(c);

@@ -322,0 +327,0 @@ // Left/right-flanking for a delimiter run, given the chars on either side.

@@ -11,6 +11,2 @@ import { type DiagnosticCode } from "./diagnostics.js";

computed?: boolean;
span?: {
rows: number;
cols: number;
};
}

@@ -17,0 +13,0 @@ export interface TableModel {

@@ -130,4 +130,7 @@ // GEML reference parser — Milestone 3: tables (§6).

// ---------------------------------------------------------------------------
// The bracket suffix has to contain a `%` to be a format (§6). Without that
// test, a column whose own name is bracketed — `[Data] = …` — parses as an
// empty name plus the format `Data`, and the formula silently targets nothing.
function splitName(lhs) {
const m = /^(.*?)\s*\[([^\]]*)\]\s*$/.exec(lhs.trim());
const m = /^(.*?)\s*\[([^\]]*%[^\]]*)\]\s*$/.exec(lhs.trim());
let name = (m ? m[1] : lhs).trim();

@@ -138,2 +141,7 @@ if (name.startsWith('"') && name.endsWith('"'))

}
// A result IEEE-754 produces but a table cannot hold (§6): `x / 0` is ±∞, `0 / 0`
// is NaN. The cell keeps no value and displays `-`, which is what a reader sees;
// naming the cause is the difference between "this row had no data" and "this
// row divided by zero", so it is said out loud like a substituted cell is.
const nanMsg = (where, v) => `${where}: ${Number.isNaN(v) ? "result is not a number (0/0)" : "division by zero"}; the cell holds no value and shows \`-\``;
// Default rendering for an unformatted computed number: drop IEEE-754 display

@@ -143,2 +151,4 @@ // noise (0.1+0.2 → "0.3", sum of 1-dp inputs → "263.6") without altering the

function defaultNum(v) {
if (!isFinite(v))
return "-";
return String(parseFloat(v.toPrecision(12)));

@@ -149,2 +159,4 @@ }

function applyFormat(fmt, v) {
if (!isFinite(v))
return "-";
return fmt.replace(/%%|%[-+ 0]*\d*(?:\.\d+)?[fFeEgGd]/g, (m) => {

@@ -239,12 +251,2 @@ if (m === "%%")

// ---------------------------------------------------------------------------
// Spans
// ---------------------------------------------------------------------------
// Parse `r2c1:2x1` → target cell (1-based row/col over body) + size.
function parseSpan(s) {
const m = /^r(\d+)c(\d+):(\d+)x(\d+)$/.exec(s.trim());
if (!m)
return null;
return { row: +m[1], col: +m[2], rows: +m[3], cols: +m[4] };
}
// ---------------------------------------------------------------------------
// Public entry

@@ -318,7 +320,37 @@ // ---------------------------------------------------------------------------

};
// A cell a formula reads but cannot read as a number (`x`, `N/A`, `TBD`, an
// empty cell) counts as 0, so one dirty row does not void the whole column.
// But counting it silently is how a table quietly reports the wrong total, so
// say which cell was substituted. One warning per cell, not per mention: a
// formula naming the same column twice describes one substitution.
const substituted = new Set();
const colResolve = (name, row) => {
const ci = colIndex(name);
return ci < 0 ? null : cellNum(ci, row);
if (ci < 0)
return null;
const n = cellNum(ci, row);
if (n !== null)
return n;
const key = `${ci}\0${row}`;
if (!substituted.has(key)) {
substituted.add(key);
const text = model.rows[row]?.[ci]?.text ?? "";
diagnostics.push({
severity: "warning",
code: "compute-non-numeric-cell",
message: `column \`${name}\` row ${row + 1} is not a number (${text === "" ? "empty" : `\`${text}\``}); counted as 0`,
});
}
return 0;
};
const computeAgg = (fn, ci) => {
if (fn === "count") {
let c = 0;
for (let r = 0; r < model.rows.length; r++) {
const text = model.rows[r]?.[ci]?.text;
if (text !== undefined && text !== "")
c++;
}
return c;
}
const vals = [];

@@ -330,4 +362,2 @@ for (let r = 0; r < model.rows.length; r++) {

}
if (fn === "count")
return vals.length;
if (vals.length === 0)

@@ -408,9 +438,10 @@ return 0;

const cell = ensureCell(model.rows[r], ci);
if (Number.isFinite(v)) {
const text = fmt ? applyFormat(fmt, v) : defaultNum(v);
const text = fmt ? applyFormat(fmt, v) : defaultNum(v);
cell.text = text;
cell.computed = true;
cell.inlines = [{ type: "text", value: text }];
if (Number.isFinite(v))
cell.value = v;
cell.text = text;
cell.computed = true;
cell.inlines = [{ type: "text", value: text }];
}
else
diagnostics.push({ severity: "warning", code: "compute-not-a-number", message: nanMsg(`compute \`${name}\` row ${r + 1}`, v) });
}

@@ -469,6 +500,8 @@ catch (e) {

const v = evalExpr(toks, 0, noRow, aggResolve);
if (Number.isFinite(v)) {
const text = fmt ? applyFormat(fmt, v) : defaultNum(v);
summary[ci] = { text, inlines: [{ type: "text", value: text }], value: v, computed: true };
}
const text = fmt ? applyFormat(fmt, v) : defaultNum(v);
summary[ci] = { text, inlines: [{ type: "text", value: text }], computed: true };
if (Number.isFinite(v))
summary[ci].value = v;
else
diagnostics.push({ severity: "warning", code: "compute-not-a-number", message: nanMsg(`summary \`${name}\``, v) });
}

@@ -483,28 +516,2 @@ catch (e) {

}
// Spans: `span="r2c1:2x1"` (one or many: span, span2, …).
const spanDecls = Object.entries(attrs)
.filter(([k]) => k === "span" || /^span\d+$/.test(k))
.map(([, v]) => v)
.filter((v) => typeof v === "string");
for (const sd of spanDecls) {
const sp = parseSpan(sd);
if (!sp) {
diagnostics.push({ severity: "error", code: "bad-span", message: `bad span \`${sd}\` (want \`rNcM:RxC\`)` });
continue;
}
const cell = model.rows[sp.row - 1]?.[sp.col - 1];
if (!cell) {
diagnostics.push({ severity: "warning", code: "span-outside-table", message: `span \`${sd}\` targets a cell outside the table` });
continue;
}
// A span can never extend past the grid: clamp its extent to the rows/cols
// actually available from the target cell. Without this, `span="r1c1:9e6x9e6"`
// makes the renderer's O(rows×cols) coverage sweep hang (DoS). Every row has
// exactly `columns.length` cells (built above), so the column bound is exact.
const maxRows = model.rows.length - (sp.row - 1);
const maxCols = columns.length - (sp.col - 1);
const rows = Math.max(1, Math.min(sp.rows, maxRows));
const cols = Math.max(1, Math.min(sp.cols, maxCols));
cell.span = { rows, cols };
}
return { model, diagnostics };

@@ -511,0 +518,0 @@ }

@@ -128,3 +128,6 @@ // GEML -> Markdown projection (the inverse direction of from-md.ts).

if (b.mode === "flow") {
// Footnote definition: a `note.footnote` carrying its ref as the id.
// A note the author marked `.footnote` projects to a Markdown footnote
// definition. The parser no longer synthesizes this class — the `[^id]: text`
// definition line was withdrawn from §5.2 — but an author still writes it,
// and it is the only way this projection can be produced.
if (b.type === "note" && b.classes.includes("footnote") && b.id) {

@@ -131,0 +134,0 @@ const text = (b.children ?? []).map((c) => block(c, notes)).join(" ").replace(/\n+/g, " ").trim();

{
"name": "@geml/geml",
"version": "1.5.0",
"version": "1.5.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