@geml/geml 1.6.0 → 1.7.1
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/codemap/browser-stub.mjs +5 -0
- package/codemap/build.mjs +20 -6
- package/codemap/exclude.mjs +18 -14
- package/codemap/refresh.mjs +4 -1
- package/codemap/verify.mjs +14 -4
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +4 -1
- package/dist/diagnostics.d.ts +1 -1
- package/dist/diagnostics.js +16 -0
- package/dist/from-md.js +6 -1
- package/dist/geml.d.ts +5 -1
- package/dist/geml.js +797 -36
- package/dist/mcp.js +23 -2
- package/dist/render-html.d.ts +5 -0
- package/dist/render-html.js +10 -1
- package/dist/render.d.ts +1 -0
- package/dist/render.js +72 -3
- package/dist/serialize.js +12 -0
- package/dist/table.js +27 -1
- package/dist/to-md.js +29 -2
- package/package.json +2 -1
- package/skill/SKILL.md +82 -0
- package/skill/references/authoring.geml +333 -0
package/codemap/browser-stub.mjs
CHANGED
|
@@ -12,6 +12,9 @@ export const writeFileSync = () => {};
|
|
|
12
12
|
export const existsSync = () => false;
|
|
13
13
|
export const realpathSync = (p) => p;
|
|
14
14
|
export const statSync = () => ({ isDirectory: () => false });
|
|
15
|
+
export const mkdirSync = () => {};
|
|
16
|
+
export const readdirSync = () => [];
|
|
17
|
+
export const copyFileSync = () => {};
|
|
15
18
|
export const basename = (p) => p;
|
|
16
19
|
export const dirname = (p) => p;
|
|
17
20
|
export const resolve = (...p) => p.join("/");
|
|
@@ -21,6 +24,8 @@ export const relative = (_from, to) => to;
|
|
|
21
24
|
export const sep = "/";
|
|
22
25
|
export const fileURLToPath = (u) => String(u);
|
|
23
26
|
export const spawnSync = () => ({ status: 1 });
|
|
27
|
+
// node:os — `geml skill install` resolves the home directory; CLI-only.
|
|
28
|
+
export const homedir = () => "/";
|
|
24
29
|
export const createHash = () => ({
|
|
25
30
|
update() { return this; },
|
|
26
31
|
digest() { return ""; },
|
package/codemap/build.mjs
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
//
|
|
37
37
|
// After building, run: geml codemap verify <out-dir>
|
|
38
38
|
import { writeFileSync, mkdirSync, existsSync, readFileSync, statSync } from "node:fs";
|
|
39
|
-
import { join, resolve, basename, dirname, relative } from "node:path";
|
|
39
|
+
import { join, resolve, basename, dirname, relative, sep } from "node:path";
|
|
40
40
|
import { fileURLToPath } from "node:url";
|
|
41
41
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
42
42
|
import { emit } from "./emit.mjs";
|
|
@@ -100,7 +100,7 @@ if (root && !inputs.length) {
|
|
|
100
100
|
const { files, manifests, pkgs } = collectSourceFiles(rootAbs);
|
|
101
101
|
const excluder = makeExcluder({
|
|
102
102
|
root: rootAbs, globs: excludeGlobs0, gitignore: !args.includes("--no-gitignore"),
|
|
103
|
-
files: [...files, ...manifests, ...pkgs],
|
|
103
|
+
files: [...files, ...manifests, ...pkgs], run: execFileSync,
|
|
104
104
|
});
|
|
105
105
|
const jobs = detectLanguages(rootAbs, { files, manifests, pkgs, excluder });
|
|
106
106
|
detectedLanguages = [...new Set(jobs.map((j) => j.language))];
|
|
@@ -136,7 +136,10 @@ if (root && !inputs.length) {
|
|
|
136
136
|
// PATH (npx / joern) whose .cmd/.bat shim uses %~dp0 breaks if the name is
|
|
137
137
|
// quoted — cmd then resolves %~dp0 against the cwd, not the shim's dir. A
|
|
138
138
|
// spaced launcher PATH is a full path, so quoting it keeps %~dp0 correct.
|
|
139
|
-
|
|
139
|
+
// WHEN it does quote, it defers to shq below: escaping only `"` left a
|
|
140
|
+
// trailing backslash (a path ending `...\`) escaping our own closing quote,
|
|
141
|
+
// which merges the next token into this one. Same CRT rules, one source.
|
|
142
|
+
const q = (s) => (/[\s"]/.test(String(s)) ? shq(s) : String(s));
|
|
140
143
|
// Hardened quote for command ARGUMENTS on win32. Node does NOT escape args
|
|
141
144
|
// under shell:true — it only concatenates them (Node DEP0190) — so an
|
|
142
145
|
// unquoted argument such as a source directory named `a&calc` reaching the
|
|
@@ -378,7 +381,7 @@ const excluder = makeExcluder({
|
|
|
378
381
|
globs: excludeGlobs,
|
|
379
382
|
gitignore: !args.includes("--no-gitignore"),
|
|
380
383
|
files: [...new Set(symbols.map((s) => s.file))],
|
|
381
|
-
|
|
384
|
+
run: execFileSync,
|
|
382
385
|
});
|
|
383
386
|
const kept = symbols.filter((s) => !excluder(s.file));
|
|
384
387
|
const excludedCount = symbols.length - kept.length;
|
|
@@ -391,7 +394,7 @@ if (root && !recordRecipe && !entryHints.length) {
|
|
|
391
394
|
const c = collectSourceFiles(rootAbs);
|
|
392
395
|
const excl = makeExcluder({
|
|
393
396
|
root: rootAbs, globs: excludeGlobs, gitignore: !args.includes("--no-gitignore"),
|
|
394
|
-
files: [...c.files, ...c.manifests, ...c.pkgs],
|
|
397
|
+
files: [...c.files, ...c.manifests, ...c.pkgs], run: execFileSync,
|
|
395
398
|
});
|
|
396
399
|
entryHints = detectEntries(rootAbs, {
|
|
397
400
|
files: c.files.filter((f) => !excl(f)),
|
|
@@ -420,7 +423,18 @@ try {
|
|
|
420
423
|
const { edges: httpEdges, audit } = buildCrossStackOverlay({
|
|
421
424
|
symbols,
|
|
422
425
|
files: scanFiles,
|
|
423
|
-
|
|
426
|
+
// `rel` is indexer OUTPUT, not a path this build authored, so a `..`
|
|
427
|
+
// segment must not turn a source read into an escape from the scanned
|
|
428
|
+
// root. Same gate the document resolver uses: resolve first, then require
|
|
429
|
+
// the result to BE the root or sit under it — compared with the separator,
|
|
430
|
+
// so a sibling named `<root>-evil` is not mistaken for a child.
|
|
431
|
+
readText: (rel) => {
|
|
432
|
+
try {
|
|
433
|
+
const p = resolve(rootAbs, ...String(rel).split("/"));
|
|
434
|
+
if (p !== rootAbs && !p.startsWith(rootAbs + sep)) return null;
|
|
435
|
+
return readFileSync(p, "utf8");
|
|
436
|
+
} catch { return null; }
|
|
437
|
+
},
|
|
424
438
|
});
|
|
425
439
|
for (const e of httpEdges) edges.push(e);
|
|
426
440
|
if (httpEdges.length) {
|
package/codemap/exclude.mjs
CHANGED
|
@@ -15,17 +15,17 @@ import { execFileSync as _execFileSync } from "node:child_process";
|
|
|
15
15
|
// Minimal gitignore-flavoured glob: `**` spans path separators, `*` stays
|
|
16
16
|
// within a segment, everything else is literal. Anchored to the whole path.
|
|
17
17
|
export function globToRegExp(glob) {
|
|
18
|
+
// The glob comes from a `--exclude` argument, so nothing in it may reach the
|
|
19
|
+
// compiled pattern as *syntax*. Split on the wildcards, keeping them (the
|
|
20
|
+
// capture group), which leaves the array strictly alternating: even indices
|
|
21
|
+
// are literal text, odd indices are `*`, `**` or `**/`. Literals go through a
|
|
22
|
+
// total regex-metacharacter escape; wildcards map to fixed patterns. Neither
|
|
23
|
+
// path can carry an unescaped metacharacter through.
|
|
24
|
+
const parts = String(glob).split(/(\*\*\/?|\*)/);
|
|
18
25
|
let re = "";
|
|
19
|
-
for (let i = 0; i <
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (glob[i + 1] === "*") { re += ".*"; i++; if (glob[i + 1] === "/") i++; }
|
|
23
|
-
else re += "[^/]*";
|
|
24
|
-
} else if ("\\^$+?.()|{}[]".includes(c)) {
|
|
25
|
-
re += "\\" + c;
|
|
26
|
-
} else {
|
|
27
|
-
re += c;
|
|
28
|
-
}
|
|
26
|
+
for (let i = 0; i < parts.length; i++) {
|
|
27
|
+
if (i % 2 === 0) re += parts[i].replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
28
|
+
else re += parts[i] === "*" ? "[^/]*" : ".*"; // `**` and `**/` span separators
|
|
29
29
|
}
|
|
30
30
|
return new RegExp("^" + re + "$");
|
|
31
31
|
}
|
|
@@ -33,10 +33,14 @@ export function globToRegExp(glob) {
|
|
|
33
33
|
// Ask git which of `files` it ignores. Returns a Set of the ignored paths.
|
|
34
34
|
// check-ignore exits 1 when nothing matches and 128 when git is unavailable /
|
|
35
35
|
// the dir is not a repo — both mean "ignore nothing", not a build failure.
|
|
36
|
-
|
|
36
|
+
// The injected runner is named `run`, not `exec`: it is always an execFile-shaped
|
|
37
|
+
// (program, args[]) call that spawns NO shell, whereas a callback named `exec`
|
|
38
|
+
// reads — to a human skimming, and to a static analyser — as the shell-string
|
|
39
|
+
// child_process API. The name should not imply the dangerous one.
|
|
40
|
+
export function gitIgnored(root, files, run = _execFileSync) {
|
|
37
41
|
if (!files.length) return new Set();
|
|
38
42
|
try {
|
|
39
|
-
const out =
|
|
43
|
+
const out = run("git", ["-C", root, "check-ignore", "--stdin"], { input: files.join("\n"), encoding: "utf8" });
|
|
40
44
|
return new Set(out.split(/\r?\n/).filter(Boolean));
|
|
41
45
|
} catch (e) {
|
|
42
46
|
const out = e && e.stdout ? String(e.stdout) : "";
|
|
@@ -45,8 +49,8 @@ export function gitIgnored(root, files, exec = _execFileSync) {
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
// Build a predicate (file) => shouldExclude.
|
|
48
|
-
export function makeExcluder({ root, globs = [], gitignore = true, files = [],
|
|
52
|
+
export function makeExcluder({ root, globs = [], gitignore = true, files = [], run } = {}) {
|
|
49
53
|
const res = globs.map(globToRegExp);
|
|
50
|
-
const ignored = gitignore ? gitIgnored(root, files,
|
|
54
|
+
const ignored = gitignore ? gitIgnored(root, files, run) : new Set();
|
|
51
55
|
return (file) => ignored.has(file) || res.some((r) => r.test(file));
|
|
52
56
|
}
|
package/codemap/refresh.mjs
CHANGED
|
@@ -95,7 +95,10 @@ let trusted = isRecipeTrusted(fingerprint);
|
|
|
95
95
|
// full path is quoted), every argument via shq (ALWAYS double-quoted, so
|
|
96
96
|
// cmd.exe treats & | < > ( ) ^ and whitespace as literal). An injected
|
|
97
97
|
// metachar inside a dir-name argument is therefore inert.
|
|
98
|
-
|
|
98
|
+
// q quotes only when it must; WHEN it does it defers to shq, so a program path
|
|
99
|
+
// ending in a backslash cannot escape our own closing quote and swallow the
|
|
100
|
+
// next token. One set of CRT rules, in one place.
|
|
101
|
+
const q = (s) => (/[\s"]/.test(String(s)) ? shq(s) : String(s));
|
|
99
102
|
const shq = (s) => `"${String(s).replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1')}"`;
|
|
100
103
|
// Human-readable render of a step for the log / refusal message — DISPLAY ONLY,
|
|
101
104
|
// never executed. Falls back to String() for a stale (non-structured) step.
|
package/codemap/verify.mjs
CHANGED
|
@@ -37,18 +37,28 @@ if (!cli) cli = existsSync(localParser) ? localParser : "geml";
|
|
|
37
37
|
// `.geml` filename containing & | ( ) would otherwise break out and inject.
|
|
38
38
|
// cmd.exe treats those metacharacters and whitespace as literal inside quotes;
|
|
39
39
|
// CRT rules for embedded " / trailing \.
|
|
40
|
-
|
|
40
|
+
// q quotes only when it must; WHEN it does it defers to shq, so a program path
|
|
41
|
+
// ending in a backslash cannot escape our own closing quote and swallow the
|
|
42
|
+
// next token. One set of CRT rules, in one place.
|
|
43
|
+
const q = (s) => (/[\s"]/.test(String(s)) ? shq(s) : String(s));
|
|
41
44
|
const shq = (s) => `"${String(s).replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1')}"`;
|
|
45
|
+
// A codemap document's `src=` routes are written relative to the indexed
|
|
46
|
+
// SOURCE root (`geml-parser/src/attrs.ts` from a document two levels down), so
|
|
47
|
+
// checking one has to be told that root — the same value `serve` derives, from
|
|
48
|
+
// the recorded recipe, falling back to the parent of the graph directory.
|
|
49
|
+
let srcRoot = resolve(rootDir, "..");
|
|
50
|
+
try { srcRoot = resolve(rootDir, JSON.parse(readFileSync(join(rootDir, "_index", "refresh.json"), "utf8")).root ?? ".."); } catch { /* no recipe: parent */ }
|
|
51
|
+
const checkArgs = (file) => ["check", "--root", srcRoot, file];
|
|
42
52
|
const runCheck = (file) => {
|
|
43
53
|
// The built-parser path: run node on geml.js directly (array args, no shell).
|
|
44
|
-
if (cli.endsWith(".js")) return spawnSync(process.execPath, [cli,
|
|
54
|
+
if (cli.endsWith(".js")) return spawnSync(process.execPath, [cli, ...checkArgs(file)], { encoding: "utf8" });
|
|
45
55
|
// A non-.js cli may be a .cmd/.bat launcher (e.g. geml.cmd on PATH), which
|
|
46
56
|
// Node can only spawn through the shell. Hand cmd.exe ONE pre-escaped command
|
|
47
57
|
// string (never an args array — that is the unescaped, injection-prone path).
|
|
48
58
|
if (process.platform === "win32") {
|
|
49
|
-
return spawnSync([q(cli), ...
|
|
59
|
+
return spawnSync([q(cli), ...checkArgs(file).map(shq)].join(" "), { encoding: "utf8", shell: true });
|
|
50
60
|
}
|
|
51
|
-
return spawnSync(cli,
|
|
61
|
+
return spawnSync(cli, checkArgs(file), { encoding: "utf8" });
|
|
52
62
|
};
|
|
53
63
|
if (!existsSync(localParser)) {
|
|
54
64
|
console.error("verify: the profile pass needs the built parser (cd geml-parser && npm install && npm run build)");
|
package/dist/chart.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type Value } from "./attrs.js";
|
|
|
3
3
|
import { type TableModel } from "./table.js";
|
|
4
4
|
export type ChartType = "bar" | "line" | "area" | "pie" | "scatter";
|
|
5
5
|
export type RowScope = "data" | "all" | "summary";
|
|
6
|
+
export declare const USES: Record<ChartType, Set<string>>;
|
|
6
7
|
export interface ChartDataset {
|
|
7
8
|
categories: string[];
|
|
8
9
|
numbers: Record<string, number[]>;
|
package/dist/chart.js
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
// doc and §7.
|
|
9
9
|
const TYPES = new Set(["bar", "line", "area", "pie", "scatter"]);
|
|
10
10
|
// Channels each type can use; supplying any other is a warning (ignored).
|
|
11
|
-
|
|
11
|
+
// Exported for the record-array projection (GEP-0005): it must validate only
|
|
12
|
+
// the channels the chart TYPE actually reads, matching buildChart's leniency
|
|
13
|
+
// (an unused channel is a warning here, so it must not be an error there).
|
|
14
|
+
export const USES = {
|
|
12
15
|
bar: new Set(["x", "y", "series"]),
|
|
13
16
|
line: new Set(["x", "y", "series"]),
|
|
14
17
|
area: new Set(["x", "y", "series"]),
|
package/dist/diagnostics.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
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";
|
|
1
|
+
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";
|
|
2
2
|
export interface Diagnostic {
|
|
3
3
|
severity: "error" | "warning";
|
|
4
4
|
code: DiagnosticCode;
|
package/dist/diagnostics.js
CHANGED
|
@@ -19,6 +19,12 @@ export const SEVERITY = {
|
|
|
19
19
|
"block-nesting-too-deep": "error",
|
|
20
20
|
"list-nesting-too-deep": "error",
|
|
21
21
|
"inline-nesting-too-deep": "error",
|
|
22
|
+
"stray-labeled-fence": "warning",
|
|
23
|
+
"fence-like-line": "warning",
|
|
24
|
+
"unresolvable-code-source": "warning",
|
|
25
|
+
"bad-code-source": "error",
|
|
26
|
+
"bad-source-range": "error",
|
|
27
|
+
"stale-code-snapshot": "warning",
|
|
22
28
|
"duplicate-id": "error",
|
|
23
29
|
"unresolved-reference": "error",
|
|
24
30
|
"unresolved-footnote": "error",
|
|
@@ -37,6 +43,8 @@ export const SEVERITY = {
|
|
|
37
43
|
"unknown-metadata-reference": "error",
|
|
38
44
|
"table-src-and-body": "error",
|
|
39
45
|
"unknown-table-format": "warning",
|
|
46
|
+
"bad-table-delimiter": "error",
|
|
47
|
+
"ignored-table-delimiter": "warning",
|
|
40
48
|
"bad-compute-formula": "error",
|
|
41
49
|
"unlexable-compute-formula": "error",
|
|
42
50
|
"compute-error": "error",
|
|
@@ -62,6 +70,14 @@ export const SEVERITY = {
|
|
|
62
70
|
"chart-missing-summary-row": "error",
|
|
63
71
|
"chart-summary-row-unavailable": "warning",
|
|
64
72
|
"chart-non-numeric-value": "error",
|
|
73
|
+
"chart-data-not-records": "error",
|
|
74
|
+
"data-parse": "error",
|
|
75
|
+
"unknown-data-format": "warning",
|
|
76
|
+
"data-format-no-engine": "warning",
|
|
77
|
+
"bad-data-schema": "error",
|
|
78
|
+
"data-src-and-body": "error",
|
|
79
|
+
"bad-data-source": "error",
|
|
80
|
+
"unresolvable-data-source": "error",
|
|
65
81
|
};
|
|
66
82
|
// ---------------------------------------------------------------------------
|
|
67
83
|
// Source normalization (spec §0)
|
package/dist/from-md.js
CHANGED
|
@@ -55,7 +55,12 @@ function metaLine(line) {
|
|
|
55
55
|
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")))
|
|
56
56
|
v = v.slice(1, -1);
|
|
57
57
|
const bareSafe = /^[^\s"]+$/.test(v);
|
|
58
|
-
|
|
58
|
+
// No backslash escape here — a `data` block value is read by `coerce`, which
|
|
59
|
+
// simply strips the outer quotes (§4); GEML defines no `\"` escape at all.
|
|
60
|
+
// Emitting one wrote the backslash into the parsed value, so `a"b` came back
|
|
61
|
+
// as `a\"b`. Quoting the value verbatim round-trips instead: coerce keeps
|
|
62
|
+
// everything between the first and last quote, embedded quotes included.
|
|
63
|
+
return bareSafe ? `${m[1]}=${v}` : `${m[1]}="${v}"`;
|
|
59
64
|
}
|
|
60
65
|
// Rewrite Markdown autolinks `<https://…>` / `<mailto:…>` into GEML links
|
|
61
66
|
// `[url](url)` (GEML has no autolink syntax). Inline code spans are left intact.
|
package/dist/geml.d.ts
CHANGED
|
@@ -8,11 +8,14 @@ export { type Value } from "./attrs.js";
|
|
|
8
8
|
export { type Inline } from "./inline.js";
|
|
9
9
|
export { type TableModel } from "./table.js";
|
|
10
10
|
export { mdToGeml, type ConvertResult } from "./from-md.js";
|
|
11
|
-
export { renderHtml } from "./render-html.js";
|
|
11
|
+
export { renderHtml, pageAssets } from "./render-html.js";
|
|
12
12
|
export { type RenderOptions } from "./render.js";
|
|
13
13
|
export { serialize } from "./serialize.js";
|
|
14
14
|
export { gemlToMd } from "./to-md.js";
|
|
15
15
|
export type BodyMode = "raw" | "flow" | "data";
|
|
16
|
+
export type DataValue = null | boolean | number | string | DataValue[] | {
|
|
17
|
+
[key: string]: DataValue;
|
|
18
|
+
};
|
|
16
19
|
export interface ListItem {
|
|
17
20
|
text: string;
|
|
18
21
|
inlines: Inline[];
|
|
@@ -53,6 +56,7 @@ export type Block = {
|
|
|
53
56
|
data?: Record<string, Value>;
|
|
54
57
|
table?: TableModel;
|
|
55
58
|
chart?: ChartModel;
|
|
59
|
+
value?: DataValue;
|
|
56
60
|
hidden?: boolean;
|
|
57
61
|
};
|
|
58
62
|
export { type Diagnostic, type DiagnosticCode, SEVERITY } from "./diagnostics.js";
|