From f7e7a46cd97b6c14f8cf0354d91dc753b71ad33e Mon Sep 17 00:00:00 2001 From: hallelx2 Date: Tue, 4 Aug 2026 21:33:39 +0100 Subject: [PATCH 1/3] feat(blogs): markdown content pipeline and a redesign on the tokens The blog was a single 59 KB client component with posts hardcoded in a BLOG_POSTS array and three fabricated authors carrying Unsplash stock photos. Nothing about it could be edited, scheduled, or moved to a database without editing TSX. Content now lives in content/posts/*.md with frontmatter, loaded through lib/posts.ts. The Post type is shaped as a database row on purpose -- slug as primary key, publishedAt as an ISO string so it sorts and indexes without parsing, body kept as raw markdown so the renderer can change without a migration. Pages call getAllPosts and getPost and know nothing about the filesystem, so moving to Postgres means reimplementing two functions rather than touching every page. Reading time is computed from the body, never written by hand, because a hand-written one is wrong the moment the post is edited. Frontmatter parsing and markdown rendering are written rather than pulled in. This app sits in a pnpm workspace, so a new dependency edits a lockfile shared by every app in the monorepo -- blast radius well beyond a blog. The supported subset is closed and under our control. Two token bugs fixed against DESIGN.md, both of which had been quietly flattening the brand: --color-bg-base #ffffff -> #fcfcfd canvas is "never pure" --color-text-secondary #3f3f36 -> #3f3f46 a typo The base layer also hardcoded pure white with !important, overriding the token entirely. It now reads from the token. Added the tokens the design source specifies but the stylesheet never encoded: hairline, grid line, display tracking (-0.03em), eyebrow tracking (0.16em). Plus utilities for the rules that were previously enforced by memory -- .signal-gradient exists as a named class precisely because DESIGN.md permits the blue-to-pink gradient on ONE line per view, and a named utility makes a second occurrence visible in review. Diagrams are React components rather than hand-authored SVG. Absolutely positioned SVG text does not reflow, does not use the site fonts, is not selectable or translatable, and needs re-authoring whenever a token changes. Components inherit Geist and restyle themselves. Where a raster is genuinely needed for an OG card, screenshot the component rather than maintaining a second artwork that will drift. Verified: typecheck clean, production build clean, all 7 posts prerendered as static HTML, series ordering and prev/next resolved from the `part` field. --- apps/blogs/.claude/launch.json | 11 + apps/blogs/app/globals.css | 61 +- apps/blogs/app/page.tsx | 1278 ++--------------- apps/blogs/app/posts/[slug]/page.tsx | 115 ++ apps/blogs/components/Diagrams.tsx | 214 +++ apps/blogs/components/Figure.tsx | 38 + .../posts/01-a-pdf-is-not-a-document.md | 77 + .../posts/02-thinking-like-a-printer.md | 111 ++ .../posts/03-where-tables-actually-live.md | 80 ++ .../content/posts/04-building-pdftable.md | 77 + .../posts/05-the-bugs-that-never-crashed.md | 122 ++ .../06-measuring-instead-of-believing.md | 140 ++ .../content/posts/07-what-is-still-missing.md | 104 ++ apps/blogs/lib/markdown.tsx | 257 ++++ apps/blogs/lib/posts.ts | 130 ++ apps/blogs/public/blog-assets/00-hero.svg | 53 + .../public/blog-assets/01-pdf-anatomy.svg | 58 + .../public/blog-assets/02-advance-drift.svg | 74 + .../public/blog-assets/03-encoding-chain.svg | 59 + .../blog-assets/04-table-strategies.svg | 83 ++ .../public/blog-assets/05-clipped-paren.svg | 69 + .../blog-assets/06-benchmark-ceiling.svg | 65 + .../blog-assets/07-hybrid-architecture.svg | 87 ++ apps/blogs/tsconfig.tsbuildinfo | 1 + 24 files changed, 2221 insertions(+), 1143 deletions(-) create mode 100644 apps/blogs/.claude/launch.json create mode 100644 apps/blogs/app/posts/[slug]/page.tsx create mode 100644 apps/blogs/components/Diagrams.tsx create mode 100644 apps/blogs/components/Figure.tsx create mode 100644 apps/blogs/content/posts/01-a-pdf-is-not-a-document.md create mode 100644 apps/blogs/content/posts/02-thinking-like-a-printer.md create mode 100644 apps/blogs/content/posts/03-where-tables-actually-live.md create mode 100644 apps/blogs/content/posts/04-building-pdftable.md create mode 100644 apps/blogs/content/posts/05-the-bugs-that-never-crashed.md create mode 100644 apps/blogs/content/posts/06-measuring-instead-of-believing.md create mode 100644 apps/blogs/content/posts/07-what-is-still-missing.md create mode 100644 apps/blogs/lib/markdown.tsx create mode 100644 apps/blogs/lib/posts.ts create mode 100644 apps/blogs/public/blog-assets/00-hero.svg create mode 100644 apps/blogs/public/blog-assets/01-pdf-anatomy.svg create mode 100644 apps/blogs/public/blog-assets/02-advance-drift.svg create mode 100644 apps/blogs/public/blog-assets/03-encoding-chain.svg create mode 100644 apps/blogs/public/blog-assets/04-table-strategies.svg create mode 100644 apps/blogs/public/blog-assets/05-clipped-paren.svg create mode 100644 apps/blogs/public/blog-assets/06-benchmark-ceiling.svg create mode 100644 apps/blogs/public/blog-assets/07-hybrid-architecture.svg create mode 100644 apps/blogs/tsconfig.tsbuildinfo diff --git a/apps/blogs/.claude/launch.json b/apps/blogs/.claude/launch.json new file mode 100644 index 0000000..a25aa6c --- /dev/null +++ b/apps/blogs/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "blogs", + "runtimeExecutable": "npx", + "runtimeArgs": ["--no-install", "next", "dev", "--port", "7655"], + "port": 7655 + } + ] +} diff --git a/apps/blogs/app/globals.css b/apps/blogs/app/globals.css index 475d2e5..6a8a276 100644 --- a/apps/blogs/app/globals.css +++ b/apps/blogs/app/globals.css @@ -10,13 +10,18 @@ --font-data: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace; --font-serif: var(--font-instrument-serif), ui-serif, Georgia, serif; - --color-bg-base: #ffffff; + /* Canvas — near-white, never pure. DESIGN.md is explicit about this: + "Canvas base (near-white, never pure) #FCFCFD". Pure white was in + here by mistake and is the one value that flattens the whole + whitepaper feel. */ + --color-bg-base: #fcfcfd; --color-bg-dark: #0a0a0a; --color-bg-deep: #050505; --color-text-base: #0a0a0a; --color-text-dark: #0a0a0a; - --color-text-secondary: #3f3f36; + /* was #3f3f36 — a typo. DESIGN.md secondary text is #3F3F46. */ + --color-text-secondary: #3f3f46; --color-text-muted: #71717a; --color-brand-blue: #1456f0; @@ -26,6 +31,15 @@ --color-border-light: #f2f3f5; --color-border-gray: #e5e7eb; + /* Hairline and grid, named so components stop hardcoding them. */ + --color-hairline: #e5e7eb; + --color-grid-line: rgba(20, 86, 240, 0.06); + + /* Type scale. Display sizes carry -0.03em tracking per DESIGN.md; + mono eyebrows carry 0.16em. Encoded here so no component invents + its own. */ + --tracking-display: -0.03em; + --tracking-eyebrow: 0.16em; /* shadcn/ui color mappings for Tailwind v4 */ --color-background: hsl(var(--background)); @@ -80,17 +94,23 @@ @layer base { html, body { - background-color: #ffffff !important; - color: #0a0a0a; + background-color: var(--color-bg-base); + color: var(--color-text-base); margin: 0; padding: 0; width: 100%; height: 100%; + -webkit-font-smoothing: antialiased; } * { border-color: hsl(var(--border)); } + + /* Geist at 300 for body, per DESIGN.md's "extreme weight contrast". */ + body { + font-weight: 300; + } } @layer utilities { @@ -104,12 +124,41 @@ .grid-paper { background-image: - linear-gradient(to right, rgba(20, 86, 240, 0.05) 1px, transparent 1px), - linear-gradient(to bottom, rgba(20, 86, 240, 0.05) 1px, transparent 1px); + linear-gradient(to right, var(--color-grid-line) 1px, transparent 1px), + linear-gradient(to bottom, var(--color-grid-line) 1px, transparent 1px); background-size: 56px 56px; } .text-balance { text-wrap: balance; } + + /* The single signal gradient. DESIGN.md permits it on exactly ONE line + per view, so this is a named utility rather than an inline gradient — + if it shows up twice on a screen, that is now visible in review. */ + .signal-gradient { + background-image: linear-gradient(90deg, var(--color-brand-blue), var(--color-brand-pink)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + } + + /* Geist Mono uppercase eyebrow. */ + .eyebrow { + font-family: var(--font-data); + text-transform: uppercase; + letter-spacing: var(--tracking-eyebrow); + font-size: 0.6875rem; + color: var(--color-text-muted); + } + + .tracking-display { + letter-spacing: var(--tracking-display); + } + + /* tabular-nums on stats, per DESIGN.md. */ + .stat { + font-family: var(--font-data); + font-variant-numeric: tabular-nums; + } } diff --git a/apps/blogs/app/page.tsx b/apps/blogs/app/page.tsx index 8b63788..5b1213c 100644 --- a/apps/blogs/app/page.tsx +++ b/apps/blogs/app/page.tsx @@ -1,1160 +1,164 @@ -'use client'; - -import React, { useState, useEffect, useCallback } from 'react'; -import { motion, AnimatePresence } from 'motion/react'; -import Link from 'next/link'; -import { - ArrowRight, - ArrowUpRight, - X, - Calendar, - Clock, - Search, - BookOpen, - Terminal, - ChevronRight, - Menu, - Sparkles, - Newspaper, - Zap, - FileText, - ArrowLeft, - ExternalLink, -} from 'lucide-react'; -import { VectorlessIcon, VectorlessDot } from './VectorlessIcon'; - -/* ─── Types ─── */ -type Category = 'All' | 'Product Updates' | 'Engineering' | 'Guides' | 'Features'; - -interface Author { - name: string; - avatarUrl: string; - role: string; -} - -interface BlogPost { - id: string; - title: string; - snippet: string; - content: string[]; - category: Category; - date: string; - readTime: string; - author: Author; - imageType: 'architecture' | 'database' | 'code' | 'benchmark'; -} - -/* ─── Authors ─── */ -const AUTHORS: Record = { - hallel: { - name: 'Oludele Halleluyah', - avatarUrl: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=120&h=120', - role: 'Founder & Principal Engineer', - }, - jane: { - name: 'Jane Cooper', - avatarUrl: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&q=80&w=120&h=120', - role: 'AI Research Specialist', - }, - guy: { - name: 'Guy Hawkins', - avatarUrl: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&q=80&w=120&h=120', - role: 'Infrastructure Architect', - }, +import Link from "next/link"; +import { getAllPosts } from "@/lib/posts"; +import { VectorlessIcon } from "./VectorlessIcon"; + +export const metadata = { + title: "Vectorless — Engineering notes", + description: + "Notes from building structure-preserving retrieval: PDF parsing, table extraction, and the measurements behind them.", }; -/* ─── Blog Data ─── */ -const BLOG_POSTS: BlogPost[] = [ - { - id: 'retrieval-reasoning-era', - title: 'Retrieval for the reasoning era: beyond vector search', - snippet: - 'Why traditional RAG chunking is a black box that destroys document structure, and how LLM reasoning over document maps restores it.', - category: 'Engineering', - date: 'Jun 20, 2026', - readTime: '5 min read', - author: AUTHORS.hallel, - imageType: 'architecture', - content: [ - 'Traditional RAG shatters your documents into arbitrary 500-token chunks. It computes vector embeddings for each chunk and places them in a vector database. But this similarity search is a black box. It has no understanding of headings, lists, table hierarchies, or structural flow.', - 'Vectorless takes a fundamentally different approach. We believe that documents have structure for a reason. By preserving document trees (headings, sections, chapters) and building section-by-section maps, we let LLM agents read and navigate the document outline like a human does.', - 'Why this matters:', - '1. True Citations: Since sections are preserved, citations refer to actual logical units, not arbitrary character ranges.', - '2. Full Context: When an agent selects a section, it gets the entire, unbroken section context.', - '3. Deterministic Explanation: You can inspect exactly why the agent chose a particular section, making retrieval transparent.', - ], - }, - { - id: 'self-hosting-vectorless-engine', - title: 'Self-hosting the Vectorless Engine with Docker and Neon', - snippet: - 'A step-by-step walkthrough to deploy the core Go retrieval engine in your private cloud with secure database isolation.', - category: 'Guides', - date: 'Jun 18, 2026', - readTime: '6 min read', - author: AUTHORS.hallel, - imageType: 'database', - content: [ - 'The core Vectorless engine is open-source and built for speed in Go. To run it in your own environment, you only need three infrastructure primitives: PostgreSQL (with pgvector), S3-compatible object storage (e.g. Cloudflare R2 or MinIO), and a job queue (like River or QStash).', - 'In this post, we demonstrate how to spin up the Docker-based container stack, configure the Admin API endpoints, and establish encrypted BYOK (Bring Your Own Key) access for your organizational users.', - 'We will also explore setting up tenant boundaries so that document parser caches do not cross authorization scopes, guaranteeing metadata security.', - ], - }, - { - id: 'llm-reasoning-retrieval', - title: 'How LLM reasoning models solve the chunk-overlap nightmare', - snippet: - 'How advanced reasoning loops avoid retrieval failures by reading summaries before pulling full-text contents.', - category: 'Features', - date: 'Jun 14, 2026', - readTime: '4 min read', - author: AUTHORS.guy, - imageType: 'code', - content: [ - 'In classical retrieval, developers tweak overlap percentages to avoid splitting sentences in half. This is a fragile bandage. By generating an interactive table of contents (ToC) with summaries, we present the agent with a clean, high-level map.', - 'The agent reasons about which sections are relevant *before* reading them, bypassing overlap limits completely. This post explains the mathematical and logical validation behind this flow.', - ], - }, - { - id: 'vectorless-release-1-0', - title: 'Vectorless Release 1.0.0: Ingestion Streaming & Citations', - snippet: - 'Announcing the stable v1 release of the Vectorless engine. Built on a modular Go core with SSE streaming and unified citations.', - category: 'Product Updates', - date: 'Jun 05, 2026', - readTime: '3 min read', - author: AUTHORS.hallel, - imageType: 'architecture', - content: [ - 'We are thrilled to launch the fully redesigned Vectorless Core Engine 1.0.0. Built natively in Go for ultra-fast document parsing and tree building, this release is exclusively focused on making document retrieval deterministic and trace-verifiable.', - 'Key Features in 1.0:', - '- Server-Sent Events Ingestion: Live progress logs while processing heavy PDFs and Word documents.', - '- Integrated Citation Validation: Guarantee that retrieved chunks match the exact heading paths.', - '- Native ConnectRPC & REST support: Expose standard JSON endpoints alongside protobuf definitions.', - ], - }, - { - id: 'evaluating-retrieval-financebench', - title: 'Evaluating Retrieval Accuracy: FinanceBench Case Study', - snippet: - 'How structure-preserving retrieval outperforms traditional LangChain chunking setups on complex quarterly financial filings.', - category: 'Engineering', - date: 'May 28, 2026', - readTime: '8 min read', - author: AUTHORS.jane, - imageType: 'benchmark', - content: [ - 'Evaluating retrieval systems is notoriously hard. In this study, we ran the FinanceBench benchmark dataset across three configurations: (A) Traditional recursive chunking, (B) Parent-document retrieval, and (C) Vectorless structure-preserving retrieval.', - 'Vectorless achieved 94.2% accuracy on complex tabular cross-referencing questions, compared to 62.1% for traditional chunking, because it did not truncate financial table nodes.', - ], - }, - { - id: 'hybrid-retrieval-trees', - title: 'Hybrid Retrieval: Combining Structural Trees with Keyword Search', - snippet: - 'How the Vectorless hybrid strategy overlays BM25 keyword matching with LLM reasoning maps for the best of both worlds.', - category: 'Engineering', - date: 'May 14, 2026', - readTime: '5 min read', - author: AUTHORS.jane, - imageType: 'architecture', - content: [ - 'While LLM reasoning over document outlines yields superior accuracy for contextual questions, sometimes users search for highly specific keywords or exact code definitions.', - 'To address this, Vectorless 1.0 supports a hybrid strategy. It performs a fast lexical pre-scan (BM25) over the document node tree to identify candidate sections, which are then annotated directly inside the Document Map.', - 'The LLM agent then reasons over both the semantic summaries and the keyword frequency hits to make the final retrieval decision. This combines the speed of classic search with the intelligence of agentic reasoning.', - ], - }, -]; - -/* ─── Blueprint Illustrations (SVG Schematics) ─── */ -function BlueprintIllustration({ type, large = false }: { type: BlogPost['imageType']; large?: boolean }) { - const h = large ? 'h-[200px]' : 'h-[140px]'; - return ( -
- {/* Coordinate overlay */} -
-
- SYS_REF // VECTORLESS -
-
- SCALE // 1:1 -
- {/* Cross-hair */} -
-
-
- - {type === 'architecture' && ( - - {/* Hierarchy tree */} - - - - - - - - - - - {/* Node dots */} - - - - - - - - - - - {/* Target callout */} - - - - - TARGET - - - {/* Label */} - - - node.root - - - )} - - {type === 'database' && ( - - - - {/* DB cylinders */} - - - - - - - {/* Index callout */} - - - - v_index - - - {/* Data flow arrows */} - - - write_buffer - - - - - - - - - )} - - {type === 'code' && ( - - - - - {/* Traffic lights */} - - - - - {/* Code lines */} - - - - - - - - - )} - - {type === 'benchmark' && ( - - {/* Grid lines */} - {[30, 50, 70, 90].map((y) => ( - - ))} - - {/* Axes */} - - - - {/* Baseline curve (traditional) */} - - - {/* Vectorless performance curve */} - - - {/* Area under Vectorless curve */} - - - {/* Data points */} - - - - - {/* Annotation */} - - - 94.2% ACC - - - {/* Legend */} - - - Vectorless - - - - Traditional - - - )} -
- ); -} - -/* ─── Main Blog Page ─── */ -export default function BlogPage() { - const [selectedCategory, setSelectedCategory] = useState('All'); - const [activeArticle, setActiveArticle] = useState(null); - const [scrolled, setScrolled] = useState(false); - const [isNavOpen, setIsNavOpen] = useState(false); - - // Interactive retrieval sandbox state - const [showDemoModal, setShowDemoModal] = useState(false); - const [selectedDoc, setSelectedDoc] = useState<'vectorless-whitepaper.pdf' | 'fastify-manual.md' | 'terms-of-service.docx'>('vectorless-whitepaper.pdf'); - const [ingestionStep, setIngestionStep] = useState(0); - const [demoQuery, setDemoQuery] = useState(''); - const [isRetrieving, setIsRetrieving] = useState(false); - const [retrievedResult, setRetrievedResult] = useState(null); - const [highlightedSection, setHighlightedSection] = useState(null); - - useEffect(() => { - const handleScroll = () => setScrolled(window.scrollY > 24); - window.addEventListener('scroll', handleScroll, { passive: true }); - return () => window.removeEventListener('scroll', handleScroll); - }, []); - - // Ingestion simulation - useEffect(() => { - if (showDemoModal) { - const t1 = setTimeout(() => setIngestionStep(1), 700); - const t2 = setTimeout(() => setIngestionStep(2), 1400); - const t3 = setTimeout(() => setIngestionStep(3), 2100); - return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); }; - } - }, [showDemoModal, selectedDoc]); - - const openSandbox = useCallback(() => { - setIngestionStep(0); - setRetrievedResult(null); - setHighlightedSection(null); - setShowDemoModal(true); - }, []); - - const selectDoc = useCallback((docId: typeof selectedDoc) => { - setIngestionStep(0); - setRetrievedResult(null); - setHighlightedSection(null); - setSelectedDoc(docId); - }, []); - - const handleRetrieve = useCallback((e: React.FormEvent) => { - e.preventDefault(); - if (!demoQuery.trim()) return; - setIsRetrieving(true); - setRetrievedResult(null); - setHighlightedSection(null); - - setTimeout(() => { - setIsRetrieving(false); - if (selectedDoc === 'vectorless-whitepaper.pdf') { - if (demoQuery.toLowerCase().includes('performance') || demoQuery.toLowerCase().includes('benchmark')) { - setHighlightedSection(3); - setRetrievedResult( - 'Section 3: Performance & Benchmarks\n\nVectorless outperformed traditional chunking systems on quarterly reports, achieving 94.2% accuracy. Because it does not split financial tabular nodes, it avoids destroying structural context.' - ); - } else { - setHighlightedSection(1); - setRetrievedResult( - 'Section 1: Introduction to Reasoned Retrieval\n\nInstead of chunking documents into arbitrary sizes and calculating embeddings, Vectorless builds a hierarchical structure tree which is summarized section-by-section.' - ); - } - } else if (selectedDoc === 'fastify-manual.md') { - if (demoQuery.toLowerCase().includes('route') || demoQuery.toLowerCase().includes('matching')) { - setHighlightedSection(2); - setRetrievedResult( - 'Section 2: Routing Primitives\n\nFastify uses a radix tree router internally for extremely fast route resolution. It parses registered routes into prefix trees for O(L) matching speed.' - ); - } else { - setHighlightedSection(1); - setRetrievedResult( - 'Section 1: Getting Started\n\nInstall fastify using npm. Boot the server and register core plugins to set up dynamic request and response validation pipelines.' - ); - } - } else { - setHighlightedSection(1); - setRetrievedResult( - 'Section 1: Definitions & Scope\n\nThis document governs the terms of service for the Vectorless API. Under self-hosted instances, your organization retains complete data ownership.' - ); - } - }, 1200); - }, [demoQuery, selectedDoc]); - - const categories: Category[] = ['All', 'Product Updates', 'Engineering', 'Guides', 'Features']; - - const filteredPosts = BLOG_POSTS.filter((post) => { - if (selectedCategory === 'All') return true; - return post.category === selectedCategory; +function formatDate(iso: string) { + return new Date(iso).toLocaleDateString("en-GB", { + day: "numeric", + month: "short", + year: "numeric", }); +} - const featuredPost = BLOG_POSTS[0]; // First post is the featured one - const displayPosts = selectedCategory === 'All' ? BLOG_POSTS.slice(1) : filteredPosts; +export default function Home() { + const posts = getAllPosts(); + const [lead, ...rest] = posts; + const series = lead?.series; return ( -
- {/* ═══ FLOATING GLASS NAVBAR ═══ */} - - - {/* ═══ MAIN CONTENT ═══ */} - - {activeArticle ? ( - /* ━━━ ARTICLE READER VIEW ━━━ */ - - {/* Reader header band */} -
-
-
- - -
- {activeArticle.category} - - {activeArticle.date} - - {activeArticle.readTime} -
- -

- {activeArticle.title} -

- -
-
- {activeArticle.author.name} -
-
- {activeArticle.author.name} - - {activeArticle.author.role} - -
-
-
-
- - {/* Reader body */} -
-
- {activeArticle.content.map((paragraph, pIdx) => { - if (/^\d+\.\s/.test(paragraph)) { - const parts = paragraph.split(/^\d+\.\s/); - const titleAndText = parts[1].split(/:\s/); - return ( -
- {titleAndText.length > 1 ? ( - <> -

{titleAndText[0]}

-

{titleAndText[1]}

- - ) : ( -

{parts[1]}

- )} -
- ); - } - if (paragraph.startsWith('- ')) { - return ( -
-

{paragraph.slice(2)}

-
- ); - } - return ( -

- {paragraph} -

- ); - })} -
- - {/* Bottom navigation */} -
- -
+ + + {/* ── Lead post ───────────────────────────────────────────────── */} + {lead && ( +
+ {series && ( +
+ {series} · {posts.length} parts
- - ) : ( - /* ━━━ MAGAZINE INDEX VIEW ━━━ */ - - {/* ─── HERO SECTION ─── */} -
- {/* Gradient backdrop */} -
- {/* Grid paper */} -
- -
-
- {/* Chip */} - - - - - - - Vectorless Engineering Journal - - - - {/* Headline */} - - Retrieval,{' '} - - rethought. - - - - {/* Subtitle */} - - A technical journal on the design of structure-preserving retrieval architectures, no-chunking - models, and deterministic RAG systems for AI agents. - -
- - {/* Decorative bottom border with date */} -
-
- Vol. 01 — {new Date().getFullYear()} -
-
- {BLOG_POSTS.length} Articles Published - - Updated Weekly -
-
-
-
- - {/* ─── CATEGORIES + SANDBOX BAR ─── */} -
-
-
- {categories.map((cat) => { - const isActive = selectedCategory === cat; - return ( - - ); - })} -
- - -
-
- - {/* ─── FEATURED ARTICLE (FULL WIDTH) ─── */} - - {selectedCategory === 'All' && featuredPost && ( - -
setActiveArticle(featuredPost)} - className="group max-w-[1200px] mx-auto px-6 md:px-12 lg:px-16 py-16 md:py-20 cursor-pointer" - > -
-
-
-
- {featuredPost.category} - - {featuredPost.date} - - {featuredPost.readTime} -
- -

- {featuredPost.title} -

- -

- {featuredPost.snippet} -

-
- -
-
-
- {featuredPost.author.name} -
-
- {featuredPost.author.name} - - {featuredPost.author.role} - -
-
- -
- Read article - -
-
-
- -
- -
-
-
-
- )} -
- - {/* ─── ARTICLE GRID ─── */} -
- {selectedCategory === 'All' && ( -
-

- Latest Articles -

-
-
- )} - -
- - {displayPosts.map((post, idx) => ( - setActiveArticle(post)} - className="group flex flex-col cursor-pointer bg-white hover:bg-[#FAFBFF] transition-colors duration-300" - > -
- {/* Meta */} -
- {post.category} - {post.date} -
- - {/* Headline */} -

- {post.title} -

- - {/* Snippet */} -

- {post.snippet} -

- - {/* Blueprint */} -
- -
- - {/* Author + Read link */} -
-
-
- {post.author.name} -
-
- {post.author.name} - - {post.readTime} - -
-
- - -
-
-
- ))} -
-
- - {/* Empty State */} - {displayPosts.length === 0 && ( -
- - No matching articles found - -
- )} -
- - {/* ─── NEWSLETTER CTA ─── */} -
-
-
-
- - - Stay in the loop - -
- -

- Engineering updates,{' '} - - delivered. - -

- -

- Get new posts on retrieval architectures, product releases, and the future of document AI — straight to - your inbox. -

- -
- - -
- -

- No spam. Unsubscribe anytime. -

-
+ )} + +
+
+ {String(lead.part ?? 1).padStart(2, "0")}
-
- - {/* ─── FOOTER ─── */} -
-
-
{ - setActiveArticle(null); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }} - > - - Vectorless -
- -

- © {new Date().getFullYear()} Vectorless. All rights reserved. +

+

+ {lead.title} +

+

+ {lead.summary}

- -
- - Privacy - - - Terms - - - GitHub - - +
+ {formatDate(lead.publishedAt)} + · + {lead.readingMinutes} min
-
-
- )} - - - {/* ═══ INTERACTIVE RETRIEVAL SANDBOX MODAL ═══ */} - - {showDemoModal && ( -
- setShowDemoModal(false)} - className="absolute inset-0 bg-[#0A0A0A]/50 backdrop-blur-sm" - /> + + +
+ )} - - {/* Left Panel: Controls */} -
+ {/* ── The rest, as an index rather than a card grid ───────────── */} +
+
    + {rest.map((post) => ( +
  • + + + {String(post.part ?? "").padStart(2, "0")} +
    -
    - - Interactive API - - - - Live - -
    - -

    Retrieval Sandbox

    -

    - Select a document, watch structural ingestion, then run reasoning queries to see how Vectorless - navigates document maps. +

    + {post.title} +

    +

    + {post.summary}

    - -

    - Select Document -

    -
    - {[ - { id: 'vectorless-whitepaper.pdf' as const, size: '2.4 MB', type: 'PDF' }, - { id: 'fastify-manual.md' as const, size: '480 KB', type: 'Markdown' }, - { id: 'terms-of-service.docx' as const, size: '1.1 MB', type: 'Word Doc' }, - ].map((doc) => { - const isSel = selectedDoc === doc.id; - return ( - - ); - })} -
    + + {post.readingMinutes} min + + +
  • + ))} +
- {ingestionStep >= 2 && ( -
-

- Execute Query -

-
- setDemoQuery(e.target.value)} - placeholder="e.g., How does performance compare?" - className="w-full text-xs border border-border-gray rounded-xl pl-4 pr-9 py-3 bg-[#FCFCFD] focus:outline-none focus:border-brand-blue focus:ring-1 focus:ring-brand-blue/20 transition-all" - /> - -
- -
- )} -
- - {/* Right Panel: Terminal */} -
-
-
-
- - - vectorless_core_stdout - - -
- -
-
$ ./vectorless ingest {selectedDoc}
-
- - [1/3] File parsed: created node tree -
- - {ingestionStep >= 1 && ( -
- - [2/3] Structural outline mapped (ToC) -
- )} - - {ingestionStep >= 2 && ( -
- - [3/3] Gemini-1.5 summary manifests generated -
- )} - - {isRetrieving && ( -
$ querying LLM agent reasoning engine...
- )} - - {retrievedResult && ( -
-
REASONING OUTLINE SELECTION:
-

- {retrievedResult} -

-
- )} -
-
+ {posts.length === 0 && ( +

+ No posts yet — add markdown to content/posts/. +

+ )} + - {ingestionStep >= 2 && ( -
- * Structural citations are generated dynamically matching header paths. -
- )} -
-
-
+
+
+
+ + Vectorless
- )} - -
+ Structure-preserving retrieval, with citations. +
+ + ); } diff --git a/apps/blogs/app/posts/[slug]/page.tsx b/apps/blogs/app/posts/[slug]/page.tsx new file mode 100644 index 0000000..a0d1248 --- /dev/null +++ b/apps/blogs/app/posts/[slug]/page.tsx @@ -0,0 +1,115 @@ +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { getAllPosts, getPost, getSeriesNeighbours } from "@/lib/posts"; +import { renderMarkdown } from "@/lib/markdown"; +import { VectorlessIcon } from "../../VectorlessIcon"; + +export function generateStaticParams() { + return getAllPosts().map((p) => ({ slug: p.slug })); +} + +export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) { + const { slug } = await params; + const post = getPost(slug); + if (!post) return {}; + return { title: `${post.title} — Vectorless`, description: post.summary }; +} + +function formatDate(iso: string) { + return new Date(iso).toLocaleDateString("en-GB", { + day: "numeric", + month: "long", + year: "numeric", + }); +} + +export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) { + const { slug } = await params; + const post = getPost(slug); + if (!post) notFound(); + + const { prev, next } = getSeriesNeighbours(slug); + + return ( +
+
+
+ + + Vectorless + + + All notes + +
+
+ +
+
+ {post.series && post.part + ? `${post.series} · Part ${post.part}` + : formatDate(post.publishedAt)} +
+ +

+ {post.title} +

+ +

{post.summary}

+ +
+ {formatDate(post.publishedAt)} + · + {post.readingMinutes} min read + {post.tags.length > 0 && ( + <> + · + {post.tags.join(", ")} + + )} +
+ +
{renderMarkdown(post.body)}
+ + {/* Series navigation — the reason `part` exists on the model. */} + {(prev || next) && ( + + )} +
+ +
+
+
+ + Vectorless +
+ + All notes + +
+
+
+ ); +} diff --git a/apps/blogs/components/Diagrams.tsx b/apps/blogs/components/Diagrams.tsx new file mode 100644 index 0000000..46f1164 --- /dev/null +++ b/apps/blogs/components/Diagrams.tsx @@ -0,0 +1,214 @@ +import React from "react"; +import { Figure } from "./Figure"; + +/** + * Diagrams as components, not image files. + * + * Hand-positioned SVG text is a bad medium for this: every label is + * absolutely placed, nothing reflows, the type does not match the site's + * fonts, and none of it is selectable or translatable. These are laid + * out with the same CSS as the rest of the page, so they inherit Geist, + * respond to a token change, and stay sharp at any zoom. + * + * Where a raster is genuinely needed — an OG card, a social preview — + * screenshot the rendered component rather than authoring a second + * artwork that will drift from this one. + */ + +function Eyebrow({ children, tone = "muted" }: { children: React.ReactNode; tone?: "muted" | "blue" | "pink" }) { + const color = + tone === "blue" ? "text-brand-blue" : tone === "pink" ? "text-brand-pink" : "text-text-muted"; + return
{children}
; +} + +/* ── Glyph advance widths, and how a flat guess accumulates ─────────── */ + +const TRUE_WIDTHS = [ + { g: "i", w: 222 }, + { g: "m", w: 833 }, + { g: "W", w: 944 }, + { g: "i", w: 222 }, +]; + +export function DriftDiagram() { + const scale = 0.28; // points-per-thousandth, chosen so the row fits + const trueTotal = TRUE_WIDTHS.reduce((a, b) => a + b.w, 0); + const guessTotal = TRUE_WIDTHS.length * 500; + + return ( +
+
+ True AFM widths +
+ {TRUE_WIDTHS.map((c, i) => ( +
+
+ {c.g} +
+
{c.w}
+
+ ))} +
word ends here
+
+ +
+ Flat 500 guess — what pdftable did +
+ {TRUE_WIDTHS.map((c, i) => ( +
+
+ {c.g} +
+
500
+
+ ))} +
+
+
+
+
error after only four glyphs
+
+ +
+
+ + 4 glyphs ~0.3pt off + + + 40 glyphs 11.99pt off + + more than a whole character of drift +
+
+
+
+ ); +} + +/* ── The three namespaces between a byte and a character ───────────── */ + +export function EncodingChain() { + const steps = [ + { n: "1 · Character code", v: "0x61", note: "one byte in the stream", mono: true }, + { n: "2 · Glyph name", v: '"alpha"', note: "an Adobe glyph name", mono: true }, + { n: "3 · Unicode", v: "α", note: "what a human or a model reads", mono: false }, + ]; + return ( +
+
+
+ {steps.map((s, i) => ( +
+ {s.n} +
+ {s.v} +
+
{s.note}
+
+ ))} +
+ +
+ The failure — Symbol with no /Encoding declared +
+ 0x61 + + "a" + instead of + "alpha" + + α +
+
+ A page of Greek comes back as nonsense Latin. +
+
+
+
+ ); +} + +/* ── Benchmark bars ─────────────────────────────────────────────────── */ + +const RESULTS = [ + { label: "pdfplumber · lines", value: 0.37, tone: "muted" as const }, + { label: "pdftable · lines", value: 0.362, tone: "blue" as const }, + { label: "pdftable · auto", value: 0.358, tone: "faint" as const }, + { label: "pdfplumber · text", value: 0.267, tone: "pink" as const }, + { label: "pdftable + perfect grid", value: 0.935, tone: "solid" as const }, +]; + +export function BenchmarkBars() { + return ( +
+
+ ICDAR 2013 · adjacency-relation F1 +
+ {RESULTS.map((r) => { + const bg = + r.tone === "solid" + ? "bg-brand-blue" + : r.tone === "blue" + ? "bg-brand-blue/55" + : r.tone === "faint" + ? "bg-brand-blue/30" + : r.tone === "pink" + ? "bg-brand-pink/35" + : "bg-text-muted/30"; + return ( +
+
+ {r.label} +
+
+
+
+
+ {r.value.toFixed(3)} +
+
+ ); + })} +
+
+ Hand pdftable a correct grid and it scores{" "} + 0.935. Left to find the grid + itself, 0.362. +
+
+
+ ); +} + +/* ── Registry, so markdown can reference a diagram by name ──────────── */ + +export const DIAGRAMS: Record = { + drift: DriftDiagram, + encoding: EncodingChain, + benchmark: BenchmarkBars, +}; diff --git a/apps/blogs/components/Figure.tsx b/apps/blogs/components/Figure.tsx new file mode 100644 index 0000000..fbd69b9 --- /dev/null +++ b/apps/blogs/components/Figure.tsx @@ -0,0 +1,38 @@ +import React from "react"; + +/** + * A framed, captioned figure. + * + * Diagrams in these posts are React components rather than image files, + * so they stay selectable, translatable, legible at any zoom, and + * restyle themselves when a token changes. `Figure` gives them the same + * hairline frame and caption treatment either way, so a component + * diagram and a raster screenshot sit on the page identically. + */ +export function Figure({ + src, + alt, + caption, + children, +}: { + src?: string; + alt?: string; + caption?: string; + children?: React.ReactNode; +}) { + return ( +
+
+ {children ?? ( + // eslint-disable-next-line @next/next/no-img-element + {alt + )} +
+ {caption && ( +
+ {caption} +
+ )} +
+ ); +} diff --git a/apps/blogs/content/posts/01-a-pdf-is-not-a-document.md b/apps/blogs/content/posts/01-a-pdf-is-not-a-document.md new file mode 100644 index 0000000..559630b --- /dev/null +++ b/apps/blogs/content/posts/01-a-pdf-is-not-a-document.md @@ -0,0 +1,77 @@ +--- +slug: a-pdf-is-not-a-document +title: "A PDF is not a document" +summary: "What the file actually contains, the three things it does not, and why every word and table has to be inferred from geometry." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 1 +tags: [PDF, parsing] +--- + +![What a PDF actually contains](/blog-assets/01-pdf-anatomy.svg) + +I started building Vectorless on a simple premise: answer questions from documents by reasoning over their structure rather than chopping them into chunks and hoping an embedding finds the right one. That premise needs the structure to survive being read out of the file. + +Then I opened a PDF properly for the first time and found there is no structure in there to survive. + +## What the file actually holds + +A PDF is a program for a printer. Its content stream is a list of drawing commands, and they look like this: + +``` +BT begin text +/F1 12 Tf select font F1 at 12 points +72 720 Td put the pen at (72, 720) +(Revenue) Tj paint these glyphs +ET end text +``` + +That is the whole model. Move the pen, paint some shapes, move on. The format was designed in 1993 so that a page would print identically on any device, and it succeeded completely at that. Nothing in the design was ever concerned with a machine reading the page back. + +Three things you would expect to find are simply absent: + +**Words.** No spaces are stored as structure. Many PDFs emit a space *glyph*, but plenty convey a gap by just moving the pen further right. Either way, nothing marks where one word ends. + +**Lines.** No line objects, no paragraph objects. Glyphs happen to share a Y coordinate, and you infer a line from that. + +**Tables.** Nothing at all. A table is some line segments drawn on the page and some glyphs painted near them. The relationship between them exists only in a human reader's head. + +Every one of those has to be reconstructed from geometry. That reconstruction is the entire job of pdfplumber, of pdfminer, and of the library this series is about. + +## The consequence that matters + +Because the page is a sequence of paint operations, **the file never records where a glyph landed** — only where the pen started and how far each glyph advances. + +Position is a running sum: + +``` +position of glyph N = start + + width of glyph 0 + + width of glyph 1 + + … + + width of glyph N-1 +``` + +Get one width wrong and you have not misplaced one glyph. You have misplaced **every glyph after it on that line**, and the error compounds. Part two is largely about what happens when those widths are wrong, because that is not a hypothetical — it is what pdftable was doing. + +## Why this is a retrieval problem, not a formatting problem + +It would be easy to file all of this under "cosmetic". The text still comes out roughly right; the coordinates are only for layout. + +That is wrong in a way that matters for anything built on top. + +Table detection is driven entirely by geometry. pdfplumber's `text` strategy infers column boundaries by looking for words that share an X position down the page, and row boundaries from words that share a Y extent across it. Word bounding boxes are the only input. Drift the boxes and you move the column edges. Move a column edge and cells land in the wrong place. The table handed to a language model is then quietly wrong — plausible, well-formed, and wrong. + +At that point the failure is invisible. The model reasons confidently over corrupted input and returns a bad answer, and every layer above the parser looks fine. It reads as *the model got it wrong*. Nobody thinks to check a font metric. + +That is the failure mode this whole series is about, and it shaped how I ended up working: **the dangerous bugs in a PDF parser are the ones that do not crash.** + +## What sits underneath + +pdftable is built on [pdfcpu](https://github.com/pdfcpu/pdfcpu) for the parts nobody should rewrite — xref tables, object resolution, FlateDecode. Everything above that is implemented directly: operator dispatch, graphics and text state, glyph positioning, ToUnicode CMaps, font encodings. + +The split is deliberate. Everything except one file is standard-library-only, so if pdfcpu ever needs replacing, the blast radius is a single file. + +--- + +**Next:** [Thinking like a printer](/posts/thinking-like-a-printer) — fonts, advance widths, the fourteen fonts every PDF reader is assumed to know, and the three separate namespaces sitting between a byte in the file and a character on your screen. \ No newline at end of file diff --git a/apps/blogs/content/posts/02-thinking-like-a-printer.md b/apps/blogs/content/posts/02-thinking-like-a-printer.md new file mode 100644 index 0000000..71c1516 --- /dev/null +++ b/apps/blogs/content/posts/02-thinking-like-a-printer.md @@ -0,0 +1,111 @@ +--- +slug: thinking-like-a-printer +title: "Thinking like a printer" +summary: "Advance widths, the fourteen fonts every reader is assumed to know, descenders, and the three namespaces between a byte and a character." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 2 +tags: [PDF, fonts] +--- + +![How a wrong glyph width accumulates into drift](/blog-assets/02-advance-drift.svg) + +To read a PDF you have to reconstruct what a printer would have done with it. That turns out to be harder than it sounds, because a printer knows several things the file does not bother to tell you. + +## Letters are not the same width + +In Helvetica, at a font size of 1000 units: + +| glyph | width | +| --- | --- | +| `i` | 222 | +| space | 278 | +| `m` | 833 | +| `W` | 944 | + +Widths are stored in thousandths of an em so they are independent of size. At 12 point, an `m` advances `833 / 1000 × 12 = 10.0pt`. + +A PDF normally ships these in a `/Widths` array on the font dictionary. Normally. + +## The fourteen fonts nobody stores + +PDF 1.7 §9.6.2.2 exempts fourteen fonts from carrying `/Widths` at all: Helvetica, Times and Courier in four styles each, plus Symbol and ZapfDingbats. + +The reason is archaeological. PostScript printers in the 1980s shipped with those fonts burned into ROM, so every device already had the metrics. Storing them again would have wasted bytes on a 300 KB hard disk. The spec encoded that assumption, and PDFs produced this year still rely on it — the 3M annual report I tested against omits `/Widths` throughout. + +The bargain is that **the spec offloads the data onto the reader**. Every conforming implementation ships the Adobe Font Metrics tables. pdfminer does. Poppler does. PDF.js does. + +pdftable did not. When it found no `/Widths` it fell through to a flat guess: + +```go +func (f *Font) CharWidth(cid uint16) float64 { + if w, ok := f.Widths[cid]; ok { + return w + } + return 500 // half an em, "a reasonable guess" +} +``` + +Every glyph the same width. `i` and `m` identical. Measured against pdfplumber on real fixtures, that produced **11.99 points of maximum word-position drift** — more than a whole character of accumulated error by the end of a line. + +Bundling the real Adobe Core 14 metrics took that to **0.0000pt**. Not approximately; the coordinates match bit for bit. + +## The vertical half, which I missed at first + +Fixing widths made horizontal positions exact and left vertical positions untouched. The residual was suspiciously tidy: + +``` +12pt text off by 2.484pt +24pt text off by 4.968pt +``` + +Both are exactly `0.207 × font size`. Helvetica's descender — how far `p`, `g` and `y` hang below the baseline — is −207/1000. + +The same exemption that lets those fonts omit `/Widths` also lets them omit `/FontDescriptor`, which is where ascent and descent live. Without it, a glyph's box collapses to `[baseline, baseline + size]` instead of resting on the real descender. + +There was a second fault hiding behind the first, and it would have survived fixing it: + +```go +descent := font.Descent * 0.001 // before +descent := font.Descent * 0.001 * fontSize // after +``` + +Descent is in thousandths of an em, so reaching text space needs *both* factors — the same two the advance width already gets. Without the font size the descender was a fixed fraction of a **point** rather than a fraction of the **glyph**: twelve times too small at 12pt, and wrong for every embedded font that *did* supply a descriptor. + +It hid because the one test touching it built its fixture with `Descent: 0`. The broken arithmetic was multiplied by zero. + +Columns come from X. **Rows come from Y.** So this one could merge or split table rows, which is a structural failure rather than a cosmetic one. + +## Three namespaces between a byte and a character + +![The encoding chain](/blog-assets/03-encoding-chain.svg) + +Text extraction has its own chain, and people routinely collapse three distinct things into one: + +1. **Character code** — a byte, 0–255. Meaningless alone. +2. **Glyph name** — an Adobe name (`A`, `alpha`, `a1`). The font's *encoding* maps code → name. +3. **Unicode** — what a human or a model reads. The *Adobe Glyph List* maps name → Unicode. + +For ordinary fonts this is dull: `0x41` → `"A"` → `U+0041`. + +Symbol breaks it. Symbol carries its own built-in encoding where `0x61` means `"alpha"` → `U+03B1`. pdftable did not know that and applied StandardEncoding, so `0x61` became `"a"`. Any document using Symbol for Greek — most older scientific papers — extracted nonsense. + +The two chains then cross. Width lookup runs `code → Unicode → width`, so a wrong Unicode step corrupts the geometry too. One fault, both outputs. + +ZapfDingbats has a related but opposite problem. Its glyphs are named `a1` through `a191`, and those names are **not** in the standard Adobe Glyph List — Adobe ships them in a separate file precisely because they are font-specific. So `a1` must mean ✁ *inside ZapfDingbats* and must not mean anything in a Latin font that happens to use the same name in a `/Differences` array. Resolving them globally would silently corrupt any such document. + +That distinction is why pdftable keeps two tables: Symbol's names go in the shared resolver because they are genuine AGL entries, and Dingbats' go in a font-scoped one. + +## Generating the tables rather than typing them + +I was about to transcribe roughly 200 `a1` → `✁` mappings by hand, and stopped. + +That is exactly the kind of opaque data where one wrong digit is invisible in review and corrupts extraction permanently. Instead I fetched Adobe's `glyphlist.txt` and `zapfdingbats.txt` and generated the Go tables mechanically. All 190 Symbol names resolved against the AGL with zero misses — which is itself a check that the two sources agree. + +The same discipline applied to the vertical metrics, generated from pdfminer.six's `fontmetrics.py` — the same Adobe AFM data, and the implementation being measured against. + +Two values in there look like typos and are not: the AGL maps `Delta` to U+2206 INCREMENT rather than U+0394, and `Omega` to U+2126 OHM SIGN. That is what Adobe specifies, matching it is what keeps parity with pdfminer, and both are pinned by a test with a comment so the next reader does not "fix" them. + +--- + +**Next:** [Where tables actually live](/posts/where-tables-actually-live) — why a table has to be inferred, the two strategies for doing it, and what happens to images and vector graphics. \ No newline at end of file diff --git a/apps/blogs/content/posts/03-where-tables-actually-live.md b/apps/blogs/content/posts/03-where-tables-actually-live.md new file mode 100644 index 0000000..ecce98d --- /dev/null +++ b/apps/blogs/content/posts/03-where-tables-actually-live.md @@ -0,0 +1,80 @@ +--- +slug: where-tables-actually-live +title: "Where tables actually live" +summary: "A table is never stored — it is reconstructed. Two strategies, two failure modes, and the geometry that makes citations clickable." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 3 +tags: [tables, layout] +--- + +![Two strategies for finding a table](/blog-assets/04-table-strategies.svg) + +There is no table in a PDF. There is no `` tag, no cell object, no row. There is a set of line segments and a set of glyphs, and the fact that a human looking at them sees a table. + +So a table extractor is a reconstruction engine, and everything it produces is a hypothesis. + +## Two ways to guess + +pdfplumber established the two approaches that everything else now uses, and pdftable implements both. + +**`lines`** takes the vector graphics the page drew — line segments, rectangle outlines, curve segments that happen to be axis-aligned — and looks for places they *cross*. Each intersection is a potential cell corner. Walk the intersections, find closed boxes, and those are your cells. + +**`text`** ignores drawing entirely and clusters word positions. Words sharing an X0 down the page suggest a column boundary; words sharing a vertical extent across the page suggest a row. Cluster, threshold, emit edges. + +They fail in opposite directions, and the ICDAR 2013 benchmark makes the shapes obvious: + +| strategy | precision | recall | +| --- | --- | --- | +| `lines` | 0.865 | 0.229 | +| `text` | 0.167 | 0.679 | + +`lines` is nearly always right and nearly always silent. `text` sees almost everything and is wrong most of the time — because prose has word alignment too, and nothing in the algorithm knows the difference between a table and a paragraph in a narrow column. + +## The failure I did not expect + +`lines` needs rulings that **intersect**. That sounds obvious until you meet a table ruled only horizontally. + +That style — a rule above the header, one below it, one at the bottom, and nothing vertical — is the house style of most government and academic publishing. LaTeX's `booktabs` package exists to enforce it. It looks clean, it is extremely common, and it produces **zero ruling intersections**. + +On the ICDAR 2013 corpus, 28 of 125 documents had no table detected at all. Every one was ruled on a single axis: + +``` +us-017: 218 horizontal rules, 0 vertical +us-018: 226 horizontal rules, 0 vertical +us-024: 135 horizontal rules, 0 vertical +us-025: 225 horizontal rules, 0 vertical +``` + +The obvious fix is to use rules for one axis and word alignment for the other. pdftable supports mixed strategies per axis, so this needed no new machinery — only automatic selection. Part six covers what happened when I measured it, which was not what I predicted. + +## Not everything on a page is text or rules + +Two other things show up and both need handling. + +**Rectangles.** Many producers draw cell backgrounds as filled rectangles rather than stroking borders. Those four edges look exactly like a table grid to an edge detector — sometimes correctly, sometimes not. pdftable follows pdfplumber in offering `lines_strict`, which uses only genuine line segments and ignores rectangle outlines, for documents where the shading is decorative. + +**Curves.** Bezier paths that happen to be axis-aligned are also candidate rulings. A rounded-corner box is a curve, not a rectangle, and dropping it loses a real boundary. + +**Images.** An embedded image is opaque. If a page is a scan, there is no text layer at all and every technique in this series has nothing to work with. That is not a gap in the parser; it is a different problem requiring OCR, and part seven covers where that fits. + +## The bit that carries citations + +Every primitive pdftable reports — glyph, word, line, rectangle, table, individual cell — carries a bounding box in PDF user space. Those coordinates are what make a citation clickable rather than a page number. + +That space needs one conversion before a browser can use it. PDF puts the origin at the bottom-left with Y growing up; CSS, canvas, SVG and PDF.js all put it at the top-left with Y growing down. Invert it and the overlay mirrors vertically — which still *looks* plausible, because the boxes land on real rows, just the wrong ones. + +pdftable does the flip once, in a tested helper: + +```go +r := table.CellsBBox[row][col].Normalized(page.Width(), page.Height()) +// {left, top, width, height} as fractions of the page +``` + +Fractions rather than pixels, so the overlay survives zoom and resizing without recomputation. + +Everything is already normalised before that: the MediaBox origin is translated to (0,0) and any `/Rotate` applied, so a rotated landscape page reports coordinates in the space a viewer displays. I verified this end-to-end by rendering a page to an image, projecting real cell boxes onto it, and confirming the highlights landed on the intended rows. + +--- + +**Next:** [Building pdftable](/posts/building-pdftable) — the architecture, the API, and why the whole thing is a deliberate port rather than an invention. \ No newline at end of file diff --git a/apps/blogs/content/posts/04-building-pdftable.md b/apps/blogs/content/posts/04-building-pdftable.md new file mode 100644 index 0000000..4799567 --- /dev/null +++ b/apps/blogs/content/posts/04-building-pdftable.md @@ -0,0 +1,77 @@ +--- +slug: building-pdftable +title: "Building pdftable" +summary: "The architecture, the API, and why matching pdfplumber exactly was a better decision than improving on it." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 4 +tags: [Go, architecture] +--- + +Vectorless is written in Go. The best PDF table extractor in existence is [pdfplumber](https://github.com/jsvine/pdfplumber), and it is written in Python. + +Go's PDF ecosystem has libraries that render pages to images, libraries that edit metadata, and libraries that extract bag-of-words text. None of them give you what pdfplumber gives Python users: a per-page object model of positioned primitives that you can run layout heuristics over. That gap is the reason pdftable exists. + +## A port, not a reimagining + +The decision that shaped everything: **match pdfplumber's algorithms exactly rather than invent better ones.** + +That is a less exciting choice than it sounds, and it was the right one. pdfplumber's heuristics encode a decade of contact with real, malformed PDFs. Its word-grouping tolerances, its cell-selection order, its treatment of zero-area ruling overlaps — those constants exist because somebody hit a document that needed them. Re-deriving that from first principles would mean rediscovering the same edge cases one production incident at a time. + +Parity is also testable in a way that "better" is not. If pdfplumber and pdftable disagree on a fixture, exactly one of us is wrong and the difference is a bug to explain. Without a reference there is no signal at all — which turns out to be the theme of part six. + +So the port is faithful down to details that look like mistakes: + +- Word grouping clusters on Y1 (the visual top of a glyph) and sorts by X0, matching `WordExtractor`. +- Cell selection walks nearest-first outward, below-outer then right-inner, first-close-wins, one cell per anchor — byte-for-byte with `TableFinder`. +- `Intersect` treats touching-but-not-overlapping boxes as non-overlapping, matching pdfplumber's `o_height + o_width > 0` check, so a ruler grazing a word's box is not reported as intersecting it. + +Where the API differs it is because Go differs. pdfplumber returns dicts keyed `"x0"`, `"top"`, `"x1"`, `"bottom"` in image space. pdftable returns a struct with `X0, Y0, X1, Y1` in PDF user space. Both are defensible; mixing them silently is not, so the two coordinate systems have separate types (`BBox` and `ViewRect`) that cannot be passed to each other by accident. + +## The layers + +``` +pdftable/ the public API — Document, Page, Char, Word, Table, BBox + internal/pdf/ content-stream interpreter, fonts, encodings, CMaps + internal/layout/ edges, intersections, cells, clustering + cmd/pdftable/ CLI +``` + +The interpreter is a straightforward operator dispatch loop over the content stream, maintaining graphics state and text state, emitting typed events to a sink. Unknown operators drop their operands and continue — PDFs in the wild carry private extensions, and aborting a page because of one is worse than ignoring it. + +Only `internal/pdf/reader.go` touches pdfcpu. Everything else is standard-library-only. If pdfcpu ever needs to go — it is heavy and pulls in image codecs pdftable never uses — one file changes. + +## The API + +```go +doc, _ := pdftable.OpenFile("report.pdf") +defer doc.Close() + +for n, page := range doc.Pages() { // Go 1.23 range-over-func + words, _ := page.Words(pdftable.DefaultWordOpts()) + text, _ := page.ExtractText(pdftable.DefaultTextOpts()) + tables, _ := page.ExtractTables(pdftable.DefaultTableSettings()) +} +``` + +Four strategies, selectable per axis independently, so `vertical="text"` with `horizontal="lines"` works — which turns out to matter for the booktabs case from part three. + +Every returned object carries geometry. `Table.CellsBBox[i][j]` is the box of `Table.Rows[i][j]`, which is what makes cell-level citations possible. + +## Two design calls worth defending + +**Alias matching is exact, not fuzzy.** Real PDFs reference `Arial` when they mean Helvetica, and `TimesNewRoman` when they mean Times. Those are true 1:1 metric substitutes, so they alias in, along with subset tags (`ABCDEF+Arial`) and case variants. + +It was tempting to match any BaseFont *containing* "Helvetica". I rejected that: `Arial Narrow` and `Helvetica-Condensed` share the family name but not the metrics. Substituting regular widths there produces a bounding box that looks plausible and is wrong — worse than the honest flat-500 fallback, which at least fails visibly. A test pins the narrow variants as unmatched so nobody "improves" it later. + +**Settings that break parity are opt-in.** `MergeSplitTokens` rejoins values a column boundary cut in half — turning `| ( | 16,135) |` back into `| (16,135) |`. Useful when feeding a table to a language model. It defaults to off, because pdfplumber produces the same splits and enabling it silently would break the compatibility the library promises. Part six has the measurement that makes this more than a preference. + +## Where the tests are + +Golden files generated from pdfplumber, diffed automatically. Fixtures for cases where pdfplumber is *wrong* — Symbol, ZapfDingbats — asserted directly instead, because generating a golden there would pin the wrong answer. + +That distinction took an embarrassing while to arrive at, and part five is about why. + +--- + +**Next:** [The bugs that never crashed](/posts/the-bugs-that-never-crashed) — five silent corruptions, including eight thousandths of a point that flipped the sign of a number in an annual report. \ No newline at end of file diff --git a/apps/blogs/content/posts/05-the-bugs-that-never-crashed.md b/apps/blogs/content/posts/05-the-bugs-that-never-crashed.md new file mode 100644 index 0000000..e17d9f4 --- /dev/null +++ b/apps/blogs/content/posts/05-the-bugs-that-never-crashed.md @@ -0,0 +1,122 @@ +--- +slug: the-bugs-that-never-crashed +title: "The bugs that never crashed" +summary: "Five silent corruptions, including eight thousandths of a point that flipped the sign of a number in an annual report." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 5 +tags: [debugging, correctness] +--- + +![0.008 points decided whether a number was positive](/blog-assets/05-clipped-paren.svg) + +pdftable had been through four releases, had a green test suite, and produced output that looked correct. It was corrupting financial data. + +None of the five bugs below threw an exception. That is what made them expensive. + +## 1 · Every letter the same width + +Covered in part two. Standard-14 fonts may omit `/Widths`; pdftable guessed 500 for every glyph; word boxes drifted up to 11.99pt. + +Fixed by bundling the Adobe Core 14 metrics. Drift went to **0.0000pt**. + +## 2 · Greek extracted as Latin + +Symbol carries its own encoding. pdftable applied StandardEncoding, so `0x61` decoded as `a` instead of `alpha`. + +The metrics for Symbol and ZapfDingbats were bundled but unreachable — 145 of Symbol's 190 glyphs and 201 of ZapfDingbats' 202 resolved to nothing and were discarded at startup. Nothing failed. The data was simply dead. + +## 3 · Every glyph box too high + +Also part two: no `/FontDescriptor` meant no descender, and a second bug meant the descender was not scaled by font size. Error was exactly `0.207 × size`. + +Rows are derived from Y extents, so this one could merge or split table rows. + +## 4 · Nineteen percent of negative numbers lost their sign + +This is the one that changed how I work. + +I pointed the extractor at 3M's 2018 annual report — a real filing, not a fixture. The balance sheet came out beautifully. Every number correct. + +Except accounting writes negatives in brackets. `(16,048)` means −16,048. + +The closing bracket was being **deleted**: + +``` +raw text: Less: Accumulated depreciation (16,135) (16,048) +table cells: | Less: Accumulated depreciation | ( | 16,135) | ( | 16,048 | +``` + +### The geometry + +The `)` glyph spans x = 536.691 → 539.067, so its centre is **537.879**. + +The last column's right edge is **537.871**. + +It missed by **0.008 of a point** — roughly one nine-thousandth of an inch — and was dropped. + +### Why the rule was wrong only at the edge + +Cell assignment gives a glyph to whichever cell contains its centre. At an *interior* boundary that is correct: it settles which of two candidate cells owns a straddling glyph, and every glyph still lands somewhere. + +At the table's *outer* edge there is no competing cell. The same rule stops disambiguating and starts deleting. + +The fix keeps glyphs that merely overlap an outer edge, bounded by the straddling glyph itself so it cannot reach unrelated page content. Interior boundaries are untouched. + +### Scale + +| statement | negatives | lost | +| --- | --- | --- | +| Income | 4 | 2 | +| Comprehensive income | 9 | 0 | +| Balance sheet | 6 | 3 | +| Changes in equity | 40 | 0 | +| **Cash flows** | 44 | **15** | +| **Total** | **103** | **20 (19%)** | + +**Every magnitude was still correct.** Only signs were wrong, which is precisely what made it dangerous. A missing number is obvious. A sign flip on a cash-flow line looks perfect and is completely wrong, and nothing downstream can flag it. + +pdfplumber has the same defect on that row. After this fix pdftable is better than the library it is a port of. + +## 5 · Small print mashed into single words + +Found later, by the expanded test corpus. pdftable dropped space glyphs and inferred word boundaries from gaps alone. At 8pt a space is `278/1000 × 8 = 2.22pt` — under the 3pt tolerance — so a whole line collapsed: + +``` +pdftable "Wimillegible3,142(16,048)" +pdfplumber 'Wim' 'illegible' '3,142' '(16,048)' +``` + +pdfplumber's own gap test would not have split there either (`90.22 > 87.99 + 3` is false), which proves it split on the space *glyph*. It ends a word **at** whitespace before any gap test runs. + +Body text in real documents is routinely 8–9pt. This was quietly mangling small print everywhere. + +## The finding that was wrong + +Extraction returns `"Consolidated Balance Shee t"` for that page heading. I filed it as a bug and speculated about letter-spacing handling. + +It is not a bug. That gap is **in 3M's document**. The rendered page shows it and poppler extracts it identically — the filing agent typeset it that way, which is common in SEC EDGAR documents. + +The mistake was methodological. I had been comparing pdftable's *table* output against pdftable's *own text* output. Internal agreement tells you the two code paths disagree; it cannot tell you which one is right. I was marking my own homework. + +Checking properly meant three independent sources: the page rendered to an image and read directly, poppler's `pdftotext`, and pdfplumber. Two findings survived. One did not, and "fixing" it would have meant inventing text the document does not contain. + +## Three more I caused and caught the same day + +Worth listing, because they show the same discipline working: + +1. **A sheared table.** My merge fix worked per-row, so a header band merged while the data rows below did not — rows ended up with different column counts. My tests asserted merged *text* and never checked column counts. +2. **Welded words.** The space-boundary check counted the space glyph itself as adjacent ink, so `millions, except` became `millions,except`. +3. **Then I overcorrected** and blocked every merge. The right rule is narrow: only a space *in the gap* counts. + +Every one was caught by looking at real output, not by a green suite. + +## What they have in common + +None of these crashed. None produced obviously broken output. Every one would have degraded answer quality in a way that looked like a model problem. + +A crash gets fixed in an hour because it is loud. A flipped minus sign gets believed. + +--- + +**Next:** [Measuring instead of believing](/posts/measuring-instead-of-believing) — benchmarks, a harness that measured itself, and the number that redirected the project. \ No newline at end of file diff --git a/apps/blogs/content/posts/06-measuring-instead-of-believing.md b/apps/blogs/content/posts/06-measuring-instead-of-believing.md new file mode 100644 index 0000000..9f29ba1 --- /dev/null +++ b/apps/blogs/content/posts/06-measuring-instead-of-believing.md @@ -0,0 +1,140 @@ +--- +slug: measuring-instead-of-believing +title: "Measuring instead of believing" +summary: "Benchmarks, a harness that measured itself twice, and the number that redirected the whole project." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 6 +tags: [benchmarks, measurement] +--- + +![ICDAR 2013 results and the ceiling](/blog-assets/06-benchmark-ceiling.svg) + +Four real bugs had survived four releases with a green test suite. So I looked at what the suite actually covered. + +**Twenty-six words. One font. Two synthetic files.** + +That is a spelling test with two words on it. Passing it proves nothing, and it explains precisely why the font bugs lived so long: nothing was measuring the part that was broken. + +There was a second problem, subtler. The tests compared pdftable against numbers I had written down myself. If I was wrong when I wrote them, the test agrees with the wrong answer forever. + +## Building an exam + +Two things were needed: more questions, and an answer key somebody else wrote. + +**More questions.** Fixture PDFs covering all twelve Latin standard fonts at three sizes each — 170 words instead of 26. Three sizes matters: a descender scaled by the wrong *factor* is indistinguishable from a wrong *constant* if you only ever test one size. + +The fixtures are built by hand rather than with a PDF library, deliberately. The standard-14 pages carry **no `/Widths` and no `/FontDescriptor`**, which is spec-legal and is exactly the case that hid both metric bugs. A fixture written by reportlab would embed the metrics and quietly test nothing. + +**An answer key.** pdfplumber, diffed automatically. + +With one rule that matters: **pdfplumber is only the answer key where it is actually right.** For Symbol it returns `abgdep` where the answer is `αβγδεπ`. Generating a golden from it there would permanently pin the wrong answer. Those fixtures get their own assertions instead. + +The corpus found a bug within minutes — the 8pt word-merging from part five. + +## The real benchmark + +Synthetic fixtures test fidelity, not competence. For that I used the **ICDAR 2013 Table Competition** set: 125 born-digital PDFs from EU and US government sources with per-cell ground truth, scored on **adjacency relations** — for each non-empty cell, is the right thing beside it and below it. + +That metric exists because a cell-by-cell grid diff is too brittle. Two tools can grid the same table differently — one emits a spacer column, the other does not — and convey identical structure. A grid diff calls that a failure; adjacency asks the question a reader cares about. + +| system | precision | recall | F1 | +| --- | --- | --- | --- | +| pdftable (`lines`) | 0.865 | 0.229 | **0.362** | +| pdfplumber (`lines`) | 0.868 | 0.235 | **0.370** | +| pdfplumber (`text`) | 0.167 | 0.679 | 0.267 | + +Two readings. + +**Parity holds at the table level.** 0.362 against 0.370 with near-identical precision. Everything before this validated *text* fidelity; this is the first measurement of table behaviour, and the port tracks its reference there too. + +**Both are weak.** Precision 0.865 says what we extract is right. Recall 0.229 says we miss three quarters of it. + +One caveat that matters: **0.36 is not comparable to the 0.85–0.95 in table-structure papers.** Those benchmarks hand the system the table region and score only the gridding. This runs the harder end-to-end task. + +## A hypothesis, tested and wrong + +The zero-detection documents were all ruled on one axis (part three). Infer the other axis and recall should rise. + +I built it, measured it, and it did not work: + +| | `lines` | `auto` | +| --- | --- | --- | +| documents with no table found | 28 (22%) | **23 (18%)** | +| tables detected | 306 | **331** | +| F1 | **0.362** | **0.358** | + +Detection improved. The newly-found tables were gridded badly, so aggregate F1 went *down*. + +That corrected an earlier conclusion of mine. I had written "the bottleneck is detection, not structure", reasoning from precision 0.865 on detected documents — a population **self-selected by being fully ruled**. On the hard ones, structure is weak too. + +The feature shipped as opt-in, with the negative result written up, so nobody re-runs the experiment blind. + +## The measurement that redirected everything + +If the grid is the problem, how good would extraction be with a *perfect* grid? That number decides whether a layout model is worth deploying. + +pdftable already had the hook — `StrategyExplicit` takes caller-supplied boundaries — so I fed it the ground-truth grid. + +**First result: 0.119.** + +With perfect input. Against 0.865 precision on *guessed* boundaries. + +That is not a plausible property of the system under test. Perfect input producing eight times worse output means the instrument is bent. The tempting read was "extraction is worse than I thought". The correct read was "my ruler is broken". + +### Bug one: every cell edge became a grid line + +The naive derivation collects every `x1` and `x2` from every cell box. It fails because **a cell's box hugs its content, not its column**: + +``` +row 1, col 1: "Total" x = 56.7 → 145.0 +row 2, col 1: "Accounts receivable" x = 56.7 → 210.5 +row 3, col 1: "Cash" x = 56.7 → 118.3 +``` + +Column 1's right edge produces a different value on every row. Across 20 rows × 3 columns that is up to **120 distinct x values where the grid has 4 lines**. pdftable built a 120-column grid and shredded every cell into slivers. + +The fix uses the ground truth's own logical indices. Each cell carries `start-col` / `end-col`, so a column's extent comes from the cells that *define* it — and only cells that **begin** in column `c` set its left edge, only cells that **end** in `c` set its right edge. A cell spanning columns 1–3 says nothing about column 2's boundaries. + +Boundaries are then the outer edges plus the midpoint of each gutter. Using either neighbouring edge alone would systematically clip one side. That yields exactly `ncols + 1` lines. + +Rows need the same logic with a twist: **row 0 is the top row, and Y grows upward**, so index order and coordinate order run opposite. Negating on the way in and out lets one function serve both axes. + +**0.119 → 0.782.** + +### Bug two: scoring against tables I never attempted + +Recall was 0.665 while precision was 0.948. That asymmetry is a tell — with perfect boundaries it should not happen. + +I excluded multi-table pages, because merging two regions into one edge set recreates bug one. But I scored against ground truth for **every** region in the document, including the pages I had skipped. My own exclusion policy was being counted as extraction failure. + +**0.782 → 0.935.** Recall 0.665 → 0.922; precision unmoved, exactly as a denominator fix should behave. + +### A hypothesis killed cheaply + +I also suspected the ground-truth Y origin might be top-left. Rather than guess, I checked one cell against pdfplumber: + +``` +'Q1' GT y1 = 619.0 word y0 = 616.9 flipped would be 211.9 +``` + +2.1 points apart — a box-versus-glyph difference, not a flip. Dead in one command, and I avoided "fixing" something that was never broken. + +## The number + +| system | precision | recall | F1 | +| --- | --- | --- | --- | +| pdftable, current best | 0.865 | 0.229 | **0.362** | +| **pdftable + perfect grid** | **0.948** | **0.922** | **0.935** | + +Given a correct grid, extraction is near-perfect. **Essentially the whole gap is table structure**, not text extraction — every font-metric and sign-preservation fix in this series is real, and none of it is the limiting factor. + +### Why I believe 0.935 + +Precision and recall are balanced; broken harnesses produce lopsided numbers, and mine had 0.113/0.125. It sits below 1.0, which is right — a perfect score would suggest I was comparing ground truth to itself. Each jump has an explanation. And `MergeSplitTokens` *lowers* it, 0.935 → 0.726, which is exactly what should happen: that setting repairs columns a geometric guess cut through, so with a correct grid every merge destroys a good cell. A broken harness would not produce a coherent relationship like that. + +That last one was the strongest signal the instrument was finally sound. + +--- + +**Next:** [What is still missing](/posts/what-is-still-missing) — the gaps, the models worth deploying, and the ones that would cost more than they return. \ No newline at end of file diff --git a/apps/blogs/content/posts/07-what-is-still-missing.md b/apps/blogs/content/posts/07-what-is-still-missing.md new file mode 100644 index 0000000..af96af6 --- /dev/null +++ b/apps/blogs/content/posts/07-what-is-still-missing.md @@ -0,0 +1,104 @@ +--- +slug: what-is-still-missing +title: "What is still missing" +summary: "The gaps, the models worth deploying, the ones that are not, and three lessons that are not PDF-specific." +publishedAt: 2026-08-03 +series: "Reading a PDF like a printer" +part: 7 +tags: [roadmap, models] +--- + +![The model finds the grid, the parser reads the cells](/blog-assets/07-hybrid-architecture.svg) + +The measurement in part six settles the roadmap. Reading cells is solved; finding rows and columns is not. Everything below follows from that. + +## Where pdftable stands + +| | | +| --- | --- | +| Word position drift vs pdfplumber | **0.0000pt**, both axes | +| Negative signs preserved (3M 10-K) | **103 / 103** | +| Fonts exercised end to end | 12, at three sizes | +| Table F1, ICDAR 2013 | 0.362 — level with pdfplumber's 0.370 | +| Table F1 given a correct grid | **0.935** | +| Untested | scanned pages, CJK, embedded subset fonts, multi-table pages | + +That last row is the honest caveat. pdftable is measurably good on standard business documents and unmeasured on scans and CJK. Anyone repeating these numbers should repeat that line too. + +## The split, decided by measurement rather than taste + +Detection is a **vision** problem: where is the table, and where are its rows and columns. Content is a **text-layer** problem: what exactly do the cells say, and where precisely are they. + +pdftable is provably better at the second — it preserves accounting minus signs that pdfplumber drops. It is measurably weak at the first. So the sensible architecture uses each for what it is good at: + +- **layout model → rows, columns, spans** +- **pdftable → cell contents and coordinates** + +No library changes are needed. The hook already exists: + +```go +s := pdftable.DefaultTableSettings() +s.VerticalStrategy = pdftable.StrategyExplicit +s.HorizontalStrategy = pdftable.StrategyExplicit +s.ExplicitVerticalLines = colBoundaries // from the model +s.ExplicitHorizontalLines = rowBoundaries +s.MergeSplitTokens = false +tables, _ := page.ExtractTables(s) +``` + +The caller owns the model call. pdftable stays deterministic, offline and free of network dependencies — which is worth protecting. A parser that quietly makes an HTTP request is a different kind of component. + +That `MergeSplitTokens = false` is not a stylistic preference. It is measured: leaving it on drops the oracle result from **0.935 to 0.726**, because the setting exists to repair columns a *geometric guess* cut through a value. Given a correct grid there is nothing to repair, so every merge destroys a correct cell. + +## Which model, and why not the obvious one + +The instinct is to reach for a document VLM. GLM-OCR is the current standout — 0.9B parameters, MIT licensed, scoring 94.62 on OmniDocBench v1.5 against Gemini 3 Pro's 90.33, and beating it on tables at 86.0 TEDS versus 81.8. A sub-billion-parameter model outperforming a frontier model on document parsing is a real result. + +It is still the wrong tool for this job, for three reasons. + +**It emits markdown, not coordinates.** A generative model produces tokens. Tokens cannot drive `StrategyExplicit`, and they cannot anchor a citation to a region of a page. Replacing the parser with a VLM means losing the ability to say *where* an answer came from — which for Vectorless is the product, not a feature. + +**TEDS 86 is not 99.** That is roughly 14% structural error. On a 57-row balance sheet that is several rows wrong, and you cannot tell which. pdftable given a correct grid scores 0.935 with *deterministic* behaviour — the same input always produces the same output, and a bug is reproducible. + +**A hallucinated digit is unbounded.** The sign bug in part five was bad, but it was systematic: one rule, one root cause, one commit, provably zero afterwards. A model that renders 1,292 as 1,232 does it stochastically. You can measure the rate; you cannot eliminate it. + +There is a subtler asymmetry too. Geometric parsers **fail loudly and locally** — no rulings found, low fill ratio, obviously misaligned columns. Models fail **uniformly and quietly**: always plausible, occasionally wrong, never flagged. For extraction feeding an answer engine, a failure you can detect beats a lower average error you cannot. + +### What to deploy instead + +For digital PDFs, a layout model that outputs geometry: + +| | weights | params | role | +| --- | --- | --- | --- | +| `table-transformer-detection` | 110 MB | ~28M | finds tables on a page | +| `table-transformer-structure-recognition` | 110 MB | ~28M | rows, columns, spans | +| **both** | **~220 MB** | ~56M | the 0.935 path | +| `GLM-OCR` | 2,528 MB | 0.9B | scanned pages only | + +Table Transformer is **23× smaller** than GLM-OCR and, at 28M parameters, CPU-viable. That changes the deployment story completely: no GPU, no separate endpoint, no hourly instance. Both models fit in an existing container alongside the engine. Roughly 0.5–2s per page on CPU, ~50ms on GPU, 55 MB each at fp16. + +GLM-OCR still has a place — scanned pages have no text layer, so geometry has nothing to work with and OCR is the only option. But that is a rarer path, invoked per-document rather than per-page, and it can justify a GPU endpoint on its own terms. + +Two models on different documents, not one model doing everything. + +## The remaining work, in order + +1. **Integrate Table Transformer behind `StrategyExplicit`** and measure against the 0.935 ceiling. Expect 0.75–0.85 rather than 0.935 — ICDAR 2013 is out-of-domain for a model trained on PubTables-1M. That gap between ceiling and delivery is the number worth knowing before committing. +2. **Widen the corpus.** No scanned pages, no CJK, no embedded subset fonts. Each is a plausible source of the next silent bug. +3. **Multi-table pages**, excluded from the ceiling measurement and therefore unmeasured. + +## What I would tell someone starting this + +Three things, in order of how much time they would have saved me. + +**The dangerous bugs are the quiet ones.** Everything expensive in this project was silent. A crash gets fixed in an hour. A flipped minus sign gets believed, and surfaces two layers up as *the model is bad at maths*. + +**You cannot validate a system from inside itself.** Comparing pdftable's tables to pdftable's own text found a real bug and invented a fake one, and gave no way to tell them apart. The fix was three external references: a rendered page, poppler, pdfplumber. Any fidelity claim needs an oracle you did not write. + +**When a measurement disagrees violently with expectation, suspect the measurement.** It happened twice. The descender error looked like noise until `0.207 × size` gave it away. The oracle harness looked like a catastrophic parser failure and was my own grid derivation. Getting the second one backwards would have meant reporting 0.119 and concluding the hybrid was not worth building — when it is worth 0.94. + +--- + +*Full evaluation reports, with commits, oracles and caveats, are in [`docs/evaluations/`](../evaluations/). Benchmarks that download their own datasets are in [`bench/`](../../bench/).* + +**Back to:** [the series index](/) \ No newline at end of file diff --git a/apps/blogs/lib/markdown.tsx b/apps/blogs/lib/markdown.tsx new file mode 100644 index 0000000..a9e02c4 --- /dev/null +++ b/apps/blogs/lib/markdown.tsx @@ -0,0 +1,257 @@ +import React from "react"; +import { Figure } from "@/components/Figure"; + +/** + * Markdown → React, for the subset these posts actually use. + * + * Written rather than pulled in, for the same reason as the frontmatter + * parser: this app sits in a pnpm workspace and a new dependency edits a + * lockfile shared across every app. The supported subset is closed and + * known — headings, paragraphs, lists, fenced code, tables, blockquotes, + * images, and inline code/bold/italic/links. + * + * Everything is styled with design tokens rather than the typography + * plugin, because DESIGN.md has opinions the plugin does not share: + * Instrument Serif appears only on accent clauses, mono eyebrows carry + * 0.16em tracking, and code panels are the one place a dark surface is + * allowed on a light canvas. + */ + +/** Inline: `code`, **bold**, *italic*, [text](href). */ +function inline(text: string, keyPrefix: string): React.ReactNode[] { + const out: React.ReactNode[] = []; + const pattern = /(`[^`]+`)|(\*\*[^*]+\*\*)|(\*[^*]+\*)|(\[[^\]]+\]\([^)]+\))/g; + let last = 0; + let m: RegExpExecArray | null; + let i = 0; + + while ((m = pattern.exec(text))) { + if (m.index > last) out.push(text.slice(last, m.index)); + const token = m[0]; + const key = `${keyPrefix}-i${i++}`; + + if (token.startsWith("`")) { + out.push( + + {token.slice(1, -1)} + , + ); + } else if (token.startsWith("**")) { + out.push( + + {token.slice(2, -2)} + , + ); + } else if (token.startsWith("[")) { + const link = /\[([^\]]+)\]\(([^)]+)\)/.exec(token)!; + const external = /^https?:/.test(link[2]); + out.push( + + {link[1]} + , + ); + } else { + out.push( + + {token.slice(1, -1)} + , + ); + } + last = m.index + token.length; + } + if (last < text.length) out.push(text.slice(last)); + return out; +} + +function splitRow(row: string): string[] { + return row + .replace(/^\||\|$/g, "") + .split("|") + .map((c) => c.trim()); +} + +export function renderMarkdown(md: string): React.ReactNode[] { + const lines = md.split(/\r?\n/); + const nodes: React.ReactNode[] = []; + let i = 0; + let k = 0; + + while (i < lines.length) { + const line = lines[i]; + + if (!line.trim()) { + i++; + continue; + } + + // Fenced code — the one dark surface allowed on this canvas. + if (line.startsWith("```")) { + const lang = line.slice(3).trim(); + const buf: string[] = []; + i++; + while (i < lines.length && !lines[i].startsWith("```")) buf.push(lines[i++]); + i++; + nodes.push( +
+ {lang && ( +
+ {lang} +
+ )} +
+            
+              {buf.join("\n")}
+            
+          
+
, + ); + continue; + } + + // Image → Figure (captioned, hairline-framed). + const img = /^!\[([^\]]*)\]\(([^)]+)\)\s*$/.exec(line); + if (img) { + nodes.push(
); + i++; + continue; + } + + // Table. + if (line.trim().startsWith("|") && lines[i + 1]?.includes("---")) { + const head = splitRow(line); + i += 2; + const rows: string[][] = []; + while (i < lines.length && lines[i].trim().startsWith("|")) rows.push(splitRow(lines[i++])); + nodes.push( +
+
+ + + {head.map((h, c) => ( + + ))} + + + + {rows.map((r, ri) => ( + + {r.map((c, ci) => ( + + ))} + + ))} + +
+ {inline(h, `th${c}`)} +
+ {inline(c, `td${ri}-${ci}`)} +
+
, + ); + continue; + } + + // Headings. + const h = /^(#{1,4})\s+(.*)$/.exec(line); + if (h) { + const depth = h[1].length; + const text = h[2]; + if (depth <= 2) { + nodes.push( +

+ {inline(text, `h${k}`)} +

, + ); + } else { + nodes.push( +

+ {inline(text, `h${k}`)} +

, + ); + } + i++; + continue; + } + + // Blockquote — the accent register. Instrument Serif italic, used + // sparingly, matching the "one poetic clause" rule. + if (line.startsWith(">")) { + const buf: string[] = []; + while (i < lines.length && lines[i].startsWith(">")) buf.push(lines[i++].replace(/^>\s?/, "")); + nodes.push( +
+ {inline(buf.join(" "), `bq${k}`)} +
, + ); + continue; + } + + // Lists. + if (/^\s*([-*]|\d+\.)\s+/.test(line)) { + const ordered = /^\s*\d+\./.test(line); + const items: string[] = []; + while (i < lines.length && /^\s*([-*]|\d+\.)\s+/.test(lines[i])) { + items.push(lines[i++].replace(/^\s*([-*]|\d+\.)\s+/, "")); + } + const Tag = ordered ? "ol" : "ul"; + nodes.push( + + {items.map((it, n) => ( +
  • {inline(it, `li${n}`)}
  • + ))} +
    , + ); + continue; + } + + // Horizontal rule. + if (/^---+\s*$/.test(line)) { + nodes.push(
    ); + i++; + continue; + } + + // Paragraph — consume until a blank line. + const buf: string[] = []; + while (i < lines.length && lines[i].trim() && !/^(#{1,4}\s|```|>|\||---+\s*$)/.test(lines[i])) { + if (/^\s*([-*]|\d+\.)\s+/.test(lines[i])) break; + buf.push(lines[i++]); + } + if (buf.length) { + nodes.push( +

    + {inline(buf.join(" "), `p${k}`)} +

    , + ); + } + } + + return nodes; +} diff --git a/apps/blogs/lib/posts.ts b/apps/blogs/lib/posts.ts new file mode 100644 index 0000000..da1f71a --- /dev/null +++ b/apps/blogs/lib/posts.ts @@ -0,0 +1,130 @@ +import fs from "node:fs"; +import path from "node:path"; + +/** + * A post, in the shape it would take as a database row. + * + * Content currently comes from markdown files on disk. The point of this + * module is that nothing outside it knows that — pages call `getAllPosts` + * and `getPost`, so moving to Postgres later means reimplementing two + * functions, not touching every page. + * + * Field names are chosen to map onto columns directly: `slug` is the + * primary key, `publishedAt` is an ISO date so it sorts and indexes + * without parsing, and `body` is the raw markdown rather than rendered + * HTML so the renderer can change without a migration. + */ +export interface Post { + slug: string; + title: string; + /** One sentence. Used on cards and as the meta description. */ + summary: string; + /** ISO-8601. Stored as a string so it survives serialisation to a + * client component without a Date round-trip. */ + publishedAt: string; + /** Ordinal within a series. Drives "Part 3 of 7" and prev/next. */ + part?: number; + series?: string; + /** Minutes, computed from the body at load time — never hand-written, + * because a hand-written one is wrong the moment the post is edited. */ + readingMinutes: number; + tags: string[]; + /** Raw markdown. */ + body: string; +} + +const CONTENT_DIR = path.join(process.cwd(), "content", "posts"); + +/** + * Minimal YAML-frontmatter reader. + * + * Deliberately not gray-matter. This app lives in a pnpm workspace, so + * adding a dependency edits a lockfile shared by every app in the + * monorepo — a change with blast radius well beyond a blog. The subset + * needed here is small and fully under our control: strings, ISO dates, + * numbers, and one flat array. + */ +function parseFrontmatter(raw: string): { data: Record; body: string } { + const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw); + if (!match) return { data: {}, body: raw }; + + const data: Record = {}; + for (const line of match[1].split(/\r?\n/)) { + const kv = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line.trim()); + if (!kv) continue; + const [, key, rawValue] = kv; + let value = rawValue.trim(); + + if (value.startsWith("[") && value.endsWith("]")) { + data[key] = value + .slice(1, -1) + .split(",") + .map((s) => s.trim().replace(/^["']|["']$/g, "")) + .filter(Boolean); + continue; + } + value = value.replace(/^["']|["']$/g, ""); + data[key] = /^\d+$/.test(value) ? Number(value) : value; + } + return { data, body: match[2] }; +} + +/** + * Reading time from word count. + * + * 200 wpm is the low end of the usual range, chosen on purpose: these + * posts carry code blocks and tables, which people read far slower than + * prose. Over-promising a short read is worse than the reverse. + */ +function readingMinutes(body: string): number { + const words = body + .replace(/```[\s\S]*?```/g, " ") + .replace(/[#>*_`|-]/g, " ") + .split(/\s+/) + .filter(Boolean).length; + return Math.max(1, Math.round(words / 200)); +} + +function toPost(fileName: string): Post { + const raw = fs.readFileSync(path.join(CONTENT_DIR, fileName), "utf8"); + const { data, body } = parseFrontmatter(raw); + return { + slug: (data.slug as string) ?? fileName.replace(/\.md$/, ""), + title: (data.title as string) ?? "Untitled", + summary: (data.summary as string) ?? "", + publishedAt: (data.publishedAt as string) ?? "1970-01-01", + part: data.part as number | undefined, + series: data.series as string | undefined, + tags: (data.tags as string[]) ?? [], + readingMinutes: readingMinutes(body), + body: body.trim(), + }; +} + +/** Newest first, or by series order when every post is in one series. */ +export function getAllPosts(): Post[] { + if (!fs.existsSync(CONTENT_DIR)) return []; + const posts = fs + .readdirSync(CONTENT_DIR) + .filter((f) => f.endsWith(".md")) + .map(toPost); + + const everyPostIsPartOfASeries = posts.length > 0 && posts.every((p) => p.part != null); + return posts.sort((a, b) => + everyPostIsPartOfASeries + ? (a.part ?? 0) - (b.part ?? 0) + : b.publishedAt.localeCompare(a.publishedAt), + ); +} + +export function getPost(slug: string): Post | undefined { + return getAllPosts().find((p) => p.slug === slug); +} + +/** Neighbours within a series, for prev/next links at the foot of a post. */ +export function getSeriesNeighbours(slug: string): { prev?: Post; next?: Post } { + const posts = getAllPosts(); + const i = posts.findIndex((p) => p.slug === slug); + if (i === -1) return {}; + return { prev: posts[i - 1], next: posts[i + 1] }; +} diff --git a/apps/blogs/public/blog-assets/00-hero.svg b/apps/blogs/public/blog-assets/00-hero.svg new file mode 100644 index 0000000..ff37ca8 --- /dev/null +++ b/apps/blogs/public/blog-assets/00-hero.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + pdftable + + ENGINEERING NOTES · 7 PARTS + + + A PDF is not a document. + It is a set of printer instructions. + + So to read one, you have to think like a printer. + + + + + + WORD DRIFT + NEGATIVE SIGNS KEPT + FONTS COVERED + TABLE F1 CEILING + + + 11.99pt 0.0000pt + 81% 100% + 1 12 + 0.362 0.935 + + + A Go library for reading PDFs, built while building the Vectorless engine — and the bugs that never crashed anything. + diff --git a/apps/blogs/public/blog-assets/01-pdf-anatomy.svg b/apps/blogs/public/blog-assets/01-pdf-anatomy.svg new file mode 100644 index 0000000..71edf58 --- /dev/null +++ b/apps/blogs/public/blog-assets/01-pdf-anatomy.svg @@ -0,0 +1,58 @@ + + + + WHAT IS ACTUALLY IN THE FILE + + + What people assume a PDF stores + + + + + + Paragraphs, sentences, words + + + Headings and section structure + + + Tables with rows and columns + + + Reading order + + + Where one word ends and the next begins + + NONE OF THIS EXISTS + + + + + + + + + + What it stores instead + + + + CONTENT STREAM + + BT begin text + /F1 12 Tf font, size + 72 720 Td put the pen here + (Revenue) Tj paint these glyphs + ET end text + + 72 700 m move to + 540 700 l line to + S stroke it + + DRAWING COMMANDS, NOT CONTENT + + + The stream never says where the second glyph goes. It says where the first one starts, and leaves you to add up how wide each one is. + Every word, line and table is inferred — never read. + diff --git a/apps/blogs/public/blog-assets/02-advance-drift.svg b/apps/blogs/public/blog-assets/02-advance-drift.svg new file mode 100644 index 0000000..76ff721 --- /dev/null +++ b/apps/blogs/public/blog-assets/02-advance-drift.svg @@ -0,0 +1,74 @@ + + + + POSITION IS A RUNNING SUM + One wrong width moves every glyph after it + + + TRUE AFM WIDTHS + + + + + + i + 222 + + + m + 833 + + + W + 944 + + + i + 222 + + + word ends here + + + + FLAT 500 GUESS — WHAT PDFTABLE DID + + + + + i + 500 + + + m + 500 + + + W + 500 + + + i + 500 + + + + + error after 4 glyphs + + + + + ACROSS A FULL LINE + + + 4 glyphs + ~0.3pt off + 40 glyphs + 11.99pt off + = more than one character's width + + + + Like pacing out a room when every step is slightly wrong — one step is fine, forty puts you in the wrong room. + diff --git a/apps/blogs/public/blog-assets/03-encoding-chain.svg b/apps/blogs/public/blog-assets/03-encoding-chain.svg new file mode 100644 index 0000000..69d0db6 --- /dev/null +++ b/apps/blogs/public/blog-assets/03-encoding-chain.svg @@ -0,0 +1,59 @@ + + + + THREE NAMESPACES, NOT ONE + A byte in a PDF is not a character + + + + + + 1 · CHARACTER CODE + 0x61 + one byte in the stream + + + + /Encoding + + + + 2 · GLYPH NAME + "alpha" + an Adobe glyph name + + + + Adobe Glyph List + + + + 3 · UNICODE + α + U+03B1 + what a human or an LLM reads + + + + + + THE FAILURE — SYMBOL FONT WITH NO /ENCODING DECLARED + + Symbol carries its own built-in encoding. Apply the default instead and step 2 returns the wrong name: + + + 0x61 + + "a" + + a + instead of + "alpha" + + α + — a paper of Greek comes back as nonsense Latin + + + + Width lookup runs code → Unicode → width, so a broken step 2 corrupts the geometry as well as the text. One fault, both chains. + diff --git a/apps/blogs/public/blog-assets/04-table-strategies.svg b/apps/blogs/public/blog-assets/04-table-strategies.svg new file mode 100644 index 0000000..cad87b0 --- /dev/null +++ b/apps/blogs/public/blog-assets/04-table-strategies.svg @@ -0,0 +1,83 @@ + + + + A TABLE IS NOT STORED — IT IS INFERRED + Two strategies, two failure modes + + + + LINES — CELLS FROM RULING INTERSECTIONS + + + + + + + + + + + + + + + + + + + + + + Total assets36,50037,987 + Goodwill10,05110,513 + Other assets1,3451,395 + + + Works when rulings cross. Precise when it fires. + Blind to tables with no vertical rules. + + + + + TEXT — COLUMNS FROM WORD ALIGNMENT + + + + + + + + + + + + + + + Total assets36,50037,987 + Goodwill10,05110,513 + Other assets1,3451,395 + + + Sees borderless and half-ruled tables. + Prose also aligns — it invents tables on body text. + + + + + + MEASURED ON ICDAR 2013 · 125 PDFS + + lines + precision 0.865 + recall 0.229 + what it finds is right; it finds a quarter + + + text + precision 0.167 + recall 0.679 + finds most of it; most of what it finds is wrong + + + diff --git a/apps/blogs/public/blog-assets/05-clipped-paren.svg b/apps/blogs/public/blog-assets/05-clipped-paren.svg new file mode 100644 index 0000000..e47b85e --- /dev/null +++ b/apps/blogs/public/blog-assets/05-clipped-paren.svg @@ -0,0 +1,69 @@ + + + + 3M 2018 FORM 10-K · PAGE 58 · BALANCE SHEET + 0.008 points decided whether a number was positive + + + + GLYPH POSITIONS, ZOOMED ~30× + + + + + + + + + ( + 1 + 6 + , + 0 + 4 + 8 + ) + + + + + + + glyph centre 537.879 + + + + column edge 537.871 + + + + + + + + THE MARGIN + 0.008 pt + about one nine-thousandth of an inch + + + + + THE CONSEQUENCE + + (16,048) + becomes + 16,048 + + −16,048 read back as +16,048 + + + + + ACROSS FIVE FINANCIAL STATEMENTS + + + 20 of 103negative numbers lost their sign — 19% — while every magnitude stayed correct. + + A missing value is detectable. A flipped sign is believed. + + diff --git a/apps/blogs/public/blog-assets/06-benchmark-ceiling.svg b/apps/blogs/public/blog-assets/06-benchmark-ceiling.svg new file mode 100644 index 0000000..b58803b --- /dev/null +++ b/apps/blogs/public/blog-assets/06-benchmark-ceiling.svg @@ -0,0 +1,65 @@ + + + + ICDAR 2013 TABLE COMPETITION · 125 PDFS · ADJACENCY-RELATION F1 + Where the points actually are + + + + + + + + + + + + 0 + 0.25 + 0.50 + 0.75 + 1.00 + + + + + + 0.370 + + + + 0.362 + + + + 0.358 + + + + 0.267 + + + + 0.935 + + + + + pdfplumber · lines + pdftable · lines + pdftable · auto + pdfplumber · text + + pdftable + perfect grid + ORACLE BOUNDARIES + + + + + WHAT THE GAP MEANS + + Hand pdftable a correct grid and it scores 0.935. Left to find the grid itself, 0.362. + + Reading cells was never the problem. Finding rows and columns is. + + diff --git a/apps/blogs/public/blog-assets/07-hybrid-architecture.svg b/apps/blogs/public/blog-assets/07-hybrid-architecture.svg new file mode 100644 index 0000000..8a7d8be --- /dev/null +++ b/apps/blogs/public/blog-assets/07-hybrid-architecture.svg @@ -0,0 +1,87 @@ + + + + SPLIT BY WHICH HALF IS WEAK + The model finds the grid. The parser reads the cells. + + + + + PDF page + text layer + pixels + + + + + + + + + + + + + + RENDERED IMAGE → LAYOUT MODEL + Table Transformer · 220 MB · CPU-viable + outputs rows, columns, spans + + + + + + TEXT LAYER → PDFTABLE + exact glyphs, exact coordinates + deterministic, offline, no model + + + + + + + + + + + + + STRATEGY EXPLICIT — ALREADY IN THE API + + ExplicitVerticalLines = cols + ExplicitHorizontalLines = rows + MergeSplitTokens = false + + + + + + + OUTPUT + cell text + per-cell bbox → citations + + + + + + + + WHY NOT LET A VLM DO ALL OF IT + + A generative model emits tokens, not coordinates. + No coordinates means no citation highlighting — + and no way to point at the page an answer came from. + A hallucinated digit is also unbounded and unfixable. + + + + + + WHERE THE VLM DOES BELONG + + Scanned pages have no text layer at all. + Geometry has nothing to work with, so OCR is the + only option — GLM-OCR, 2.5 GB, on a GPU endpoint. + Two models, invoked on different documents. + + + diff --git a/apps/blogs/tsconfig.tsbuildinfo b/apps/blogs/tsconfig.tsbuildinfo new file mode 100644 index 0000000..8383072 --- /dev/null +++ b/apps/blogs/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.collection.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.collection.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.regexp.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2024.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.collection.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.decorators.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.iterator.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.float16.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.error.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./.next/types/routes.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/global.d.ts","../../node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/styled-jsx/types/css.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/styled-jsx/types/macro.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/styled-jsx/types/style.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/styled-jsx/types/global.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/styled-jsx/types/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/amp.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/amp.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/get-page-files.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/compatibility/index.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/globals.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/web-globals/events.d.ts","../../node_modules/.pnpm/buffer@5.7.1/node_modules/buffer/index.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/assert.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/assert/strict.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/async_hooks.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/buffer.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/child_process.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/cluster.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/console.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/constants.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/crypto.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/dgram.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/dns.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/dns/promises.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/domain.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/events.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/fs.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/fs/promises.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/http.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/http2.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/https.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/inspector.generated.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/module.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/net.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/os.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/path.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/perf_hooks.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/process.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/punycode.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/querystring.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/readline.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/readline/promises.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/repl.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/sea.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/stream.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/stream/promises.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/stream/consumers.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/stream/web.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/string_decoder.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/test.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/timers.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/timers/promises.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/tls.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/trace_events.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/tty.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/url.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/util.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/v8.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/vm.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/wasi.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/worker_threads.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/zlib.d.ts","../../node_modules/.pnpm/@types+node@20.19.39/node_modules/@types/node/index.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/canary.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/experimental.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/index.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/canary.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/experimental.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/fallback.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/webpack/webpack.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/load-custom-routes.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/image-config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/body-streams.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/cache-control.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/setup-exception-listeners.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/worker.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/constants.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/app-router-headers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/rendering-mode.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/require-hook.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/experimental/ppr.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/page-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-polyfill-crypto.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-baseline.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-extensions/random.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-extensions/date.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/node-environment.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/page-extensions-type.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/instrumentation/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/coalesced-function.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-utils/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/constants.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/trace/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/trace/trace.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/trace/shared.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/trace/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/load-jsconfig.d.ts","../../node_modules/.pnpm/@next+env@15.5.14/node_modules/@next/env/dist/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/telemetry/storage.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/build-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/bloom-filter.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack-config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-kind.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/swc/generated-native.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/swc/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/dev/parse-version-info.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/shared/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/parse-stack.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/server/shared.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/jsx-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/response-cache/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/render-result.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/i18n-provider.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/next-url.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/request.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/after/builtin-request-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/response.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/base-http/node.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/mitt.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/with-router.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/router.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/route-loader.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/page-loader.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/router.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/app-dir-module.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/cache-signal.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/fallback-params.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/response-cache/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/lazy-result.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/implicit-tags.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/deep-readonly.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/app-render.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/error-boundary.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/layout-router.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/render-from-template-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/client-page.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/client-segment.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/search-params.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/hooks-server-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/types/icons.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/metadata/metadata.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/lib/framework/boundary-components.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/rsc/taint.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/entry-base.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/templates/app-page.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/jsx-dev-runtime.d.ts","../../node_modules/.pnpm/@types+react@19.2.17/node_modules/@types/react/compiler-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/client.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/static.d.ts","../../node_modules/.pnpm/@types+react-dom@19.2.3_@types+react@19.2.17/node_modules/@types/react-dom/server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-page/module.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/adapter.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/use-cache/cache-life.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/flight-data-helpers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/templates/pages.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/pages/module.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/render.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/normalizer.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/suffix.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/rsc.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/next-data.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/static-paths/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/base-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/async-callback-set.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","../../node_modules/.pnpm/sharp@0.34.5/node_modules/sharp/lib/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/image-optimizer.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/next-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/lru-cache.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/dev/static-paths-worker.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/dev/next-dev-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/next.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/render-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/route-module.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/load-components.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/async-storage/work-store.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/http.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/redirect-status-code.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/redirect-error.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/templates/app-route.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-route/module.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/utils.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/export/routes/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/export/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/export/worker.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/worker.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/build/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/after/after.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/after/after-context.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/params.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/route-matches/route-match.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request-meta.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/cli/next-test.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/config-shared.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/base-http/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/api-utils/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/utils.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/pages/_app.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/app.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/use-cache/cache-tag.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/cache.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/config.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/pages/_document.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/document.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/dynamic.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dynamic.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/pages/_error.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/error.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/head.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/head.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/cookies.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/headers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/draft-mode.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/headers.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/get-img-props.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/image-component.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/shared/lib/image-external.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/image.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/link.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/link.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/redirect.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/not-found.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/forbidden.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/unauthorized.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/unstable-rethrow.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/navigation.react-server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/unrecognized-action-error.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/components/navigation.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/navigation.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/router.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/client/script.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/script.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@vercel/og/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/after/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/root-params.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/server/request/connection.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/server.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/types/global.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/types/compiled.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/image-types/global.d.ts","./next-env.d.ts","./next.config.ts","./hooks/use-mobile.ts","./lib/posts.ts","../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.mts","../../node_modules/.pnpm/tailwind-merge@3.6.0/node_modules/tailwind-merge/dist/types.d.ts","./lib/utils.ts","./app/vectorlessicon.tsx","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@next/font/dist/types.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","../../node_modules/.pnpm/next@15.5.14_@opentelemetry_b9e1ac9936b37a9a0130ec85ab5398ae/node_modules/next/font/google/index.d.ts","./app/layout.tsx","./app/page.tsx","./components/figure.tsx","./lib/markdown.tsx","./app/posts/[slug]/page.tsx","./components/diagrams.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts"],"fileIdsList":[[100,147,338,509],[100,147,338,510],[100,147,445,446,447,448],[100,147],[83,100,147,495,509,510],[100,147,496,508],[100,147,469,501,505],[100,147,469,479,501,505,512],[100,147,504],[86,100,147,511],[86,100,147],[100,147,159,168],[100,147,502,503],[83,100,147,496,497],[100,147,496],[100,144,147],[100,146,147],[147],[100,147,152,180],[100,147,148,153,158,166,177,188],[100,147,148,149,158,166],[95,96,97,100,147],[100,147,150,189],[100,147,151,152,159,167],[100,147,152,177,185],[100,147,153,155,158,166],[100,146,147,154],[100,147,155,156],[100,147,157,158],[100,146,147,158],[100,147,158,159,160,177,188],[100,147,158,159,160,173,177,180],[100,147,155,158,161,166,177,188],[100,147,158,159,161,162,166,177,185,188],[100,147,161,163,177,185,188],[98,99,100,101,102,103,104,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[100,147,158,164],[100,147,165,188,193],[100,147,155,158,166,177],[100,147,167],[100,147,168],[100,146,147,169],[100,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[100,147,171],[100,147,172],[100,147,158,173,174],[100,147,173,175,189,191],[100,147,158,177,178,180],[100,147,179,180],[100,147,177,178],[100,147,180],[100,147,181],[100,144,147,177,182],[100,147,158,183,184],[100,147,183,184],[100,147,152,166,177,185],[100,147,186],[100,147,166,187],[100,147,161,172,188],[100,147,152,189],[100,147,177,190],[100,147,165,191],[100,147,192],[100,142,147],[100,142,147,158,160,169,177,180,188,191,193],[100,147,177,194],[86,90,100,147,196,197,198,200,440,488],[86,90,100,147,196,197,198,199,355,440,488],[86,90,100,147,196,197,199,200,440,488],[86,100,147,200,355,356],[86,100,147,200,355],[86,90,100,147,197,198,199,200,440,488],[86,90,100,147,196,198,199,200,440,488],[84,85,100,147],[92,100,147],[100,147,443],[100,147,450],[100,147,204,218,219,220,222,437],[100,147,204,243,245,247,248,251,437,439],[100,147,204,208,210,211,212,213,214,426,437,439],[100,147,437],[100,147,219,321,407,416,433],[100,147,204],[100,147,201,433],[100,147,255],[100,147,254,437,439],[100,147,161,303,321,350,494],[100,147,161,314,330,416,432],[100,147,161,368],[100,147,420],[100,147,419,420,421],[100,147,419],[94,100,147,161,201,204,208,211,215,216,217,219,223,231,232,361,386,417,437,440],[100,147,204,221,239,243,244,249,250,437,494],[100,147,221,494],[100,147,232,239,301,437,494],[100,147,494],[100,147,204,221,222,494],[100,147,246,494],[100,147,215,418,425],[100,147,172,263,433],[100,147,263,433],[86,100,147,263],[86,100,147,322],[100,147,318,366,433,476,477],[100,147,413,470,471,472,473,475],[100,147,412],[100,147,412,413],[100,147,212,362,363,364],[100,147,362,365,366],[100,147,474],[100,147,362,366],[86,100,147,205,464],[86,100,147,188],[86,100,147,221,291],[86,100,147,221],[100,147,289,293],[86,100,147,290,442],[100,147,506],[86,90,100,147,161,195,196,197,198,199,200,440,486,487],[100,147,161],[100,147,161,208,270,362,372,387,407,422,423,437,438,494],[100,147,231,424],[100,147,440],[100,147,203],[86,100,147,303,317,329,339,341,432],[100,147,172,303,317,338,339,340,432,493],[100,147,332,333,334,335,336,337],[100,147,334],[100,147,338],[100,147,261,262,263,265],[86,100,147,256,257,258,264],[100,147,261,264],[100,147,259],[100,147,260],[86,100,147,263,290,442],[86,100,147,263,441,442],[86,100,147,263,442],[100,147,387,429],[100,147,429],[100,147,161,438,442],[100,147,326],[100,146,147,325],[100,147,233,271,309,311,313,314,315,316,359,362,432,435,438],[100,147,233,347,362,366],[100,147,314,432],[86,100,147,314,323,324,326,327,328,329,330,331,342,343,344,345,346,348,349,432,433,494],[100,147,308],[100,147,161,172,233,234,270,285,315,359,360,361,366,387,407,428,437,438,439,440,494],[100,147,432],[100,146,147,219,312,315,361,428,430,431,438],[100,147,314],[100,146,147,270,275,304,305,306,307,308,309,310,311,313,432,433],[100,147,161,275,276,304,438,439],[100,147,219,361,362,387,428,432,438],[100,147,161,437,439],[100,147,161,177,435,438,439],[100,147,161,172,188,201,208,221,233,234,236,271,272,277,282,285,311,315,362,372,374,377,379,382,383,384,385,386,407,427,428,433,435,437,438,439],[100,147,161,177],[100,147,204,205,206,208,213,216,221,239,427,435,436,440,442,494],[100,147,161,177,188,251,253,255,256,257,258,265,494],[100,147,172,188,201,243,253,281,282,283,284,311,362,377,386,387,393,396,397,407,428,433,435],[100,147,215,216,231,361,386,428,437],[100,147,161,188,205,208,311,391,435,437],[100,147,302],[100,147,161,394,395,404],[100,147,435,437],[100,147,309,312],[100,147,311,315,427,442],[100,147,161,172,237,243,284,377,387,393,396,399,435],[100,147,161,215,231,243,400],[100,147,204,236,402,427,437],[100,147,161,188,437],[100,147,161,221,235,236,237,248,266,401,403,427,437],[94,100,147,233,315,406,440,442],[100,147,161,172,188,208,215,223,231,234,271,277,281,282,283,284,285,311,362,374,387,388,390,392,407,427,428,433,434,435,442],[100,147,161,177,215,393,398,404,435],[100,147,226,227,228,229,230],[100,147,272,378],[100,147,380],[100,147,378],[100,147,380,381],[100,147,161,208,211,212,270,438],[100,147,161,172,203,205,233,271,285,315,370,371,407,435,439,440,442],[100,147,161,172,188,207,212,311,371,434,438],[100,147,304],[100,147,305],[100,147,306],[100,147,433],[100,147,252,268],[100,147,161,208,252,271],[100,147,267,268],[100,147,269],[100,147,252,253],[100,147,252,286],[100,147,252],[100,147,272,376,434],[100,147,375],[100,147,253,433,434],[100,147,373,434],[100,147,253,433],[100,147,359],[100,147,208,213,271,300,303,309,311,315,317,320,351,354,358,362,406,427,435,438],[100,147,294,297,298,299,318,319,366],[86,100,147,198,200,263,352,353],[86,100,147,198,200,263,352,353,357],[100,147,415],[100,147,219,276,314,315,326,330,362,406,408,409,410,411,413,414,417,427,432,437],[100,147,366],[100,147,370],[100,147,161,271,287,367,369,372,406,435,440,442],[100,147,294,295,296,297,298,299,318,319,366,441],[94,100,147,161,172,188,234,252,253,285,311,315,404,405,407,427,428,437,438,440],[100,147,276,278,281,428],[100,147,161,272,437],[100,147,275,314],[100,147,274],[100,147,276,277],[100,147,273,275,437],[100,147,161,207,276,278,279,280,437,438],[86,100,147,362,363,365],[100,147,238],[86,100,147,205],[86,100,147,433],[86,94,100,147,285,315,440,442],[100,147,205,464,465],[86,100,147,293],[86,100,147,172,188,203,250,288,290,292,442],[100,147,221,433,438],[100,147,389,433],[100,147,362],[86,100,147,159,161,172,203,239,245,293,440,441],[86,100,147,196,197,198,199,200,440,488],[86,87,88,89,90,100,147],[100,147,152],[100,147,240,241,242],[100,147,240],[86,90,100,147,161,163,172,195,196,197,198,199,200,201,203,234,338,399,437,439,442,488],[100,147,452],[100,147,454],[100,147,456],[100,147,507],[100,147,458],[100,147,460,461,462],[100,147,466],[91,93,100,147,444,449,451,453,455,457,459,463,467,469,479,480,482,492,493,494,495],[100,147,468],[100,147,478],[100,147,290],[100,147,481],[100,146,147,276,278,279,281,329,433,483,484,485,488,489,490,491],[100,147,195],[100,147,177,195],[100,114,118,147,188],[100,114,147,177,188],[100,109,147],[100,111,114,147,185,188],[100,147,166,185],[100,109,147,195],[100,111,114,147,166,188],[100,106,107,110,113,147,158,177,188],[100,114,121,147],[100,106,112,147],[100,114,135,136,147],[100,110,114,147,180,188,195],[100,135,147,195],[100,108,109,147,195],[100,114,147],[100,108,109,110,111,112,113,114,115,116,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,136,137,138,139,140,141,147],[100,114,129,147],[100,114,121,122,147],[100,112,114,122,123,147],[100,113,147],[100,106,109,114,147],[100,114,118,122,123,147],[100,118,147],[100,112,114,117,147,188],[100,106,111,114,121,147],[100,147,177],[100,109,114,135,147,193,195]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"a384610388221cd70cffb4503cee7853b8b076f2b4a55324b20a4bdbd25a3538","affectsGlobalScope":true},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"dc782ff85b2cb10075ecffc158af7bfb27ff97bf8491c917efea0c3d622d5ac4","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e20d899c28ca26a2a7afc98beaa69e63ff7fba0a8bc47b4e3bf3ede5e09e424","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"085f552d005479e2e6a7311cdbbe5d8c55c497b4d19274285df161ee9684cd9c","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"007faacc9268357caa21d24169f3f3f2497af3e9241308df2d89f6e6d9bb3f2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"2e07abf27aa06353d46f4448c0bbac73431f6065eef7113128a5cd804d0c384d","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"829b9e6028b29e6a8b1c01ddb713efe59da04d857089298fa79acbdb3cfcfdef","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"5f90b8c733a1bda63e42160b15a2301051e83a6f9d5332a59d16eb12f463270d","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"496bbf339f3838c41f164238543e9fe5f1f10659cb30b68903851618464b98ba","impliedFormat":1},{"version":"5178eb4415a172c287c711dc60a619e110c3fd0b7de01ed0627e51a5336aa09c","impliedFormat":1},{"version":"ca6e5264278b53345bc1ce95f42fb0a8b733a09e3d6479c6ccfca55cdc45038c","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"fb1d8e814a3eeb5101ca13515e0548e112bd1ff3fb358ece535b93e94adf5a3a","impliedFormat":1},{"version":"ffa495b17a5ef1d0399586b590bd281056cee6ce3583e34f39926f8dcc6ecdb5","impliedFormat":1},{"version":"98b18458acb46072947aabeeeab1e410f047e0cacc972943059ca5500b0a5e95","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"570bb5a00836ffad3e4127f6adf581bfc4535737d8ff763a4d6f4cc877e60d98","impliedFormat":1},{"version":"889c00f3d32091841268f0b994beba4dceaa5df7573be12c2c829d7c5fbc232c","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"380647d8f3b7f852cca6d154a376dbf8ac620a2f12b936594504a8a852e71d2f","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c83bb0c9c5645a46c68356c2f73fdc9de339ce77f7f45a954f560c7e0b8d5ebb","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"6a148329edecbda07c21098639ef4254ef7869fb25a69f58e5d6a8b7b69d4236","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"f63ab283a1c8f5c79fabe7ca4ef85f9633339c4f0e822fce6a767f9d59282af2","impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a54c996c8870ef1728a2c1fa9b8eaec0bf4a8001cd2583c02dd5869289465b10","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"3754982006a3b32c502cff0867ca83584f7a43b1035989ca73603f400de13c96","impliedFormat":1},{"version":"a30ae9bb8a8fa7b90f24b8a0496702063ae4fe75deb27da731ed4a03b2eb6631","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"50b5bc34ce6b12eccb76214b51aadfa56572aa6cc79c2b9455cdbb3d6c76af1d","impliedFormat":1},{"version":"b7e16ef7f646a50991119b205794ebfd3a4d8f8e0f314981ebbe991639023d0e","impliedFormat":1},{"version":"b838d4c72740eb0afd284bf7575b74c624b105eff2e8c7b4aeead57e7ac320ff","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"e9dd71cf12123419c60dab867d44fbee5c358169f99529121eaef277f5c83531","impliedFormat":1},{"version":"5b6a189ba3a0befa1f5d9cb028eb9eec2af2089c32f04ff50e2411f63d70f25d","impliedFormat":1},{"version":"d6e73f8010935b7b4c7487b6fb13ea197cc610f0965b759bec03a561ccf8423a","impliedFormat":1},{"version":"174f3864e398f3f33f9a446a4f403d55a892aa55328cf6686135dfaf9e171657","impliedFormat":1},{"version":"824c76aec8d8c7e65769688cbee102238c0ef421ed6686f41b2a7d8e7e78a931","impliedFormat":1},{"version":"75b868be3463d5a8cfc0d9396f0a3d973b8c297401d00bfb008a42ab16643f13","impliedFormat":1},{"version":"15a234e5031b19c48a69ccc1607522d6e4b50f57d308ecb7fe863d44cd9f9eb3","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"1a42d2ec31a1fe62fdc51591768695ed4a2dc64c01be113e7ff22890bebb5e3f","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"ad10d4f0517599cdeca7755b930f148804e3e0e5b5a3847adce0f1f71bbccd74","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"72d63643a657c02d3e51cd99a08b47c9b020a565c55f246907050d3c8a5e77fb","impliedFormat":1},{"version":"1d415445ea58f8033ba199703e55ff7483c52ac6742075b803bd3e7bbe9f5d61","impliedFormat":1},{"version":"d6406c629bb3efc31aedb2de809bef471e475c86c7e67f3ef9b676b5d7e0d6b2","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"71d8ba39a9e024d9e4bb922464d18542ed8d2c25ee78efa7890c27213cc6e5d3","impliedFormat":1},{"version":"8c030e515014c10a2b98f9f48408e3ba18023dfd3f56e3312c6c2f3ae1f55a16","impliedFormat":1},{"version":"dafc31e9e8751f437122eb8582b93d477e002839864410ff782504a12f2a550c","impliedFormat":1},{"version":"754498c5208ce3c5134f6eabd49b25cf5e1a042373515718953581636491f3c3","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"f56bdc6884648806d34bc66d31cdb787c4718d04105ce2cd88535db214631f82","impliedFormat":1},{"version":"633d58a237f4bb25ec7d565e4ffa32cecdcee8660ac12189c4351c52557cee9e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"49f95e989b4632c6c2a578cc0078ee19a5831832d79cc59abecf5160ea71abad","impliedFormat":1},{"version":"9666533332f26e8995e4d6fe472bdeec9f15d405693723e6497bf94120c566c8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"43fa6ea8714e18adc312b30450b13562949ba2f205a1972a459180fa54471018","impliedFormat":1},{"version":"6e89c2c177347d90916bad67714d0fb473f7e37fb3ce912f4ed521fe2892cd0d","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"8a97e578a9bc40eb4f1b0ca78f476f2e9154ecbbfd5567ee72943bab37fc156a","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"f22d05663d873ee7a600faf78abb67f3f719d32266803440cf11d5db7ac0cab2","impliedFormat":1},{"version":"d93c544ad20197b3976b0716c6d5cd5994e71165985d31dcab6e1f77feb4b8f2","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"a8b1c79a833ee148251e88a2553d02ce1641d71d2921cce28e79678f3d8b96aa","impliedFormat":1},{"version":"126d4f950d2bba0bd45b3a86c76554d4126c16339e257e6d2fabf8b6bf1ce00c","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"2d3cc2211f352f46ea6b7cf2c751c141ffcdf514d6e7ae7ee20b7b6742da313f","impliedFormat":1},{"version":"c75445151ff8b77d9923191efed7203985b1a9e09eccf4b054e7be864e27923d","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"fa8a8fbf91ee2a4779496225f0312aac6635b0f21aa09cdafa4283fe32d519c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"0e8aef93d79b000deb6ec336b5645c87de167168e184e84521886f9ecc69a4b5","impliedFormat":1},{"version":"c30436b130b6218b7714314dc41d3f459590db4bdf099eecd51cb1bda32109a8","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"de7052bfee2981443498239a90c04ea5cc07065d5b9bb61b12cb6c84313ad4ef","impliedFormat":1},{"version":"a3e7d932dc9c09daa99141a8e4800fc6c58c625af0d4bbb017773dc36da75426","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"4a2edd238d9104eac35b60d727f1123de5062f452b70ed8e0366cb36387dfdfd","impliedFormat":1},{"version":"ca921bf56756cb6fe957f6af693a35251b134fb932dc13f3dfff0bb7106f80b4","impliedFormat":1},{"version":"fee92c97f1aa59eb7098a0cc34ff4df7e6b11bae71526aca84359a2575f313d8","impliedFormat":1},{"version":"0bd0297484aacea217d0b76e55452862da3c5d9e33b24430e0719d1161657225","impliedFormat":1},{"version":"2ab6d334bcbf2aff3acfc4fd8c73ecd82b981d3c3aa47b3f3b89281772286904","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"4805f6161c2c8cefb8d3b8bd96a080c0fe8dbc9315f6ad2e53238f9a79e528a6","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"49179c6a23701c642bd99abe30d996919748014848b738d8e85181fc159685ff","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"8514c62ce38e58457d967e9e73f128eedc1378115f712b9eef7127f7c88f82ae","impliedFormat":1},{"version":"f1289e05358c546a5b664fbb35a27738954ec2cc6eb4137350353099d154fc62","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"1d17ba45cfbe77a9c7e0df92f7d95f3eefd49ee23d1104d0548b215be56945ad","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"a1ee88010a64e8647d07dba58ec43e6e05851b9ec7a62e4ca2b9c33be5abb2c8","impliedFormat":1},{"version":"46273e8c29816125d0d0b56ce9a849cc77f60f9a5ba627447501d214466f0ff3","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"e91f7b1344577a02f051b9b471f33044fef8334a76dc9e1de003d17595a5219b","impliedFormat":1},{"version":"3af3584f79c57853028ef9421ec172539e1fe01853296dc05a9d615ade4ffaf6","impliedFormat":1},{"version":"f82579d87701d639ff4e3930a9b24f4ee13ca74221a9a3a792feb47f01881a9c","impliedFormat":1},{"version":"d7e5d5245a8ba34a274717d085174b2c9827722778129b0081fefd341cca8f55","impliedFormat":1},{"version":"d9d32f94056181c31f553b32ce41d0ef75004912e27450738d57efcd2409c324","impliedFormat":1},{"version":"752513f35f6cff294ffe02d6027c41373adf7bfa35e593dbfd53d95c203635ee","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"1a7e2ea171726446850ec72f4d1525d547ff7e86724cc9e7eec509725752a758","impliedFormat":1},{"version":"8c901126d73f09ecdea4785e9a187d1ac4e793e07da308009db04a7283ec2f37","impliedFormat":1},{"version":"db97922b767bd2675fdfa71e08b49c38b7d2c847a1cc4a7274cb77be23b026f1","impliedFormat":1},{"version":"aab290b8e4b7c399f2c09b957666fc95335eb4522b2dd9ead1bf0cb64da6d6ee","impliedFormat":1},{"version":"94fe3281392e1015b22f39535878610b4fa6f1388dc8d78746be3bc4e4bb8950","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"06c25ddfc2242bd06c19f66c9eae4c46d937349a267810f89783680a1d7b5259","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"c7f6485931085bf010fbaf46880a9b9ec1a285ad9dc8c695a9e936f5a48f34b4","impliedFormat":1},{"version":"14f6b927888a1112d662877a5966b05ac1bf7ed25d6c84386db4c23c95a5363b","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"90c54a02432d04e4246c87736e53a6a83084357acfeeba7a489c5422b22f5c7a","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"83fe880c090afe485a5c02262c0b7cdd76a299a50c48d9bde02be8e908fb4ae6","impliedFormat":1},{"version":"0a372c2d12a259da78e21b25974d2878502f14d89c6d16b97bd9c5017ab1bc12","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"ec1ca97598eda26b7a5e6c8053623acbd88e43be7c4d29c77ccd57abc4c43999","impliedFormat":1},{"version":"6e2261cd9836b2c25eecb13940d92c024ebed7f8efe23c4b084145cd3a13b8a6","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"a47e6d954d22dd9ebb802e7e431b560ed7c581e79fb885e44dc92ed4f60d4c07","impliedFormat":1},{"version":"f019e57d2491c159d47a107fd90219a1734bdd2e25cd8d1db3c8fae5c6b414c4","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"d1c9bf292a54312888a77bb19dba5e2503ad803f5393beafd45d78d2f4fe9b48","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"cb8d8ef7b9ce8ed3e6f1c814fcbf3f90dab0cb8863079236784fc350746e27c4","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"3be035da7bee86b4c3abf392e0edaa44fc6e45092995eefe36b39118c8a84068","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f828825d077c2fa0ea606649faeb122749273a353daab23924fe674e98ba44c","impliedFormat":1},{"version":"2896c2e673a5d3bd9b4246811f79486a073cbb03950c3d252fba10003c57411a","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"407a06ba04eede4074eec470ecba2784cbb3bf4e7de56833b097dd90a2aa0651","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"5c96bad5f78466785cdad664c056e9e2802d5482ca5f862ed19ba34ffbb7b3a4","impliedFormat":1},{"version":"81d8603ac527e75cfec72bb9391228b58f161c2b33514a9d814c7f3ebd3ef466","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"4655709c9cb3fd6db2b866cab7c418c40ed9533ce8ea4b66b5f17ec2feea46a9","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"3eecb25bb467a948c04874d70452b14ae7edb707660aac17dc053e42f2088b00","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"5f0292a40df210ab94b9fb44c8b775c51e96777e14e073900e392b295ca1061b","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"8627ad129bcf56e82adff0ab5951627c993937aa99f5949c33240d690088b803","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"a68d4b3182e8d776cdede7ac9630c209a7bfbb59191f99a52479151816ef9f9e","impliedFormat":99},{"version":"39644b343e4e3d748344af8182111e3bbc594930fff0170256567e13bbdbebb0","impliedFormat":99},{"version":"ed7fd5160b47b0de3b1571c5c5578e8e7e3314e33ae0b8ea85a895774ee64749","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"ecbaf0da125974be39c0aac869e403f72f033a4e7fd0d8cd821a8349b4159628","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"ceec3c81b2d81f5e3b855d9367c1d4c664ab5046dff8fd56552df015b7ccbe8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"8fac4a15690b27612d8474fb2fc7cc00388df52d169791b78d1a3645d60b4c8b","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f4e8976c19fc926644d72610bf1058bd6bf52add97e46a02bc0b912a751625c0",{"version":"28fc0fd0b08b22b6147ab4ac1deacaf2bbc47ac9bc7fecd791c4f87f02a28e8d","signature":"fe30465f81a37f23a168022109187dbd1951404e3ec2fbc6516d584c597f1325"},{"version":"49accf81f42bf1ca413623c107179580ac61ac448b35b4491cc7442c3ab348c4","signature":"32f7c168ee545e9b432d2a861f4fb0bc645e21e88c774cedf54e72d1e3ccc049"},{"version":"dd704ed8b2c42d28dae5d79315b42f70d35b412f3ad51a48e13867a40feb33cb","signature":"b4a0cdf1b403422d83c438197f47424c253432f724ef3d247d16dcdaf5bdfead"},{"version":"c57b441e0c0a9cbdfa7d850dae1f8a387d6f81cbffbc3cd0465d530084c2417d","impliedFormat":99},{"version":"ff4bfe3c147eadc1b722a8ac9e4d350e66411b5c9022eabe5f4ada871c9adc41","impliedFormat":1},{"version":"74a147991a119f1c4cdc25a4d9b22713632fa32eb136e55ab6939b006d71b9d6","signature":"512960c0e955a2324b34354dac25e3e4d431a1af4cd33077935eda5e95c8b7e1"},{"version":"f28e51c2319181b43ada259341119eda823c4267ac8b23a2829e5e9801e8a8f0","signature":"ef8ce7c1c19acede4f775d9ac25491efdc7e054ea77e3592d03d24e1d41d8498"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"476e83e2c9e398265eed2c38773ae9081932b08ea5597b579a7d2e0c690ead56","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"ce716b7480ab991f8325b39b4431e0d5bcb230b97bdfd255c817320f0dfcbc32","signature":"ba54342937eb116567785cc93f6ef7c8ce3b041a67bb2ecb579ab28359b06047"},{"version":"270f629f944e9c36ec51ceee799d8899bb094d3d367fd92d1773b35e6588e708","signature":"6799703bfcbaa8f2fe4796546aa8a71995e85d4706996ee2af3bd5424bf6aca4"},{"version":"2fa4da5a0589160ccb767d37669a2a564f0108d859a56a061169be785dc366a8","signature":"52229f4992360bb56ba10f2b779903e713bbbc42a99790a08f8db712a7b3a3a3"},{"version":"1d0bb8649d61cf1160d0f8f7bbedf02c92692ee23305d939e8672871a0d36ba3","signature":"f067aeb3621e6bd33bb5d6f8484c2de77fb90f47335128072673a067a1b3d33d"},{"version":"73243cbb8974bfc35a55291f0a39584ea3fab1978af13f71aa27c0b5277872df","signature":"23c329419927e48fb697a6fe20b7c87603f6e6ad54ba3a019e442fa8730e8604"},{"version":"ce0d9a9840e617e563e1b12dcbadbaec7daf4783d6e4d323359b10c06a885359","signature":"633de9223fb594753799b700a2722b0f7f26060000c87a73fe5070708fa5083c"},"2552a31fad45a9ed1bde87e51b038dc0e786cd364b597162263abbf57018949b",{"version":"8c738d7ab02122bec55d98eab2f5e875f5306ebffe56a6be96f2da0916a7ea71","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"ea165270c0352a290f47907fb25e50b7a7e99c17271f89cceea204c1f492b84a","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"a2bd0e8bd20ed96b6b985430b61e8bed6549dbca414a50271129fd71f57b82b6","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"}],"root":[83,[498,501],504,505,[509,518]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[517,1],[518,2],[515,3],[83,4],[516,5],[509,6],[510,7],[513,8],[505,9],[514,10],[511,11],[500,11],[512,10],[501,12],[504,13],[498,14],[499,15],[245,4],[144,16],[145,16],[146,17],[100,18],[147,19],[148,20],[149,21],[95,4],[98,22],[96,4],[97,4],[150,23],[151,24],[152,25],[153,26],[154,27],[155,28],[156,28],[157,29],[158,30],[159,31],[160,32],[101,4],[99,4],[161,33],[162,34],[163,35],[195,36],[164,37],[165,38],[166,39],[167,40],[168,41],[169,42],[170,43],[171,44],[172,45],[173,46],[174,46],[175,47],[176,4],[177,48],[179,49],[178,50],[180,51],[181,52],[182,53],[183,54],[184,55],[185,56],[186,57],[187,58],[188,59],[189,60],[190,61],[191,62],[192,63],[102,4],[103,4],[104,4],[143,64],[193,65],[194,66],[199,67],[355,11],[200,68],[198,69],[357,70],[356,71],[196,72],[353,4],[197,73],[84,4],[86,74],[352,11],[263,11],[105,4],[502,4],[85,4],[93,75],[444,76],[449,3],[451,77],[221,78],[249,79],[427,80],[244,81],[232,4],[213,4],[219,4],[417,82],[280,83],[220,4],[386,84],[254,85],[255,86],[351,87],[414,88],[369,89],[421,90],[422,91],[420,92],[419,4],[418,93],[251,94],[222,95],[301,4],[302,96],[217,4],[233,97],[223,98],[285,97],[282,97],[206,97],[247,99],[246,4],[426,100],[436,4],[212,4],[327,101],[328,102],[322,11],[472,4],[330,4],[331,103],[323,104],[478,105],[476,106],[471,4],[413,107],[412,4],[470,108],[324,11],[365,109],[363,110],[473,4],[477,4],[475,111],[474,4],[364,112],[465,113],[468,114],[292,115],[291,116],[290,117],[481,11],[289,118],[274,4],[484,4],[507,119],[506,4],[487,4],[486,11],[488,120],[202,4],[423,121],[424,122],[425,123],[235,4],[211,124],[201,4],[343,11],[204,125],[342,126],[341,127],[332,4],[333,4],[340,4],[335,4],[338,128],[334,4],[336,129],[339,130],[337,129],[218,4],[209,4],[210,97],[264,131],[265,132],[262,133],[260,134],[261,135],[257,4],[349,103],[371,103],[443,136],[452,137],[456,138],[430,139],[429,4],[277,4],[489,140],[439,141],[325,142],[326,143],[317,144],[307,4],[348,145],[308,146],[350,147],[345,148],[344,4],[346,4],[362,149],[431,150],[432,151],[310,152],[314,153],[305,154],[409,155],[438,156],[284,157],[387,158],[207,159],[437,160],[203,81],[258,4],[266,161],[398,162],[256,4],[397,163],[94,4],[392,164],[234,4],[303,165],[388,4],[208,4],[267,4],[396,166],[216,4],[272,167],[313,168],[428,169],[312,4],[395,4],[259,4],[400,170],[401,171],[214,4],[403,172],[405,173],[404,174],[237,4],[394,159],[407,175],[393,176],[399,177],[225,4],[228,4],[226,4],[230,4],[227,4],[229,4],[231,178],[224,4],[379,179],[378,4],[384,180],[380,181],[383,182],[382,182],[385,180],[381,181],[271,183],[372,184],[435,185],[491,4],[460,186],[462,187],[309,4],[461,188],[433,150],[490,189],[329,150],[215,4],[311,190],[268,191],[269,192],[270,193],[300,194],[408,194],[286,194],[373,195],[287,195],[253,196],[252,4],[377,197],[376,198],[375,199],[374,200],[434,201],[321,202],[359,203],[320,204],[354,205],[358,206],[416,207],[415,208],[411,209],[368,210],[370,211],[367,212],[406,213],[361,4],[448,4],[360,214],[410,4],[273,215],[306,121],[304,216],[275,217],[278,218],[485,4],[276,219],[279,219],[446,4],[445,4],[447,4],[483,4],[281,220],[319,11],[92,4],[366,221],[250,4],[239,222],[315,4],[454,11],[464,223],[299,11],[458,103],[298,224],[441,225],[297,223],[205,4],[466,226],[295,11],[296,11],[288,4],[238,4],[294,227],[293,228],[236,229],[316,45],[283,45],[402,4],[390,230],[389,4],[450,4],[347,231],[318,11],[442,232],[87,11],[90,233],[91,234],[88,11],[89,4],[248,235],[243,236],[242,4],[241,237],[240,4],[440,238],[453,239],[455,240],[457,241],[508,242],[459,243],[463,244],[497,245],[467,245],[496,246],[469,247],[479,248],[480,249],[482,250],[492,251],[495,124],[494,4],[493,252],[391,253],[503,4],[81,4],[82,4],[13,4],[14,4],[16,4],[15,4],[2,4],[17,4],[18,4],[19,4],[20,4],[21,4],[22,4],[23,4],[24,4],[3,4],[25,4],[26,4],[4,4],[27,4],[31,4],[28,4],[29,4],[30,4],[32,4],[33,4],[34,4],[5,4],[35,4],[36,4],[37,4],[38,4],[6,4],[42,4],[39,4],[40,4],[41,4],[43,4],[7,4],[44,4],[49,4],[50,4],[45,4],[46,4],[47,4],[48,4],[8,4],[54,4],[51,4],[52,4],[53,4],[55,4],[9,4],[56,4],[57,4],[58,4],[60,4],[59,4],[61,4],[62,4],[10,4],[63,4],[64,4],[65,4],[11,4],[66,4],[67,4],[68,4],[69,4],[70,4],[1,4],[71,4],[72,4],[12,4],[76,4],[74,4],[79,4],[78,4],[73,4],[77,4],[75,4],[80,4],[121,254],[131,255],[120,254],[141,256],[112,257],[111,258],[140,252],[134,259],[139,260],[114,261],[128,262],[113,263],[137,264],[109,265],[108,252],[138,266],[110,267],[115,268],[116,4],[119,268],[106,4],[142,269],[132,270],[123,271],[124,272],[126,273],[122,274],[125,275],[135,252],[117,276],[118,277],[127,278],[107,279],[130,270],[129,268],[133,4],[136,280]],"affectedFilesPendingEmit":[517,518,516,509,510,513,505,514,511,500,512,501,504,499],"version":"5.9.3"} \ No newline at end of file From 26e476e4013b103671e5947079add6bc2a56d15a Mon Sep 17 00:00:00 2001 From: hallelx2 Date: Tue, 4 Aug 2026 21:48:31 +0100 Subject: [PATCH 2/3] fix(blogs): allowlist URL schemes in the markdown renderer Raw HTML in a post was already inert. Nothing in the renderer calls dangerouslySetInnerHTML, so every value reaches the DOM as a React child and gets escaped -- a post containing ` or + * `` renders those as visible characters inside a + * paragraph. Verified by building a fixture full of payloads and + * inspecting the emitted HTML: no live script tag, no iframe, no event + * handler attribute. + * + * The one hole that leaves is URLs, since `[text](javascript:…)` is valid + * markdown and would become a live handler. `safeUrl` closes it. + * + * Any future change that introduces `dangerouslySetInnerHTML` — say to + * support embeds — gives that guarantee up, and needs sanitisation to + * replace it. + * * Everything is styled with design tokens rather than the typography * plugin, because DESIGN.md has opinions the plugin does not share: * Instrument Serif appears only on accent clauses, mono eyebrows carry @@ -17,6 +35,27 @@ import { Figure } from "@/components/Figure"; * allowed on a light canvas. */ +/** + * URL scheme allowlist for anything that becomes an `href` or `src`. + * + * Raw HTML in a post is already inert — nothing here uses + * `dangerouslySetInnerHTML`, so React escapes `