clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

Draw mermaid diagrams at build time, with Pele

cat committed 3527968
A ```mermaid fence in markdown was a <pre> that ui.ts filled in the
browser, loading mermaid and picking a light or dark theme
once. Now the build draws it as inline SVG with Pele 1.6.0 (pinned), and
pages ship no diagram script: mermaid, its asset, the data-mermaid
attribute and CI's check for it are gone.

style.css maps Pele's --pele-* variables to the page's tokens (nodes on
--raised outlined in --border-strong, square corners, the sans and mono, bold
at --fw-strong, the chart and syntax colours for series), so a diagram
follows light and dark mode without being drawn again.

Pele sizes nodes by measuring their labels: with a canvas in a browser,
and at build time with a table of Helvetica's widths. The labels are set
in the page's sans, so diagrams.ts gives it a measurer with that font's
widths instead (regular, and bold at 650), made by lib/text-widths.sh;
the mono is 0.6em, as Pele assumes. Over 174 labels of the diagram types
in a browser, its widths are within 0.2% of Open Sans as drawn; Pele's
own table is off by up to 14%.

Markdown is anyone's who can push a repo. Pele escapes a diagram's text,
drops HTML in labels, ignores init directives' CSS and fonts, and keeps
links to safe schemes. Its SVG then goes through a sanitizer of its own,
which keeps only the tags, attributes and style properties Pele draws
with, no images and no url(), and Pele's own classes; raw SVG in the
markdown is still dropped. The markdown's sanitizer sees a placeholder
named by a random token where each diagram goes.

A diagram that doesn't parse shows its source and Pele's reason, and the
page builds. The demo wiki has a diagram, and CI checks it is drawn;
link previews leave diagrams' labels out of their text.
commit
3527968279601aec7e3a495aa3e7f40d26138530
parent
a5bc3d54d3da53d29f6c7b75b96e88376ff8e36b
author
cat <admin@dev.site>
ci
✓ passed in 77s, signed by cat
kind
feat risk score 2.2 of 3, Jev's estimate
review
#15 Draw mermaid diagrams at build time, with Pele
ci attestation
forge-attestation 1
type ci
commit 3527968279601aec7e3a495aa3e7f40d26138530
tree 16351a3308f70ce9c3f97bf25ab829d4e7541c39
script .forge/ci aa9f4eac26627c6559e68cd4b5e5e76afb8e65e8
result pass
exit 0
started 2026-10-19T03:15:53Z
duration 77s
runner (not shown here; refs/notes/ci has it)
system Darwin 25.5.0 arm64
env host
log sha256:c5b107dfcc3b02e7abb0dcf10bb31d2714575914f90aa45ac7a86c3123ac02ad 262 lines
|pushed wip/i18n
|b9f05e2 running .forge/ci: greet.sh: say what it is
|  | sh: /bin/sh
|  | ok   greet
|b9f05e2 pass in 0s, signed
|pushed main
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|f1738d5 running .forge/ci: wave at the world
|  | sh: /bin/sh
|  | FAIL greet : got 'hello, world o/', want 'hello, world'
|f1738d5 fail in 0s, signed
|forge: 1 commit(s) did not pass
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|69be005 running .forge/ci: update the test for the wave
|  | sh: /bin/sh
|  | ok   greet
|69be005 pass in 0s, signed
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|warning: You appear to have cloned an empty repository.
|site home: deployed cc676cf -> /sites/ana/dotfiles/home/
|created ana/notes
|remote: review #1: /ana/notes/reviews/1/
|pushed ana/todo
|landing #1 A todo list: ana/todo -> main
|de4ba0a running .forge/ci: web, server and Python versions of greet
|  | sh: /bin/sh
|  | ok   greet
|de4ba0a pass in 0s, signed
|pushed main
|9eb5682 running .forge/ci: messages: notes for the later regions
|  | sh: /bin/sh
|  | ok   greet
|9eb5682 pass in 0s, signed
|remote: review #7: /dami/greet/reviews/7/
|pushed dami/regions
|created dami/forge
|warning: You appear to have cloned an empty repository.
|site guide: mkdir -p guide/fonts && cp site/assets/fonts/*.woff2 guide/fonts/
|site guide: deployed b9b6afa -> /sites/dami/forge/guide/
|(node:62751) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/dotfiles in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.0qhZ9Z/wt/demo/root/.forge/www
|(node:62878) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/notes in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.0qhZ9Z/wt/demo/root/.forge/www
|(node:63022) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/forge in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.0qhZ9Z/wt/demo/root/.forge/www
|(node:63155) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/greet in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.0qhZ9Z/wt/demo/root/.forge/www
|(node:63824) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built - in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.0qhZ9Z/wt/demo/root/.forge/www
|234 of 234 pages render the same without 11ty
|guide: 8 files, links ok
|ok

Signature: good, cat

17 files changed, +465 -1297
M.forge/ci4+-
MREADME.md15+-
Mdemo/make-demo.sh14+
Mguide/internals.html2+-
Mlib/assets.ts6+-
Alib/diagrams.ts111++
Mlib/forge.ts9+-
Mlib/markdown.ts29+-
Alib/text-widths.sh32+
Mpackage-lock.json1375+++---------------------------
Mpackage.json2+-
Msite/_includes/base.njk2+-
Msite/assets/style.css14+-
Msite/assets/ui.ts17+-
Atest/scenarios/diagram-broken.sh35+
Atest/scenarios/diagram-hostile.sh58+
Atest/scenarios/diagram-svg.sh37+
M .forge/ci +3 -1
M README.md +14 -1
@@ -704,6 +704,19 @@ files are cached in `.forge/cache/highlight/` by blob id, so pushing a new
704704 branch doesn't redo files that other branches already have. Token colors are705705 CSS variables (`--tok-*`) in `style.css`.706706 707+Mermaid diagrams in markdown (` ```mermaid ` fences) are drawn at build708+time too, as inline SVG, by [Pele](https://pele.run), so pages ship no709+diagram script. `style.css` colours them through Pele's `--pele-*`710+variables, which follow light and dark mode without drawing them again.711+There is no browser at build time to measure text in, so labels are sized712+from a table of the sans font's character widths in `lib/diagrams.ts`713+(`lib/text-widths.sh` makes it from the font). A diagram that doesn't parse714+shows its source and the reason instead. Markdown is anyone's who can push715+a repo: Pele escapes a diagram's text and filters its links and styles,716+then the SVG goes through a sanitizer of its own that keeps only the717+shapes, text and links Pele draws with (no images, no `url()`), while raw718+SVG in the markdown itself is still dropped.719+707720 ## The lake and insights708721 709722 The forge's facts are append-only, but they don't last: `events.jsonl` is
@@ -1332,7 +1345,7 @@ rendered, `/assets/<hash>.css`, shared by pages of a kind. Import
13321345 `macros.njk` with context, or blocks in macros don't reach the bundle.13331346 13341347 Assets ship under content-hashed names (`/assets/style.<hash>.css`,1335-`ui.<hash>.js`, mermaid, sqlite-wasm), so browsers can cache them for good;1348+`ui.<hash>.js`, sqlite-wasm), so browsers can cache them for good;13361349 the stylesheet's `url(./fonts/open-sans.woff2)` becomes the font's hashed name. The same13371350 sources give the same names, and pages' fingerprints include them, so13381351 unchanged pages are still hard-linked. A build keeps the files of the last
M demo/make-demo.sh +14 -0
M guide/internals.html +1 -1
M lib/assets.ts +2 -4
@@ -1,7 +1,7 @@
11 // Assets under content-hashed names, /assets/style.<hash>.css, so browsers22 // can cache them forever: the stylesheet, the logo, the fonts (in fonts/,33 // shipped next to the rest), the vendored scripts, our TypeScript with its4-// types stripped, mermaid, sqlite-wasm (its script and4+// types stripped, sqlite-wasm (its script and55 // its .wasm, for code search), and @simplewebauthn/browser (passkeys, on66 // the sign-in page). 11ty's bundle plugin77 // writes the templates' own CSS next to them, also named by a hash.
@@ -19,12 +19,10 @@ import { fileURLToPath } from 'node:url';
1919 const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');2020 const src = path.join(root, 'site', 'assets');2121 const fonts = path.join(src, 'fonts');22-const MERMAID = path.join(root, 'node_modules', 'mermaid', 'dist', 'mermaid.min.js');2322 const SQLITE = path.join(root, 'node_modules', '@sqlite.org', 'sqlite-wasm', 'dist');2423 const WEBAUTHN = path.join(root, 'node_modules', '@simplewebauthn', 'browser', 'dist', 'bundle', 'index.umd.min.js');2524 // Files from node_modules: short name, the name they ship as, the source.2625 const VENDOR: [string, string, string][] = [27- ['mermaid', 'mermaid.js', MERMAID],2826 // The browser build, an ES module that codesearch.ts imports in its worker.2927 ['sqlite3', 'sqlite3.js', path.join(SQLITE, 'index.mjs')],3028 ['sqlite3wasm', 'sqlite3.wasm', path.join(SQLITE, 'sqlite3.wasm')],
@@ -46,7 +44,7 @@ export interface Asset {
4644 4745 // By short name: style, logo, open-sans, noto-sans-mono, noto-serif,4846 // noto-serif-italic, fixi, ssexi, diffs, keys, live,49-// search, query, codesearch, login, ui, mermaid, sqlite3, sqlite3wasm, webauthn.47+// search, query, codesearch, login, ui, sqlite3, sqlite3wasm, webauthn.5048 export type Assets = Record<string, Asset>;5149 5250 let memo: { key: string; assets: Assets } | null = null;
A lib/diagrams.ts +111 -0
@@ -0,0 +1,111 @@
1+// Mermaid diagrams, drawn at build time: a ```mermaid fence in markdown2+// becomes inline SVG (Pele, https://pele.run) that the stylesheet colours3+// through its --pele-* variables, so it follows light and dark with no4+// script. A diagram that doesn't parse stays its source, with a note.5+//6+// The markdown is anyone's who can push a repo. Pele escapes the text in a7+// diagram and filters its links and styles; then the SVG goes through a8+// sanitizer of its own that keeps only what Pele draws with, apart from9+// the markdown's, whose raw HTML still can't have SVG.10+import { createHash } from 'node:crypto';11+import { PeleError, render, type TextMeasurer } from 'pele';12+import sanitize from 'sanitize-html';13+14+// Text widths of the sans (Open Sans), regular and bold (--fw-strong,15+// which style.css sets diagrams' bold in), in thousandths of an em:16+// U+0020-007E then U+00A0-017F, two base-36 digits each, made by17+// lib/text-widths.sh. Pele measures labels to size nodes, with a canvas in18+// a browser; at build time there is none, and its own table is Helvetica's.19+const REGULAR = '787cb2hyfwmzk9638787fbfw778y7ba7fwfwfwfwfwfwfwfwfwfw7b7bfwfwfwc0owhkhyhik6fgeck7kh7r7hh0eiozkxlmgqlmh5f8fbk9gkpng2fjfw93a793fwc67pfgh0dbh0fm9cf3h17070el70pqh1gqh0h0bdd99wh1dwljejdxd1aff9affw787cfwfwfwfwf9eag4n49tdsfw8yn4dwbwfw9o9o7ph6i77b669oaedskklclmc0hkhkhkhkhkhko4hifgfgfgfg7r7r7r7rk6kxlmlmlmlmlmfwlmk9k9k9k9fjgqhbfgfgfgfgfgfgnydbfmfmfmfm70707070goh1gqgqgqgqgqfwgqh1h1h1h1dxh0dxhkfghkfghkfghidbhidbhidbhidbk6h0k6h1fgfmfgfmfgfmfgfmfgfmk7f3k7f3k7f3k7f3khh1khh17r707r707r707r707r70f8e17h70h0elelei70ei70ei70ei79ei70kxh1kxh1kxh1iskxh1lmgqlmgqlmgqppqch5bdh5bdh5bdf8d9f8d9f8d9f8d9fb9wfb9wfb9wk9h1k9h1k9h1k9h1k9h1k9h1pnljfjdxfjfwd1fwd1fwd18z';20+const BOLD = '787tcmhyfwoikp729494f6fw7r8y7sb5fwfwfwfwfwfwfwfwfwfw7s7sfwfwfwcyoxisiihnkhfjf1k5l28u8ri3fepwm6m0ham0i1fafwkthpqkhxgwg296b596fwbm9hgghfe1hfg7affkhy8484gl84qwhyh3hfhfcbdobjhyfcn8fofcdfatfaatfw787tfwfwfwfwfadpgon4afg9fw8yn4dwbwfwabab9hi2i77s5tabapg9mgnkn1cyisisisisisispvhnfjfjfjfj8u8u8u8ukhm6m0m0m0m0m0fwm0ktktktktgwhaj5ggggggggggggp3e1g7g7g7g784848484h2hyh3h3h3h3h3fwh3hyhyhyhyfchffcisggisggisgghne1hne1hne1hne1khhfkhhsfjg7fjg7fjg7fjg7fjg7k5fkk5fkk5fkk5fkl2hyl2hy8u848u848u848u848u84hmg78r84i3glglfe84fe84fe84fe9dfe84m6hym6hym6hykrm6hym0h3m0h3m0h3qpqzi1cbi1cbi1cbfadofadofadofadofwbjfwbjfwbjkthykthykthykthykthykthyqkn8gwfcgwg2dfg2dfg2dfa8';21+const table = (s: string): Map<number, number> => {22+ const codes = [...Array(0x7f - 0x20).keys()].map((i) => i + 0x20).concat([...Array(0x180 - 0xa0).keys()].map((i) => i + 0xa0));23+ return new Map(codes.map((c, i) => [c, parseInt(s.slice(i * 2, i * 2 + 2), 36)]));24+};25+const widths = [table(REGULAR), table(BOLD)];26+// Characters the sans lacks come from other fonts, as in Pele's own table:27+// marks and zero-width spaces take no room, East Asian wide ones an em,28+// the rest a guess. The mono (Noto Sans Mono) is 0.6em throughout.29+const ZERO = /[̀-ͯ​-‏︀-️]/;30+const WIDE = /[ᄀ-ᅟ⺀-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]|[^\0-￿]/u;31+const measurer: TextMeasurer = {32+ width(text, size, style) {33+ const mono = (style & 4) !== 0, w = widths[style & 1];34+ let sum = 0;35+ for (const ch of text) {36+ const c = ch.codePointAt(0)!;37+ if (c < 0x20 || ZERO.test(ch)) continue;38+ sum += WIDE.test(ch) ? (mono ? 1200 : 1000) : mono ? 600 : (w.get(c) ?? 580);39+ }40+ return (sum * size) / 1000;41+ },42+};43+44+// What Pele draws with, and no more: shapes, text, links. No images, no45+// foreign objects, no <style>, no use or xlink references; inline styles only46+// with these properties, and no url() in them or in any attribute.47+const PRESENTATION = ['class', 'style', 'transform', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'stroke-linejoin', 'opacity'];48+const SHAPE = [...PRESENTATION, 'x', 'y', 'width', 'height', 'rx', 'ry', 'cx', 'cy', 'r', 'd', 'points'];49+const TEXT = [...PRESENTATION, 'x', 'y', 'font-size', 'font-weight', 'font-style', 'font-family', 'text-anchor', 'xml:space'];50+const SAFE = /^(?![\s\S]*url\s*\()[-\w\s#%.,()+*/"']*$/i;51+const STYLES = [52+ '--_a', '--_b', '--_bg', '--_c', '--_fg', '--_fm', '--_hw', '--_l', '--_m', '--_r', '--_s', '--_tw', '--_w',53+ 'max-width', 'height', 'color', 'direction', 'font', 'font-family', 'font-size', 'font-style', 'font-weight',54+ 'letter-spacing', 'word-spacing', 'text-transform', 'text-decoration', 'fill', 'stroke', 'stroke-width',55+ 'stroke-dasharray', 'opacity', 'rx',56+];57+const SVG: sanitize.IOptions = {58+ allowedTags: ['svg', 'g', 'path', 'rect', 'circle', 'ellipse', 'polygon', 'text', 'tspan', 'title', 'desc', 'a'],59+ allowedAttributes: {60+ svg: [...SHAPE, 'xmlns', 'viewBox', 'preserveAspectRatio', 'role', 'aria-roledescription', 'aria-labelledby', 'aria-describedby', 'aria-hidden'],61+ g: [...TEXT],62+ path: SHAPE, rect: SHAPE, circle: SHAPE, ellipse: SHAPE, polygon: SHAPE,63+ text: TEXT, tspan: TEXT,64+ title: ['id'], desc: ['id'],65+ a: ['href', 'rel', 'target'],66+ },67+ // Pele's own classes; a diagram's classDef names aren't kept, so they68+ // can't pick up the page's styles.69+ allowedClasses: { '*': [/^pele(-[\w-]+)?$/, 'clickable'] },70+ allowedStyles: { '*': Object.fromEntries(STYLES.map((p) => [p, [SAFE]])) },71+ allowedSchemes: ['http', 'https', 'mailto'],72+ allowProtocolRelative: false,73+ // SVG's attribute names have capitals (viewBox); keep them as Pele wrote them.74+ parser: { lowerCaseAttributeNames: false },75+};76+77+const escape = (s: string): string => s.replace(/[&<>"]/g, (c) => `&${{ '&': 'amp', '<': 'lt', '>': 'gt', '"': 'quot' }[c]};`);78+79+// One diagram's HTML. `href` rewrites a link in it, as the markdown's own80+// links are (relative ones resolve to forge pages).81+export function diagram(src: string, href: (url: string) => string = (u) => u): string {82+ // Ids (for the title and description) from the source, so a page that83+ // didn't change builds the same.84+ const idPrefix = 'diagram-' + createHash('sha256').update(src).digest('hex').slice(0, 8);85+ const options = { measurer, images: false, linkSchemes: ['http', 'https', 'mailto'] };86+ let svg;87+ try {88+ svg = render(src, { ...options, idPrefix }).svg;89+ } catch (e) {90+ // Pele's message names the line, and can quote it: shown as text.91+ let why = 'Pele failed on it';92+ if (e instanceof PeleError) why = e.message.split('\n')[0].replace(/[.:]$/, '').slice(0, 160);93+ return `<figure class="diagram-source"><pre><code class="language-mermaid">${escape(src)}</code></pre><figcaption>Not drawn: ${escape(why)}.</figcaption></figure>\n`;94+ }95+ const clean = sanitize(svg, {96+ ...SVG,97+ // Every tag: no url() in any attribute; a link's href rewritten. (A98+ // transform for '*' replaces one for the tag, so it is all here.)99+ transformTags: {100+ '*': (tag, attribs) => ({101+ tagName: tag,102+ attribs: Object.fromEntries(103+ Object.entries(attribs)104+ .filter(([, v]) => !/url\s*\(/i.test(v))105+ .map(([k, v]) => [k, tag === 'a' && k === 'href' ? href(v) : v]),106+ ),107+ }),108+ },109+ });110+ return `<figure class="diagram">${clean}</figure>\n`;111+}
M lib/forge.ts +5 -4
@@ -167,8 +167,8 @@ export function loadForge(env: NodeJS.ProcessEnv = process.env): Forge {
167167 // fingerprint matches the last build (and whose file still exists) isn't168168 // rendered; its file is hard-linked from the live site instead.169169 const live = env.FORGE_LIVE_WWW || null;170- // Pages link assets by hashed name, so a new name (mermaid or Node updated,171- // say) must re-render them too.170+ // Pages link assets by hashed name, so a new name (sqlite-wasm or Node171+ // updated, say) must re-render them too.172172 const code = fingerprint(codeVersion(), Object.values(assets()).map((a) => a.file));173173 const skipped: string[] = [];174174 const pageCaches: Forge['pageCaches'] = [];
@@ -556,10 +556,11 @@ export const filters = {
556556 },557557 // A page's description, for link previews: one line of plain text, cut at558558 // a word near n characters. With html, the text of rendered markdown559- // (markdown-it puts a newline between blocks, so tags can just go).559+ // (markdown-it puts a newline between blocks, so tags can just go), less560+ // its diagrams, whose labels are no sentence.560561 excerpt(s: string | null | undefined, html = false, n = 200): string {561562 let text = String(s ?? '');562- if (html) text = decode(text.replace(/<[^>]*>/g, ''));563+ if (html) text = decode(text.replace(/<figure class="diagram">.*?<\/figure>/gs, '').replace(/<[^>]*>/g, ''));563564 text = text.replace(/\s+/g, ' ').trim();564565 if (text.length <= n) return text;565566 const cut = text.lastIndexOf(' ', n);
M lib/markdown.ts +20 -9
@@ -2,10 +2,12 @@
22 //33 // Raw HTML is allowed through a sanitizer (the common README idioms:44 // centered paragraphs, sized images, <details>, <picture>), never scripts,5-// styles or event handlers. Relative links and images resolve to forge pages6-// and raw files, in markdown and in raw HTML alike.5+// styles, event handlers or SVG. Relative links and images resolve to forge6+// pages and raw files, in markdown and in raw HTML alike.7+import { randomUUID } from 'node:crypto';78 import MarkdownIt from 'markdown-it';89 import sanitize from 'sanitize-html';10+import { diagram } from './diagrams.ts';911 import { highlight, resolve } from './highlight.ts';1012 1113 type Token = ReturnType<MarkdownIt['parse']>[number];
@@ -15,12 +17,20 @@ const md = new MarkdownIt({ html: true, linkify: true, typographer: false, highl
1517 // "NOTICE.md" is not a website, even though .md is a TLD.1618 md.linkify.set({ fuzzyLink: false });1719 18-// ```mermaid blocks become diagrams in the browser (site/assets/diagrams.ts).20+// ```mermaid blocks become SVG diagrams (lib/diagrams.ts), sanitized on21+// their own. In their place the markdown's HTML has a placeholder, which22+// names a random token the source can't know, until it has been sanitized.23+interface Env {24+ token: string;25+ diagrams: string[];26+ href: (url: string) => string;27+}1928 const fence = md.renderer.rules.fence!;20-md.renderer.rules.fence = (tokens, idx, options, env, self) => {29+md.renderer.rules.fence = (tokens, idx, options, env: Env, self) => {2130 const t = tokens[idx];22- if (t.info.trim() === 'mermaid') return `<pre class="mermaid">${md.utils.escapeHtml(t.content)}</pre>\n`;23- return fence(tokens, idx, options, env, self);31+ if (t.info.trim() !== 'mermaid') return fence(tokens, idx, options, env, self);32+ env.diagrams.push(diagram(t.content, env.href));33+ return `<div>${env.token}:${env.diagrams.length - 1}</div>\n`;2434 };2535 2636 const isRelative = (u: string | null | undefined): u is string => !!u && !/^([a-z][a-z0-9+.-]*:|\/\/|#|\/)/i.test(u);
@@ -61,13 +71,13 @@ const ALLOWED = {
6171 input: ['type', 'checked', 'disabled'],6272 },6373 // highlight.js tokens: "hljs-title" plus sub-scopes like "function_".64- allowedClasses: { '*': ['mermaid', /^language-/], span: [/^hljs-/, /^[a-z]+_+$/] },74+ allowedClasses: { '*': [/^language-/], span: [/^hljs-/, /^[a-z]+_+$/] },6575 allowedSchemes: ['http', 'https', 'mailto'],6676 allowProtocolRelative: false,6777 };6878 6979 export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u }: LinkResolver = {}): string {70- const env = {};80+ const env: Env = { token: randomUUID(), diagrams: [], href: (u) => (isRelative(u) ? link(u) : u) };7181 const tokens = md.parse(src, env);7282 const slug = slugger();7383 const walk = (list: Token[]): void => {
@@ -90,7 +100,7 @@ export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u
90100 });91101 const rendered = md.renderer.render(tokens, md.options, env);92102 // Raw HTML in the source (img, a, source) resolves the same way markdown does.93- return sanitize(rendered, {103+ const html = sanitize(rendered, {94104 ...ALLOWED,95105 transformTags: {96106 img: (tag, attribs) => ({ tagName: tag, attribs: { ...attribs, src: isRelative(attribs.src) ? image(attribs.src) : attribs.src, loading: 'lazy', decoding: 'async' } }),
@@ -104,6 +114,7 @@ export function renderMarkdown(src: string, { link = (u) => u, image = (u) => u
104114 }),105115 },106116 });117+ return env.diagrams.length ? html.replace(new RegExp(`<div>${env.token}:(\\d+)</div>`, 'g'), (_, i: string) => env.diagrams[Number(i)]) : html;107118 }108119 109120 // Resolve a relative link against the directory of the file it appears in.
A lib/text-widths.sh +32 -0
M package-lock.json +117 -1258
@@ -14,7 +14,7 @@
1414 "@typesafe-ai/sdk": "^0.7.0",1515 "highlight.js": "^11.12.0",1616 "markdown-it": "^14.1.0",17- "mermaid": "^12.2.0",17+ "pele": "1.6.0",1818 "sanitize-html": "^2.18.1"1919 },2020 "devDependencies": {
@@ -217,62 +217,6 @@
217217 "node": ">=18"218218 }219219 },220- "node_modules/@antfu/install-pkg": {221- "version": "2.1.0",222- "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-2.1.0.tgz",223- "integrity": "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==",224- "license": "MIT",225- "dependencies": {226- "package-manager-detector": "^1.8.0",227- "tinyexec": "^1.3.1"228- },229- "funding": {230- "url": "https://github.com/sponsors/antfu"231- }232- },233- "node_modules/@braintree/sanitize-url": {234- "version": "7.1.2",235- "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",236- "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",237- "license": "MIT"238- },239- "node_modules/@chevrotain/cst-dts-gen": {240- "version": "13.2.0",241- "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-13.2.0.tgz",242- "integrity": "sha512-bA9dvWlhAUcHkNwU+8w9yiUxJPlj/Lm3VqS7MD+NbIR/7LmpNLuK/AWNFdCo/T3SK5ND0/UHf3IJvl/Jn/i6yg==",243- "license": "Apache-2.0",244- "dependencies": {245- "@chevrotain/gast": "13.2.0",246- "@chevrotain/types": "13.2.0"247- }248- },249- "node_modules/@chevrotain/gast": {250- "version": "13.2.0",251- "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-13.2.0.tgz",252- "integrity": "sha512-bCARdAsG+eEbOveSXcOAC7pVxA04I1ionzeMOwIYf5NX7SaS7zgn4Dgug2BSqoL+iyKeMNSorQjsFSHJHsCNwg==",253- "license": "Apache-2.0",254- "dependencies": {255- "@chevrotain/types": "13.2.0"256- }257- },258- "node_modules/@chevrotain/regexp-to-ast": {259- "version": "13.2.0",260- "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-13.2.0.tgz",261- "integrity": "sha512-kxpUkLtiaK09ph+dib+Yvm+3sWSnuwlo25gqfKRgTPNmF6H7uW4gVkMjaRdtnN783S9sM6vUNWh9CsxYX4QwKw==",262- "license": "Apache-2.0"263- },264- "node_modules/@chevrotain/types": {265- "version": "13.2.0",266- "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-13.2.0.tgz",267- "integrity": "sha512-7qrYHRlKfWf2AvBfJi5eVOrKAkbxvMFsy/iIHTGIiApNcEecCaVIdR+mIQhyCBsTDJxBz4JWXHMo6krbhR9hVg==",268- "license": "Apache-2.0"269- },270- "node_modules/@chevrotain/utils": {271- "version": "13.2.0",272- "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-13.2.0.tgz",273- "integrity": "sha512-SzKYY68rseOrb9KILVzPsl6SjkQtdWw01SRGB5aCVI5kFcHltQsOYV3JB0Ffst72DgkvHCyDQ0NLwxtmCCf97Q==",274- "license": "Apache-2.0"275- },276220 "node_modules/@duckdb/node-api": {277221 "version": "1.5.6-r.1",278222 "resolved": "https://registry.npmjs.org/@duckdb/node-api/-/node-api-1.5.6-r.1.tgz",
@@ -411,41 +355,12 @@
411355 "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",412356 "license": "MIT"413357 },414- "node_modules/@iconify/types": {415- "version": "2.0.0",416- "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",417- "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",418- "license": "MIT"419- },420- "node_modules/@iconify/utils": {421- "version": "3.1.7",422- "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.7.tgz",423- "integrity": "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==",424- "license": "MIT",425- "dependencies": {426- "@antfu/install-pkg": "^2.0.1",427- "@iconify/types": "^2.0.0",428- "import-meta-resolve": "^4.2.0"429- }430- },431358 "node_modules/@levischuck/tiny-cbor": {432359 "version": "0.2.11",433360 "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",434361 "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==",435362 "license": "MIT"436363 },437- "node_modules/@mermaid-js/parser": {438- "version": "2.0.1",439- "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.1.tgz",440- "integrity": "sha512-j37dj/h6p6pots1L7pLPPNuABEEu9WP4OYg9pW1g+nYG331j0+3canE9OagtYdytYvdSAMG+2fKU5cxep6LgjA==",441- "license": "MIT",442- "dependencies": {443- "@chevrotain/types": "~13.2.0"444- },445- "engines": {446- "node": ">=22.12.0"447- }448- },449364 "node_modules/@peculiar/asn1-android": {450365 "version": "2.11.0",451366 "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.11.0.tgz",
@@ -749,265 +664,6 @@
749664 "node": ">=22"750665 }751666 },752- "node_modules/@types/d3": {753- "version": "7.4.3",754- "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",755- "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",756- "license": "MIT",757- "dependencies": {758- "@types/d3-array": "*",759- "@types/d3-axis": "*",760- "@types/d3-brush": "*",761- "@types/d3-chord": "*",762- "@types/d3-color": "*",763- "@types/d3-contour": "*",764- "@types/d3-delaunay": "*",765- "@types/d3-dispatch": "*",766- "@types/d3-drag": "*",767- "@types/d3-dsv": "*",768- "@types/d3-ease": "*",769- "@types/d3-fetch": "*",770- "@types/d3-force": "*",771- "@types/d3-format": "*",772- "@types/d3-geo": "*",773- "@types/d3-hierarchy": "*",774- "@types/d3-interpolate": "*",775- "@types/d3-path": "*",776- "@types/d3-polygon": "*",777- "@types/d3-quadtree": "*",778- "@types/d3-random": "*",779- "@types/d3-scale": "*",780- "@types/d3-scale-chromatic": "*",781- "@types/d3-selection": "*",782- "@types/d3-shape": "*",783- "@types/d3-time": "*",784- "@types/d3-time-format": "*",785- "@types/d3-timer": "*",786- "@types/d3-transition": "*",787- "@types/d3-zoom": "*"788- }789- },790- "node_modules/@types/d3-array": {791- "version": "3.2.2",792- "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",793- "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",794- "license": "MIT"795- },796- "node_modules/@types/d3-axis": {797- "version": "3.0.6",798- "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",799- "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",800- "license": "MIT",801- "dependencies": {802- "@types/d3-selection": "*"803- }804- },805- "node_modules/@types/d3-brush": {806- "version": "3.0.6",807- "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",808- "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",809- "license": "MIT",810- "dependencies": {811- "@types/d3-selection": "*"812- }813- },814- "node_modules/@types/d3-chord": {815- "version": "3.0.6",816- "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",817- "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",818- "license": "MIT"819- },820- "node_modules/@types/d3-color": {821- "version": "3.1.3",822- "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",823- "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",824- "license": "MIT"825- },826- "node_modules/@types/d3-contour": {827- "version": "3.0.6",828- "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",829- "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",830- "license": "MIT",831- "dependencies": {832- "@types/d3-array": "*",833- "@types/geojson": "*"834- }835- },836- "node_modules/@types/d3-delaunay": {837- "version": "6.0.4",838- "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",839- "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",840- "license": "MIT"841- },842- "node_modules/@types/d3-dispatch": {843- "version": "3.0.7",844- "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",845- "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",846- "license": "MIT"847- },848- "node_modules/@types/d3-drag": {849- "version": "3.0.7",850- "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",851- "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",852- "license": "MIT",853- "dependencies": {854- "@types/d3-selection": "*"855- }856- },857- "node_modules/@types/d3-dsv": {858- "version": "3.0.7",859- "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",860- "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",861- "license": "MIT"862- },863- "node_modules/@types/d3-ease": {864- "version": "3.0.2",865- "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",866- "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",867- "license": "MIT"868- },869- "node_modules/@types/d3-fetch": {870- "version": "3.0.7",871- "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",872- "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",873- "license": "MIT",874- "dependencies": {875- "@types/d3-dsv": "*"876- }877- },878- "node_modules/@types/d3-force": {879- "version": "3.0.10",880- "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",881- "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",882- "license": "MIT"883- },884- "node_modules/@types/d3-format": {885- "version": "3.0.4",886- "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",887- "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",888- "license": "MIT"889- },890- "node_modules/@types/d3-geo": {891- "version": "3.1.1",892- "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz",893- "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==",894- "license": "MIT",895- "dependencies": {896- "@types/geojson": "*"897- }898- },899- "node_modules/@types/d3-hierarchy": {900- "version": "3.1.7",901- "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",902- "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",903- "license": "MIT"904- },905- "node_modules/@types/d3-interpolate": {906- "version": "3.0.4",907- "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",908- "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",909- "license": "MIT",910- "dependencies": {911- "@types/d3-color": "*"912- }913- },914- "node_modules/@types/d3-path": {915- "version": "3.1.1",916- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",917- "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",918- "license": "MIT"919- },920- "node_modules/@types/d3-polygon": {921- "version": "3.0.2",922- "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",923- "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",924- "license": "MIT"925- },926- "node_modules/@types/d3-quadtree": {927- "version": "3.0.6",928- "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",929- "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",930- "license": "MIT"931- },932- "node_modules/@types/d3-random": {933- "version": "3.0.4",934- "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz",935- "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==",936- "license": "MIT"937- },938- "node_modules/@types/d3-scale": {939- "version": "4.0.9",940- "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",941- "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",942- "license": "MIT",943- "dependencies": {944- "@types/d3-time": "*"945- }946- },947- "node_modules/@types/d3-scale-chromatic": {948- "version": "3.1.0",949- "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",950- "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",951- "license": "MIT"952- },953- "node_modules/@types/d3-selection": {954- "version": "3.0.12",955- "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.12.tgz",956- "integrity": "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw==",957- "license": "MIT"958- },959- "node_modules/@types/d3-shape": {960- "version": "3.2.0",961- "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz",962- "integrity": "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==",963- "license": "MIT",964- "dependencies": {965- "@types/d3-path": "*"966- }967- },968- "node_modules/@types/d3-time": {969- "version": "3.0.4",970- "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",971- "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",972- "license": "MIT"973- },974- "node_modules/@types/d3-time-format": {975- "version": "4.0.3",976- "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",977- "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",978- "license": "MIT"979- },980- "node_modules/@types/d3-timer": {981- "version": "3.0.2",982- "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",983- "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",984- "license": "MIT"985- },986- "node_modules/@types/d3-transition": {987- "version": "3.0.9",988- "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",989- "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",990- "license": "MIT",991- "dependencies": {992- "@types/d3-selection": "*"993- }994- },995- "node_modules/@types/d3-zoom": {996- "version": "3.0.9",997- "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.9.tgz",998- "integrity": "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA==",999- "license": "MIT",1000- "dependencies": {1001- "@types/d3-interpolate": "*",1002- "@types/d3-selection": "*"1003- }1004- },1005- "node_modules/@types/geojson": {1006- "version": "7946.0.16",1007- "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",1008- "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",1009- "license": "MIT"1010- },1011667 "node_modules/@types/linkify-it": {1012668 "version": "5.0.0",1013669 "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
@@ -1160,13 +816,6 @@
1160816 "node": ">=20.19.0"1161817 }1162818 },1163- "node_modules/@types/trusted-types": {1164- "version": "2.0.7",1165- "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",1166- "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",1167- "license": "MIT",1168- "optional": true1169- },1170819 "node_modules/@typesafe-ai/sdk": {1171820 "version": "0.7.0",1172821 "resolved": "https://registry.npmjs.org/@typesafe-ai/sdk/-/sdk-0.7.0.tgz",
@@ -1510,696 +1159,153 @@
15101159 "license": "Apache-2.0",15111160 "optional": true,15121161 "os": [1513- "win32"1514- ],1515- "engines": {1516- "node": ">=16.20.0"1517- }1518- },1519- "node_modules/@upsetjs/venn.js": {1520- "version": "2.0.0",1521- "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",1522- "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",1523- "license": "MIT",1524- "optionalDependencies": {1525- "d3-selection": "^3.0.0",1526- "d3-transition": "^3.0.1"1527- }1528- },1529- "node_modules/a-sync-waterfall": {1530- "version": "1.0.1",1531- "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",1532- "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",1533- "license": "MIT"1534- },1535- "node_modules/acorn": {1536- "version": "8.19.0",1537- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",1538- "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",1539- "license": "MIT",1540- "bin": {1541- "acorn": "bin/acorn"1542- },1543- "engines": {1544- "node": ">=0.4.0"1545- }1546- },1547- "node_modules/acorn-walk": {1548- "version": "8.3.5",1549- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",1550- "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",1551- "license": "MIT",1552- "dependencies": {1553- "acorn": "^8.11.0"1554- },1555- "engines": {1556- "node": ">=0.4.0"1557- }1558- },1559- "node_modules/argparse": {1560- "version": "2.0.1",1561- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",1562- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",1563- "license": "Python-2.0"1564- },1565- "node_modules/asap": {1566- "version": "2.0.6",1567- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",1568- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",1569- "license": "MIT"1570- },1571- "node_modules/asn1js": {1572- "version": "3.1.0",1573- "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz",1574- "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==",1575- "license": "BSD-3-Clause",1576- "dependencies": {1577- "pvtsutils": "^1.3.6",1578- "pvutils": "^1.2.0",1579- "tslib": "^2.8.1"1580- },1581- "engines": {1582- "node": ">=12.0.0"1583- }1584- },1585- "node_modules/balanced-match": {1586- "version": "1.0.2",1587- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",1588- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",1589- "license": "MIT"1590- },1591- "node_modules/bcp-47": {1592- "version": "2.1.1",1593- "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz",1594- "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==",1595- "license": "MIT",1596- "dependencies": {1597- "is-alphabetical": "^2.0.0",1598- "is-alphanumerical": "^2.0.0",1599- "is-decimal": "^2.0.0"1600- },1601- "funding": {1602- "type": "github",1603- "url": "https://github.com/sponsors/wooorm"1604- }1605- },1606- "node_modules/bcp-47-match": {1607- "version": "2.0.3",1608- "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",1609- "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",1610- "license": "MIT",1611- "funding": {1612- "type": "github",1613- "url": "https://github.com/sponsors/wooorm"1614- }1615- },1616- "node_modules/bcp-47-normalize": {1617- "version": "2.3.0",1618- "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-2.3.0.tgz",1619- "integrity": "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==",1620- "license": "MIT",1621- "dependencies": {1622- "bcp-47": "^2.0.0",1623- "bcp-47-match": "^2.0.0"1624- },1625- "funding": {1626- "type": "github",1627- "url": "https://github.com/sponsors/wooorm"1628- }1629- },1630- "node_modules/brace-expansion": {1631- "version": "1.1.21",1632- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",1633- "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",1634- "license": "MIT",1635- "dependencies": {1636- "balanced-match": "^1.0.0",1637- "concat-map": "0.0.1"1638- }1639- },1640- "node_modules/chevrotain": {1641- "version": "13.2.0",1642- "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-13.2.0.tgz",1643- "integrity": "sha512-GTouk5YzgBxlTPCaDn5q0q2EJKkrYmnPF+HmF8v0Qo4r3iyPG7HJivkN1wthCXZcm3sIcZFpGTRovLPNdJNRCA==",1644- "license": "Apache-2.0",1645- "dependencies": {1646- "@chevrotain/cst-dts-gen": "13.2.0",1647- "@chevrotain/gast": "13.2.0",1648- "@chevrotain/regexp-to-ast": "13.2.0",1649- "@chevrotain/types": "13.2.0",1650- "@chevrotain/utils": "13.2.0"1651- },1652- "engines": {1653- "node": ">=22.0.0"1654- }1655- },1656- "node_modules/chokidar": {1657- "version": "4.0.3",1658- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",1659- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",1660- "license": "MIT",1661- "dependencies": {1662- "readdirp": "^4.0.1"1663- },1664- "engines": {1665- "node": ">= 14.16.0"1666- },1667- "funding": {1668- "url": "https://paulmillr.com/funding/"1669- }1670- },1671- "node_modules/commander": {1672- "version": "10.0.1",1673- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",1674- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",1675- "license": "MIT",1676- "engines": {1677- "node": ">=14"1678- }1679- },1680- "node_modules/concat-map": {1681- "version": "0.0.1",1682- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",1683- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",1684- "license": "MIT"1685- },1686- "node_modules/cose-base": {1687- "version": "1.0.3",1688- "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",1689- "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",1690- "license": "MIT",1691- "dependencies": {1692- "layout-base": "^1.0.0"1693- }1694- },1695- "node_modules/cytoscape": {1696- "version": "3.34.4",1697- "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.4.tgz",1698- "integrity": "sha512-msY99tMY8iWTg+sIPGfSqDcPNOt4xcj+v/K/7qUFaCUxxlqnMkUYLBjRUzn9W5CU/R2hrmtJa5+UtovrEM2ctg==",1699- "license": "MIT",1700- "engines": {1701- "node": ">=0.10"1702- }1703- },1704- "node_modules/cytoscape-cose-bilkent": {1705- "version": "4.1.0",1706- "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",1707- "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",1708- "license": "MIT",1709- "dependencies": {1710- "cose-base": "^1.0.0"1711- },1712- "peerDependencies": {1713- "cytoscape": "^3.2.0"1714- }1715- },1716- "node_modules/cytoscape-fcose": {1717- "version": "2.2.0",1718- "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",1719- "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",1720- "license": "MIT",1721- "dependencies": {1722- "cose-base": "^2.2.0"1723- },1724- "peerDependencies": {1725- "cytoscape": "^3.2.0"1726- }1727- },1728- "node_modules/cytoscape-fcose/node_modules/cose-base": {1729- "version": "2.2.0",1730- "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",1731- "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",1732- "license": "MIT",1733- "dependencies": {1734- "layout-base": "^2.0.0"1735- }1736- },1737- "node_modules/cytoscape-fcose/node_modules/layout-base": {1738- "version": "2.0.1",1739- "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",1740- "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",1741- "license": "MIT"1742- },1743- "node_modules/d3": {1744- "version": "7.9.0",1745- "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",1746- "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",1747- "license": "ISC",1748- "dependencies": {1749- "d3-array": "3",1750- "d3-axis": "3",1751- "d3-brush": "3",1752- "d3-chord": "3",1753- "d3-color": "3",1754- "d3-contour": "4",1755- "d3-delaunay": "6",1756- "d3-dispatch": "3",1757- "d3-drag": "3",1758- "d3-dsv": "3",1759- "d3-ease": "3",1760- "d3-fetch": "3",1761- "d3-force": "3",1762- "d3-format": "3",1763- "d3-geo": "3",1764- "d3-hierarchy": "3",1765- "d3-interpolate": "3",1766- "d3-path": "3",1767- "d3-polygon": "3",1768- "d3-quadtree": "3",1769- "d3-random": "3",1770- "d3-scale": "4",1771- "d3-scale-chromatic": "3",1772- "d3-selection": "3",1773- "d3-shape": "3",1774- "d3-time": "3",1775- "d3-time-format": "4",1776- "d3-timer": "3",1777- "d3-transition": "3",1778- "d3-zoom": "3"1779- },1780- "engines": {1781- "node": ">=12"1782- }1783- },1784- "node_modules/d3-array": {1785- "version": "3.2.4",1786- "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",1787- "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",1788- "license": "ISC",1789- "dependencies": {1790- "internmap": "1 - 2"1791- },1792- "engines": {1793- "node": ">=12"1794- }1795- },1796- "node_modules/d3-axis": {1797- "version": "3.0.0",1798- "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",1799- "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",1800- "license": "ISC",1801- "engines": {1802- "node": ">=12"1803- }1804- },1805- "node_modules/d3-brush": {1806- "version": "3.0.0",1807- "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",1808- "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",1809- "license": "ISC",1810- "dependencies": {1811- "d3-dispatch": "1 - 3",1812- "d3-drag": "2 - 3",1813- "d3-interpolate": "1 - 3",1814- "d3-selection": "3",1815- "d3-transition": "3"1816- },1817- "engines": {1818- "node": ">=12"1819- }1820- },1821- "node_modules/d3-chord": {1822- "version": "3.0.1",1823- "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",1824- "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",1825- "license": "ISC",1826- "dependencies": {1827- "d3-path": "1 - 3"1828- },1829- "engines": {1830- "node": ">=12"1831- }1832- },1833- "node_modules/d3-color": {1834- "version": "3.1.0",1835- "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",1836- "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",1837- "license": "ISC",1838- "engines": {1839- "node": ">=12"1840- }1841- },1842- "node_modules/d3-contour": {1843- "version": "4.0.2",1844- "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",1845- "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",1846- "license": "ISC",1847- "dependencies": {1848- "d3-array": "^3.2.0"1849- },1850- "engines": {1851- "node": ">=12"1852- }1853- },1854- "node_modules/d3-delaunay": {1855- "version": "6.0.4",1856- "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",1857- "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",1858- "license": "ISC",1859- "dependencies": {1860- "delaunator": "5"1861- },1862- "engines": {1863- "node": ">=12"1864- }1865- },1866- "node_modules/d3-dispatch": {1867- "version": "3.0.1",1868- "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",1869- "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",1870- "license": "ISC",1871- "engines": {1872- "node": ">=12"1873- }1874- },1875- "node_modules/d3-drag": {1876- "version": "3.0.0",1877- "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",1878- "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",1879- "license": "ISC",1880- "dependencies": {1881- "d3-dispatch": "1 - 3",1882- "d3-selection": "3"1883- },1884- "engines": {1885- "node": ">=12"1886- }1887- },1888- "node_modules/d3-dsv": {1889- "version": "3.0.1",1890- "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",1891- "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",1892- "license": "ISC",1893- "dependencies": {1894- "commander": "7",1895- "iconv-lite": "0.6",1896- "rw": "1"1897- },1898- "bin": {1899- "csv2json": "bin/dsv2json.js",1900- "csv2tsv": "bin/dsv2dsv.js",1901- "dsv2dsv": "bin/dsv2dsv.js",1902- "dsv2json": "bin/dsv2json.js",1903- "json2csv": "bin/json2dsv.js",1904- "json2dsv": "bin/json2dsv.js",1905- "json2tsv": "bin/json2dsv.js",1906- "tsv2csv": "bin/dsv2dsv.js",1907- "tsv2json": "bin/dsv2json.js"1908- },1909- "engines": {1910- "node": ">=12"1911- }1912- },1913- "node_modules/d3-dsv/node_modules/commander": {1914- "version": "7.2.0",1915- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",1916- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",1917- "license": "MIT",1918- "engines": {1919- "node": ">= 10"1920- }1921- },1922- "node_modules/d3-ease": {1923- "version": "3.0.1",1924- "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",1925- "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",1926- "license": "BSD-3-Clause",1927- "engines": {1928- "node": ">=12"1929- }1930- },1931- "node_modules/d3-fetch": {1932- "version": "3.0.1",1933- "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",1934- "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",1935- "license": "ISC",1936- "dependencies": {1937- "d3-dsv": "1 - 3"1938- },1939- "engines": {1940- "node": ">=12"1941- }1942- },1943- "node_modules/d3-force": {1944- "version": "3.0.0",1945- "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",1946- "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",1947- "license": "ISC",1948- "dependencies": {1949- "d3-dispatch": "1 - 3",1950- "d3-quadtree": "1 - 3",1951- "d3-timer": "1 - 3"1952- },1953- "engines": {1954- "node": ">=12"1955- }1956- },1957- "node_modules/d3-format": {1958- "version": "3.1.2",1959- "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",1960- "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",1961- "license": "ISC",1962- "engines": {1963- "node": ">=12"1964- }1965- },1966- "node_modules/d3-geo": {1967- "version": "3.1.1",1968- "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",1969- "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",1970- "license": "ISC",1971- "dependencies": {1972- "d3-array": "2.5.0 - 3"1973- },1974- "engines": {1975- "node": ">=12"1976- }1977- },1978- "node_modules/d3-hierarchy": {1979- "version": "3.1.2",1980- "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",1981- "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",1982- "license": "ISC",1983- "engines": {1984- "node": ">=12"1985- }1986- },1987- "node_modules/d3-interpolate": {1988- "version": "3.0.1",1989- "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",1990- "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",1991- "license": "ISC",1992- "dependencies": {1993- "d3-color": "1 - 3"1994- },1995- "engines": {1996- "node": ">=12"1997- }1998- },1999- "node_modules/d3-path": {2000- "version": "3.1.0",2001- "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",2002- "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",2003- "license": "ISC",2004- "engines": {2005- "node": ">=12"2006- }2007- },2008- "node_modules/d3-polygon": {2009- "version": "3.0.1",2010- "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",2011- "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",2012- "license": "ISC",2013- "engines": {2014- "node": ">=12"2015- }2016- },2017- "node_modules/d3-quadtree": {2018- "version": "3.0.1",2019- "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",2020- "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",2021- "license": "ISC",2022- "engines": {2023- "node": ">=12"2024- }2025- },2026- "node_modules/d3-random": {2027- "version": "3.0.1",2028- "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",2029- "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",2030- "license": "ISC",2031- "engines": {2032- "node": ">=12"2033- }2034- },2035- "node_modules/d3-sankey": {2036- "version": "0.12.3",2037- "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",2038- "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",2039- "license": "BSD-3-Clause",2040- "dependencies": {2041- "d3-array": "1 - 2",2042- "d3-shape": "^1.2.0"2043- }2044- },2045- "node_modules/d3-sankey/node_modules/d3-array": {2046- "version": "2.12.1",2047- "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",2048- "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",2049- "license": "BSD-3-Clause",2050- "dependencies": {2051- "internmap": "^1.0.0"2052- }2053- },2054- "node_modules/d3-sankey/node_modules/d3-path": {2055- "version": "1.0.9",2056- "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",2057- "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",2058- "license": "BSD-3-Clause"2059- },2060- "node_modules/d3-sankey/node_modules/d3-shape": {2061- "version": "1.3.7",2062- "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",2063- "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",2064- "license": "BSD-3-Clause",2065- "dependencies": {2066- "d3-path": "1"1162+ "win32"1163+ ],1164+ "engines": {1165+ "node": ">=16.20.0"20671166 }20681167 },2069- "node_modules/d3-sankey/node_modules/internmap": {1168+ "node_modules/a-sync-waterfall": {20701169 "version": "1.0.1",2071- "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",2072- "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",2073- "license": "ISC"1170+ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",1171+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",1172+ "license": "MIT"20741173 },2075- "node_modules/d3-scale": {2076- "version": "4.0.2",2077- "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",2078- "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",2079- "license": "ISC",2080- "dependencies": {2081- "d3-array": "2.10.0 - 3",2082- "d3-format": "1 - 3",2083- "d3-interpolate": "1.2.0 - 3",2084- "d3-time": "2.1.1 - 3",2085- "d3-time-format": "2 - 4"1174+ "node_modules/acorn": {1175+ "version": "8.19.0",1176+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz",1177+ "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==",1178+ "license": "MIT",1179+ "bin": {1180+ "acorn": "bin/acorn"20861181 },20871182 "engines": {2088- "node": ">=12"1183+ "node": ">=0.4.0"20891184 }20901185 },2091- "node_modules/d3-scale-chromatic": {2092- "version": "3.1.0",2093- "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",2094- "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",2095- "license": "ISC",1186+ "node_modules/acorn-walk": {1187+ "version": "8.3.5",1188+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",1189+ "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",1190+ "license": "MIT",20961191 "dependencies": {2097- "d3-color": "1 - 3",2098- "d3-interpolate": "1 - 3"1192+ "acorn": "^8.11.0"20991193 },21001194 "engines": {2101- "node": ">=12"1195+ "node": ">=0.4.0"21021196 }21031197 },2104- "node_modules/d3-selection": {2105- "version": "3.0.0",2106- "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",2107- "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",2108- "license": "ISC",2109- "engines": {2110- "node": ">=12"2111- }1198+ "node_modules/argparse": {1199+ "version": "2.0.1",1200+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",1201+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",1202+ "license": "Python-2.0"21121203 },2113- "node_modules/d3-shape": {2114- "version": "3.2.0",2115- "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",2116- "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",2117- "license": "ISC",2118- "dependencies": {2119- "d3-path": "^3.1.0"2120- },2121- "engines": {2122- "node": ">=12"2123- }1204+ "node_modules/asap": {1205+ "version": "2.0.6",1206+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",1207+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",1208+ "license": "MIT"21241209 },2125- "node_modules/d3-time": {1210+ "node_modules/asn1js": {21261211 "version": "3.1.0",2127- "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",2128- "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",2129- "license": "ISC",1212+ "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz",1213+ "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==",1214+ "license": "BSD-3-Clause",21301215 "dependencies": {2131- "d3-array": "2 - 3"1216+ "pvtsutils": "^1.3.6",1217+ "pvutils": "^1.2.0",1218+ "tslib": "^2.8.1"21321219 },21331220 "engines": {2134- "node": ">=12"1221+ "node": ">=12.0.0"21351222 }21361223 },2137- "node_modules/d3-time-format": {2138- "version": "4.1.0",2139- "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",2140- "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",2141- "license": "ISC",1224+ "node_modules/balanced-match": {1225+ "version": "1.0.2",1226+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",1227+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",1228+ "license": "MIT"1229+ },1230+ "node_modules/bcp-47": {1231+ "version": "2.1.1",1232+ "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.1.tgz",1233+ "integrity": "sha512-KLw+H/gd2p4zly1X7Yh/qziuyae5/w/QFnvTng9eZL5fvszL7Whl3MBoWF8yxL7ksUjBfOD+OxkytiqbBpG+Fw==",1234+ "license": "MIT",21421235 "dependencies": {2143- "d3-time": "1 - 3"1236+ "is-alphabetical": "^2.0.0",1237+ "is-alphanumerical": "^2.0.0",1238+ "is-decimal": "^2.0.0"21441239 },2145- "engines": {2146- "node": ">=12"1240+ "funding": {1241+ "type": "github",1242+ "url": "https://github.com/sponsors/wooorm"21471243 }21481244 },2149- "node_modules/d3-timer": {2150- "version": "3.0.1",2151- "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",2152- "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",2153- "license": "ISC",2154- "engines": {2155- "node": ">=12"1245+ "node_modules/bcp-47-match": {1246+ "version": "2.0.3",1247+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",1248+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",1249+ "license": "MIT",1250+ "funding": {1251+ "type": "github",1252+ "url": "https://github.com/sponsors/wooorm"21561253 }21571254 },2158- "node_modules/d3-transition": {2159- "version": "3.0.1",2160- "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",2161- "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",2162- "license": "ISC",1255+ "node_modules/bcp-47-normalize": {1256+ "version": "2.3.0",1257+ "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-2.3.0.tgz",1258+ "integrity": "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==",1259+ "license": "MIT",21631260 "dependencies": {2164- "d3-color": "1 - 3",2165- "d3-dispatch": "1 - 3",2166- "d3-ease": "1 - 3",2167- "d3-interpolate": "1 - 3",2168- "d3-timer": "1 - 3"2169- },2170- "engines": {2171- "node": ">=12"1261+ "bcp-47": "^2.0.0",1262+ "bcp-47-match": "^2.0.0"21721263 },2173- "peerDependencies": {2174- "d3-selection": "2 - 3"1264+ "funding": {1265+ "type": "github",1266+ "url": "https://github.com/sponsors/wooorm"21751267 }21761268 },2177- "node_modules/d3-zoom": {2178- "version": "3.0.0",2179- "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",2180- "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",2181- "license": "ISC",1269+ "node_modules/brace-expansion": {1270+ "version": "1.1.21",1271+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",1272+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",1273+ "license": "MIT",1274+ "dependencies": {1275+ "balanced-match": "^1.0.0",1276+ "concat-map": "0.0.1"1277+ }1278+ },1279+ "node_modules/chokidar": {1280+ "version": "4.0.3",1281+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",1282+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",1283+ "license": "MIT",21821284 "dependencies": {2183- "d3-dispatch": "1 - 3",2184- "d3-drag": "2 - 3",2185- "d3-interpolate": "1 - 3",2186- "d3-selection": "2 - 3",2187- "d3-transition": "2 - 3"1285+ "readdirp": "^4.0.1"21881286 },21891287 "engines": {2190- "node": ">=12"1288+ "node": ">= 14.16.0"1289+ },1290+ "funding": {1291+ "url": "https://paulmillr.com/funding/"21911292 }21921293 },2193- "node_modules/dagre-d3-es": {2194- "version": "7.0.14",2195- "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",2196- "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",1294+ "node_modules/commander": {1295+ "version": "10.0.1",1296+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",1297+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",21971298 "license": "MIT",2198- "dependencies": {2199- "d3": "^7.9.0",2200- "lodash-es": "^4.17.21"1299+ "engines": {1300+ "node": ">=14"22011301 }22021302 },1303+ "node_modules/concat-map": {1304+ "version": "0.0.1",1305+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",1306+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",1307+ "license": "MIT"1308+ },22031309 "node_modules/dayjs": {22041310 "version": "1.11.23",22051311 "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",
@@ -2223,15 +1329,6 @@
22231329 }22241330 }22251331 },2226- "node_modules/delaunator": {2227- "version": "5.1.0",2228- "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",2229- "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",2230- "license": "ISC",2231- "dependencies": {2232- "robust-predicates": "^3.0.2"2233- }2234- },22351332 "node_modules/depd": {22361333 "version": "2.0.0",22371334 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -2309,15 +1406,6 @@
23091406 "url": "https://github.com/fb55/domhandler?sponsor=1"23101407 }23111408 },2312- "node_modules/dompurify": {2313- "version": "3.4.17",2314- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz",2315- "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==",2316- "license": "(MPL-2.0 OR Apache-2.0)",2317- "optionalDependencies": {2318- "@types/trusted-types": "^2.0.7"2319- }2320- },23211409 "node_modules/domutils": {23221410 "version": "2.8.0",23231411 "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -2338,12 +1426,6 @@
23381426 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",23391427 "license": "MIT"23401428 },2341- "node_modules/elkjs": {2342- "version": "0.9.3",2343- "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz",2344- "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==",2345- "license": "EPL-2.0"2346- },23471429 "node_modules/encodeurl": {23481430 "version": "2.0.0",23491431 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -2377,18 +1459,6 @@
23771459 "errno": "cli.js"23781460 }23791461 },2380- "node_modules/es-toolkit": {2381- "version": "1.52.0",2382- "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz",2383- "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==",2384- "license": "MIT",2385- "workspaces": [2386- "docs",2387- "benchmarks",2388- "tests/types",2389- "tests/browser-compat"2390- ]2391- },23921462 "node_modules/escape-html": {23931463 "version": "1.0.3",23941464 "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -2530,12 +1600,6 @@
25301600 "node": ">=11"25311601 }25321602 },2533- "node_modules/hachure-fill": {2534- "version": "0.5.2",2535- "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",2536- "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",2537- "license": "MIT"2538- },25391603 "node_modules/highlight.js": {25401604 "version": "11.12.0",25411605 "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz",
@@ -2605,43 +1669,12 @@
26051669 "url": "https://opencollective.com/express"26061670 }26071671 },2608- "node_modules/iconv-lite": {2609- "version": "0.6.3",2610- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",2611- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",2612- "license": "MIT",2613- "dependencies": {2614- "safer-buffer": ">= 2.1.2 < 3.0.0"2615- },2616- "engines": {2617- "node": ">=0.10.0"2618- }2619- },2620- "node_modules/import-meta-resolve": {2621- "version": "4.2.0",2622- "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",2623- "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",2624- "license": "MIT",2625- "funding": {2626- "type": "github",2627- "url": "https://github.com/sponsors/wooorm"2628- }2629- },26301672 "node_modules/inherits": {26311673 "version": "2.0.4",26321674 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",26331675 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",26341676 "license": "ISC"26351677 },2636- "node_modules/internmap": {2637- "version": "2.0.3",2638- "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",2639- "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",2640- "license": "ISC",2641- "engines": {2642- "node": ">=12"2643- }2644- },26451678 "node_modules/is-alphabetical": {26461679 "version": "2.0.1",26471680 "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -2740,36 +1773,6 @@
27401773 "node": ">=8"27411774 }27421775 },2743- "node_modules/katex": {2744- "version": "0.18.10",2745- "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.10.tgz",2746- "integrity": "sha512-/B6p9eY9DX7aHBfpkHdpirDTZ5QH9xTwL9y837PWuzuv41O5jFdqNQwVQEQsimY0/iVNkwBY4+4hMfhQ7d4Dbw==",2747- "funding": [2748- "https://opencollective.com/katex",2749- "https://github.com/sponsors/katex"2750- ],2751- "license": "MIT",2752- "dependencies": {2753- "commander": "^15.0.0"2754- },2755- "bin": {2756- "katex": "cli.js"2757- }2758- },2759- "node_modules/katex/node_modules/commander": {2760- "version": "15.0.0",2761- "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",2762- "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",2763- "license": "MIT",2764- "engines": {2765- "node": ">=22.12.0"2766- }2767- },2768- "node_modules/khroma": {2769- "version": "2.1.0",2770- "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",2771- "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="2772- },27731776 "node_modules/kind-of": {27741777 "version": "6.0.3",27751778 "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -2797,12 +1800,6 @@
27971800 "dayjs": "^1.11.23"27981801 }27991802 },2800- "node_modules/layout-base": {2801- "version": "1.0.2",2802- "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",2803- "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",2804- "license": "MIT"2805- },28061803 "node_modules/linkify-it": {28071804 "version": "5.0.2",28081805 "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
@@ -2848,12 +1845,6 @@
28481845 "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==",28491846 "license": "MIT"28501847 },2851- "node_modules/lodash-es": {2852- "version": "4.18.1",2853- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",2854- "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",2855- "license": "MIT"2856- },28571848 "node_modules/luxon": {28581849 "version": "3.7.2",28591850 "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
@@ -2902,58 +1893,12 @@
29021893 "url": "https://github.com/fb55/entities?sponsor=1"29031894 }29041895 },2905- "node_modules/marked": {2906- "version": "16.4.2",2907- "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",2908- "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",2909- "license": "MIT",2910- "bin": {2911- "marked": "bin/marked.js"2912- },2913- "engines": {2914- "node": ">= 20"2915- }2916- },29171896 "node_modules/mdurl": {29181897 "version": "2.1.0",29191898 "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz",29201899 "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==",29211900 "license": "MIT"29221901 },2923- "node_modules/mermaid": {2924- "version": "12.2.0",2925- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-12.2.0.tgz",2926- "integrity": "sha512-w7nr/SFrLO7BaVTs3dQTvYSV6nYSBj0Yy9DjBhsIJtAcHqRzXSq4Pg32ujHZ/AdWsAMNEvvhbm5dt9E2tOiHzw==",2927- "license": "MIT",2928- "dependencies": {2929- "@braintree/sanitize-url": "^7.1.2",2930- "@iconify/utils": "^3.0.2",2931- "@mermaid-js/parser": "^2.0.1",2932- "@types/d3": "^7.4.3",2933- "@upsetjs/venn.js": "^2.0.0",2934- "chevrotain": "~13.2.0",2935- "cytoscape": "^3.34.0",2936- "cytoscape-cose-bilkent": "^4.1.0",2937- "cytoscape-fcose": "^2.2.0",2938- "d3": "^7.9.0",2939- "d3-sankey": "^0.12.3",2940- "dagre-d3-es": "7.0.14",2941- "dayjs": "^1.11.21",2942- "dompurify": "^3.4.12",2943- "elkjs": "^0.9.3",2944- "es-toolkit": "^1.45.1",2945- "katex": "^0.18.0",2946- "khroma": "^2.1.0",2947- "marked": "^16.3.0",2948- "roughjs": "^4.6.6",2949- "stylis": "^4.3.6",2950- "ts-dedent": "^2.2.0",2951- "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"2952- },2953- "engines": {2954- "node": ">=22.12.0"2955- }2956- },29571902 "node_modules/mime": {29581903 "version": "3.0.0",29591904 "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@@ -3121,12 +2066,6 @@
31212066 "node": ">= 0.8"31222067 }31232068 },3124- "node_modules/package-manager-detector": {3125- "version": "1.9.0",3126- "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz",3127- "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==",3128- "license": "MIT"3129- },31302069 "node_modules/parse-srcset": {31312070 "version": "1.0.2",31322071 "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
@@ -3142,11 +2081,14 @@
31422081 "node": ">= 0.8"31432082 }31442083 },3145- "node_modules/path-data-parser": {3146- "version": "0.1.0",3147- "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",3148- "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",3149- "license": "MIT"2084+ "node_modules/pele": {2085+ "version": "1.6.0",2086+ "resolved": "https://registry.npmjs.org/pele/-/pele-1.6.0.tgz",2087+ "integrity": "sha512-VfAiVItsOGlGhvnBfphdT28VCG7mXMZxQNOwAGsCtxI2ztbUsT39MraEOYjsapLxVETkdUZscm0DdotYDQMLdw==",2088+ "license": "MIT",2089+ "engines": {2090+ "node": ">=20"2091+ }31502092 },31512093 "node_modules/picocolors": {31522094 "version": "1.1.1",
@@ -3175,22 +2117,6 @@
31752117 "semver-compare": "^1.0.0"31762118 }31772119 },3178- "node_modules/points-on-curve": {3179- "version": "0.2.0",3180- "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",3181- "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",3182- "license": "MIT"3183- },3184- "node_modules/points-on-path": {3185- "version": "0.2.1",3186- "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",3187- "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",3188- "license": "MIT",3189- "dependencies": {3190- "path-data-parser": "0.1.0",3191- "points-on-curve": "0.2.0"3192- }3193- },31942120 "node_modules/postcss": {31952121 "version": "8.5.30",31962122 "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz",
@@ -3333,36 +2259,6 @@
33332259 "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",33342260 "license": "Apache-2.0"33352261 },3336- "node_modules/robust-predicates": {3337- "version": "3.0.3",3338- "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",3339- "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",3340- "license": "Unlicense"3341- },3342- "node_modules/roughjs": {3343- "version": "4.6.6",3344- "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",3345- "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",3346- "license": "MIT",3347- "dependencies": {3348- "hachure-fill": "^0.5.2",3349- "path-data-parser": "^0.1.0",3350- "points-on-curve": "^0.2.0",3351- "points-on-path": "^0.2.1"3352- }3353- },3354- "node_modules/rw": {3355- "version": "1.3.3",3356- "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",3357- "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",3358- "license": "BSD-3-Clause"3359- },3360- "node_modules/safer-buffer": {3361- "version": "2.1.2",3362- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",3363- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",3364- "license": "MIT"3365- },33662262 "node_modules/sanitize-html": {33672263 "version": "2.18.1",33682264 "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.18.1.tgz",
@@ -3613,21 +2509,6 @@
36132509 "node": ">=0.10.0"36142510 }36152511 },3616- "node_modules/stylis": {3617- "version": "4.4.0",3618- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",3619- "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",3620- "license": "MIT"3621- },3622- "node_modules/tinyexec": {3623- "version": "1.3.1",3624- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",3625- "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",3626- "license": "MIT",3627- "engines": {3628- "node": ">=18"3629- }3630- },36312512 "node_modules/tinyglobby": {36322513 "version": "0.2.17",36332514 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
@@ -3653,15 +2534,6 @@
36532534 "node": ">=0.6"36542535 }36552536 },3656- "node_modules/ts-dedent": {3657- "version": "2.3.0",3658- "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz",3659- "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",3660- "license": "MIT",3661- "engines": {3662- "node": ">=6.10"3663- }3664- },36652537 "node_modules/tslib": {36662538 "version": "2.8.1",36672539 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -3749,19 +2621,6 @@
37492621 "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",37502622 "license": "MIT"37512623 },3752- "node_modules/uuid": {3753- "version": "14.0.3",3754- "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz",3755- "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==",3756- "funding": [3757- "https://github.com/sponsors/broofa",3758- "https://github.com/sponsors/ctavan"3759- ],3760- "license": "MIT",3761- "bin": {3762- "uuid": "dist-node/bin/uuid"3763- }3764- },37652624 "node_modules/ws": {37662625 "version": "8.22.0",37672626 "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
M package.json +1 -1
M site/_includes/base.njk +1 -1
M site/assets/style.css +13 -1
@@ -462,7 +462,19 @@ details.att pre, details.howto pre { white-space: pre-wrap; overflow-wrap: anywh
462462 /* In the raised file viewer, code sits on the page's colour, not in a second box. */463463 .viewer .markdown pre, .viewer .markdown code { background: var(--bg); border-color: var(--bg); }464464 .viewer .markdown pre code { background: none; }465-.markdown pre.mermaid { background: none; border: 0; text-align: center; }465+/* Diagrams, drawn at build time (lib/diagrams.ts): Pele's SVG takes its466+ colours and fonts from these, so it follows light and dark as it is. */467+.markdown .diagram { margin: var(--space-4) 0; text-align: center; }468+.markdown .diagram-source { margin: var(--space-4) 0; }469+.markdown .diagram-source figcaption { color: var(--muted); font-size: var(--text-sm); }470+.pele {471+ --pele-bg: var(--bg); --pele-fg: var(--fg); --pele-muted: var(--muted); --pele-line: var(--muted);472+ --pele-surface: var(--raised); --pele-surface-alt: var(--hover); --pele-border: var(--border-strong); --pele-accent: var(--accent);473+ --pele-critical: var(--fail); --pele-error: var(--fail); --pele-font: var(--sans); --pele-font-mono: var(--mono); --pele-radius: var(--radius);474+ --pele-title-weight: var(--fw-strong); --pele-heading-weight: var(--fw-strong); --pele-bold-weight: var(--fw-strong);475+ --pele-series-1: var(--viz-1); --pele-series-2: var(--pass); --pele-series-3: var(--warn); --pele-series-4: var(--fail);476+ --pele-series-5: var(--tok-title); --pele-series-6: var(--tok-const); --pele-series-7: var(--tok-builtin); --pele-series-8: var(--muted);477+}466478 .markdown blockquote { margin: var(--space-4) 0; padding: 0 var(--space-4); color: var(--muted); border-left: var(--bar) solid var(--border); }467479 .markdown table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; margin: var(--space-4) 0; font-size: var(--text-md); }468480 .markdown th, .markdown td { border-bottom: var(--hairline) solid var(--border); padding: var(--space-2) var(--space-3) var(--space-2) 0; text-align: left; vertical-align: top; }
M site/assets/ui.ts +1 -16
A test/scenarios/diagram-broken.sh +35 -0
A test/scenarios/diagram-hostile.sh +58 -0
@@ -0,0 +1,58 @@
1+# Markdown is anyone's who can push a repo, and a diagram's text goes into2+# the page as SVG: none of it can run a script, load anything, restyle the3+# page or link anywhere but where links may go. Pele escapes and filters4+# it, and its SVG passes a sanitizer that keeps only what Pele draws with;5+# SVG written straight into the markdown is still dropped.6+. "$(dirname "$0")/../lib.sh"7+user dami8+repo dami/greet9+clone dami dami/greet "$T/w/d"10+cat >"$T/w/d/README.md" <<'EOF'11+# greet12+13+```mermaid14+---15+title: "<script>alert('title')</script>"16+---17+%%{init: {"themeCSS": "body { display: none }", "fontFamily": "x;background:url(https://evil.example/f)"}}%%18+flowchart LR19+ accTitle: </title><script>alert('acc')</script>20+ A["<script>alert(1)</script>"] --> B["<img src=x onerror=alert(2)>"]21+ B --> C["</text></svg><style>body{display:none}</style>"]22+ C --> D["#lt;a href=#quot;javascript:alert(3)#quot;#gt;x#lt;/a#gt;"]23+ D --> E@{ img: "https://evil.example/pixel.png", label: "img" }24+ click A "javascript:alert(4)"25+ click B href "data:text/html,<script>alert(5)</script>"26+ click C "//evil.example/x"27+ click D "docs/guide.md" "tip"28+ style A fill:red,stroke:#000,font-family:x29+ classDef box fill:#f9f30+ class B box31+```32+33+<svg onload="alert(6)"><rect width="9999" height="9999"/><a href="javascript:alert(7)"><text>raw</text></a></svg>34+EOF35+at "$T/w/d" commit "first" docs/guide.md36+push_as dami "$T/w/d" origin main37+build38+p=$FORGE_WWW/dami/greet/index.html39+fig=$(grep -o '<figure class="diagram">.*</figure>' "$p")40+[ -n "$fig" ] || fail "no diagram on $p"41+tags() { printf '%s' "$fig" | grep -oE '<[^>]*>'; }42+# Only Pele's shapes, text and links: no script, style, image or foreign43+# object, and no attribute but these.44+eq "$(tags | grep -oE '^<[a-z]+' | LC_ALL=C sort -u | tr '\n' ' ')" '<a <figure <g <path <rect <svg <text <title <tspan '45+eq "$(tags | grep -oE ' [a-zA-Z:-]+=' | tr -d ' =' | LC_ALL=C sort -u | tr '\n' ' ')" 'aria-labelledby aria-roledescription class d fill font-weight height href id rel role stroke stroke-linecap style text-anchor transform viewBox width x xml:space xmlns y '46+if tags | grep -qi 'url('; then fail "a url() in the diagram"; fi47+# One link: the relative one, resolved to the forge's page. javascript:,48+# data: and network-path links are dropped.49+eq "$(tags | grep -oE '<a [^>]*>' | sort -u)" '<a href="/dami/greet/blob/main/docs/guide.md/" rel="noopener">'50+# Inline styles keep only drawing properties; classDef names don't reach51+# the page's classes.52+eq "$(tags | grep -oE ' class="[^"]*"' | sed 's/ class="//; s/"$//' | tr ' ' '\n' | grep -vE '^(pele(-[a-zA-Z-]+)?|clickable|diagram)$' | sort -u)" ''53+# The text is text: what looked like markup is escaped or gone.54+printf '%s' "$fig" | grep -q '<title id="diagram-[0-9a-f]\{8\}-title">&lt;/title&gt;&lt;script&gt;alert(.acc.)&lt;/script&gt;</title>' || fail "accTitle not escaped"55+# Init directives don't reach the SVG (their CSS stays out of its tags; the56+# label that says it is text), and raw SVG in the markdown is dropped.57+if tags | grep -q 'display'; then fail "the diagram's CSS reached a tag"; fi58+lacks "$p" 'evil\.example|onload|onerror|<rect width="9999"'
A test/scenarios/diagram-svg.sh +37 -0
@@ -0,0 +1,37 @@
1+# A ```mermaid fence in markdown is drawn at build time: the page has the2+# diagram as inline SVG with its labels as text, and no script draws it.3+. "$(dirname "$0")/../lib.sh"4+user dami5+repo dami/greet6+clone dami dami/greet "$T/w/d"7+cat >"$T/w/d/README.md" <<'EOF'8+# greet9+10+```mermaid11+flowchart LR12+ push[git push] --> pre{pre-receive} -->|ok| post[post-receive] --> build[forge build] --> pages[pages]13+```14+15+Pushes become pages.16+17+```mermaid18+flowchart LR19+ a --> b20+```21+EOF22+at "$T/w/d" commit "first" greet.sh23+push_as dami "$T/w/d" origin main24+build25+p=$FORGE_WWW/dami/greet/index.html26+has "$p" '<figure class="diagram"><svg xmlns="http://www\.w3\.org/2000/svg" class="pele pele-flowchart" viewBox="0 0 [0-9.]+ [0-9.]+"'27+has "$p" '<text class="pele-label"[^>]*>git push</text>'28+has "$p" '>post-receive</text>'29+# Coloured by the stylesheet's --pele-* variables, not colours of its own.30+has "$p" 'style="max-width:100%;height:auto;--_bg:var\(--pele-bg,'31+has "$FORGE_WWW/assets/$(cd "$FORGE_WWW/assets" && ls style.*.css)" '--pele-fg: var\(--fg\)'32+# A link preview's text skips the labels.33+has "$p" '<meta property="og:description" content="greet Pushes become pages\.">'34+# The scripts are the ones every page has; none is for diagrams.35+eq "$(grep -o '<script[^>]*>' "$p" | sed -E 's/\.[A-Za-z0-9_-]{10}\.js//')" "$(grep -o '<script[^>]*>' "$FORGE_WWW/dami/greet/log/main/index.html" | sed -E 's/\.[A-Za-z0-9_-]{10}\.js//')"36+lacks "$p" 'mermaid'37+eq "$(ls "$FORGE_WWW/assets" | grep -ci -e mermaid -e pele)" 0