duckfn-docs-kit 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/AGENTS.md +294 -224
  2. package/README.md +10 -6
  3. package/bin/sql-verify.mjs +12 -12
  4. package/dist/IconButton.d.ts +22 -0
  5. package/dist/codemirror.d.ts +29 -0
  6. package/dist/index.d.ts +28 -7
  7. package/dist/index.js +2 -2
  8. package/dist/mermaid/DfkMermaid.d.ts +7 -0
  9. package/dist/mermaid/config.d.ts +48 -0
  10. package/dist/mermaid/remark.d.ts +40 -0
  11. package/dist/mermaid/remark.js +32 -0
  12. package/dist/mermaid/render.d.ts +94 -0
  13. package/dist/mermaid/styles.d.ts +6 -0
  14. package/dist/mermaid/title.d.ts +23 -0
  15. package/dist/{register-DKLiYs-F.js → register-Dev_kc3Z.js} +921 -579
  16. package/dist/remark.d.ts +1 -1
  17. package/dist/sql/browserRunner.d.ts +41 -0
  18. package/dist/sql/browserRunner.js +186 -0
  19. package/dist/sql/client.js +1 -1
  20. package/dist/sql/harness.d.ts +59 -0
  21. package/dist/sql/harness.js +8328 -0
  22. package/dist/sql/remark.d.ts +6 -1
  23. package/dist/sql/renderers.d.ts +2 -0
  24. package/dist/sql/runtime.d.ts +20 -0
  25. package/dist/sql/verify.d.ts +4 -5
  26. package/dist/sql/verify.js +36 -49
  27. package/package.json +6 -2
  28. package/src/IconButton.ts +50 -0
  29. package/src/codemirror.ts +88 -0
  30. package/src/index.ts +31 -7
  31. package/src/mermaid/DfkMermaid.css +289 -0
  32. package/src/mermaid/DfkMermaid.ts +557 -0
  33. package/src/mermaid/config.ts +74 -0
  34. package/src/mermaid/remark.ts +98 -0
  35. package/src/mermaid/render.ts +178 -0
  36. package/src/mermaid/styles.ts +24 -0
  37. package/src/mermaid/title.ts +127 -0
  38. package/src/register.ts +3 -0
  39. package/src/remark.ts +1 -1
  40. package/src/sql/DfkSql.css +13 -10
  41. package/src/sql/DfkSql.ts +11 -46
  42. package/src/sql/browserRunner.ts +402 -0
  43. package/src/sql/harness.ts +134 -0
  44. package/src/sql/remark.ts +6 -1
  45. package/src/sql/renderers.ts +24 -3
  46. package/src/sql/runtime.ts +44 -0
  47. package/src/sql/sql.css +13 -11
  48. package/src/sql/verify.ts +23 -41
  49. package/dist/sql/editor.d.ts +0 -16
  50. package/dist/sql/nodeRunner.d.ts +0 -51
  51. package/dist/sql/nodeRunner.js +0 -115
  52. package/src/sql/editor.ts +0 -75
  53. package/src/sql/nodeRunner.ts +0 -298
@@ -34,8 +34,13 @@ export interface RunnableSqlConfig {
34
34
  *
35
35
  * `html` and `iframe` are the same renderer: both sandbox the markup in an
36
36
  * iframe, so scripts run with an opaque origin.
37
+ *
38
+ * `mermaid` renders the column's mermaid source as a diagram through
39
+ * `<dfk-mermaid>` (the same element a ```mermaid fence produces), so the result
40
+ * gets the element's zoom, fullscreen, source editing and SVG download for
41
+ * free.
37
42
  */
38
- show?: 'table' | 'html' | 'iframe' | 'svg' | 'text';
43
+ show?: 'table' | 'html' | 'iframe' | 'svg' | 'text' | 'mermaid';
39
44
  /**
40
45
  * What this block is expected to do when the docs' own SQL test suite runs it
41
46
  * (`duckfn-docs-kit/sql/verify`). Defaults to `'ok'`; `'error'` marks a block
@@ -5,6 +5,8 @@ import type { RunnableSqlConfig } from './remark';
5
5
  *
6
6
  * The registry is the seam later phases plug into. It ships `table` (VisActor
7
7
  * VTable), a `text` fallback, the markup previews `iframe` / `html` / `svg`,
8
+ * `mermaid` (which hands the cell to the kit's own `<dfk-mermaid>` element, so a
9
+ * query can produce a diagram the reader can zoom, expand, edit and download),
8
10
  * plus the `error` view every renderer shares.
9
11
  *
10
12
  * Heavy dependencies (`@visactor/vtable`) load through dynamic `import()`
@@ -49,6 +49,26 @@ export type RuntimeState = 'idle' | 'loading' | 'ready' | 'error';
49
49
  export interface RuntimeOptions {
50
50
  /** Let `LOAD` accept an extension whose signature does not verify. */
51
51
  allowUnsignedExtensions?: boolean;
52
+ /**
53
+ * Serve the DuckDB-Wasm engine from explicit same-origin URLs instead of the
54
+ * jsDelivr CDN. Used by the offline SQL verifier (`sql/browserRunner`): the
55
+ * harness passes the locally-served `duckdb-*.wasm` / worker script so a CI
56
+ * run never reaches the network. When set, `#create()` skips
57
+ * `getJsDelivrBundles()`/`selectBundle()` and the cross-origin blob-worker
58
+ * wrapper (the worker is same-origin here, so it is constructed directly).
59
+ */
60
+ bundle?: LocalBundle;
61
+ }
62
+ /**
63
+ * A locally-served DuckDB-Wasm bundle: absolute same-origin URLs for the
64
+ * engine wasm and its worker script. `pthreadWorker` is only needed for the
65
+ * cross-origin-isolated (COI) bundle; the default non-COI `eh`/`mvp` bundles run
66
+ * single-threaded and leave it unset.
67
+ */
68
+ export interface LocalBundle {
69
+ mainModule: string;
70
+ mainWorker: string;
71
+ pthreadWorker?: string;
52
72
  }
53
73
  /** Options for {@link DuckDBRuntime.loadExtension}. */
54
74
  export interface LoadExtensionOptions {
@@ -1,4 +1,4 @@
1
- import { type WasmPlatform } from './nodeRunner';
1
+ import { type WasmPlatform } from './browserRunner';
2
2
  export interface VerifyOptions {
3
3
  /** Docs site root; defaults to the working directory. */
4
4
  siteDir?: string;
@@ -16,11 +16,10 @@ export interface VerifyOptions {
16
16
  /** Per-block timeout in milliseconds; a hang is reported instead of blocking CI. */
17
17
  timeoutMs?: number;
18
18
  /**
19
- * Directory the blocks run in. Defaults to a fresh temporary directory that
20
- * is removed afterwards: a block may `COPY … TO 'a.csv'`, and on Node
21
- * DuckDB's file system is the real one, relative to the working directory.
19
+ * The browser executable that runs the blocks. Defaults to a detected
20
+ * Chrome/Edge; the `DFK_BROWSER` environment variable is the same override.
22
21
  */
23
- workingDir?: string;
22
+ browser?: string;
24
23
  /** Write the full result list here as JSON. */
25
24
  reportFile?: string;
26
25
  }
@@ -1,52 +1,39 @@
1
1
  import { collectRunnableSql as e, expectsError as t } from "./collect.js";
2
- import { WasmSqlRunner as n } from "./nodeRunner.js";
2
+ import { BrowserSqlRunner as n } from "./browserRunner.js";
3
3
  import { join as r, resolve as i } from "node:path";
4
- import { mkdirSync as a, mkdtempSync as o, readdirSync as s, rmSync as c, statSync as l, writeFileSync as u } from "node:fs";
5
- import { tmpdir as d } from "node:os";
4
+ import { readdirSync as a, statSync as o, writeFileSync as s } from "node:fs";
6
5
  //#region src/sql/verify.ts
7
- var f = ["docs", "i18n"], p = 3e4;
8
- async function m(t = {}) {
9
- let n = i(t.siteDir ?? process.cwd()), s = e({
6
+ var c = ["docs", "i18n"], l = 3e4;
7
+ async function u(t = {}) {
8
+ let n = i(t.siteDir ?? process.cwd()), r = e({
10
9
  siteDir: n,
11
- contentDirs: t.contentDirs ?? v(n)
12
- }), l = t.extension, f = l ? /^https?:\/\//i.test(l) ? l : i(l) : y(n), m = t.timeoutMs ?? p, g = t.workingDir ? i(t.workingDir) : o(r(d(), "duckfn-sql-verify-"));
13
- a(g, { recursive: !0 });
14
- let _ = process.cwd();
15
- process.chdir(g);
16
- let b;
17
- try {
18
- b = await h(s, f, t, m);
19
- } finally {
20
- process.chdir(_), t.workingDir || c(g, {
21
- recursive: !0,
22
- force: !0
23
- });
24
- }
25
- let x = {
26
- blocks: b,
27
- asDeclared: b.filter((e) => e.outcome === "ok" || e.outcome === "error-as-expected"),
28
- unexpected: b.filter((e) => e.outcome === "unexpected-error" || e.outcome === "unexpected-success")
10
+ contentDirs: t.contentDirs ?? m(n)
11
+ }), a = t.extension, o = await d(r, a ? /^https?:\/\//i.test(a) ? a : i(a) : h(n), t, t.timeoutMs ?? l), c = {
12
+ blocks: o,
13
+ asDeclared: o.filter((e) => e.outcome === "ok" || e.outcome === "error-as-expected"),
14
+ unexpected: o.filter((e) => e.outcome === "unexpected-error" || e.outcome === "unexpected-success")
29
15
  };
30
- return t.reportFile && u(t.reportFile, `${JSON.stringify(x, null, 2)}\n`), x;
16
+ return t.reportFile && s(t.reportFile, `${JSON.stringify(c, null, 2)}\n`), c;
31
17
  }
32
- async function h(e, t, r, i) {
18
+ async function d(e, t, r, i) {
33
19
  let a = await n.create({
34
20
  extension: t,
35
21
  platform: r.platform,
36
- engine: r.engine
22
+ engine: r.engine,
23
+ browser: r.browser
37
24
  }), o = [];
38
25
  try {
39
26
  let t = null;
40
- for (let n of e) n.file !== t && (t = n.file, await a.newPage()), o.push(await g(a, n, i));
27
+ for (let n of e) n.file !== t && (t = n.file, await a.newPage()), o.push(await f(a, n, i));
41
28
  } finally {
42
29
  await a.close();
43
30
  }
44
31
  return o;
45
32
  }
46
- async function g(e, n, r) {
33
+ async function f(e, n, r) {
47
34
  let i = t(n.config);
48
35
  try {
49
- let t = await _(e.run(n.sql), r);
36
+ let t = await p(e.run(n.sql), r);
50
37
  return {
51
38
  file: n.file,
52
39
  line: n.line,
@@ -63,43 +50,43 @@ async function g(e, n, r) {
63
50
  };
64
51
  }
65
52
  }
66
- function _(e, t) {
53
+ function p(e, t) {
67
54
  let n, r = new Promise((e, r) => {
68
55
  n = setTimeout(() => r(/* @__PURE__ */ Error(`timed out after ${t}ms`)), t);
69
56
  });
70
57
  return Promise.race([e, r]).finally(() => clearTimeout(n));
71
58
  }
72
- function v(e) {
59
+ function m(e) {
73
60
  let t = [];
74
- b(r(e, f[0])) && t.push(f[0]);
75
- let n = r(e, f[1]);
76
- if (b(n)) for (let e of s(n)) b(r(n, e, "docusaurus-plugin-content-docs", "current")) && t.push(r("i18n", e, "docusaurus-plugin-content-docs", "current"));
61
+ g(r(e, c[0])) && t.push(c[0]);
62
+ let n = r(e, c[1]);
63
+ if (g(n)) for (let e of a(n)) g(r(n, e, "docusaurus-plugin-content-docs", "current")) && t.push(r("i18n", e, "docusaurus-plugin-content-docs", "current"));
77
64
  return t.length > 0 ? t : ["."];
78
65
  }
79
- function y(e) {
80
- let t = r(e, "static", "duckdb-extensions"), n = b(t) ? s(t).filter((e) => e.endsWith(".duckdb_extension.wasm")) : [];
66
+ function h(e) {
67
+ let t = r(e, "static", "duckdb-extensions"), n = g(t) ? a(t).filter((e) => e.endsWith(".duckdb_extension.wasm")) : [];
81
68
  if (n.length === 0) throw Error(`sql/verify: no extension found in ${t} — pass --extension <file|url>, or let the site's extension preload plugin fetch it first`);
82
69
  if (n.length > 1) throw Error(`sql/verify: several extensions found in ${t} (${n.join(", ")}) — pass --extension to pick one`);
83
70
  return r(t, n[0]);
84
71
  }
85
- function b(e) {
86
- return l(e, { throwIfNoEntry: !1 })?.isDirectory() ?? !1;
72
+ function g(e) {
73
+ return o(e, { throwIfNoEntry: !1 })?.isDirectory() ?? !1;
87
74
  }
88
- async function x(e) {
89
- let t = C(e);
75
+ async function _(e) {
76
+ let t = y(e);
90
77
  if (t.help) {
91
- process.stdout.write(S);
78
+ process.stdout.write(v);
92
79
  return;
93
80
  }
94
- let n = Date.now(), r = await m(t), i = ((Date.now() - n) / 1e3).toFixed(1), a = r.blocks.filter((e) => e.outcome === "error-as-expected").length;
81
+ let n = Date.now(), r = await u(t), i = ((Date.now() - n) / 1e3).toFixed(1), a = r.blocks.filter((e) => e.outcome === "error-as-expected").length;
95
82
  if (t.quiet || (process.stdout.write(`\n${r.blocks.length} block(s) in ${i}s\n`), process.stdout.write(` ${r.asDeclared.length} as declared (${a} erroring on purpose), ${r.unexpected.length} unexpected\n`)), r.unexpected.length > 0) {
96
83
  process.stdout.write("unexpected behaviour:\n");
97
84
  for (let e of r.unexpected) process.stdout.write(`- ${e.file}:${e.line} [${e.outcome}] :: ${e.detail}\n`);
98
85
  process.exitCode = 1;
99
86
  }
100
87
  }
101
- var S = "Usage: duckfn-sql-verify [options]\n\nRuns every runnable SQL block of a duckfn docs site in DuckDB-Wasm, and checks\nthat each one behaves as its own metadata declares (\"expect\": \"error\" for a\nblock that demonstrates a failure).\n\n --site <dir> Docs site root (default: the working directory)\n --content <dir> Content directory, relative to the site root (repeatable;\n default: docs/ plus every i18n/<locale>/… translation)\n --extension <path> The extension to preload: a .duckdb_extension.wasm path,\n or an absolute http(s) URL (default: the single file under\n static/duckdb-extensions/)\n --platform <eh|mvp> DuckDB-Wasm bundle, which must match the extension build\n (default: eh)\n --engine <path> Engine wasm override\n --timeout <ms> Per-block timeout (default: 30000)\n --working-dir <dir> Directory the blocks run in (default: a temporary one)\n --report <file> Write the full result list as JSON\n --quiet Only report unexpected behaviour\n --help Show this help\n";
102
- function C(e) {
88
+ var v = "Usage: duckfn-sql-verify [options]\n\nRuns every runnable SQL block of a duckfn docs site in a headless browser\n(DuckDB-Wasm), and checks that each one behaves as its own metadata declares\n(\"expect\": \"error\" for a block that demonstrates a failure).\n\n --site <dir> Docs site root (default: the working directory)\n --content <dir> Content directory, relative to the site root (repeatable;\n default: docs/ plus every i18n/<locale>/… translation)\n --extension <path> The extension to preload: a .duckdb_extension.wasm path,\n or an absolute http(s) URL (default: the single file under\n static/duckdb-extensions/)\n --platform <eh|mvp> DuckDB-Wasm bundle, which must match the extension build\n (default: eh)\n --engine <path> Engine wasm override\n --browser <path> Browser executable (default: a detected Chrome/Edge,\n or the DFK_BROWSER environment variable)\n --timeout <ms> Per-block timeout (default: 30000)\n --report <file> Write the full result list as JSON\n --quiet Only report unexpected behaviour\n --help Show this help\n";
89
+ function y(e) {
103
90
  let t = {}, n = [];
104
91
  for (let r = 0; r < e.length; r++) {
105
92
  let i = e[r], a = () => {
@@ -123,12 +110,12 @@ function C(e) {
123
110
  case "--engine":
124
111
  t.engine = a();
125
112
  break;
113
+ case "--browser":
114
+ t.browser = a();
115
+ break;
126
116
  case "--timeout":
127
117
  t.timeoutMs = Number(a());
128
118
  break;
129
- case "--working-dir":
130
- t.workingDir = a();
131
- break;
132
119
  case "--report":
133
120
  t.reportFile = a();
134
121
  break;
@@ -145,4 +132,4 @@ function C(e) {
145
132
  return n.length > 0 && (t.contentDirs = n), t;
146
133
  }
147
134
  //#endregion
148
- export { x as cliMain, m as verifySqlDocs };
135
+ export { _ as cliMain, u as verifySqlDocs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "duckfn-docs-kit",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Shared Docusaurus building blocks (TOC toggle, home-page web components, brand tokens, remark version placeholder) for duckfn-family extension docs sites.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -45,7 +45,7 @@
45
45
  "AGENTS.md"
46
46
  ],
47
47
  "scripts": {
48
- "build": "vite build && tsc -p tsconfig.build.json",
48
+ "build": "vite build && vite build --config vite.harness.config.ts && tsc -p tsconfig.build.json",
49
49
  "typecheck": "tsc -p tsconfig.json --noEmit",
50
50
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
51
51
  "prepack": "npm run build"
@@ -59,9 +59,13 @@
59
59
  "@codemirror/state": "^6.7.6",
60
60
  "@codemirror/view": "^6.43.13",
61
61
  "@duckdb/duckdb-wasm": "1.33.1-dev64.0",
62
+ "@panzoom/panzoom": "^4.6.2",
62
63
  "@visactor/vtable": "^1.26.8",
63
64
  "codemirror": "^6.0.2",
65
+ "filenamify": "^7.0.3",
64
66
  "iconify-icon": "^3.0.3",
67
+ "mermaid": "^12.0.0",
68
+ "playwright-core": "^1.63.0",
65
69
  "sql-formatter": "^15.9.0"
66
70
  },
67
71
  "devDependencies": {
@@ -0,0 +1,50 @@
1
+ import type {IconifyIconHTMLElement} from 'iconify-icon';
2
+ import {el} from './dom';
3
+
4
+ /**
5
+ * A compact icon-only button with a hover/focus tooltip, built once and then
6
+ * mutated. Shared by `<dfk-sql>`'s code-block cluster and `<dfk-mermaid>`'s
7
+ * diagram cluster: both want the same "floating icon row over content" idiom,
8
+ * and neither wants a second implementation of it.
9
+ *
10
+ * The tooltip is also the accessible name — an icon-only control has no text to
11
+ * fall back on. The two class names are written by this module and styled in
12
+ * *each* consumer's shadow-root CSS; that duplication is unavoidable (a shadow
13
+ * boundary stops one sheet from reaching the other tree), so the rules carry the
14
+ * same names and are kept in sync by hand.
15
+ */
16
+ export class IconButton {
17
+ readonly root = el('button', {class: 'dfk-icon-button', type: 'button'});
18
+ readonly #icon: IconifyIconHTMLElement = el('iconify-icon', {
19
+ class: 'dfk-icon',
20
+ attrs: {'aria-hidden': 'true'},
21
+ });
22
+
23
+ constructor(icon: string, onClick: () => void) {
24
+ this.root.appendChild(this.#icon);
25
+ this.root.addEventListener('click', onClick);
26
+ this.setIcon(icon);
27
+ }
28
+
29
+ setIcon(icon: string): void {
30
+ this.#icon.setAttribute('icon', icon);
31
+ }
32
+
33
+ setLabel(text: string): void {
34
+ this.root.setAttribute('data-tip', text);
35
+ this.root.setAttribute('aria-label', text);
36
+ }
37
+
38
+ /** Marks a toggle as currently on (e.g. the SQL block's wrap toggle). */
39
+ setOn(on: boolean): void {
40
+ this.root.classList.toggle('dfk-icon-on', on);
41
+ }
42
+
43
+ setDisabled(disabled: boolean): void {
44
+ if (disabled) {
45
+ this.root.setAttribute('disabled', '');
46
+ } else {
47
+ this.root.removeAttribute('disabled');
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The CodeMirror 6 editor behind the kit's editable content: the code view of a
3
+ * runnable SQL block (`<dfk-sql>`) and the diagram-source dialog of a
4
+ * `<dfk-mermaid>`. One implementation, two languages — the SQL block asks for
5
+ * highlighting, the diagram editor takes the plain-text default.
6
+ *
7
+ * Every CodeMirror module arrives through a dynamic `import()` inside
8
+ * {@link mountCodeEditor}: a page full of blocks pays nothing for the editor on
9
+ * its critical path, and Docusaurus' Node prerender never evaluates any of it.
10
+ *
11
+ * This is browser-only code: `document` is touched only through the container
12
+ * the caller hands over, and everything else is behind the `import()`s.
13
+ */
14
+
15
+ export interface CodeEditor {
16
+ getValue(): string;
17
+ setValue(value: string): void;
18
+ /** Soft-wraps long lines, or stops wrapping them (the block's wrap toggle). */
19
+ setWrap(wrapped: boolean): void;
20
+ destroy(): void;
21
+ }
22
+
23
+ export interface CodeEditorOptions {
24
+ /**
25
+ * Highlight the document as SQL. Omitted, the editor is plain text — which is
26
+ * what a mermaid diagram gets: there is no first-party CodeMirror language for
27
+ * it, and a docs reader is editing prose-shaped source, not writing SQL.
28
+ */
29
+ language?: 'sql';
30
+ }
31
+
32
+ export async function mountCodeEditor(
33
+ container: HTMLElement,
34
+ value: string,
35
+ onChange: (value: string) => void,
36
+ options: CodeEditorOptions = {},
37
+ ): Promise<CodeEditor> {
38
+ const [{basicSetup}, {EditorView, keymap}, {defaultKeymap, historyKeymap}, {Compartment}] =
39
+ await Promise.all([
40
+ import('codemirror'),
41
+ import('@codemirror/view'),
42
+ import('@codemirror/commands'),
43
+ import('@codemirror/state'),
44
+ ]);
45
+ // Loaded only for the blocks that ask for it, so a diagram editor does not pull
46
+ // the SQL grammar (and its lezer parsers) into the page.
47
+ const language =
48
+ options.language === 'sql' ? [(await import('@codemirror/lang-sql')).sql()] : [];
49
+
50
+ // Wrapping is toggled from the outside, and reconfiguring it must not disturb
51
+ // the document or the undo history — that is exactly what a compartment is
52
+ // for, so the extension is swapped in place rather than rebuilt.
53
+ const wrap = new Compartment();
54
+
55
+ const view = new EditorView({
56
+ doc: value,
57
+ extensions: [
58
+ basicSetup,
59
+ ...language,
60
+ keymap.of([...defaultKeymap, ...historyKeymap]),
61
+ wrap.of([]),
62
+ EditorView.updateListener.of((update) => {
63
+ if (update.docChanged) {
64
+ onChange(update.state.doc.toString());
65
+ }
66
+ }),
67
+ ],
68
+ parent: container,
69
+ // `root` is left to CodeMirror's own `getRoot(container)`. The container sits
70
+ // in the component's shadow root, so style-mod mounts the base theme into
71
+ // that same shadow root — exactly where the `.cm-*` rules are needed.
72
+ // Pinning it to `document` would put them outside the editor's tree instead,
73
+ // where a shadow boundary stops them.
74
+ });
75
+
76
+ return {
77
+ getValue: () => view.state.doc.toString(),
78
+ setValue: (next: string) =>
79
+ view.dispatch({
80
+ changes: {from: 0, to: view.state.doc.length, insert: next},
81
+ }),
82
+ setWrap: (wrapped: boolean) =>
83
+ view.dispatch({
84
+ effects: wrap.reconfigure(wrapped ? EditorView.lineWrapping : []),
85
+ }),
86
+ destroy: () => view.destroy(),
87
+ };
88
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,14 @@
1
1
  import type {HTMLAttributes} from 'react';
2
+ // Imported as well as re-exported: the `HTMLElementTagNameMap` augmentation below
3
+ // names the classes, and a module augmentation can only refer to local bindings.
4
+ import {DfkFeatures} from './home/DfkFeatures';
5
+ import {DfkHero} from './home/DfkHero';
6
+ import {DfkMermaid} from './mermaid/DfkMermaid';
7
+ import {DfkNextSteps} from './home/DfkNextSteps';
8
+ import {DfkSql} from './sql/DfkSql';
2
9
 
3
10
  /**
4
- * Browser entry for duckfn-docs-kit: the home-page custom elements and the
11
+ * Browser entry for duckfn-docs-kit: the custom elements it registers and the
5
12
  * value types their `set*` methods accept.
6
13
  *
7
14
  * The components are retained-mode (build once, then mutate held nodes) and
@@ -13,17 +20,16 @@ import type {HTMLAttributes} from 'react';
13
20
  * web component, which `registerDfkElements()` registers as a side effect, so
14
21
  * this package ships no icon data.
15
22
  *
16
- * `TocToggle` and the remark plugin keep their own subpaths
17
- * (`duckfn-docs-kit/toc-toggle/TocToggle`, `duckfn-docs-kit/remark`) instead of
23
+ * `TocToggle` and the remark plugins keep their own subpaths
24
+ * (`duckfn-docs-kit/toc-toggle/TocToggle`, `duckfn-docs-kit/remark`,
25
+ * `duckfn-docs-kit/sql/remark`, `duckfn-docs-kit/mermaid/remark`) instead of
18
26
  * being merged here: a Docusaurus config file must never pull browser code into
19
27
  * Node, and a site that only wants the TOC collapse button should not pay for
20
28
  * the bundled `iconify-icon`.
21
29
  */
22
- export {DfkFeatures} from './home/DfkFeatures';
23
- export {DfkHero} from './home/DfkHero';
24
- export {DfkNextSteps} from './home/DfkNextSteps';
25
- export {DfkSql} from './sql/DfkSql';
30
+ export {DfkFeatures, DfkHero, DfkMermaid, DfkNextSteps, DfkSql};
26
31
  export {registerDfkElements} from './register';
32
+ export type {DfkMermaidConfig, DfkMermaidConfigInput} from './mermaid/config';
27
33
  export type {RunnableSqlConfig} from './sql/remark';
28
34
  export type {
29
35
  FeatureItem,
@@ -47,6 +53,23 @@ export type {
47
53
  */
48
54
  export type DfkElementProps = HTMLAttributes<HTMLElement>;
49
55
 
56
+ /**
57
+ * The kit's tags in the DOM's own tag map, so `document.createElement('dfk-sql')`
58
+ * (and the kit's `el()` helper) is typed as the class it upgrades to. Without
59
+ * this, every place that builds a `dfk-*` element from scratch — `sql/renderers.ts`
60
+ * building a `<dfk-mermaid>` for a `mermaid` result, `docs/src/pages/index.tsx`
61
+ * mounting the home elements — would have to cast the result.
62
+ */
63
+ declare global {
64
+ interface HTMLElementTagNameMap {
65
+ 'dfk-hero': DfkHero;
66
+ 'dfk-features': DfkFeatures;
67
+ 'dfk-next-steps': DfkNextSteps;
68
+ 'dfk-sql': DfkSql;
69
+ 'dfk-mermaid': DfkMermaid;
70
+ }
71
+ }
72
+
50
73
  declare module 'react' {
51
74
  namespace JSX {
52
75
  interface IntrinsicElements {
@@ -54,6 +77,7 @@ declare module 'react' {
54
77
  'dfk-features': DfkElementProps;
55
78
  'dfk-next-steps': DfkElementProps;
56
79
  'dfk-sql': DfkElementProps;
80
+ 'dfk-mermaid': DfkElementProps;
57
81
  }
58
82
  }
59
83
  }