@takazudo/zfb-md-wasm 0.1.0-next.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Takeshi Takatsudo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # @takazudo/zfb-md-wasm
2
+
3
+ A WebAssembly build of [zfb](https://github.com/Takazudo/zudo-front-builder)'s
4
+ markdown / MDX → JavaScript conversion pipeline, for **browser-side dynamic
5
+ conversion**. The headline use case is **CMS live preview**: convert MDX to a
6
+ runnable ES module (or markdown to HTML) in the browser, on every keystroke,
7
+ with output **parity** to what zfb produces at build time.
8
+
9
+ Parity is the reason this package exists instead of running
10
+ [`@mdx-js/mdx`](https://mdxjs.com/) in the browser: `@mdx-js/mdx` is a
11
+ _different_ pipeline, so its preview would not match zfb's built output.
12
+ `zfb-md-wasm` compiles the same Rust pipeline zfb itself uses, so a preview is
13
+ faithful to the real thing.
14
+
15
+ Node ≥ 20 can also load the package (this is what the test suite uses), but the
16
+ browser is the target. A server with shell access should keep calling the `zfb`
17
+ binary directly.
18
+
19
+ ## Install
20
+
21
+ ```sh
22
+ pnpm add @takazudo/zfb-md-wasm
23
+ ```
24
+
25
+ ## Two API tiers
26
+
27
+ Both functions take the markdown/MDX `source` and an options object (every
28
+ field optional; `{}` selects all defaults). Both return a result object plus a
29
+ `diagnostics` array — **expected failures come back as diagnostics, never as a
30
+ thrown error** (see the trap contract below for what _does_ throw).
31
+
32
+ ### `compile(source, options?)` — MDX → ES-module JS
33
+
34
+ Full MDX → JSX → SWC → ES module. The emitted module has a `MDXContent`
35
+ default export (a component function) using the automatic JSX runtime.
36
+
37
+ ```ts
38
+ import { compile } from "@takazudo/zfb-md-wasm";
39
+
40
+ const { code, frontmatter, diagnostics } = await compile(
41
+ "---\ntitle: Hello\n---\n\n# Welcome\n\n<Callout>Sum is {1 + 2}</Callout>\n",
42
+ { filename: "post.mdx", jsxRuntime: "preact" },
43
+ );
44
+ // code -> ES-module JS source (string) or null on failure
45
+ // frontmatter -> { title: "Hello" }
46
+ // diagnostics -> []
47
+ ```
48
+
49
+ Frontmatter values are returned in the `frontmatter` field — they are **not**
50
+ exposed as an in-content binding. The compiled module has no `frontmatter`
51
+ variable in scope, so a `{frontmatter.title}` reference inside the source
52
+ would throw `ReferenceError` at runtime; read the values from the result
53
+ object instead.
54
+
55
+ ### `renderHtml(source, options?)` — markdown → HTML
56
+
57
+ Markdown → hast → HTML string, **skipping SWC at runtime**. Use this for a
58
+ plain-markdown preview when you don't need to evaluate a component module.
59
+
60
+ ```ts
61
+ import { renderHtml } from "@takazudo/zfb-md-wasm";
62
+
63
+ const { html, frontmatter, diagnostics } = await renderHtml(
64
+ "# Heading\n\nSome **bold** text.\n",
65
+ { filename: "post.md" },
66
+ );
67
+ // html -> "<h1>Heading</h1><p>Some <strong>bold</strong> text.</p>"
68
+ ```
69
+
70
+ `renderHtml` accepts and ignores `jsxRuntime` / `development`, so one options
71
+ object can serve both tiers.
72
+
73
+ ### `version()` / `init()`
74
+
75
+ `version()` returns the package version for host-side compatibility checks.
76
+ Published artifacts are stamped with the release semver at build time; local
77
+ development builds fall back to the Rust manifest placeholder.
78
+ `init()` eagerly loads and instantiates the wasm module; it's optional (every
79
+ call instantiates on first use) but useful to front-load the one-time
80
+ fetch/compile cost at app startup.
81
+
82
+ ## Options shape
83
+
84
+ ```ts
85
+ interface ZfbMdWasmOptions {
86
+ filename?: string; // must end .md/.mdx; drives frontmatter dispatch + diagnostics
87
+ jsxRuntime?: "preact" | "react"; // compile only; default "preact"
88
+ development?: boolean; // compile only; default false
89
+ pipeline?: {
90
+ theme?: string | null; // a syntect theme name, or null for no highlighting
91
+ gfm?: {
92
+ strikethrough?: boolean; table?: boolean; autolinkLiteral?: boolean;
93
+ taskListItem?: boolean; footnoteDefinition?: boolean;
94
+ };
95
+ cjkFriendly?: boolean;
96
+ hardBreaks?: boolean;
97
+ features?: Record<string, unknown>; // zfb's MarkdownFeaturesConfig, verbatim
98
+ };
99
+ }
100
+ ```
101
+
102
+ `pipeline` is zfb's **resolved features config** as JSON — the same shape zfb
103
+ derives from `zfb.config.ts` at build time. See "Limitations" for why it's
104
+ resolved JSON rather than a config file. Unknown fields are rejected at both
105
+ nesting levels (an `options`-source diagnostic).
106
+
107
+ ## Evaluating compiled modules in a browser
108
+
109
+ `compile()` returns ES-module _source_. To run it, turn it into a module (a
110
+ blob URL is the usual trick) and dynamic-import it:
111
+
112
+ ```ts
113
+ const { code, diagnostics } = await compile(source, { filename: "preview.mdx" });
114
+ if (code === null) {
115
+ // Compilation failed — render `diagnostics` instead of a module.
116
+ return;
117
+ }
118
+ const url = URL.createObjectURL(new Blob([code], { type: "text/javascript" }));
119
+ const { default: MDXContent } = await import(/* @vite-ignore */ url);
120
+ URL.revokeObjectURL(url);
121
+ // render <MDXContent components={{ Callout }} /> with your framework
122
+ ```
123
+
124
+ Pass your PascalCase components (the `<Callout>` in the source above) through
125
+ the module's `components` prop.
126
+
127
+ ### ⚠️ You must supply the JSX runtime — and the preact case needs one alias
128
+
129
+ The compiled module imports its JSX runtime by bare specifier
130
+ (`preact/jsx-runtime` or `react/jsx-runtime`), so the page must resolve those —
131
+ via an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap)
132
+ or your bundler.
133
+
134
+ **There is one asymmetry to know about.** zfb's emitter takes the JSX _factory_
135
+ from your chosen runtime but **always imports `Fragment` from
136
+ `react/jsx-runtime`**, regardless of `jsxRuntime`. This is zfb's production
137
+ emitter shape (so it's parity-correct, not a bug) — but it means a **preact**
138
+ consumer must alias `react/jsx-runtime` onto preact's, or `Fragment` will fail
139
+ to resolve at runtime:
140
+
141
+ ```html
142
+ <script type="importmap">
143
+ {
144
+ "imports": {
145
+ "preact/jsx-runtime": "https://esm.sh/preact/jsx-runtime",
146
+ "react/jsx-runtime": "https://esm.sh/preact/jsx-runtime"
147
+ }
148
+ }
149
+ </script>
150
+ ```
151
+
152
+ A `react` consumer just maps `react/jsx-runtime` to React's own and needs no
153
+ alias.
154
+
155
+ ## Node usage
156
+
157
+ For tests and tooling, the package loads and runs under Node ≥ 20 with no extra
158
+ setup — the same `compile` / `renderHtml` / `version` API. This is exactly how
159
+ this package's own vitest suite exercises the wasm.
160
+
161
+ ## Parity guarantee & limitations
162
+
163
+ Output matches zfb's native pipeline on a fixed fixture corpus (the parity
164
+ suite gates exact-match). Deliberate limitations of the browser build:
165
+
166
+ - **Filesystem-bound plugins are inert.** `transclude`, `imageDimensions`, and
167
+ `linkValidation` are registered but never touch a filesystem (there is none),
168
+ exactly as zfb's own MDX loader runs them with build-context roots unarmed.
169
+ Host-callback versions are a possible future epic.
170
+ - **Config is resolved JSON, not `zfb.config.ts`.** Evaluating a TypeScript
171
+ config needs a JS engine; that stays build-side. Resolve your config to JSON
172
+ first and pass it as `pipeline`.
173
+ - **No cross-file features.** Route-table link resolution and cross-file anchor
174
+ resolution need the whole project graph, which a single-document browser call
175
+ doesn't have.
176
+ - **The single artifact carries SWC even for `renderHtml`-only use.** One
177
+ cdylib can't tree-shake SWC away when only `renderHtml` is called; a slim
178
+ `renderHtml`-only artifact is a documented possible follow-up.
179
+ - **Syntax highlighting uses syntect's `fancy-regex` backend** (native zfb uses
180
+ `oniguruma`, which can't compile to wasm). The two are byte-identical on
181
+ zfb's fixture corpus; any grammar-level divergences are tracked in the
182
+ crate's informational backend-divergence test.
183
+
184
+ ## Artifact size
185
+
186
+ Shipping SWC in the bytes makes this a large module. The build applies a
187
+ size-optimized cargo profile (`opt-level = "z"`, LTO, one codegen unit,
188
+ `panic = "abort"`) plus `wasm-opt`, which roughly halves the raw binary. The
189
+ current build produces **~2.9 MB raw / ~1.3 MB gzipped** for the `.wasm`. The CI
190
+ `wasm-md` job prints the authoritative gzipped size on every run — treat that
191
+ as the source of truth rather than this figure, which can drift.
192
+
193
+ ## Error / trap / re-init contract
194
+
195
+ - **Expected failures never throw.** Parse errors, malformed options JSON,
196
+ unknown themes, a bad filename — all come back as structured `Diagnostic[]`
197
+ entries (`{ severity, source, message, line, column }`) on a normal result,
198
+ with `code` / `html` set to `null`. `line`/`column` are 1-based; for
199
+ `markdown` / `frontmatter` sources they point into the original source, for
200
+ `options` into the options JSON.
201
+ - **A wasm _trap_ is always a bug, and it poisons the instance.** On
202
+ `wasm32-unknown-unknown` a Rust panic (or other internal fault) lowers to a
203
+ wasm trap; `catch_unwind` is not reliable recovery, so the instance is dead
204
+ afterward. This wrapper handles that for you: it catches the
205
+ `WebAssembly.RuntimeError`, **drops the poisoned instance and re-instantiates
206
+ a fresh one in the background** (from the cached compiled module, so no
207
+ recompile), and throws a `ZfbMdWasmTrapError` for that one call. The next
208
+ `compile` / `renderHtml` / `version` call transparently uses the fresh
209
+ instance. The API is stateless per call, so re-init is lossless.
210
+
211
+ If you ever see a `ZfbMdWasmTrapError`, please report it with the input that
212
+ triggered it — the crate is designed never to trap on structured input.
213
+ (Fuzzing the trap surface is a documented follow-up.)
214
+
215
+ ## License
216
+
217
+ MIT © Takeshi Takatsudo
@@ -0,0 +1,88 @@
1
+ import type { CompileResult, RenderHtmlResult, ZfbMdWasmOptions } from "./types.js";
2
+ /**
3
+ * Thrown when a wasm call traps (a Rust panic, or another internal fault
4
+ * that lowers to a wasm trap). This is always a bug, never an expected
5
+ * input-validation failure -- expected failures (parse errors, malformed
6
+ * options JSON, unknown themes, ...) come back as structured
7
+ * `Diagnostic[]` in a normal `CompileResult`/`RenderHtmlResult`, never as a
8
+ * thrown error. See this package's README "Error / trap / re-init
9
+ * contract" section.
10
+ *
11
+ * By the time this is thrown, the poisoned instance has already been dropped
12
+ * and a replacement has been instantiated (from the cached compiled module, so
13
+ * no recompile). Concurrent trap reporters for the same poisoned generation
14
+ * wait for that same replacement instead of each starting another one; the
15
+ * next `compile` / `renderHtml` / `version` call transparently uses the fresh
16
+ * instance.
17
+ */
18
+ export declare class ZfbMdWasmTrapError extends Error {
19
+ constructor(cause: unknown);
20
+ }
21
+ /**
22
+ * Thrown once this wrapper has already recovered from too many wasm traps in
23
+ * one module lifetime. ES module records cannot be evicted, and wasm-bindgen
24
+ * `--target web` glue cannot be safely re-used after initialization, so the
25
+ * only bounded behavior after repeated poisoning is to stop recovering and ask
26
+ * the host to reload/recreate the JS realm before trying again.
27
+ */
28
+ export declare class ZfbMdWasmTrapRecoveryLimitError extends Error {
29
+ constructor(maxRecoveries: number, cause: unknown);
30
+ }
31
+ /**
32
+ * Eagerly loads and instantiates the wasm module. Optional -- `compile` /
33
+ * `renderHtml` / `version` all call this implicitly on first use -- but
34
+ * useful to front-load the one-time fetch/compile cost, e.g. on app
35
+ * startup before the first user-triggered call.
36
+ */
37
+ export declare function init(): Promise<void>;
38
+ /**
39
+ * Compile MDX source into ES-module JavaScript. Mirrors the crate's
40
+ * `compile(source, options_json) -> string` export, with JSON
41
+ * marshaling handled for you.
42
+ */
43
+ export declare function compile(source: string, options?: ZfbMdWasmOptions): Promise<CompileResult>;
44
+ /**
45
+ * Render markdown source to an HTML fragment (no SWC at runtime). Mirrors
46
+ * the crate's `renderHtml(source, options_json) -> string` export, with
47
+ * JSON marshaling handled for you.
48
+ */
49
+ export declare function renderHtml(source: string, options?: ZfbMdWasmOptions): Promise<RenderHtmlResult>;
50
+ /**
51
+ * Package version for host-side compatibility checks.
52
+ *
53
+ * Release artifacts are stamped with the published package semver at build
54
+ * time; local development builds fall back to the Rust manifest placeholder.
55
+ */
56
+ export declare function version(): Promise<string>;
57
+ /**
58
+ * @internal Test-only (zfb#1577's trap/re-init test). Forces a genuine wasm
59
+ * trap on the currently-active instance by invoking its raw `compile` export
60
+ * with a garbage return pointer, so the result-doubleword `i32.store` lands
61
+ * outside the instance's linear memory -- a `WebAssembly.RuntimeError:
62
+ * memory access out of bounds`. From the JS host's point of view this is
63
+ * the same exception class a real Rust panic->unreachable trap produces
64
+ * (both are `instanceof WebAssembly.RuntimeError`), which is what
65
+ * `callWasm`'s catch clause keys on -- so this exercises the real
66
+ * catch-and-reinstantiate path without needing to coax an actual Rust panic
67
+ * out of a crate that is deliberately designed to never panic on structured
68
+ * input. This is NOT part of the supported public API: it is a named export
69
+ * of the package entry, so a `import { __forceTrapForTests } from
70
+ * "@takazudo/zfb-md-wasm"` does resolve -- but the `__`-prefix marks it
71
+ * internal, and the raw ABI details it pokes at (argument order, retptr
72
+ * convention) are wasm-bindgen internals that can change on a wasm-bindgen
73
+ * version bump. Do not call it outside this package's own tests.
74
+ */
75
+ export declare function __forceTrapForTests(): Promise<void>;
76
+ /**
77
+ * @internal Test-only observability for the trap recovery contract. This is a
78
+ * named export only so the built-package Vitest suite can assert the public
79
+ * wrapper's concurrency/cap behavior without mocking the wasm-bindgen glue.
80
+ */
81
+ export declare function __getTrapRecoveryStateForTests(): {
82
+ currentGeneration: number;
83
+ freshInstanceStarts: number;
84
+ maxTrapRecoveries: number;
85
+ trapRecoveriesStarted: number;
86
+ terminal: boolean;
87
+ };
88
+ export type { CompileResult, RenderHtmlResult, Diagnostic, DiagnosticSource, ZfbMdWasmOptions, PipelineOptions, GfmOptions, MarkdownFeaturesConfig, JsxRuntime, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ const GLUE_URL = new URL("./wasm/zfb_md_wasm.js", import.meta.url);
2
+ const WASM_URL = new URL("./wasm/zfb_md_wasm_bg.wasm", import.meta.url);
3
+ function isNode() {
4
+ return typeof process !== "undefined" && !!process.versions?.node;
5
+ }
6
+ // Node's built-in `fetch` does not support `file:` URLs, so the wasm bytes
7
+ // must be read from disk directly there; browsers (and other fetch-capable
8
+ // hosts, e.g. a bundler-served dev server) go through `fetch` against the
9
+ // module-relative URL, which is the standard wasm-bindgen `--target web`
10
+ // browser consumption path.
11
+ async function loadWasmBytes() {
12
+ if (isNode()) {
13
+ const [{ readFile }, { fileURLToPath }] = await Promise.all([
14
+ import("node:fs/promises"),
15
+ import("node:url"),
16
+ ]);
17
+ const buf = await readFile(fileURLToPath(WASM_URL));
18
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
19
+ }
20
+ const res = await fetch(WASM_URL);
21
+ if (!res.ok) {
22
+ throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${res.status} ${res.statusText}`);
23
+ }
24
+ return res.arrayBuffer();
25
+ }
26
+ let compiledModulePromise;
27
+ function getCompiledModule() {
28
+ compiledModulePromise ??= loadWasmBytes().then((bytes) => WebAssembly.compile(bytes));
29
+ return compiledModulePromise;
30
+ }
31
+ const MAX_TRAP_RECOVERIES = 16;
32
+ let currentGeneration = 0;
33
+ let trapRecoveriesStarted = 0;
34
+ let freshInstanceStarts = 0;
35
+ let terminalTrapRecoveryError;
36
+ /**
37
+ * Instantiates a fresh copy of the wasm-bindgen glue module.
38
+ *
39
+ * wasm-bindgen's `--target web` output guards `init`/`initSync` with an
40
+ * "already initialized" early return keyed on a module-scope singleton, so
41
+ * recovering from a trapped (poisoned) instance requires a genuinely fresh ES
42
+ * module record -- re-calling `initSync` on the existing module record is a
43
+ * no-op. Re-importing the same specifier with a distinct query string yields a
44
+ * new module record per the ECMAScript module spec (Node and browsers agree on
45
+ * this); the underlying `WebAssembly.Module` (compiled code) is cached and
46
+ * reused via `getCompiledModule`, so this only re-runs cheap instantiation,
47
+ * never a recompile. Module-record growth is bounded by
48
+ * `MAX_TRAP_RECOVERIES`: once that many poisoned instances have been replaced,
49
+ * recovery stops permanently and callers receive
50
+ * `ZfbMdWasmTrapRecoveryLimitError` instead of minting another module record.
51
+ */
52
+ async function freshInstance(generation) {
53
+ freshInstanceStarts += 1;
54
+ const [module, glue] = await Promise.all([
55
+ getCompiledModule(),
56
+ import(
57
+ /* @vite-ignore */ `${GLUE_URL.href}?zfbMdWasmGen=${generation}`),
58
+ ]);
59
+ const raw = glue.initSync({ module });
60
+ return { generation, glue, raw };
61
+ }
62
+ let instancePromise;
63
+ function getInstance() {
64
+ if (terminalTrapRecoveryError) {
65
+ return Promise.reject(terminalTrapRecoveryError);
66
+ }
67
+ instancePromise ??= freshInstance(currentGeneration);
68
+ return instancePromise;
69
+ }
70
+ /**
71
+ * Thrown when a wasm call traps (a Rust panic, or another internal fault
72
+ * that lowers to a wasm trap). This is always a bug, never an expected
73
+ * input-validation failure -- expected failures (parse errors, malformed
74
+ * options JSON, unknown themes, ...) come back as structured
75
+ * `Diagnostic[]` in a normal `CompileResult`/`RenderHtmlResult`, never as a
76
+ * thrown error. See this package's README "Error / trap / re-init
77
+ * contract" section.
78
+ *
79
+ * By the time this is thrown, the poisoned instance has already been dropped
80
+ * and a replacement has been instantiated (from the cached compiled module, so
81
+ * no recompile). Concurrent trap reporters for the same poisoned generation
82
+ * wait for that same replacement instead of each starting another one; the
83
+ * next `compile` / `renderHtml` / `version` call transparently uses the fresh
84
+ * instance.
85
+ */
86
+ export class ZfbMdWasmTrapError extends Error {
87
+ constructor(cause) {
88
+ super("zfb-md-wasm: the wasm instance trapped (a Rust panic or internal fault) and has been " +
89
+ "automatically re-instantiated. This is always a bug in zfb-md-wasm -- please report it " +
90
+ "with the input that triggered it.");
91
+ this.name = "ZfbMdWasmTrapError";
92
+ this.cause = cause;
93
+ }
94
+ }
95
+ /**
96
+ * Thrown once this wrapper has already recovered from too many wasm traps in
97
+ * one module lifetime. ES module records cannot be evicted, and wasm-bindgen
98
+ * `--target web` glue cannot be safely re-used after initialization, so the
99
+ * only bounded behavior after repeated poisoning is to stop recovering and ask
100
+ * the host to reload/recreate the JS realm before trying again.
101
+ */
102
+ export class ZfbMdWasmTrapRecoveryLimitError extends Error {
103
+ constructor(maxRecoveries, cause) {
104
+ super(`zfb-md-wasm: wasm trap recovery limit reached after ${maxRecoveries} ` +
105
+ `successful re-instantiations. Further automatic recovery is disabled to avoid ` +
106
+ `unbounded ES module record growth. Reload the JS realm before using zfb-md-wasm ` +
107
+ `again, and please report the input that triggered the repeated traps.`);
108
+ this.name = "ZfbMdWasmTrapRecoveryLimitError";
109
+ this.cause = cause;
110
+ }
111
+ }
112
+ function isTrap(err) {
113
+ return typeof WebAssembly !== "undefined" && err instanceof WebAssembly.RuntimeError;
114
+ }
115
+ async function recoverAfterTrap(observedGeneration, cause) {
116
+ if (terminalTrapRecoveryError) {
117
+ throw terminalTrapRecoveryError;
118
+ }
119
+ // CAS-style single-flight: only the reporter that observed the currently
120
+ // active generation advances it and starts a fresh instantiation. Other
121
+ // concurrent reporters trapped on an older instance, so they await the
122
+ // already-started replacement instead of minting another module record.
123
+ if (observedGeneration !== currentGeneration) {
124
+ await getInstance();
125
+ return;
126
+ }
127
+ if (trapRecoveriesStarted >= MAX_TRAP_RECOVERIES) {
128
+ terminalTrapRecoveryError = new ZfbMdWasmTrapRecoveryLimitError(MAX_TRAP_RECOVERIES, cause);
129
+ instancePromise = undefined;
130
+ throw terminalTrapRecoveryError;
131
+ }
132
+ trapRecoveriesStarted += 1;
133
+ currentGeneration += 1;
134
+ instancePromise = freshInstance(currentGeneration);
135
+ await instancePromise;
136
+ }
137
+ async function callWasm(fn) {
138
+ const instance = await getInstance();
139
+ try {
140
+ return fn(instance);
141
+ }
142
+ catch (err) {
143
+ if (!isTrap(err)) {
144
+ throw err;
145
+ }
146
+ // Drop the poisoned instance and re-instantiate it from the cached compiled
147
+ // module. Concurrent trap reporters single-flight through the same
148
+ // generation advance, so one poisoned generation creates at most one fresh
149
+ // module record.
150
+ await recoverAfterTrap(instance.generation, err);
151
+ throw new ZfbMdWasmTrapError(err);
152
+ }
153
+ }
154
+ /**
155
+ * Eagerly loads and instantiates the wasm module. Optional -- `compile` /
156
+ * `renderHtml` / `version` all call this implicitly on first use -- but
157
+ * useful to front-load the one-time fetch/compile cost, e.g. on app
158
+ * startup before the first user-triggered call.
159
+ */
160
+ export async function init() {
161
+ await getInstance();
162
+ }
163
+ /**
164
+ * Compile MDX source into ES-module JavaScript. Mirrors the crate's
165
+ * `compile(source, options_json) -> string` export, with JSON
166
+ * marshaling handled for you.
167
+ */
168
+ export async function compile(source, options = {}) {
169
+ const optionsJson = JSON.stringify(options);
170
+ const json = await callWasm(({ glue }) => glue.compile(source, optionsJson));
171
+ return JSON.parse(json);
172
+ }
173
+ /**
174
+ * Render markdown source to an HTML fragment (no SWC at runtime). Mirrors
175
+ * the crate's `renderHtml(source, options_json) -> string` export, with
176
+ * JSON marshaling handled for you.
177
+ */
178
+ export async function renderHtml(source, options = {}) {
179
+ const optionsJson = JSON.stringify(options);
180
+ const json = await callWasm(({ glue }) => glue.renderHtml(source, optionsJson));
181
+ return JSON.parse(json);
182
+ }
183
+ /**
184
+ * Package version for host-side compatibility checks.
185
+ *
186
+ * Release artifacts are stamped with the published package semver at build
187
+ * time; local development builds fall back to the Rust manifest placeholder.
188
+ */
189
+ export async function version() {
190
+ return callWasm(({ glue }) => glue.version());
191
+ }
192
+ /**
193
+ * @internal Test-only (zfb#1577's trap/re-init test). Forces a genuine wasm
194
+ * trap on the currently-active instance by invoking its raw `compile` export
195
+ * with a garbage return pointer, so the result-doubleword `i32.store` lands
196
+ * outside the instance's linear memory -- a `WebAssembly.RuntimeError:
197
+ * memory access out of bounds`. From the JS host's point of view this is
198
+ * the same exception class a real Rust panic->unreachable trap produces
199
+ * (both are `instanceof WebAssembly.RuntimeError`), which is what
200
+ * `callWasm`'s catch clause keys on -- so this exercises the real
201
+ * catch-and-reinstantiate path without needing to coax an actual Rust panic
202
+ * out of a crate that is deliberately designed to never panic on structured
203
+ * input. This is NOT part of the supported public API: it is a named export
204
+ * of the package entry, so a `import { __forceTrapForTests } from
205
+ * "@takazudo/zfb-md-wasm"` does resolve -- but the `__`-prefix marks it
206
+ * internal, and the raw ABI details it pokes at (argument order, retptr
207
+ * convention) are wasm-bindgen internals that can change on a wasm-bindgen
208
+ * version bump. Do not call it outside this package's own tests.
209
+ */
210
+ export async function __forceTrapForTests() {
211
+ await callWasm(({ raw }) => {
212
+ raw.compile(0xfffffff0, 0, 0, 0, 0);
213
+ });
214
+ }
215
+ /**
216
+ * @internal Test-only observability for the trap recovery contract. This is a
217
+ * named export only so the built-package Vitest suite can assert the public
218
+ * wrapper's concurrency/cap behavior without mocking the wasm-bindgen glue.
219
+ */
220
+ export function __getTrapRecoveryStateForTests() {
221
+ return {
222
+ currentGeneration,
223
+ freshInstanceStarts,
224
+ maxTrapRecoveries: MAX_TRAP_RECOVERIES,
225
+ trapRecoveriesStarted,
226
+ terminal: !!terminalTrapRecoveryError,
227
+ };
228
+ }
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAExE,SAAS,MAAM;IACb,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;AACpE,CAAC;AAED,2EAA2E;AAC3E,2EAA2E;AAC3E,0EAA0E;AAC1E,yEAAyE;AACzE,4BAA4B;AAC5B,KAAK,UAAU,aAAa;IAC1B,IAAI,MAAM,EAAE,EAAE,CAAC;QACb,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC1D,MAAM,CAAC,kBAAkB,CAAC;YAC1B,MAAM,CAAC,UAAU,CAAC;SACnB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAgB,CAAC;IAC1F,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,6CAA6C,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC;AAED,IAAI,qBAA8D,CAAC;AAEnE,SAAS,iBAAiB;IACxB,qBAAqB,KAAK,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,IAAI,iBAAiB,GAAG,CAAC,CAAC;AAC1B,IAAI,qBAAqB,GAAG,CAAC,CAAC;AAC9B,IAAI,mBAAmB,GAAG,CAAC,CAAC;AAC5B,IAAI,yBAAsE,CAAC;AAQ3E;;;;;;;;;;;;;;;GAeG;AACH,KAAK,UAAU,aAAa,CAAC,UAAkB;IAC7C,mBAAmB,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACvC,iBAAiB,EAAE;QACnB,MAAM;QACJ,kBAAkB,CAAC,GAAG,QAAQ,CAAC,IAAI,iBAAiB,UAAU,EAAE,CACtC;KAC7B,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACnC,CAAC;AAED,IAAI,eAA8C,CAAC;AAEnD,SAAS,WAAW;IAClB,IAAI,yBAAyB,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACnD,CAAC;IACD,eAAe,KAAK,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACrD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,KAAc;QACxB,KAAK,CACH,uFAAuF;YACrF,yFAAyF;YACzF,mCAAmC,CACtC,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IACxD,YAAY,aAAqB,EAAE,KAAc;QAC/C,KAAK,CACH,uDAAuD,aAAa,GAAG;YACrE,gFAAgF;YAChF,kFAAkF;YAClF,uEAAuE,CAC1E,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;QAC9C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,SAAS,MAAM,CAAC,GAAY;IAC1B,OAAO,OAAO,WAAW,KAAK,WAAW,IAAI,GAAG,YAAY,WAAW,CAAC,YAAY,CAAC;AACvF,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,kBAA0B,EAAE,KAAc;IACxE,IAAI,yBAAyB,EAAE,CAAC;QAC9B,MAAM,yBAAyB,CAAC;IAClC,CAAC;IAED,yEAAyE;IACzE,wEAAwE;IACxE,uEAAuE;IACvE,wEAAwE;IACxE,IAAI,kBAAkB,KAAK,iBAAiB,EAAE,CAAC;QAC7C,MAAM,WAAW,EAAE,CAAC;QACpB,OAAO;IACT,CAAC;IAED,IAAI,qBAAqB,IAAI,mBAAmB,EAAE,CAAC;QACjD,yBAAyB,GAAG,IAAI,+BAA+B,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QAC5F,eAAe,GAAG,SAAS,CAAC;QAC5B,MAAM,yBAAyB,CAAC;IAClC,CAAC;IAED,qBAAqB,IAAI,CAAC,CAAC;IAC3B,iBAAiB,IAAI,CAAC,CAAC;IACvB,eAAe,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACnD,MAAM,eAAe,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,QAAQ,CAAI,EAA6B;IACtD,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,4EAA4E;QAC5E,mEAAmE;QACnE,2EAA2E;QAC3E,iBAAiB;QACjB,MAAM,gBAAgB,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,WAAW,EAAE,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAc,EACd,UAA4B,EAAE;IAE9B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,UAA4B,EAAE;IAE9B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAChF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,OAAO,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B;IAO5C,OAAO;QACL,iBAAiB;QACjB,mBAAmB;QACnB,iBAAiB,EAAE,mBAAmB;QACtC,qBAAqB;QACrB,QAAQ,EAAE,CAAC,CAAC,yBAAyB;KACtC,CAAC;AACJ,CAAC","sourcesContent":["import type { CompileResult, RenderHtmlResult, ZfbMdWasmOptions } from \"./types.js\";\n\n// The wasm-bindgen-generated glue lives at ./wasm/zfb_md_wasm.{js,d.ts} plus\n// the ./wasm/zfb_md_wasm_bg.wasm binary, produced by `pnpm build`\n// (scripts/build.mjs) -- not checked in, see .gitignore. `pnpm build` must\n// run before this module is imported (or before `tsc` can typecheck it).\ntype WasmGlueModule = typeof import(\"./wasm/zfb_md_wasm.js\");\ntype WasmRawExports = import(\"./wasm/zfb_md_wasm.js\").InitOutput;\n\nconst GLUE_URL = new URL(\"./wasm/zfb_md_wasm.js\", import.meta.url);\nconst WASM_URL = new URL(\"./wasm/zfb_md_wasm_bg.wasm\", import.meta.url);\n\nfunction isNode(): boolean {\n return typeof process !== \"undefined\" && !!process.versions?.node;\n}\n\n// Node's built-in `fetch` does not support `file:` URLs, so the wasm bytes\n// must be read from disk directly there; browsers (and other fetch-capable\n// hosts, e.g. a bundler-served dev server) go through `fetch` against the\n// module-relative URL, which is the standard wasm-bindgen `--target web`\n// browser consumption path.\nasync function loadWasmBytes(): Promise<ArrayBuffer> {\n if (isNode()) {\n const [{ readFile }, { fileURLToPath }] = await Promise.all([\n import(\"node:fs/promises\"),\n import(\"node:url\"),\n ]);\n const buf = await readFile(fileURLToPath(WASM_URL));\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer;\n }\n const res = await fetch(WASM_URL);\n if (!res.ok) {\n throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${res.status} ${res.statusText}`);\n }\n return res.arrayBuffer();\n}\n\nlet compiledModulePromise: Promise<WebAssembly.Module> | undefined;\n\nfunction getCompiledModule(): Promise<WebAssembly.Module> {\n compiledModulePromise ??= loadWasmBytes().then((bytes) => WebAssembly.compile(bytes));\n return compiledModulePromise;\n}\n\nconst MAX_TRAP_RECOVERIES = 16;\n\nlet currentGeneration = 0;\nlet trapRecoveriesStarted = 0;\nlet freshInstanceStarts = 0;\nlet terminalTrapRecoveryError: ZfbMdWasmTrapRecoveryLimitError | undefined;\n\ninterface Instance {\n generation: number;\n glue: WasmGlueModule;\n raw: WasmRawExports;\n}\n\n/**\n * Instantiates a fresh copy of the wasm-bindgen glue module.\n *\n * wasm-bindgen's `--target web` output guards `init`/`initSync` with an\n * \"already initialized\" early return keyed on a module-scope singleton, so\n * recovering from a trapped (poisoned) instance requires a genuinely fresh ES\n * module record -- re-calling `initSync` on the existing module record is a\n * no-op. Re-importing the same specifier with a distinct query string yields a\n * new module record per the ECMAScript module spec (Node and browsers agree on\n * this); the underlying `WebAssembly.Module` (compiled code) is cached and\n * reused via `getCompiledModule`, so this only re-runs cheap instantiation,\n * never a recompile. Module-record growth is bounded by\n * `MAX_TRAP_RECOVERIES`: once that many poisoned instances have been replaced,\n * recovery stops permanently and callers receive\n * `ZfbMdWasmTrapRecoveryLimitError` instead of minting another module record.\n */\nasync function freshInstance(generation: number): Promise<Instance> {\n freshInstanceStarts += 1;\n const [module, glue] = await Promise.all([\n getCompiledModule(),\n import(\n /* @vite-ignore */ `${GLUE_URL.href}?zfbMdWasmGen=${generation}`\n ) as Promise<WasmGlueModule>,\n ]);\n const raw = glue.initSync({ module });\n return { generation, glue, raw };\n}\n\nlet instancePromise: Promise<Instance> | undefined;\n\nfunction getInstance(): Promise<Instance> {\n if (terminalTrapRecoveryError) {\n return Promise.reject(terminalTrapRecoveryError);\n }\n instancePromise ??= freshInstance(currentGeneration);\n return instancePromise;\n}\n\n/**\n * Thrown when a wasm call traps (a Rust panic, or another internal fault\n * that lowers to a wasm trap). This is always a bug, never an expected\n * input-validation failure -- expected failures (parse errors, malformed\n * options JSON, unknown themes, ...) come back as structured\n * `Diagnostic[]` in a normal `CompileResult`/`RenderHtmlResult`, never as a\n * thrown error. See this package's README \"Error / trap / re-init\n * contract\" section.\n *\n * By the time this is thrown, the poisoned instance has already been dropped\n * and a replacement has been instantiated (from the cached compiled module, so\n * no recompile). Concurrent trap reporters for the same poisoned generation\n * wait for that same replacement instead of each starting another one; the\n * next `compile` / `renderHtml` / `version` call transparently uses the fresh\n * instance.\n */\nexport class ZfbMdWasmTrapError extends Error {\n constructor(cause: unknown) {\n super(\n \"zfb-md-wasm: the wasm instance trapped (a Rust panic or internal fault) and has been \" +\n \"automatically re-instantiated. This is always a bug in zfb-md-wasm -- please report it \" +\n \"with the input that triggered it.\",\n );\n this.name = \"ZfbMdWasmTrapError\";\n this.cause = cause;\n }\n}\n\n/**\n * Thrown once this wrapper has already recovered from too many wasm traps in\n * one module lifetime. ES module records cannot be evicted, and wasm-bindgen\n * `--target web` glue cannot be safely re-used after initialization, so the\n * only bounded behavior after repeated poisoning is to stop recovering and ask\n * the host to reload/recreate the JS realm before trying again.\n */\nexport class ZfbMdWasmTrapRecoveryLimitError extends Error {\n constructor(maxRecoveries: number, cause: unknown) {\n super(\n `zfb-md-wasm: wasm trap recovery limit reached after ${maxRecoveries} ` +\n `successful re-instantiations. Further automatic recovery is disabled to avoid ` +\n `unbounded ES module record growth. Reload the JS realm before using zfb-md-wasm ` +\n `again, and please report the input that triggered the repeated traps.`,\n );\n this.name = \"ZfbMdWasmTrapRecoveryLimitError\";\n this.cause = cause;\n }\n}\n\nfunction isTrap(err: unknown): boolean {\n return typeof WebAssembly !== \"undefined\" && err instanceof WebAssembly.RuntimeError;\n}\n\nasync function recoverAfterTrap(observedGeneration: number, cause: unknown): Promise<void> {\n if (terminalTrapRecoveryError) {\n throw terminalTrapRecoveryError;\n }\n\n // CAS-style single-flight: only the reporter that observed the currently\n // active generation advances it and starts a fresh instantiation. Other\n // concurrent reporters trapped on an older instance, so they await the\n // already-started replacement instead of minting another module record.\n if (observedGeneration !== currentGeneration) {\n await getInstance();\n return;\n }\n\n if (trapRecoveriesStarted >= MAX_TRAP_RECOVERIES) {\n terminalTrapRecoveryError = new ZfbMdWasmTrapRecoveryLimitError(MAX_TRAP_RECOVERIES, cause);\n instancePromise = undefined;\n throw terminalTrapRecoveryError;\n }\n\n trapRecoveriesStarted += 1;\n currentGeneration += 1;\n instancePromise = freshInstance(currentGeneration);\n await instancePromise;\n}\n\nasync function callWasm<T>(fn: (instance: Instance) => T): Promise<T> {\n const instance = await getInstance();\n try {\n return fn(instance);\n } catch (err) {\n if (!isTrap(err)) {\n throw err;\n }\n // Drop the poisoned instance and re-instantiate it from the cached compiled\n // module. Concurrent trap reporters single-flight through the same\n // generation advance, so one poisoned generation creates at most one fresh\n // module record.\n await recoverAfterTrap(instance.generation, err);\n throw new ZfbMdWasmTrapError(err);\n }\n}\n\n/**\n * Eagerly loads and instantiates the wasm module. Optional -- `compile` /\n * `renderHtml` / `version` all call this implicitly on first use -- but\n * useful to front-load the one-time fetch/compile cost, e.g. on app\n * startup before the first user-triggered call.\n */\nexport async function init(): Promise<void> {\n await getInstance();\n}\n\n/**\n * Compile MDX source into ES-module JavaScript. Mirrors the crate's\n * `compile(source, options_json) -> string` export, with JSON\n * marshaling handled for you.\n */\nexport async function compile(\n source: string,\n options: ZfbMdWasmOptions = {},\n): Promise<CompileResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) => glue.compile(source, optionsJson));\n return JSON.parse(json) as CompileResult;\n}\n\n/**\n * Render markdown source to an HTML fragment (no SWC at runtime). Mirrors\n * the crate's `renderHtml(source, options_json) -> string` export, with\n * JSON marshaling handled for you.\n */\nexport async function renderHtml(\n source: string,\n options: ZfbMdWasmOptions = {},\n): Promise<RenderHtmlResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) => glue.renderHtml(source, optionsJson));\n return JSON.parse(json) as RenderHtmlResult;\n}\n\n/**\n * Package version for host-side compatibility checks.\n *\n * Release artifacts are stamped with the published package semver at build\n * time; local development builds fall back to the Rust manifest placeholder.\n */\nexport async function version(): Promise<string> {\n return callWasm(({ glue }) => glue.version());\n}\n\n/**\n * @internal Test-only (zfb#1577's trap/re-init test). Forces a genuine wasm\n * trap on the currently-active instance by invoking its raw `compile` export\n * with a garbage return pointer, so the result-doubleword `i32.store` lands\n * outside the instance's linear memory -- a `WebAssembly.RuntimeError:\n * memory access out of bounds`. From the JS host's point of view this is\n * the same exception class a real Rust panic->unreachable trap produces\n * (both are `instanceof WebAssembly.RuntimeError`), which is what\n * `callWasm`'s catch clause keys on -- so this exercises the real\n * catch-and-reinstantiate path without needing to coax an actual Rust panic\n * out of a crate that is deliberately designed to never panic on structured\n * input. This is NOT part of the supported public API: it is a named export\n * of the package entry, so a `import { __forceTrapForTests } from\n * \"@takazudo/zfb-md-wasm\"` does resolve -- but the `__`-prefix marks it\n * internal, and the raw ABI details it pokes at (argument order, retptr\n * convention) are wasm-bindgen internals that can change on a wasm-bindgen\n * version bump. Do not call it outside this package's own tests.\n */\nexport async function __forceTrapForTests(): Promise<void> {\n await callWasm(({ raw }) => {\n raw.compile(0xfffffff0, 0, 0, 0, 0);\n });\n}\n\n/**\n * @internal Test-only observability for the trap recovery contract. This is a\n * named export only so the built-package Vitest suite can assert the public\n * wrapper's concurrency/cap behavior without mocking the wasm-bindgen glue.\n */\nexport function __getTrapRecoveryStateForTests(): {\n currentGeneration: number;\n freshInstanceStarts: number;\n maxTrapRecoveries: number;\n trapRecoveriesStarted: number;\n terminal: boolean;\n} {\n return {\n currentGeneration,\n freshInstanceStarts,\n maxTrapRecoveries: MAX_TRAP_RECOVERIES,\n trapRecoveriesStarted,\n terminal: !!terminalTrapRecoveryError,\n };\n}\n\nexport type {\n CompileResult,\n RenderHtmlResult,\n Diagnostic,\n DiagnosticSource,\n ZfbMdWasmOptions,\n PipelineOptions,\n GfmOptions,\n MarkdownFeaturesConfig,\n JsxRuntime,\n} from \"./types.js\";\n"]}
@@ -0,0 +1,74 @@
1
+ /** `zfb_content::facade::PipelineOptions`'s `gfm` sub-object, verbatim. */
2
+ export interface GfmOptions {
3
+ strikethrough?: boolean;
4
+ table?: boolean;
5
+ autolinkLiteral?: boolean;
6
+ taskListItem?: boolean;
7
+ footnoteDefinition?: boolean;
8
+ }
9
+ /**
10
+ * `MarkdownFeaturesConfig` (see `crates/zfb-md-ast/src/features_config.rs`).
11
+ * Left as an open record here -- the wasm boundary passes it through to the
12
+ * Rust `deny_unknown_fields` deserializer verbatim, which is the
13
+ * authoritative validator for its keys.
14
+ */
15
+ export type MarkdownFeaturesConfig = Record<string, unknown>;
16
+ /** `zfb_content::facade::PipelineOptions`, verbatim. */
17
+ export interface PipelineOptions {
18
+ /** A syntect theme name, or `null` for no syntax highlighting. */
19
+ theme?: string | null;
20
+ gfm?: GfmOptions;
21
+ cjkFriendly?: boolean;
22
+ hardBreaks?: boolean;
23
+ features?: MarkdownFeaturesConfig;
24
+ }
25
+ /** `jsxRuntime` option values. Consumed only by `compile`. */
26
+ export type JsxRuntime = "preact" | "react";
27
+ /**
28
+ * The options JSON document shared by `compile` and `renderHtml`. Every
29
+ * field is optional; `{}` selects all defaults. Unknown fields are
30
+ * rejected by the Rust side at both nesting levels (`deny_unknown_fields`).
31
+ */
32
+ export interface ZfbMdWasmOptions {
33
+ /**
34
+ * Must end in `.md` or `.mdx`. Drives frontmatter dispatch and
35
+ * diagnostics display. Defaults to `<anonymous>.mdx` for `compile` and
36
+ * `<anonymous>.md` for `renderHtml`.
37
+ */
38
+ filename?: string;
39
+ /** Consumed only by `compile`; `renderHtml` accepts and ignores it. */
40
+ jsxRuntime?: JsxRuntime;
41
+ /** Consumed only by `compile`; `renderHtml` accepts and ignores it. */
42
+ development?: boolean;
43
+ pipeline?: PipelineOptions;
44
+ }
45
+ /** `source` values a `Diagnostic` can carry. */
46
+ export type DiagnosticSource = "options" | "frontmatter" | "markdown" | "compile";
47
+ /**
48
+ * One diagnostic entry. `line`/`column` are 1-based. For `"markdown"` /
49
+ * `"frontmatter"` they point into the *original source* (frontmatter lines
50
+ * included). For `"options"` they point into the *options JSON document*.
51
+ * `null` when the underlying error carries no location.
52
+ */
53
+ export interface Diagnostic {
54
+ severity: "error";
55
+ source: DiagnosticSource;
56
+ message: string;
57
+ line: number | null;
58
+ column: number | null;
59
+ }
60
+ /** Result document of `compile`. */
61
+ export interface CompileResult {
62
+ /** ES-module JS source on success, `null` on failure. */
63
+ code: string | null;
64
+ /** Parsed YAML frontmatter as JSON, `null` when absent or unextractable. */
65
+ frontmatter: unknown;
66
+ diagnostics: Diagnostic[];
67
+ }
68
+ /** Result document of `renderHtml`. */
69
+ export interface RenderHtmlResult {
70
+ /** HTML fragment on success, `null` on failure. */
71
+ html: string | null;
72
+ frontmatter: unknown;
73
+ diagnostics: Diagnostic[];
74
+ }
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ // Mirrors the JSON contracts of the `zfb-md-wasm` Rust crate (zfb#1576)
2
+ // literally -- see that crate's `src/lib.rs` rustdoc for the authoritative
3
+ // shape. Keep this file in lock-step with the crate when the crate's
4
+ // options/result shapes change.
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,qEAAqE;AACrE,gCAAgC","sourcesContent":["// Mirrors the JSON contracts of the `zfb-md-wasm` Rust crate (zfb#1576)\n// literally -- see that crate's `src/lib.rs` rustdoc for the authoritative\n// shape. Keep this file in lock-step with the crate when the crate's\n// options/result shapes change.\n\n/** `zfb_content::facade::PipelineOptions`'s `gfm` sub-object, verbatim. */\nexport interface GfmOptions {\n strikethrough?: boolean;\n table?: boolean;\n autolinkLiteral?: boolean;\n taskListItem?: boolean;\n footnoteDefinition?: boolean;\n}\n\n/**\n * `MarkdownFeaturesConfig` (see `crates/zfb-md-ast/src/features_config.rs`).\n * Left as an open record here -- the wasm boundary passes it through to the\n * Rust `deny_unknown_fields` deserializer verbatim, which is the\n * authoritative validator for its keys.\n */\nexport type MarkdownFeaturesConfig = Record<string, unknown>;\n\n/** `zfb_content::facade::PipelineOptions`, verbatim. */\nexport interface PipelineOptions {\n /** A syntect theme name, or `null` for no syntax highlighting. */\n theme?: string | null;\n gfm?: GfmOptions;\n cjkFriendly?: boolean;\n hardBreaks?: boolean;\n features?: MarkdownFeaturesConfig;\n}\n\n/** `jsxRuntime` option values. Consumed only by `compile`. */\nexport type JsxRuntime = \"preact\" | \"react\";\n\n/**\n * The options JSON document shared by `compile` and `renderHtml`. Every\n * field is optional; `{}` selects all defaults. Unknown fields are\n * rejected by the Rust side at both nesting levels (`deny_unknown_fields`).\n */\nexport interface ZfbMdWasmOptions {\n /**\n * Must end in `.md` or `.mdx`. Drives frontmatter dispatch and\n * diagnostics display. Defaults to `<anonymous>.mdx` for `compile` and\n * `<anonymous>.md` for `renderHtml`.\n */\n filename?: string;\n /** Consumed only by `compile`; `renderHtml` accepts and ignores it. */\n jsxRuntime?: JsxRuntime;\n /** Consumed only by `compile`; `renderHtml` accepts and ignores it. */\n development?: boolean;\n pipeline?: PipelineOptions;\n}\n\n/** `source` values a `Diagnostic` can carry. */\nexport type DiagnosticSource = \"options\" | \"frontmatter\" | \"markdown\" | \"compile\";\n\n/**\n * One diagnostic entry. `line`/`column` are 1-based. For `\"markdown\"` /\n * `\"frontmatter\"` they point into the *original source* (frontmatter lines\n * included). For `\"options\"` they point into the *options JSON document*.\n * `null` when the underlying error carries no location.\n */\nexport interface Diagnostic {\n severity: \"error\";\n source: DiagnosticSource;\n message: string;\n line: number | null;\n column: number | null;\n}\n\n/** Result document of `compile`. */\nexport interface CompileResult {\n /** ES-module JS source on success, `null` on failure. */\n code: string | null;\n /** Parsed YAML frontmatter as JSON, `null` when absent or unextractable. */\n frontmatter: unknown;\n diagnostics: Diagnostic[];\n}\n\n/** Result document of `renderHtml`. */\nexport interface RenderHtmlResult {\n /** HTML fragment on success, `null` on failure. */\n html: string | null;\n frontmatter: unknown;\n diagnostics: Diagnostic[];\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Compile MDX source into ES-module JavaScript.
6
+ *
7
+ * Returns a JSON string: `{ "code": string|null, "frontmatter": json,
8
+ * "diagnostics": Diagnostic[] }` — see the crate docs for the options
9
+ * and diagnostics shapes.
10
+ */
11
+ export function compile(source: string, options_json: string): string;
12
+
13
+ /**
14
+ * Render markdown source to an HTML fragment (no SWC at runtime).
15
+ *
16
+ * Returns a JSON string: `{ "html": string|null, "frontmatter": json,
17
+ * "diagnostics": Diagnostic[] }` — see the crate docs for the options
18
+ * and diagnostics shapes. Exported to JS as `renderHtml`.
19
+ */
20
+ export function renderHtml(source: string, options_json: string): string;
21
+
22
+ /**
23
+ * This package's release version, stamped by CI at compile time.
24
+ *
25
+ * Development builds without `ZFB_RELEASE_VERSION` fall back to this crate's
26
+ * manifest version (`CARGO_PKG_VERSION`).
27
+ */
28
+ export function version(): string;
29
+
30
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
31
+
32
+ export interface InitOutput {
33
+ readonly memory: WebAssembly.Memory;
34
+ readonly compile: (a: number, b: number, c: number, d: number, e: number) => void;
35
+ readonly renderHtml: (a: number, b: number, c: number, d: number, e: number) => void;
36
+ readonly version: (a: number) => void;
37
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
38
+ readonly __wbindgen_export: (a: number, b: number) => number;
39
+ readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
40
+ readonly __wbindgen_export3: (a: number, b: number, c: number) => void;
41
+ }
42
+
43
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
44
+
45
+ /**
46
+ * Instantiates the given `module`, which can either be bytes or
47
+ * a precompiled `WebAssembly.Module`.
48
+ *
49
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
50
+ *
51
+ * @returns {InitOutput}
52
+ */
53
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
54
+
55
+ /**
56
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
57
+ * for everything else, calls `WebAssembly.instantiate` directly.
58
+ *
59
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
60
+ *
61
+ * @returns {Promise<InitOutput>}
62
+ */
63
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
@@ -0,0 +1,275 @@
1
+ /* @ts-self-types="./zfb_md_wasm.d.ts" */
2
+
3
+ /**
4
+ * Compile MDX source into ES-module JavaScript.
5
+ *
6
+ * Returns a JSON string: `{ "code": string|null, "frontmatter": json,
7
+ * "diagnostics": Diagnostic[] }` — see the crate docs for the options
8
+ * and diagnostics shapes.
9
+ * @param {string} source
10
+ * @param {string} options_json
11
+ * @returns {string}
12
+ */
13
+ export function compile(source, options_json) {
14
+ let deferred3_0;
15
+ let deferred3_1;
16
+ try {
17
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
18
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_export, wasm.__wbindgen_export2);
19
+ const len0 = WASM_VECTOR_LEN;
20
+ const ptr1 = passStringToWasm0(options_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
21
+ const len1 = WASM_VECTOR_LEN;
22
+ wasm.compile(retptr, ptr0, len0, ptr1, len1);
23
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
24
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
25
+ deferred3_0 = r0;
26
+ deferred3_1 = r1;
27
+ return getStringFromWasm0(r0, r1);
28
+ } finally {
29
+ wasm.__wbindgen_add_to_stack_pointer(16);
30
+ wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Render markdown source to an HTML fragment (no SWC at runtime).
36
+ *
37
+ * Returns a JSON string: `{ "html": string|null, "frontmatter": json,
38
+ * "diagnostics": Diagnostic[] }` — see the crate docs for the options
39
+ * and diagnostics shapes. Exported to JS as `renderHtml`.
40
+ * @param {string} source
41
+ * @param {string} options_json
42
+ * @returns {string}
43
+ */
44
+ export function renderHtml(source, options_json) {
45
+ let deferred3_0;
46
+ let deferred3_1;
47
+ try {
48
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
49
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_export, wasm.__wbindgen_export2);
50
+ const len0 = WASM_VECTOR_LEN;
51
+ const ptr1 = passStringToWasm0(options_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
52
+ const len1 = WASM_VECTOR_LEN;
53
+ wasm.renderHtml(retptr, ptr0, len0, ptr1, len1);
54
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
55
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
56
+ deferred3_0 = r0;
57
+ deferred3_1 = r1;
58
+ return getStringFromWasm0(r0, r1);
59
+ } finally {
60
+ wasm.__wbindgen_add_to_stack_pointer(16);
61
+ wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * This package's release version, stamped by CI at compile time.
67
+ *
68
+ * Development builds without `ZFB_RELEASE_VERSION` fall back to this crate's
69
+ * manifest version (`CARGO_PKG_VERSION`).
70
+ * @returns {string}
71
+ */
72
+ export function version() {
73
+ let deferred1_0;
74
+ let deferred1_1;
75
+ try {
76
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
77
+ wasm.version(retptr);
78
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
79
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
80
+ deferred1_0 = r0;
81
+ deferred1_1 = r1;
82
+ return getStringFromWasm0(r0, r1);
83
+ } finally {
84
+ wasm.__wbindgen_add_to_stack_pointer(16);
85
+ wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
86
+ }
87
+ }
88
+ function __wbg_get_imports() {
89
+ const import0 = {
90
+ __proto__: null,
91
+ };
92
+ return {
93
+ __proto__: null,
94
+ "./zfb_md_wasm_bg.js": import0,
95
+ };
96
+ }
97
+
98
+ let cachedDataViewMemory0 = null;
99
+ function getDataViewMemory0() {
100
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
101
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
102
+ }
103
+ return cachedDataViewMemory0;
104
+ }
105
+
106
+ function getStringFromWasm0(ptr, len) {
107
+ return decodeText(ptr >>> 0, len);
108
+ }
109
+
110
+ let cachedUint8ArrayMemory0 = null;
111
+ function getUint8ArrayMemory0() {
112
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
113
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
114
+ }
115
+ return cachedUint8ArrayMemory0;
116
+ }
117
+
118
+ function passStringToWasm0(arg, malloc, realloc) {
119
+ if (realloc === undefined) {
120
+ const buf = cachedTextEncoder.encode(arg);
121
+ const ptr = malloc(buf.length, 1) >>> 0;
122
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
123
+ WASM_VECTOR_LEN = buf.length;
124
+ return ptr;
125
+ }
126
+
127
+ let len = arg.length;
128
+ let ptr = malloc(len, 1) >>> 0;
129
+
130
+ const mem = getUint8ArrayMemory0();
131
+
132
+ let offset = 0;
133
+
134
+ for (; offset < len; offset++) {
135
+ const code = arg.charCodeAt(offset);
136
+ if (code > 0x7F) break;
137
+ mem[ptr + offset] = code;
138
+ }
139
+ if (offset !== len) {
140
+ if (offset !== 0) {
141
+ arg = arg.slice(offset);
142
+ }
143
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
144
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
145
+ const ret = cachedTextEncoder.encodeInto(arg, view);
146
+
147
+ offset += ret.written;
148
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
149
+ }
150
+
151
+ WASM_VECTOR_LEN = offset;
152
+ return ptr;
153
+ }
154
+
155
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
156
+ cachedTextDecoder.decode();
157
+ const MAX_SAFARI_DECODE_BYTES = 2146435072;
158
+ let numBytesDecoded = 0;
159
+ function decodeText(ptr, len) {
160
+ numBytesDecoded += len;
161
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
162
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
163
+ cachedTextDecoder.decode();
164
+ numBytesDecoded = len;
165
+ }
166
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
167
+ }
168
+
169
+ const cachedTextEncoder = new TextEncoder();
170
+
171
+ if (!('encodeInto' in cachedTextEncoder)) {
172
+ cachedTextEncoder.encodeInto = function (arg, view) {
173
+ const buf = cachedTextEncoder.encode(arg);
174
+ view.set(buf);
175
+ return {
176
+ read: arg.length,
177
+ written: buf.length
178
+ };
179
+ };
180
+ }
181
+
182
+ let WASM_VECTOR_LEN = 0;
183
+
184
+ let wasmModule, wasmInstance, wasm;
185
+ function __wbg_finalize_init(instance, module) {
186
+ wasmInstance = instance;
187
+ wasm = instance.exports;
188
+ wasmModule = module;
189
+ cachedDataViewMemory0 = null;
190
+ cachedUint8ArrayMemory0 = null;
191
+ return wasm;
192
+ }
193
+
194
+ async function __wbg_load(module, imports) {
195
+ if (typeof Response === 'function' && module instanceof Response) {
196
+ if (typeof WebAssembly.instantiateStreaming === 'function') {
197
+ try {
198
+ return await WebAssembly.instantiateStreaming(module, imports);
199
+ } catch (e) {
200
+ const validResponse = module.ok && expectedResponseType(module.type);
201
+
202
+ if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
203
+ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
204
+
205
+ } else { throw e; }
206
+ }
207
+ }
208
+
209
+ const bytes = await module.arrayBuffer();
210
+ return await WebAssembly.instantiate(bytes, imports);
211
+ } else {
212
+ const instance = await WebAssembly.instantiate(module, imports);
213
+
214
+ if (instance instanceof WebAssembly.Instance) {
215
+ return { instance, module };
216
+ } else {
217
+ return instance;
218
+ }
219
+ }
220
+
221
+ function expectedResponseType(type) {
222
+ switch (type) {
223
+ case 'basic': case 'cors': case 'default': return true;
224
+ }
225
+ return false;
226
+ }
227
+ }
228
+
229
+ function initSync(module) {
230
+ if (wasm !== undefined) return wasm;
231
+
232
+
233
+ if (module !== undefined) {
234
+ if (Object.getPrototypeOf(module) === Object.prototype) {
235
+ ({module} = module)
236
+ } else {
237
+ console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
238
+ }
239
+ }
240
+
241
+ const imports = __wbg_get_imports();
242
+ if (!(module instanceof WebAssembly.Module)) {
243
+ module = new WebAssembly.Module(module);
244
+ }
245
+ const instance = new WebAssembly.Instance(module, imports);
246
+ return __wbg_finalize_init(instance, module);
247
+ }
248
+
249
+ async function __wbg_init(module_or_path) {
250
+ if (wasm !== undefined) return wasm;
251
+
252
+
253
+ if (module_or_path !== undefined) {
254
+ if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
255
+ ({module_or_path} = module_or_path)
256
+ } else {
257
+ console.warn('using deprecated parameters for the initialization function; pass a single object instead')
258
+ }
259
+ }
260
+
261
+ if (module_or_path === undefined) {
262
+ module_or_path = new URL('zfb_md_wasm_bg.wasm', import.meta.url);
263
+ }
264
+ const imports = __wbg_get_imports();
265
+
266
+ if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
267
+ module_or_path = fetch(module_or_path);
268
+ }
269
+
270
+ const { instance, module } = await __wbg_load(await module_or_path, imports);
271
+
272
+ return __wbg_finalize_init(instance, module);
273
+ }
274
+
275
+ export { initSync, __wbg_init as default };
Binary file
@@ -0,0 +1,10 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export const memory: WebAssembly.Memory;
4
+ export const compile: (a: number, b: number, c: number, d: number, e: number) => void;
5
+ export const renderHtml: (a: number, b: number, c: number, d: number, e: number) => void;
6
+ export const version: (a: number) => void;
7
+ export const __wbindgen_add_to_stack_pointer: (a: number) => number;
8
+ export const __wbindgen_export: (a: number, b: number) => number;
9
+ export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
10
+ export const __wbindgen_export3: (a: number, b: number, c: number) => void;
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@takazudo/zfb-md-wasm",
3
+ "version": "0.1.0-next.80",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "zfb's md/mdx -> JS/HTML conversion pipeline compiled to WebAssembly, for browser-side dynamic conversion (CMS live preview with parity to zfb's production output).",
7
+ "keywords": [
8
+ "zfb",
9
+ "zfb-md-wasm",
10
+ "markdown",
11
+ "mdx",
12
+ "wasm",
13
+ "webassembly",
14
+ "cms",
15
+ "live-preview"
16
+ ],
17
+ "license": "MIT",
18
+ "author": "Takeshi Takatsudo <takazudo@gmail.com> (https://github.com/Takazudo)",
19
+ "homepage": "https://takazudomodular.com/pj/zudo-front-builder/",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Takazudo/zudo-front-builder.git",
23
+ "directory": "crates/zfb-md-wasm/npm"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/Takazudo/zudo-front-builder/issues"
27
+ },
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "engines": {
45
+ "node": ">=20.0.0",
46
+ "pnpm": ">=10.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.0.0",
50
+ "binaryen": "130.0.0",
51
+ "typescript": "^5.9.0",
52
+ "vitest": "^2.1.9"
53
+ },
54
+ "scripts": {
55
+ "build": "node scripts/build.mjs",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "typecheck": "tsc --noEmit"
59
+ }
60
+ }