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
package/dist/remark.d.ts CHANGED
@@ -13,7 +13,7 @@ import type { Plugin } from 'unified';
13
13
  */
14
14
  export declare const DEFAULT_VERSION_PLACEHOLDER = "{{DUCKFN_VERSION}}";
15
15
  export interface VersionPlaceholderOptions {
16
- /** The real version string to substitute in, e.g. `0.0.14`. */
16
+ /** The real version string to substitute in, e.g. `0.0.17`. */
17
17
  version: string;
18
18
  /** Override the token if a site uses a different one. */
19
19
  placeholder?: string;
@@ -0,0 +1,41 @@
1
+ /** The extension to `LOAD`: a local `.duckdb_extension.wasm` path or an http(s) URL. */
2
+ export interface RunnerOptions {
3
+ extension: string;
4
+ /** DuckDB-Wasm platform bundle; must match how the extension was built. */
5
+ platform?: WasmPlatform;
6
+ /** Engine wasm override; defaults to the platform bundle inside duckdb-wasm. */
7
+ engine?: string;
8
+ /** Browser executable; defaults to a detected Chrome/Edge (`DFK_BROWSER` wins). */
9
+ browser?: string;
10
+ }
11
+ export type WasmPlatform = 'eh' | 'mvp';
12
+ export interface RunResult {
13
+ rows: number;
14
+ columns: number;
15
+ }
16
+ /** The harness's `__dfkQuery` return shape (see `harness.ts`). */
17
+ export interface QueryResult {
18
+ columns: string[];
19
+ rows: Record<string, unknown>[];
20
+ error?: string;
21
+ }
22
+ /**
23
+ * One DuckDB-Wasm instance at a time, driven through a headless browser.
24
+ * Mirrors the surface `verify.ts` used from `WasmSqlRunner`.
25
+ */
26
+ export declare class BrowserSqlRunner {
27
+ #private;
28
+ private constructor();
29
+ static create(options: RunnerOptions): Promise<BrowserSqlRunner>;
30
+ /** Drops the current page state and opens a fresh one: new instance, new connection. */
31
+ newPage(): Promise<void>;
32
+ /** Runs one block; a SQL failure is thrown so the caller can weigh it. */
33
+ run(sql: string): Promise<RunResult>;
34
+ /**
35
+ * Runs one statement and returns its actual rows (not just a count). Used by
36
+ * the vfs browser probe to read each value; `run()` is the verifier's path.
37
+ * Unlike `run()`, a failure is returned as `{error}` rather than thrown.
38
+ */
39
+ query(sql: string): Promise<QueryResult>;
40
+ close(): Promise<void>;
41
+ }
@@ -0,0 +1,186 @@
1
+ import { createRequire as e } from "node:module";
2
+ import { basename as t, dirname as n, join as r } from "node:path";
3
+ import { existsSync as i, readFileSync as a } from "node:fs";
4
+ import { createServer as o } from "node:http";
5
+ import { chromium as s } from "playwright-core";
6
+ //#region src/sql/browserRunner.ts
7
+ var c = e(import.meta.url), l = {
8
+ eh: {
9
+ wasm: "duckdb-eh.wasm",
10
+ worker: "duckdb-browser-eh.worker.js"
11
+ },
12
+ mvp: {
13
+ wasm: "duckdb-mvp.wasm",
14
+ worker: "duckdb-browser-mvp.worker.js"
15
+ }
16
+ }, u = class e {
17
+ #e = null;
18
+ #t = null;
19
+ #n = null;
20
+ #r = "";
21
+ #i = null;
22
+ #a;
23
+ #o;
24
+ #s;
25
+ #c;
26
+ #l;
27
+ constructor(e) {
28
+ this.#a = e.extensionUrl, this.#o = e.enginePath, this.#s = e.workerPath, this.#c = e.browserExecutable, this.#l = e.allowUnsigned;
29
+ }
30
+ static async create(t) {
31
+ let i = l[t.platform ?? "eh"], a = n(c.resolve("@duckdb/duckdb-wasm/dist/duckdb-browser.mjs")), o = t.engine ? d(t.engine) : r(a, i.wasm), s = r(a, i.worker), u = /^https?:\/\//i.test(t.extension) ? t.extension : d(t.extension);
32
+ return new e({
33
+ extensionUrl: u,
34
+ enginePath: o,
35
+ workerPath: s,
36
+ browserExecutable: m(t.browser),
37
+ allowUnsigned: !0
38
+ });
39
+ }
40
+ async #u() {
41
+ if (this.#n) return;
42
+ this.#e = await f(this.#d());
43
+ let e = this.#e.address(), t = typeof e == "object" && e ? e.port : 0;
44
+ this.#r = `http://127.0.0.1:${t}/harness.html`, this.#t = await s.launch({
45
+ executablePath: this.#c,
46
+ headless: !0
47
+ }), this.#n = await this.#t.newPage();
48
+ }
49
+ #d() {
50
+ let e = /^https?:\/\//i.test(this.#a) ? { remoteUrl: this.#a } : {
51
+ localFile: this.#a,
52
+ baseName: t(this.#a)
53
+ };
54
+ return {
55
+ harnessScript: c.resolve("duckfn-docs-kit/sql/harness"),
56
+ engine: {
57
+ file: this.#o,
58
+ name: t(this.#o)
59
+ },
60
+ worker: {
61
+ file: this.#s,
62
+ name: t(this.#s)
63
+ },
64
+ extension: e,
65
+ allowUnsigned: this.#l
66
+ };
67
+ }
68
+ async newPage() {
69
+ await this.#u();
70
+ let e = this.#n;
71
+ if (!e) throw Error("sql/browserRunner: browser is not running");
72
+ this.#i = null, await e.goto(this.#r, { waitUntil: "load" });
73
+ try {
74
+ await e.evaluate(() => window.__dfkReady);
75
+ } catch (e) {
76
+ this.#i = h(e);
77
+ }
78
+ }
79
+ async run(e) {
80
+ if (this.#i) throw Error(this.#i);
81
+ let t = this.#n;
82
+ if (!t) throw Error("sql/browserRunner: no page is open — call newPage() first");
83
+ let n = await t.evaluate((e) => window.__dfkRun(e), e);
84
+ if (n?.error) throw Error(n.error);
85
+ return {
86
+ rows: n.rows,
87
+ columns: n.columns
88
+ };
89
+ }
90
+ async query(e) {
91
+ if (this.#i) return {
92
+ columns: [],
93
+ rows: [],
94
+ error: this.#i
95
+ };
96
+ let t = this.#n;
97
+ if (!t) throw Error("sql/browserRunner: no page is open — call newPage() first");
98
+ return await t.evaluate((e) => window.__dfkQuery(e), e) ?? {
99
+ columns: [],
100
+ rows: [],
101
+ error: "no result"
102
+ };
103
+ }
104
+ async close() {
105
+ try {
106
+ await this.#n?.close();
107
+ } catch {}
108
+ this.#n = null;
109
+ try {
110
+ await this.#t?.close();
111
+ } catch {}
112
+ this.#t = null, this.#e &&= (await new Promise((e) => this.#e?.close(() => e())), null);
113
+ }
114
+ };
115
+ function d(e) {
116
+ return /^file:\/\//i.test(e) ? new URL(e).pathname.replace(/^\/(\w:)/i, "$1") : e;
117
+ }
118
+ async function f(e) {
119
+ let t = a(e.harnessScript), n = a(e.engine.file), r = a(e.worker.file), i = "localFile" in e.extension ? a(e.extension.localFile) : null, s = "baseName" in e.extension ? e.extension.baseName : "", c = "remoteUrl" in e.extension ? e.extension.remoteUrl : `ext/${e.extension.baseName}`, l = `<!doctype html>
120
+ <html lang="en">
121
+ <head>
122
+ <meta charset="utf-8" />
123
+ <title>duckfn sql harness</title>
124
+ <script>
125
+ window.DFK_HARNESS_BUNDLE = {
126
+ mainModule: '/vendor/${e.engine.name}',
127
+ mainWorker: '/vendor/${e.worker.name}',
128
+ };
129
+ <\/script>
130
+ <script id="dfk-sql-runtime" type="application/json">
131
+ {"allowUnsignedExtensions":${e.allowUnsigned},"preload":[{"url":"${c}"}]}
132
+ <\/script>
133
+ </head>
134
+ <body>
135
+ <script type="module" src="/harness.js"><\/script>
136
+ </body>
137
+ </html>`, u = o((a, o) => {
138
+ let c = new URL(a.url ?? "/", "http://localhost").pathname;
139
+ if (c === "/harness.html") {
140
+ p(o, "text/html; charset=utf-8", Buffer.from(l, "utf8"));
141
+ return;
142
+ }
143
+ if (c === "/harness.js") {
144
+ p(o, "text/javascript; charset=utf-8", t);
145
+ return;
146
+ }
147
+ if (c === `/vendor/${e.engine.name}`) {
148
+ p(o, "application/wasm", n);
149
+ return;
150
+ }
151
+ if (c === `/vendor/${e.worker.name}`) {
152
+ p(o, "text/javascript; charset=utf-8", r);
153
+ return;
154
+ }
155
+ if (i && c === `/ext/${s}`) {
156
+ p(o, "application/octet-stream", i);
157
+ return;
158
+ }
159
+ o.writeHead(404, { "content-type": "text/plain" }), o.end("not found");
160
+ });
161
+ return await new Promise((e, t) => {
162
+ u.once("error", t), u.listen(0, "127.0.0.1", e);
163
+ }), u;
164
+ }
165
+ function p(e, t, n) {
166
+ e.writeHead(200, {
167
+ "content-type": t,
168
+ "content-length": n.length
169
+ }), e.end(n);
170
+ }
171
+ function m(e) {
172
+ let t = e ?? process.env.DFK_BROWSER;
173
+ if (t) return t;
174
+ let n = [];
175
+ if (process.platform === "win32") {
176
+ let e = process.env.PROGRAMFILES ?? "C:\\Program Files", t = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)", i = process.env.LOCALAPPDATA ?? "";
177
+ n.push(r(e, "Google", "Chrome", "Application", "chrome.exe"), r(t, "Google", "Chrome", "Application", "chrome.exe"), r(t, "Microsoft", "Edge", "Application", "msedge.exe"), r(e, "Microsoft", "Edge", "Application", "msedge.exe"), i ? r(i, "Google", "Chrome", "Application", "chrome.exe") : "");
178
+ } else process.platform === "darwin" ? n.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge") : n.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/microsoft-edge", "/usr/bin/chromium", "/usr/bin/chromium-browser");
179
+ for (let e of n) if (e && i(e)) return e;
180
+ throw Error("sql/browserRunner: no Chrome/Edge found — pass --browser <path> or set DFK_BROWSER");
181
+ }
182
+ function h(e) {
183
+ return e instanceof Error ? e.message : String(e);
184
+ }
185
+ //#endregion
186
+ export { u as BrowserSqlRunner };
@@ -1,4 +1,4 @@
1
- import { t as e } from "../register-DKLiYs-F.js";
1
+ import { t as e } from "../register-Dev_kc3Z.js";
2
2
  //#region src/sql/client.ts
3
3
  typeof window < "u" && e();
4
4
  //#endregion
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `duckfn-docs-kit/sql/harness` — the browser page the offline SQL verifier
3
+ * (`sql/browserRunner`) drives with Playwright.
4
+ *
5
+ * It is deliberately thin: it reuses the site's own runtime (`sql/runtime`) so
6
+ * a verified block runs through exactly the code path a reader's **Run** click
7
+ * does — same `AsyncDuckDB` wiring, same ordered extension preload, same
8
+ * "multi-statement returns the last result" `execute()`. The only difference is
9
+ * where the engine comes from: instead of the jsDelivr CDN, the runner serves
10
+ * `duckdb-*.wasm` and its worker script same-origin and hands those URLs in
11
+ * through {@link window.DFK_HARNESS_BUNDLE}, which `runtime.init` accepts as a
12
+ * {@link LocalBundle}.
13
+ *
14
+ * The two contracts the page reads are injected by the served `harness.html`:
15
+ *
16
+ * - `window.DFK_HARNESS_BUNDLE` — `{mainModule, mainWorker, pthreadWorker?}`,
17
+ * the locally-served engine bundle (an absolute same-origin URL each).
18
+ * - the `<script id="dfk-sql-runtime">` JSON tag — the preload list and
19
+ * `allowUnsignedExtensions`, in the very shape the site plugin injects
20
+ * (see `sql/runtimeConfig`), so nothing here re-implements extension loading.
21
+ *
22
+ * The runner communicates through `window.__dfkReady`, `window.__dfkRun` and
23
+ * `window.__dfkQuery`, each reached with a Playwright `page.evaluate` (which
24
+ * awaits the returned promise and marshals the value back to Node). Page reload
25
+ * per content file is what gives "one instance per page": a fresh document
26
+ * resets the module-level singleton, so pages never share a DuckDB instance
27
+ * while the blocks of one page keep sharing its connection (via
28
+ * `DuckDBRuntime.execute`).
29
+ */
30
+ import { type LocalBundle } from './runtime';
31
+ declare global {
32
+ interface Window {
33
+ /** The locally-served engine bundle, injected by the runner's harness.html. */
34
+ DFK_HARNESS_BUNDLE?: LocalBundle;
35
+ /** Resolves once the instance is up and the site preloads have loaded. */
36
+ __dfkReady?: Promise<void>;
37
+ /** Runs one block and reports rows/columns, or a captured error message. */
38
+ __dfkRun?: (sql: string) => Promise<HarnessRunResult>;
39
+ /**
40
+ * Runs one statement and returns its actual result — the rows, not just a
41
+ * count. Used by the vfs browser probe (`browserRunner.query`) to read the
42
+ * value each statement produced; the docs verifier never needs it.
43
+ */
44
+ __dfkQuery?: (sql: string) => Promise<HarnessQueryResult>;
45
+ }
46
+ }
47
+ /** What {@link window.__dfkRun} resolves with; mirrors the runner's `RunResult`. */
48
+ export interface HarnessRunResult {
49
+ rows: number;
50
+ columns: number;
51
+ /** Set when the block failed; `rows`/`columns` are then 0. */
52
+ error?: string;
53
+ }
54
+ /** What {@link window.__dfkQuery} resolves with: the marshalled result rows. */
55
+ export interface HarnessQueryResult {
56
+ columns: string[];
57
+ rows: Record<string, unknown>[];
58
+ error?: string;
59
+ }