diff --git a/.github/workflows/browser-starter.yml b/.github/workflows/browser-starter.yml
index 79caf29..baff938 100644
--- a/.github/workflows/browser-starter.yml
+++ b/.github/workflows/browser-starter.yml
@@ -3,9 +3,9 @@ name: Browser starter
on:
push:
branches: [main]
- paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
+ paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/browser-starter.yml']
pull_request:
- paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', '.github/workflows/browser-starter.yml']
+ paths: ['examples/browser/**', 'tools/check-browser-starter.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/browser-starter.yml']
workflow_dispatch:
permissions:
diff --git a/.github/workflows/react-starter.yml b/.github/workflows/react-starter.yml
index 94ad094..b63b6af 100644
--- a/.github/workflows/react-starter.yml
+++ b/.github/workflows/react-starter.yml
@@ -3,9 +3,9 @@ name: React starter
on:
push:
branches: [main]
- paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
+ paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/react-starter.yml']
pull_request:
- paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
+ paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/react-starter.yml']
workflow_dispatch:
permissions:
diff --git a/.github/workflows/vue-starter.yml b/.github/workflows/vue-starter.yml
index 188a61b..dfe05ae 100644
--- a/.github/workflows/vue-starter.yml
+++ b/.github/workflows/vue-starter.yml
@@ -3,9 +3,9 @@ name: Vue starter
on:
push:
branches: [main]
- paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/vue-starter.yml']
+ paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/vue-starter.yml']
pull_request:
- paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/vue-starter.yml']
+ paths: ['examples/vue/**', 'tools/check-vue-starter.py', 'tools/prepare-vue-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', '.github/workflows/vue-starter.yml']
workflow_dispatch:
permissions:
diff --git a/examples/browser/README.md b/examples/browser/README.md
index 711dd04..6b1ad18 100644
--- a/examples/browser/README.md
+++ b/examples/browser/README.md
@@ -58,8 +58,8 @@ explains CSP, custom assets, structured errors, and browser worker lifecycle lim
## Versions and verification
-The lockfile pins Fullbleed npm 0.3.1 / engine 2.5.10 and Vite 8.3.2.
-The browser SDK is covered by the [versioned release evidence](https://github.com/fullbleed-engine/fullbleed-node/releases/tag/v0.3.1).
+The lockfile pins Fullbleed npm 0.3.2 / engine 2.5.11 and Vite 8.3.2.
+The browser SDK is covered by the [versioned release evidence](https://github.com/fullbleed-engine/fullbleed-node/releases/tag/v0.3.2).
The repository's starter check builds this app, serves it below `/nested/demo/`,
downloads actual PDFs, checks their text and page size, exercises editing and
cancellation, and captures desktop/mobile screenshots. Playwright WebKit is not
diff --git a/examples/browser/package-lock.json b/examples/browser/package-lock.json
index 9861509..30d1398 100644
--- a/examples/browser/package-lock.json
+++ b/examples/browser/package-lock.json
@@ -8,7 +8,7 @@
"name": "fullbleed-browser-starter",
"version": "1.0.0",
"dependencies": {
- "fullbleed": "0.3.1"
+ "fullbleed": "0.3.2"
},
"devDependencies": {
"vite": "8.3.2"
@@ -357,9 +357,9 @@
}
},
"node_modules/fullbleed": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.1.tgz",
- "integrity": "sha512-95CVTnYKduXD5KcEj9MLfzPsqofahZB2BsJ62Dfg0tOTlPFEHT8vx/T3f8xp4VQYkihOPQdMDiWQzYNHJkLpWA==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.2.tgz",
+ "integrity": "sha512-bzbTUn6Hcxu0YZoP38wkrJ/nZ33+rQfZq5itdpQUl7lIO9mR1m9LC5n/2KLxjj+7dAb8IhpYoVTSmbuhQyA5Vg==",
"license": "MIT",
"dependencies": {
"@bjorn3/browser_wasi_shim": "0.4.2"
diff --git a/examples/browser/package.json b/examples/browser/package.json
index 268bb13..f8c317a 100644
--- a/examples/browser/package.json
+++ b/examples/browser/package.json
@@ -10,6 +10,6 @@
"build": "npm run assets && vite build",
"preview": "vite preview --host 127.0.0.1"
},
- "dependencies": { "fullbleed": "0.3.1" },
+ "dependencies": { "fullbleed": "0.3.2" },
"devDependencies": { "vite": "8.3.2" }
}
diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md
index 1e4e607..06dceed 100644
--- a/examples/nextjs/README.md
+++ b/examples/nextjs/README.md
@@ -20,8 +20,8 @@ Open `http://127.0.0.1:3000` and select **Download sample PDF**. The route
IDs return 404. Use `npm run dev` while editing the app. `PORT` changes the port;
`FULLBLEED_HOST` changes the production server's loopback binding.
-The lockfile pins Next.js 16.3.8, React 19.3.0 and Fullbleed Node 0.3.1
-(engine 2.5.10) from npm. The registry package is byte-identical to the
+The lockfile pins Next.js 16.3.8, React 19.3.0 and Fullbleed Node 0.3.2
+(engine 2.5.11) from npm. The registry package is byte-identical to the
verified GitHub release tarball.
Installing this starter needs no Python, Rust, system fonts or browser renderer.
diff --git a/examples/nextjs/package-lock.json b/examples/nextjs/package-lock.json
index 3abb592..2a60492 100644
--- a/examples/nextjs/package-lock.json
+++ b/examples/nextjs/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"next": "16.3.8",
"react": "19.3.0",
"react-dom": "19.3.0"
@@ -787,9 +787,9 @@
}
},
"node_modules/fullbleed": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.1.tgz",
- "integrity": "sha512-95CVTnYKduXD5KcEj9MLfzPsqofahZB2BsJ62Dfg0tOTlPFEHT8vx/T3f8xp4VQYkihOPQdMDiWQzYNHJkLpWA==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.2.tgz",
+ "integrity": "sha512-bzbTUn6Hcxu0YZoP38wkrJ/nZ33+rQfZq5itdpQUl7lIO9mR1m9LC5n/2KLxjj+7dAb8IhpYoVTSmbuhQyA5Vg==",
"license": "MIT",
"dependencies": {
"@bjorn3/browser_wasi_shim": "0.4.2"
diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json
index 039412e..9b8f154 100644
--- a/examples/nextjs/package.json
+++ b/examples/nextjs/package.json
@@ -16,7 +16,7 @@
"verify": "node tools/verify.mjs"
},
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"next": "16.3.8",
"react": "19.3.0",
"react-dom": "19.3.0"
diff --git a/examples/react/README.md b/examples/react/README.md
index 8600205..c9ce011 100644
--- a/examples/react/README.md
+++ b/examples/react/README.md
@@ -35,7 +35,7 @@ renders supplied static HTML/CSS rather than capturing the React page.
## Reuse the hook
Copy `src/usePdfPreview.ts` into your client application, install
-`fullbleed@0.3.1`, and copy the runtime with
+`fullbleed@0.3.2`, and copy the runtime with
`npx fullbleed-browser-assets public/fullbleed`. Keep the document input stable
with `useMemo` so ordinary component updates do not schedule another render:
@@ -94,7 +94,7 @@ WebAssembly, and Web Crypto are required.
## Versions and verification
-The lockfile pins Fullbleed 0.3.1 / engine 2.5.10, React 19.3.0, TypeScript 7.0.2,
+The lockfile pins Fullbleed 0.3.2 / engine 2.5.11, React 19.3.0, TypeScript 7.0.2,
and Vite 8.3.2. No Fullbleed engine or package release is needed for this example.
The repository's `React starter` workflow builds the locked project and checks
diff --git a/examples/react/package-lock.json b/examples/react/package-lock.json
index e44d28c..a624217 100644
--- a/examples/react/package-lock.json
+++ b/examples/react/package-lock.json
@@ -8,7 +8,7 @@
"name": "fullbleed-react-starter",
"version": "1.0.0",
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"react": "19.3.0",
"react-dom": "19.3.0"
},
@@ -760,9 +760,9 @@
}
},
"node_modules/fullbleed": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.1.tgz",
- "integrity": "sha512-95CVTnYKduXD5KcEj9MLfzPsqofahZB2BsJ62Dfg0tOTlPFEHT8vx/T3f8xp4VQYkihOPQdMDiWQzYNHJkLpWA==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.2.tgz",
+ "integrity": "sha512-bzbTUn6Hcxu0YZoP38wkrJ/nZ33+rQfZq5itdpQUl7lIO9mR1m9LC5n/2KLxjj+7dAb8IhpYoVTSmbuhQyA5Vg==",
"license": "MIT",
"dependencies": {
"@bjorn3/browser_wasi_shim": "0.4.2"
diff --git a/examples/react/package.json b/examples/react/package.json
index 08ae981..3c1f80f 100644
--- a/examples/react/package.json
+++ b/examples/react/package.json
@@ -12,7 +12,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"react": "19.3.0",
"react-dom": "19.3.0"
},
diff --git a/examples/vue/README.md b/examples/vue/README.md
index 6ba94ba..da87e3b 100644
--- a/examples/vue/README.md
+++ b/examples/vue/README.md
@@ -38,7 +38,7 @@ Use [supported print CSS](https://docs.fullbleed.dev/css-coverage/).
## Reuse the composable
Copy `src/usePdfPreview.ts` into your client application, install
-`fullbleed@0.3.1`, and copy the runtime with
+`fullbleed@0.3.2`, and copy the runtime with
`npx fullbleed-browser-assets public/fullbleed`:
```vue
@@ -106,7 +106,7 @@ WebAssembly, and Web Crypto are required.
## Versions and verification
-The lockfile pins Fullbleed 0.3.1 / engine 2.5.10, Vue 3.5.43, TypeScript 6.0.3,
+The lockfile pins Fullbleed 0.3.2 / engine 2.5.11, Vue 3.5.43, TypeScript 6.0.3,
and Vite 8.3.2. TypeScript 6 is used because the pinned `vue-tsc` still needs
TypeScript's JavaScript compiler entry; TypeScript 7 does not expose it.
diff --git a/examples/vue/package-lock.json b/examples/vue/package-lock.json
index 7b21840..1a3bbeb 100644
--- a/examples/vue/package-lock.json
+++ b/examples/vue/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"vue": "3.5.43"
},
"devDependencies": {
@@ -605,9 +605,9 @@
}
},
"node_modules/fullbleed": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.1.tgz",
- "integrity": "sha512-95CVTnYKduXD5KcEj9MLfzPsqofahZB2BsJ62Dfg0tOTlPFEHT8vx/T3f8xp4VQYkihOPQdMDiWQzYNHJkLpWA==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/fullbleed/-/fullbleed-0.3.2.tgz",
+ "integrity": "sha512-bzbTUn6Hcxu0YZoP38wkrJ/nZ33+rQfZq5itdpQUl7lIO9mR1m9LC5n/2KLxjj+7dAb8IhpYoVTSmbuhQyA5Vg==",
"license": "MIT",
"dependencies": {
"@bjorn3/browser_wasi_shim": "0.4.2"
diff --git a/examples/vue/package.json b/examples/vue/package.json
index 45fbe3a..389f6ad 100644
--- a/examples/vue/package.json
+++ b/examples/vue/package.json
@@ -15,7 +15,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
- "fullbleed": "0.3.1",
+ "fullbleed": "0.3.2",
"vue": "3.5.43"
},
"devDependencies": {
diff --git a/tools/check-browser-starter.py b/tools/check-browser-starter.py
index 105f784..89f06dd 100644
--- a/tools/check-browser-starter.py
+++ b/tools/check-browser-starter.py
@@ -11,6 +11,7 @@
from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
+from starter_standard_fonts import verify_edited_preview
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist',type=Path,required=True)
@@ -88,11 +89,12 @@ def download(name,pages,text):
check('customized bytes differ from the default invoice',documents[-1]['sha256']!=documents[0]['sha256'])
page.locator('#template-editor summary').click()
page.locator('#html-source').fill('
Edited template
{{customer}} / {{reference}}
')
- page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
+ page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
page.locator('#render-source').click();ready()
path=download('edited',1,['Edited template','Birch & ','TEST-2042'])
check('edited print CSS changes the PDF page size',abs(float(PdfReader(path).pages[0].mediabox.width)-419.52756)<1)
check('document script does not run on the page',page.evaluate('window.documentScriptExecuted !== true'))
+ verify_edited_preview(page, path, out, check)
page.screenshot(path=str(out/'edited-template.png'),full_page=True)
page.locator('#kind').select_option('report');page.locator('#generate').click();ready()
download('report',3,['COMMON','1,240','420,000.00'])
diff --git a/tools/check-react-starter.py b/tools/check-react-starter.py
index 54f47be..68a7cd1 100644
--- a/tools/check-react-starter.py
+++ b/tools/check-react-starter.py
@@ -12,6 +12,7 @@
from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
+from starter_standard_fonts import verify_edited_preview
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist', type=Path, required=True)
@@ -174,11 +175,12 @@ def download(name, count, text):
page.locator('#template-editor summary').click()
page.locator('#html-source').fill('React template
{{customer}} / {{reference}}
')
- page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
+ page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
ready()
edited = download('edited-template', 1, ['React template', 'Birch & ', 'REACT-2042'])
check('edited print CSS changes the page size', abs(float(PdfReader(edited).pages[0].mediabox.width) - 419.52756) < 1)
check('document scripts are not executed in the app', page.evaluate('window.documentScriptExecuted !== true'))
+ verify_edited_preview(page, edited, out, check)
page.screenshot(path=str(out / 'edited-template.png'), full_page=True)
page.locator('#kind').select_option('report')
ready()
diff --git a/tools/check-vue-starter.py b/tools/check-vue-starter.py
index 07248e0..577ffe0 100644
--- a/tools/check-vue-starter.py
+++ b/tools/check-vue-starter.py
@@ -12,6 +12,7 @@
from playwright.sync_api import sync_playwright, expect
from pypdf import PdfReader
+from starter_standard_fonts import verify_edited_preview
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--dist', type=Path, required=True)
@@ -184,11 +185,12 @@ def previews_match(name, count):
page.locator('#template-editor summary').click()
page.locator('#html-source').fill('Vue template
{{customer}} / {{reference}}
')
- page.locator('#css-source').fill('@page { size: A5; margin: 15mm } h1 { color: {{ink}}; font-size: 24pt }')
+ page.locator('#css-source').fill('@page { size: A5; margin: 15mm } body { font-family: Helvetica } h1 { color: {{ink}}; font-size: 24pt }')
ready()
edited = download('edited-template', 1, ['Vue template', 'Birch & ', 'VUE-2042'])
check('edited print CSS changes the page size', abs(float(PdfReader(edited).pages[0].mediabox.width) - 419.52756) < 1)
check('document scripts are not executed in the app', page.evaluate('window.documentScriptExecuted !== true'))
+ verify_edited_preview(page, edited, out, check)
page.screenshot(path=str(out / 'edited-template.png'), full_page=True)
page.locator('#kind').select_option('report')
ready()
diff --git a/tools/starter_standard_fonts.py b/tools/starter_standard_fonts.py
new file mode 100644
index 0000000..b56e0dc
--- /dev/null
+++ b/tools/starter_standard_fonts.py
@@ -0,0 +1,37 @@
+"""Check that a pasted Helvetica template has visible browser preview text."""
+import base64
+import hashlib
+import json
+
+from playwright.sync_api import expect
+from pypdf import PdfReader
+
+
+def verify_edited_preview(page, pdf_path, out, check):
+ pdf = PdfReader(pdf_path)
+ fonts = [item.get_object() for item in pdf.pages[0]['/Resources']['/Font'].values()]
+ names = sorted(str(font.get('/BaseFont')) for font in fonts)
+ check('edited template uses unembedded standard Helvetica faces',
+ bool(fonts) and all(name in ['/Helvetica', '/Helvetica-Bold'] for name in names)
+ and all('/FontDescriptor' not in font for font in fonts))
+ image = page.locator('#preview img')
+ expect(image).to_have_count(1)
+ result = image.evaluate('''async image => {
+ await image.decode();
+ const canvas = document.createElement('canvas');
+ canvas.width = image.naturalWidth; canvas.height = image.naturalHeight;
+ const context = canvas.getContext('2d'); context.drawImage(image, 0, 0);
+ const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data;
+ let ink = 0;
+ for (let i = 0; i < pixels.length; i += 4)
+ if (pixels[i + 3] > 0 && Math.min(pixels[i], pixels[i + 1], pixels[i + 2]) < 245) ink++;
+ return {width: canvas.width, height: canvas.height, inkPixels: ink,
+ png: canvas.toDataURL('image/png').split(',')[1]};
+ }''')
+ png = base64.b64decode(result.pop('png'))
+ (out / 'edited-preview-decoded.png').write_bytes(png)
+ result.update(fonts=names, pdfSha256=hashlib.sha256(pdf_path.read_bytes()).hexdigest(),
+ decodedPreviewSha256=hashlib.sha256(png).hexdigest())
+ (out / 'edited-preview-verification.json').write_text(
+ json.dumps(result, indent=2) + '\n', encoding='utf-8', newline='\n')
+ check('edited standard-font preview contains visible text', result['inkPixels'] > 500)
diff --git a/verification/starters-032.json b/verification/starters-032.json
new file mode 100644
index 0000000..d8e25eb
--- /dev/null
+++ b/verification/starters-032.json
@@ -0,0 +1,583 @@
+{
+ "ok": true,
+ "checked_at": "2026-10-07T03:56:27.779456+00:00",
+ "package_version": "0.3.2",
+ "engine_version": "2.5.11",
+ "npm_integrity": "sha512-bzbTUn6Hcxu0YZoP38wkrJ/nZ33+rQfZq5itdpQUl7lIO9mR1m9LC5n/2KLxjj+7dAb8IhpYoVTSmbuhQyA5Vg==",
+ "source_files": {
+ "examples/browser/.gitignore": "c023fec2bf12476cb47709e859f1fbb785aeb1e03281e52141e0bee1ee7dad72",
+ "examples/browser/LICENSE": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9",
+ "examples/browser/README.md": "6b5bec007619b8d1776dc668b4f16d2204e4ad9f92dd9b7e38352e534f1bcd13",
+ "examples/browser/index.html": "79baecb0b24716e78799893fc0076d88b2c026354f9f31acdf799c8f305c954b",
+ "examples/browser/package-lock.json": "b1f81db73d867f963443fbc2415f352297ee4d9c962f0ba601d9cdc3457e4bd5",
+ "examples/browser/package.json": "6dbc4b39b8daa889493125b5d1c091941dd8f994a26274153ffc4298e532cd18",
+ "examples/browser/public/favicon.svg": "3eed6dcf1013e48bc64886fed4cf815c609ed06b404742bdacdc44c1df8d3118",
+ "examples/browser/src/invoice.css": "0b310ce0f2964fd6b9b68e4afafa9307b4ff8c6b2f1fc753367dc755620ce427",
+ "examples/browser/src/invoice.html": "1f065ad6694021649e61b3f666cba6dc01aacdcbd8e80e97425b383e60d7d2fe",
+ "examples/browser/src/main.js": "10312b7064b0b2c19d97194b3953f639cc1f6842c35acf85f5e9543fa9c1d27f",
+ "examples/browser/src/report.css": "519591ae75c239ec602324f008dd99108ebeae0b9306df7215fa5f0442905473",
+ "examples/browser/src/report.html": "46b0e975d43914c8b4492218feae09ea0ad684091a9f9ee3b18f66a87202e521",
+ "examples/browser/src/style.css": "5005045f66a84a038a0f7a0f7e15b8b5e9eac2489f9717d4a581fec5075bf6f4",
+ "examples/browser/vite.config.js": "ec725922705a3c5ba2df191a564c54c0cac6e8997ecd3d00b8a42c647bec8e74",
+ "examples/nextjs/.gitignore": "b4abc38d76926640e3f2cbe3bf25c8f5aec6cc43dd978c48d3ad5cc20d964f66",
+ "examples/nextjs/LICENSE": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9",
+ "examples/nextjs/README.md": "96de8ae1580d61c0c34274c3fafe70a81338aa2bd927d22900874c367ae9158c",
+ "examples/nextjs/app/api/invoices/[id]/route.js": "d6348ce64b44feac7010e21fb0f55723e0858c7867222ae831c45d3440a3b052",
+ "examples/nextjs/app/globals.css": "02e0b545a14eaf15e6d3d8fc8c0e8c0f1c5ce5c1b8dc461b4343d330f291233e",
+ "examples/nextjs/app/layout.js": "2df89bff0d2f002b432b3fdb73fd64edf59c57641d9ea6df3f0051cf2329cf27",
+ "examples/nextjs/app/page.js": "24c7996f8172992f925cdb1e47455d0095397d0832f747c90ecb409f2a1d460c",
+ "examples/nextjs/lib/invoice.mjs": "fd87dd082fa19659ec572d97255c1902e1e728e43ebbc3e0ade9891fa7736cf2",
+ "examples/nextjs/next.config.mjs": "e93613419f5aaa6264f0405e5afbb6f5eea4c956e42e7ebf30a6c85a4d9d89b3",
+ "examples/nextjs/package-lock.json": "f5d9b10c7546f8df1c08b0cdb489647289f91b1c89d065c3f1a133478d5fcfbf",
+ "examples/nextjs/package.json": "e31ff035bd4d340ca73a5ee95319ee85ec4ae1d3651a6193bf2a6f0174a39e9c",
+ "examples/nextjs/public/invoice.png": "72420749ccc3deaa8025daf7baffcdf79be716b6e8e93ed605effebed01e86c7",
+ "examples/nextjs/templates/invoice.css": "cda22b39ab1f7a1eeac7cd76d162b05cd3568c2430c099123ba30a2fbd58a296",
+ "examples/nextjs/templates/invoice.html": "d9a5b1950ddab61f08628b0fcc773cb36f05d9401bb3d87273bca02452b1835e",
+ "examples/nextjs/tools/copy-public.mjs": "25005e2612d90ed5127f268144c45bcd259ceb1e68c832723549e57a06b8d3dd",
+ "examples/nextjs/tools/render-preview.mjs": "0e5f1c0e49e2ec35a77eec460d735a1228ae82a30b86a7eb687af4056c23522b",
+ "examples/nextjs/tools/start.mjs": "4c185a0801ca4f89a30a0c1e75710f1c22c84463d29477900162b05a8bc52eab",
+ "examples/nextjs/tools/verify.mjs": "54623914ff28a47ea63a553d063ac50a206df0424270dacb3f3a99054982a888",
+ "examples/react/.gitignore": "25d896ffa2be7ed008ad87c3956a0a88f54167d04a7cef5cf774d6989d3b5a7b",
+ "examples/react/LICENSE": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9",
+ "examples/react/README.md": "9b20e8bd9a23d22e4733012b94cc8ab645fa08cb70327aa2ab1027e0b85a4081",
+ "examples/react/index.html": "cadf48a3476088b2efbe9012bb4b2d21fa24f1dd3e99f974f444c88fbf1ee165",
+ "examples/react/package-lock.json": "c28b7d70a675e27cfcf0c68c06ca1ecc7f640f43f35261f20274f02e48947ce3",
+ "examples/react/package.json": "ef11b5673e83c8c9222b7b7b6f2ea7c9ca7f0c99e809eb675d7c08cc6173dd14",
+ "examples/react/public/favicon.svg": "3eed6dcf1013e48bc64886fed4cf815c609ed06b404742bdacdc44c1df8d3118",
+ "examples/react/src/App.tsx": "5dbcd45d570a3e4d69f12237d252e30afe71f52cb8411c3ffe094dc72f559b9f",
+ "examples/react/src/invoice.css": "0b310ce0f2964fd6b9b68e4afafa9307b4ff8c6b2f1fc753367dc755620ce427",
+ "examples/react/src/invoice.html": "1f065ad6694021649e61b3f666cba6dc01aacdcbd8e80e97425b383e60d7d2fe",
+ "examples/react/src/main.tsx": "f8f523ef5fc0b1e4095830466e0caa648c01f7b42036694b800c25d43d5e2c84",
+ "examples/react/src/report.css": "519591ae75c239ec602324f008dd99108ebeae0b9306df7215fa5f0442905473",
+ "examples/react/src/report.html": "46b0e975d43914c8b4492218feae09ea0ad684091a9f9ee3b18f66a87202e521",
+ "examples/react/src/style.css": "940621b782b9e39fc48f5a257c119763e11cedd158fb570a82f483ea20aa9951",
+ "examples/react/src/usePdfPreview.ts": "587e8702bd32f42abe2720f9d29dcc68e6d75aba8cb3ceacbdfcf2b583f378cc",
+ "examples/react/tsconfig.json": "382c3c4a68901c016c3038c9653f1adaf145738c68de40a34821181ead2d7289",
+ "examples/react/vite.config.js": "4b74d0189e7f58a4c75d168367e9b8a53000555dd3de594c497f70fb5e02fff0",
+ "examples/vue/.gitignore": "c023fec2bf12476cb47709e859f1fbb785aeb1e03281e52141e0bee1ee7dad72",
+ "examples/vue/LICENSE": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9",
+ "examples/vue/README.md": "b157b91e6a93417c279b565f2b5ecb96c8299c3e0524b7531704348ba0bbe4e8",
+ "examples/vue/index.html": "30d46eb1b5147bd3d9c580e7adaed5b5202f9b97ab598e189efda7c3d2d405be",
+ "examples/vue/package-lock.json": "72269d20bb6fabb34779eac61bea9d2b9eaadf5afff09e5e968053c2418fe3c4",
+ "examples/vue/package.json": "262a2d42ec0067ff76d4c80398b01de7c2fc84c455f66fd7bb62517b30669c90",
+ "examples/vue/public/favicon.svg": "3eed6dcf1013e48bc64886fed4cf815c609ed06b404742bdacdc44c1df8d3118",
+ "examples/vue/src/App.vue": "0afa15196ccedd0b0f91293cc5e362799149a2752c59deaa377f4533825ea2af",
+ "examples/vue/src/DocumentEditor.vue": "a3f3f8096d194e1eed6cd0cf2fb0a0606a04d40823ff7b794b9b1a0dbfd8c28a",
+ "examples/vue/src/invoice.css": "0b310ce0f2964fd6b9b68e4afafa9307b4ff8c6b2f1fc753367dc755620ce427",
+ "examples/vue/src/invoice.html": "1f065ad6694021649e61b3f666cba6dc01aacdcbd8e80e97425b383e60d7d2fe",
+ "examples/vue/src/main.ts": "d6d44427a8c817da9a1a8f6809a0d6e1fff87f73ee695442f174fca32db8e6f6",
+ "examples/vue/src/report.css": "519591ae75c239ec602324f008dd99108ebeae0b9306df7215fa5f0442905473",
+ "examples/vue/src/report.html": "46b0e975d43914c8b4492218feae09ea0ad684091a9f9ee3b18f66a87202e521",
+ "examples/vue/src/style.css": "940621b782b9e39fc48f5a257c119763e11cedd158fb570a82f483ea20aa9951",
+ "examples/vue/src/usePdfPreview.ts": "cc0c57d0d8733ca4bfe257ce5fc08acfb78693ca63a4298c169dc6608a89c893",
+ "examples/vue/tsconfig.json": "0d0065f1a795408837fd69a8ec9e5c5e7c6eb23b304d9710c6dbfaa96573a453",
+ "examples/vue/vite.config.js": "dfcc6c065fb26ada03802def672108735f4c2c35e0f30d11965ddfa796b8a0c0"
+ },
+ "browsers": [
+ {
+ "starter": "browser",
+ "browser_engine": "chrome",
+ "browser": "154.0.8037.98",
+ "checks": [
+ "runtime resolves beneath a nested deployment path",
+ "bundled UI font loads",
+ "invoice: download has the expected pages and text",
+ "1440px: no horizontal overflow",
+ "390px: no horizontal overflow",
+ "320px: no horizontal overflow",
+ "color-only: download has the expected pages and text",
+ "changing only ink color changes the rendered PDF",
+ "changed inputs clear the previous preview",
+ "customized: download has the expected pages and text",
+ "customized bytes differ from the default invoice",
+ "edited: download has the expected pages and text",
+ "edited print CSS changes the PDF page size",
+ "document script does not run on the page",
+ "edited template uses unembedded standard Helvetica faces",
+ "edited standard-font preview contains visible text",
+ "report: download has the expected pages and text",
+ "multi-page preview displays all report pages",
+ "each design preserves its template edits",
+ "reset restores only the selected template",
+ "invalid input displays an error without a stale download",
+ "cancellation restores controls without a stale download",
+ "recovered: download has the expected pages and text",
+ "no document uploads or cross-origin runtime requests",
+ "no uncaught page errors"
+ ],
+ "documents": [
+ {
+ "name": "invoice",
+ "filename": "invoice-NS-1042.pdf",
+ "pages": 1,
+ "sha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408"
+ },
+ {
+ "name": "color-only",
+ "filename": "invoice-NS-1042.pdf",
+ "pages": 1,
+ "sha256": "a2b69ba41f7ad4c9bcbd38d803a46e70f0b9ccacd90ffbab5b0f7d87ffca5e4b"
+ },
+ {
+ "name": "customized",
+ "filename": "invoice-TEST-2042.pdf",
+ "pages": 1,
+ "sha256": "31ff314b44a6f13534cc383e0cf24929f0695c1dd3d84769dedf8828c6486a93"
+ },
+ {
+ "name": "edited",
+ "filename": "invoice-TEST-2042.pdf",
+ "pages": 1,
+ "sha256": "f5c6c1927fc6c636cee592a3ebc7bb7b3e91dd645ab34a9370590571506d278b"
+ },
+ {
+ "name": "report",
+ "filename": "community-report.pdf",
+ "pages": 3,
+ "sha256": "139df89824e6a73c13ff62f1237bb8eb9f71e790dcbd051b1bb713ed2bc47802"
+ },
+ {
+ "name": "recovered",
+ "filename": "invoice-TEST-2042.pdf",
+ "pages": 1,
+ "sha256": "31ff314b44a6f13534cc383e0cf24929f0695c1dd3d84769dedf8828c6486a93"
+ }
+ ],
+ "edited_preview": {
+ "width": 559,
+ "height": 794,
+ "inkPixels": 5455,
+ "fonts": [
+ "/Helvetica",
+ "/Helvetica-Bold"
+ ],
+ "pdfSha256": "f5c6c1927fc6c636cee592a3ebc7bb7b3e91dd645ab34a9370590571506d278b",
+ "decodedPreviewSha256": "d83c1c009ce8df726dd205d1efaf9f4f623192b8d7386adad0612901664924bd"
+ }
+ },
+ {
+ "starter": "react",
+ "browser_engine": "chrome",
+ "browser": "154.0.8037.98",
+ "checks": [
+ "invoice: real PDF has expected pages and text",
+ "invoice matches the installed Node package",
+ "initial automatic render starts one worker",
+ "settled render terminates its worker",
+ "runtime resolves beneath a nested deployment path",
+ "bundled interface font loads",
+ "1440px: no horizontal overflow",
+ "390px: no horizontal overflow",
+ "320px: no horizontal overflow",
+ "on-demand mode does not schedule a render after edits",
+ "manual: real PDF has expected pages and text",
+ "automatic rendering can be re-enabled",
+ "edits immediately hide the old preview and release its URLs",
+ "latest-input: real PDF has expected pages and text",
+ "superseded render cannot overwrite the latest input",
+ "a burst of edits is debounced into one new worker",
+ "replacement does not overlap active worker handles",
+ "only current PDF and preview URLs remain",
+ "edited-template: real PDF has expected pages and text",
+ "edited print CSS changes the page size",
+ "document scripts are not executed in the app",
+ "edited template uses unembedded standard Helvetica faces",
+ "edited standard-font preview contains visible text",
+ "report: real PDF has expected pages and text",
+ "report matches the installed Node package",
+ "all report pages have previews",
+ "each design preserves its template edits",
+ "invalid input cannot leave an old download",
+ "cancel removes the worker and stale download",
+ "recovered: real PDF has expected pages and text",
+ "unmount releases ready PDF and preview URLs",
+ "unmount aborts a pending worker",
+ "remounted: real PDF has expected pages and text",
+ "remount starts from clean state and recovers",
+ "all created URLs and workers are released at final unmount",
+ "no uploads or cross-origin document requests",
+ "no uncaught page errors"
+ ],
+ "documents": [
+ {
+ "name": "invoice",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408"
+ },
+ {
+ "name": "manual",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "1df9b1852ad96e07d00520d190140044f528b14047df6e30432e91a512be8883"
+ },
+ {
+ "name": "latest-input",
+ "pages": 1,
+ "filename": "invoice-REACT-2042.pdf",
+ "sha256": "fccdad8d56abef7206e5efefc716af2c44c717c0e1c3619827c6d27931ed76be"
+ },
+ {
+ "name": "edited-template",
+ "pages": 1,
+ "filename": "invoice-REACT-2042.pdf",
+ "sha256": "f3d3f491c3c38a71f873f2a46de9fd6434ade3d938eb1a3d15a40ddc9d414c52"
+ },
+ {
+ "name": "report",
+ "pages": 3,
+ "filename": "community-report.pdf",
+ "sha256": "139df89824e6a73c13ff62f1237bb8eb9f71e790dcbd051b1bb713ed2bc47802"
+ },
+ {
+ "name": "recovered",
+ "pages": 1,
+ "filename": "invoice-REACT-2042.pdf",
+ "sha256": "fccdad8d56abef7206e5efefc716af2c44c717c0e1c3619827c6d27931ed76be"
+ },
+ {
+ "name": "remounted",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408"
+ }
+ ],
+ "edited_preview": {
+ "width": 559,
+ "height": 794,
+ "inkPixels": 5323,
+ "fonts": [
+ "/Helvetica",
+ "/Helvetica-Bold"
+ ],
+ "pdfSha256": "f3d3f491c3c38a71f873f2a46de9fd6434ade3d938eb1a3d15a40ddc9d414c52",
+ "decodedPreviewSha256": "ddab1397eb54c55fdc300b140c91ceb169c18c2b2128f3648f50eeefd1156db3"
+ }
+ },
+ {
+ "starter": "vue",
+ "browser_engine": "chrome",
+ "browser": "154.0.8037.98",
+ "checks": [
+ "invoice: real PDF has expected pages and text",
+ "invoice matches the installed Node package",
+ "invoice: displayed previews match the installed Node package",
+ "initial automatic render starts one worker",
+ "settled render terminates its worker",
+ "runtime resolves beneath a nested deployment path",
+ "bundled interface font loads",
+ "1440px: no horizontal overflow",
+ "390px: no horizontal overflow",
+ "320px: no horizontal overflow",
+ "on-demand mode does not schedule a render after edits",
+ "manual: real PDF has expected pages and text",
+ "automatic rendering can be re-enabled",
+ "edits immediately hide the old preview and release its URLs",
+ "latest-input: real PDF has expected pages and text",
+ "superseded render cannot overwrite the latest input",
+ "a burst of edits is debounced into one new worker",
+ "replacement does not overlap active worker handles",
+ "only current PDF and preview URLs remain",
+ "edited-template: real PDF has expected pages and text",
+ "edited print CSS changes the page size",
+ "document scripts are not executed in the app",
+ "edited template uses unembedded standard Helvetica faces",
+ "edited standard-font preview contains visible text",
+ "report: real PDF has expected pages and text",
+ "report matches the installed Node package",
+ "all report pages have previews",
+ "report: displayed previews match the installed Node package",
+ "each design preserves its template edits",
+ "invalid input cannot leave an old download",
+ "cancelling a queued preview prevents its worker from starting",
+ "runtime load failure clears previews and releases the worker",
+ "outage-recovery: real PDF has expected pages and text",
+ "cancel removes the worker and stale download",
+ "recovered: real PDF has expected pages and text",
+ "unmount releases ready PDF and preview URLs",
+ "unmount aborts a pending worker",
+ "remounted: real PDF has expected pages and text",
+ "remount starts from clean state and recovers",
+ "all created URLs and workers are released at final unmount",
+ "no uploads or cross-origin document requests",
+ "no uncaught page errors"
+ ],
+ "documents": [
+ {
+ "name": "invoice",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408"
+ },
+ {
+ "name": "manual",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "1df9b1852ad96e07d00520d190140044f528b14047df6e30432e91a512be8883"
+ },
+ {
+ "name": "latest-input",
+ "pages": 1,
+ "filename": "invoice-VUE-2042.pdf",
+ "sha256": "f63a42a366b08f4f054cca5594dbcd22510455ddfe2e9129623facfb8371e8ee"
+ },
+ {
+ "name": "edited-template",
+ "pages": 1,
+ "filename": "invoice-VUE-2042.pdf",
+ "sha256": "e23fdd55b186015b830a970af7842a5bd19e3bdc321505ff83fa11c4991fa63c"
+ },
+ {
+ "name": "report",
+ "pages": 3,
+ "filename": "community-report.pdf",
+ "sha256": "139df89824e6a73c13ff62f1237bb8eb9f71e790dcbd051b1bb713ed2bc47802"
+ },
+ {
+ "name": "outage-recovery",
+ "pages": 1,
+ "filename": "invoice-VUE-2042.pdf",
+ "sha256": "f63a42a366b08f4f054cca5594dbcd22510455ddfe2e9129623facfb8371e8ee"
+ },
+ {
+ "name": "recovered",
+ "pages": 1,
+ "filename": "invoice-VUE-2042.pdf",
+ "sha256": "f63a42a366b08f4f054cca5594dbcd22510455ddfe2e9129623facfb8371e8ee"
+ },
+ {
+ "name": "remounted",
+ "pages": 1,
+ "filename": "invoice-NS-1042.pdf",
+ "sha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408"
+ }
+ ],
+ "edited_preview": {
+ "width": 559,
+ "height": 794,
+ "inkPixels": 4790,
+ "fonts": [
+ "/Helvetica",
+ "/Helvetica-Bold"
+ ],
+ "pdfSha256": "e23fdd55b186015b830a970af7842a5bd19e3bdc321505ff83fa11c4991fa63c",
+ "decodedPreviewSha256": "5e7c7d8cc22b240314f2ffa6860b866e9496ddbcdf951cd5a8d5569a626e09c9"
+ }
+ }
+ ],
+ "unchanged_reviewed_031_documents": [
+ "browser/invoice",
+ "browser/color-only",
+ "browser/customized",
+ "browser/report",
+ "browser/recovered",
+ "react/invoice",
+ "react/manual",
+ "react/latest-input",
+ "react/report",
+ "react/recovered",
+ "react/remounted"
+ ],
+ "public_031_negative_control": {
+ "ok": true,
+ "checkedAt": "2026-10-07T03:50:38.760124+00:00",
+ "expectedBlank": true,
+ "records": [
+ {
+ "flavor": "browser",
+ "url": "https://docs.fullbleed.dev/assets/browser-demo/",
+ "package": "0.3.1",
+ "engine": "2.5.10",
+ "pdfSha256": "1b8c17ab56c4dbd073b4b108528d617d272c735ffd14056cac2cd5394e988cbc",
+ "pngSha256": "2cc00648b3ae8e45f95e8a92a750972137f2a268329c734b2f26375435a536dc",
+ "fonts": [
+ "/Courier",
+ "/Courier-Bold",
+ "/Courier-BoldOblique",
+ "/Courier-Oblique",
+ "/Helvetica",
+ "/Helvetica-Bold",
+ "/Helvetica-BoldOblique",
+ "/Helvetica-Oblique",
+ "/Times-Bold",
+ "/Times-BoldItalic",
+ "/Times-Italic",
+ "/Times-Roman"
+ ],
+ "allFontsUnembedded": true,
+ "textLabels": 12,
+ "width": 559,
+ "height": 794,
+ "inkPixels": 0
+ },
+ {
+ "flavor": "react",
+ "url": "https://docs.fullbleed.dev/assets/react-demo/",
+ "package": "0.3.1",
+ "engine": "2.5.10",
+ "pdfSha256": "1b8c17ab56c4dbd073b4b108528d617d272c735ffd14056cac2cd5394e988cbc",
+ "pngSha256": "2cc00648b3ae8e45f95e8a92a750972137f2a268329c734b2f26375435a536dc",
+ "fonts": [
+ "/Courier",
+ "/Courier-Bold",
+ "/Courier-BoldOblique",
+ "/Courier-Oblique",
+ "/Helvetica",
+ "/Helvetica-Bold",
+ "/Helvetica-BoldOblique",
+ "/Helvetica-Oblique",
+ "/Times-Bold",
+ "/Times-BoldItalic",
+ "/Times-Italic",
+ "/Times-Roman"
+ ],
+ "allFontsUnembedded": true,
+ "textLabels": 12,
+ "width": 559,
+ "height": 794,
+ "inkPixels": 0
+ },
+ {
+ "flavor": "vue",
+ "url": "https://docs.fullbleed.dev/assets/vue-demo/",
+ "package": "0.3.1",
+ "engine": "2.5.10",
+ "pdfSha256": "1b8c17ab56c4dbd073b4b108528d617d272c735ffd14056cac2cd5394e988cbc",
+ "pngSha256": "2cc00648b3ae8e45f95e8a92a750972137f2a268329c734b2f26375435a536dc",
+ "fonts": [
+ "/Courier",
+ "/Courier-Bold",
+ "/Courier-BoldOblique",
+ "/Courier-Oblique",
+ "/Helvetica",
+ "/Helvetica-Bold",
+ "/Helvetica-BoldOblique",
+ "/Helvetica-Oblique",
+ "/Times-Bold",
+ "/Times-BoldItalic",
+ "/Times-Italic",
+ "/Times-Roman"
+ ],
+ "allFontsUnembedded": true,
+ "textLabels": 12,
+ "width": 559,
+ "height": 794,
+ "inkPixels": 0
+ }
+ ]
+ },
+ "new_preview_gate_rejects_public_031": {
+ "ok": true,
+ "expectedFailure": true,
+ "package": "0.3.1",
+ "checks": [
+ {
+ "name": "edited template uses unembedded standard Helvetica faces",
+ "passed": true
+ },
+ {
+ "name": "edited standard-font preview contains visible text",
+ "passed": false
+ }
+ ]
+ },
+ "nextjs": {
+ "checkedAt": "2026-10-07T03:54:37.205Z",
+ "node": "v22.23.3",
+ "platform": "win32",
+ "next": "16.3.8",
+ "nodePackage": "0.3.2",
+ "engineVersion": "2.5.11",
+ "isolation": "process",
+ "packageLockSha256": "f5d9b10c7546f8df1c08b0cdb489647289f91b1c89d065c3f1a133478d5fcfbf",
+ "base": "http://127.0.0.1:3427",
+ "pages": 1,
+ "pdfSha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408",
+ "burst": [
+ 200,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503,
+ 503
+ ],
+ "checks": [
+ {
+ "name": "standalone artifact is isolated from the source application",
+ "passed": true
+ },
+ {
+ "name": "standalone tracing retains the verified engine and all bundled fonts",
+ "passed": true
+ },
+ {
+ "name": "isolated production server starts on loopback",
+ "passed": true
+ },
+ {
+ "name": "app exposes the sample download without a prefetched render",
+ "passed": true
+ },
+ {
+ "name": "standalone server includes the actual PDF preview asset",
+ "passed": true
+ },
+ {
+ "name": "production route returns complete private PDF bytes with a useful filename",
+ "passed": true
+ },
+ {
+ "name": "unknown invoice IDs return a private JSON error",
+ "passed": true
+ },
+ {
+ "name": "unsupported writes are rejected by the route",
+ "passed": true
+ },
+ {
+ "name": "a concurrent burst receives explicit capacity errors instead of an unbounded queue",
+ "passed": true
+ },
+ {
+ "name": "normal PDF downloads recover after a capacity burst",
+ "passed": true
+ },
+ {
+ "name": "a real missing-glyph failure returns a generic non-PDF error",
+ "passed": true
+ },
+ {
+ "name": "a valid document renders after an engine failure",
+ "passed": true
+ },
+ {
+ "name": "the route enforces its page cap against a real multipage document",
+ "passed": true
+ },
+ {
+ "name": "a valid document renders after the page limit rejects a job",
+ "passed": true
+ },
+ {
+ "name": "a failed render child returns an HTTP error while the production server stays alive",
+ "passed": true
+ },
+ {
+ "name": "the production server returns a valid PDF after a render-process failure",
+ "passed": true
+ },
+ {
+ "name": "client JavaScript excludes the server PDF engine and worker",
+ "passed": true
+ }
+ ],
+ "scope": "Real next build standalone server with released Fullbleed npm package and fictional data; HTTP recovery after deliberate child failure. No hosted platform or application-authentication claim."
+ },
+ "scope": "Local Windows / Node 22.23.3 and Chrome checks of locked public npm 0.3.2 consumers. Standard-font preview regression confirmed against live 0.3.1 editors; no new npm release, framework upgrade, native crash fix, or PDF standards claim."
+}