diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..d8bd81d --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,47 @@ +name: Validate and deploy portfolio + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run check + - run: npm run build + - uses: actions/configure-pages@v5 + if: github.event_name != 'pull_request' + - uses: actions/upload-pages-artifact@v4 + if: github.event_name != 'pull_request' + with: + path: _site + + deploy: + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..edc3a87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +_site/ +node_modules/ +.astro/ +.content-tmp/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..17d9255 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,161 @@ +--- +artifact: adr +version: "1.0" +created: 2026-08-02 +status: accepted +--- + +# ADR-001: Adopt a version-locked Astro static publishing architecture + +## Status + +Accepted + +**Date:** 2026-08-02 +**Decider:** Theodore Ouyang + +## Context + +`130U/portfolio` is a long-lived writing archive whose Markdown files are the +canonical sources. It needs a maintainable article index, bilingual Chinese and +English reading modes, aligned comparison, mathematical notation, and a small +client-side footprint. Publication is through GitHub Pages from this repository; +`130U/130U.github.io` remains outside the writable scope. + +The architecture must preserve these constraints: + +- GitHub Pages serves the project below `/portfolio`, not the domain root; +- each language remains an independently readable canonical Markdown file; +- the current π₀ pair has 57 ordered bilingual anchors, which must remain a + build-blocking invariant; +- mathematics is rendered during the build, with no runtime MathJax CDN; +- only `main` may deploy the production Pages site; pull requests may build and + validate but must not publish; +- framework and integration versions are exact-pinned with a committed lockfile. + +## Decision + +We will use Astro's default static-output architecture and initially pin the +stable Astro 7.1 patch line (`astro@7.1.4` on the decision date). Dependency +upgrades will occur only through reviewed pull requests that pass content, +build, link, accessibility, and visual checks. + +We will: + +1. Load each article's `post.json` and paired Markdown sources through two + build-time Astro Content Collections. The manifest collection validates + identity, route, lifecycle, titles, summaries, dates, topics, paper metadata, + and audit state; the document collection validates `postId` and `lang`. CI + also executes the checked-in Draft 2020-12 JSON Schema with AJV so unknown or + misspelled fields cannot be silently discarded. + Stable collection IDs are allocated under an exclusive local lock and backed + by a strict, atomically replaced high-water registry; numbers may be skipped + but never reused. CI uniqueness checks remain authoritative across separate + worktrees and separately synced devices. +2. Keep prose in plain Markdown by default. MDX is allowed only when an article + genuinely requires an embedded component. +3. Generate one stable `//` route containing both languages as static + HTML. A small progressive-enhancement script provides Chinese, English, and + aligned bilingual modes while preserving a shareable `?lang=` parameter. + Without JavaScript, both complete documents remain readable. +4. Keep the existing bilingual audit as a build gate. Astro's schema validates + metadata, but a repository check must separately enforce the 57 logical + `data-pair-id` values, language-prefixed unique DOM IDs, and paired equation + and source-link invariants. +5. Configure `site` and `base: "/portfolio"`; generated links and assets must be + tested from the production build under that base path. +6. Render mathematics at build time through Astro's maintained Unified adapter, + `remark-math`, and `rehype-katex`: + + ```js + import { unified } from "@astrojs/markdown-remark"; + import { defineConfig } from "astro/config"; + import rehypeKatex from "rehype-katex"; + import remarkMath from "remark-math"; + + export default defineConfig({ + markdown: { + processor: unified({ + remarkPlugins: [remarkMath], + rehypePlugins: [rehypeKatex], + }), + }, + }); + ``` + + The corpus uses the portable `$...$` and `$$...$$` delimiters supported by + GitHub, `remark-math`, and KaTeX. The former `\(...\)` and `\[...\]` + delimiters were mechanically normalized without changing equation bodies. + Runtime CDN rendering is not an acceptable fallback. +7. Use Astro's official GitHub Pages action. Validation may run on pull requests, + while the deploy job is triggered and authorized only from `main`. The + repository's one-time Pages source setting must be `GitHub Actions`. + +## Consequences + +### Positive + +- Canonical Markdown remains portable while hand-written parsing, TOC generation, + and page assembly are replaced by maintained framework facilities. +- Content Collections provide typed, build-time metadata validation and scale to + future articles without introducing a database or CMS. +- Static output works directly with GitHub Pages and sends no framework runtime + JavaScript by default; only explicit interactive islands ship client code. +- Both locale documents are present in the initial static HTML, improving + accessibility and archival durability while a shareable query parameter + selects Chinese, English, or aligned comparison mode. +- Build-time mathematics removes a runtime CDN dependency and avoids delaying + article rendering on third-party JavaScript. + +### Negative + +- The repository gains Node.js, Astro, a lockfile, and a framework upgrade + obligation; current Astro requires Node.js 22.12 or newer. +- The repository intentionally uses Astro's optional Unified adapter instead of + the newer default Sätteri processor because the established KaTeX plugins + provide deterministic build-time formula output. These versions are pinned + and covered by a rendered-math regression check. +- Existing `\(...\)` and `\[...\]` formulas required a one-time delimiter + conversion to the Markdown math convention. +- Astro does not prove semantic translation equivalence; bilingual consistency + remains a repository-specific responsibility. +- GitHub Pages project-base handling makes root-relative links unsafe unless they + deliberately include Astro's configured base. + +### Neutral + +- The decision governs publishing infrastructure, not article ownership or prose. + `research/` remains the source archive, and publication continues to be a + derived representation of those files. + +## Alternatives Considered + +### Eleventy + +Eleventy is a mature, flexible static site generator and would preserve a very +small runtime footprint. It was not selected because this project would need more +custom code for typed content schemas, translation pairing, and bilingual routing, +where Astro provides first-party content and i18n primitives. Eleventy remains a +reasonable fallback if Astro's dependency or upgrade cost becomes unacceptable. + +### Continue the custom generator + +The current dependency-light generator offers complete control and low initial +setup cost. It was not selected as the long-term architecture because maintaining +a Markdown parser, sanitization boundary, math pipeline, locale routing, and +accessibility behavior creates an expanding security and maintenance surface as +the archive grows. + +## References + +- [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/) +- [Markdown in Astro](https://docs.astro.build/en/guides/markdown-content/) +- [Astro internationalization routing](https://docs.astro.build/en/guides/internationalization/) +- [Astro static and on-demand rendering](https://docs.astro.build/en/guides/on-demand-rendering/) +- [Deploy Astro to GitHub Pages](https://docs.astro.build/en/guides/deploy/github/) +- [Astro islands and default client-JavaScript behavior](https://docs.astro.build/en/concepts/islands/) +- [Astro 7 release and Sätteri adoption](https://astro.build/blog/astro-7/) +- [Astro 7.1 release](https://astro.build/blog/astro-710/) +- [remark-math and rehype-katex](https://github.com/remarkjs/remark-math) +- [Astro package releases](https://www.npmjs.com/package/astro) +- [Eleventy documentation](https://www.11ty.dev/docs/) diff --git a/PUBLISHING_STANDARD.md b/PUBLISHING_STANDARD.md new file mode 100644 index 0000000..2ddafb4 --- /dev/null +++ b/PUBLISHING_STANDARD.md @@ -0,0 +1,477 @@ +# Portfolio 双语研究出版标准 + +> 版本:1.0 +> 状态:目标标准(Target Standard) +> 适用范围:`articles/`、`research/`、`reflections/` 中的全部公开内容,以及由这些内容生成的网站页面与 README 索引。 + +## 1. 目的与规范用语 + +本标准将 `portfolio` 定义为一个可长期维护的双语研究出版系统,而不是若干手写网页的集合。目标是让每篇文章都具备一致的内容模型、双语对应关系、视觉层级、证据边界、可访问性和发布审计记录。 + +本文使用以下规范用语: + +- **必须(MUST)**:不满足时不得发布。 +- **应当(SHOULD)**:原则上需要满足;若例外,必须在文章的 `AUDIT.md` 中解释。 +- **可以(MAY)**:按文章需要选择。 + +以下原则优先级最高: + +1. Markdown 是正文的唯一事实来源(single source of truth)。 +2. 元数据只在文章 manifest 中维护,不在首页、文章模板和多个索引中重复手写。 +3. 正文必须在构建期输出为完整静态 HTML;JavaScript 只提供语言切换、目录高亮等渐进增强(progressive enhancement)。 +4. 双语版本追求语义等价和结构可对照,不要求逐字、逐句或逐行翻译。 +5. 论文证据、作者主张、解释性转述和个人判断必须明确区分。 + +## 2. 仓库与文章目录模型 + +### 2.1 内容集合 + +公开内容归入三个集合: + +- `articles/`:面向较广泛读者的技术文章与观点文章。 +- `research/`:包含方法、证据和审计过程的深度研究。 +- `reflections/`:明确带有作者经验和判断的反思性写作。 + +每篇内容必须使用独立目录,不得同时混用“单 Markdown 文件”和“文章目录”两套发布方式。 + +```text +research/ + 2026-08-02-pi0-vla-flow/ + post.json + README.md + README.zh-CN.md + README.en.md + SOURCES.yaml + AUDIT.md + assets/ +``` + +目录名必须采用 `YYYY-MM-DD-short-slug`,且日期必须是真实日历日期。目录日期表示首次进入仓库的日期;目录中的 `short-slug` 是内部可读标签,不要求与公开 `post.json.slug` 相同。公开路由不得依赖目录名,以便日后调整发布日期或整理内部命名而不破坏链接。 + +### 2.2 文件职责 + +| 文件 | 要求 | 职责 | +|---|---:|---| +| `post.json` | 必须 | 唯一的文章元数据 manifest | +| `README.md` | 必须 | GitHub 入口页,链接至各语言正文、来源和审计记录 | +| `README.zh-CN.md` | 按语言 | 中文正文 | +| `README.en.md` | 按语言 | 英文正文 | +| `SOURCES.yaml` | 研究文章必须 | 结构化来源清单及稳定 source ID | +| `AUDIT.md` | 发布前必须 | 校验结果、例外、人工审阅和版本记录 | +| `assets/` | 按需 | 文章专属图片、图表和下载文件 | + +`src/` 中的 Astro 页面模板、组件与共享样式可以手工维护;文章正文、文章卡片和文章元数据不得在其中重复维护。`_site/` 属于构建产物,不得成为正文事实来源。 + +## 3. 文章 Manifest + +### 3.1 最小字段 + +每篇文章必须提供 `post.json`。推荐结构如下: + +```json +{ + "schemaVersion": 1, + "id": "research.001", + "seriesNo": 1, + "slug": "pi0", + "collection": "research", + "status": "published", + "publishedAt": "2026-08-02T00:00:00-04:00", + "updatedAt": "2026-08-02T00:00:00-04:00", + "featured": true, + "readingMinutes": 35, + "sourceLanguage": "zh-CN", + "languages": { + "zh-CN": { + "file": "README.zh-CN.md", + "title": "π₀:机器人怎样把看懂任务变成连续动作", + "summary": "一份兼顾直觉、公式和证据边界的深度阅读笔记。" + }, + "en": { + "file": "README.en.md", + "title": "π₀: How a Robot Turns Understanding into Continuous Action", + "summary": "A deep reading note connecting intuition, equations, and evidence boundaries." + } + }, + "topics": ["Robotics", "Embodied AI", "VLA"], + "paper": { + "title": "π₀: A Vision-Language-Action Flow Model for General Robot Control", + "url": "https://arxiv.org/abs/2410.24164", + "arxivId": "2410.24164" + }, + "audit": { + "status": "passed", + "file": "AUDIT.md", + "reviewedAt": "2026-08-02T00:00:00-04:00", + "openIssueCount": 0 + } +} +``` + +### 3.2 字段约束 + +- `id` 必须全仓唯一,发布后不得复用。 +- `seriesNo` 是集合内的稳定编号;不得因首页排序或新增旧文章而重新编号。`content-sequence.json` 保存各集合只增不减的高水位,脚手架在排他锁内分配编号,并通过同卷临时文件原子替换 registry;允许跳号,不允许回收或复用。创建失败或放弃的预留编号也不回滚,因此空洞属于正常审计痕迹。 +- `slug` 必须全仓唯一;正式路由由 `site + base + slug` 确定,发布后不得无重定向修改。 +- `collection` 只能是 `article`、`research` 或 `reflection`。 +- `status` 只能是 `draft`、`review` 或 `published`;若未来引入归档状态,必须先升级 schema 与路由行为。 +- `languages` 中的 `zh-CN` 与 `en` 必须各自存在文件、标题和摘要。 +- `publishedAt`、`updatedAt` 与 `audit.reviewedAt` 必须使用含时区的 ISO date-time。 +- `readingMinutes` 是当前发布版本的统一阅读时间估计;内容大改时必须复核。 +- canonical URL 由 Astro 的 `site`、`base` 与 `slug` 构建,不在 manifest 重复手写。 +- 机器可读契约以 [`schemas/post.schema.json`](schemas/post.schema.json) 和 `src/content.config.ts` 为准。 + +状态行为必须如下: + +| 状态 | 首页/索引 | Sitemap/RSS | 搜索引擎 | +|---|---|---|---| +| `draft` | 不出现 | 不出现 | `noindex` | +| `review` | 不进入公开索引或正式构建 | 不出现 | 不生成公开路由 | +| `published` | 正常出现 | 正常出现 | `index` | + +### 3.3 全局 Registry + +构建程序必须扫描三个集合下的 `post.json`,由 Astro Content Collection 形成全局 registry,并输出机器可读的 `/posts.json`。该产物不得人工编辑。 + +`content-sequence.json` 只负责防止稳定 ID 被复用,不是文章索引,也不得用于推导页面顺序。其严格契约由 `schemas/content-sequence.schema.json` 定义。脚手架锁只负责同一工作树内的事务;不同工作树或同步设备之间的冲突由 Git 合并和 CI 唯一性检查最终阻断。已发布文章若需撤下,应保留 manifest 并通过未来经 schema 定义的归档状态处理;不得通过删除目录来回收编号。 + +同一次扫描应当生成: + +- 首页最近发布内容; +- 三个集合的档案页; +- Sitemap、RSS、结构化数据,以及后续需要时的标签和年份索引; +- 根 README 与集合 README 的自动索引区域。 + +首页“最近文章”默认展示最近 3 至 6 篇 `published` 内容,并提供集合入口。排序依据为 `publishedAt`,相同日期再按稳定 `id` 排序。 + +## 4. 双语正文与锚点规则 + +### 4.1 对齐单位 + +双语对齐单位是**语义章节(semantic section)**,不是句子或屏幕上的行。每个章节必须表达相同的主要命题、公式、图表和证据边界;两种语言可以采用不同语序和解释长度。 + +启用渐进增强后的“中英对照”模式必须按章节成对排列: + +```text +章节 1:中文 | English +章节 2:中文 | English +章节 3:中文 | English +``` + +无 JavaScript 的初始静态 HTML 可以保留两份按相同顺序排列的完整文档作为可读降级;启用 JavaScript 后不得继续采用“整篇中文一条长列、整篇英文另一条长列”的布局。 + +### 4.2 稳定锚点 + +每个二级或三级核心章节必须共享同一个逻辑 `data-pair-id`,同时使用语言前缀保证最终 HTML `id` 唯一: + +```markdown + +## 4. 模型架构 +``` + +```markdown + +## 4. Architecture +``` + +锚点必须: + +- 使用小写 ASCII `kebab-case`; +- 描述章节概念,而不是绑定某种语言的标题文本; +- `data-pair-id` 在所有语言中集合相同、顺序相同; +- 原始语言锚点使用 `zh-` / `en-` 前缀,配对后的页面行独占无前缀逻辑 `id`; +- 发布后保持稳定;若必须修改,旧锚点必须保留兼容跳转; +- 在单篇文章内唯一。 + +允许语言内部增加不参与对齐的段内说明,但不得引入只在一种语言中存在的独立核心结论。确有必要时,必须使用明确的本地注释标签,并在 `AUDIT.md` 中说明。 + +### 4.3 结构一致性检查 + +发布检查必须验证: + +1. 两种语言的逻辑 `data-pair-id` 集合和顺序完全一致,实际 HTML `id` 全页唯一; +2. 对应锚点后的标题层级一致; +3. 公式 ID、图表 ID、表格 ID 和 source ID 一致; +4. 外部来源不存在仅一侧遗漏或指向不同证据的情况; +5. 所有内部链接和静态资源路径有效; +6. 各语言不存在重复 HTML `id`; +7. manifest 声明的语言文件全部存在。 + +校验的是结构与证据等价性,不应以字数相同作为翻译质量标准。 + +### 4.4 公式、图表与来源 + +- 公式应当使用稳定标签,例如 `eq-flow-path`,并在两种语言中保持一致。 +- 变量必须在首次出现时定义,明确标出形状或维度;动作向量、动作块和动作 token 不得混为一谈。 +- 图片和图表必须有稳定 ID、双语 caption 与双语替代文本(alt text)。 +- 图片不得承载正文中唯一存在的信息;关键结论必须在正文中同步说明。 +- `SOURCES.yaml` 使用稳定 ID(例如 `pi0-paper-2024`)维护来源身份;正文可以保留方便读者访问的直达链接,但双语链接必须指向同一证据。 + +推荐的来源结构: + +```yaml +- id: pi0-paper-2024 + type: paper + title: "π0: A Vision-Language-Action Flow Model for General Robot Control" + authors: "Black et al." + year: 2024 + url: "https://arxiv.org/abs/2410.24164" + accessed_at: "2026-08-02" + primary: true +``` + +## 5. 证据与解释标签 + +研究文章必须显式区分以下内容层级: + +| 中文标签 | English label | 含义 | +|---|---|---| +| 论文事实 | Evidence from the paper | 论文直接报告的正文、公式、表格或实验结果,但不自动等同于第三方复现 | +| 作者声明 | Author claim | 作者提出的“首次”“最大”或 SOTA 等判断,必须保留原始限定语 | +| 代码快照 | Code snapshot | 特定核验日期的公开实现状态,可能与论文时期或未来版本不同 | +| 通俗解释 | Plain-language explanation | 为帮助理解而进行的转述或例子,不是论文原句 | +| 我的思考 | My interpretation | 基于证据作出的路线判断,不冒充论文结论 | + +当信息没有足够来源或超出核验范围时,必须另行标明“未验证范围 / Unverified scope”。文章可以调整上述标签的显示措辞,但必须在开头给出一一对应的定义,且不得混淆证据、作者主张和本文判断。 + +视觉样式必须帮助辨认这些层级,但不能只依赖颜色;标签文字或图标必须同时存在。 + +引用必须尽量指向论文、项目页、数据集说明和官方仓库等第一方来源。历史定位或“开创性”判断必须说明比较范围,不得把新兴术语描述为已形成共识。 + +## 6. 标准文章结构 + +深度研究文章应当采用以下主线。某一节确实不适用时可以省略,但必须保持结论、方法、证据和判断之间的逻辑闭环。 + +1. **一分钟结论(One-minute takeaway)**:读者离开前必须记住的三至五点。 +2. **问题与缺口(Problem and gap)**:已有方法哪里不够,本文解决什么问题。 +3. **输入、输出与符号(Inputs, outputs, notation)**:先定义对象和维度。 +4. **模型架构(Architecture)**:模块、信息流和各模块职责。 +5. **核心方法与公式(Method and equations)**:从直觉过渡到正式表达。 +6. **训练与推理(Training vs. inference)**:明确哪些步骤只在训练发生。 +7. **数据与实验(Data and experiments)**:数据来源、指标、基线和结果。 +8. **论文证明了什么(Claims and boundaries)**:证据支持范围及未证明事项。 +9. **历史与路线位置(Historical and technical context)**:与相关路径的可比维度。 +10. **本文思辨(Interpretation)**:作者自己的判断及其推理链。 +11. **优势与长期缺陷(Strengths and durable limitations)**:区分短期性能差距与结构性约束。 +12. **常见误解与自测(Misconceptions and self-check)**:帮助读者验证理解。 +13. **来源、审计与版本(Sources, audit, changelog)**:可复核入口。 + +每个技术章节应当遵循以下内部顺序: + +> 结论 → 通俗解释 → 正式定义或公式 → 证据来源 → 边界与例外 + +标题层级必须表达逻辑结构,不得仅为了视觉大小跳级。页面只允许一个 `

`;正文核心章节从 `

` 开始。 + +## 7. 文章页与视觉 Tokens + +### 7.1 页面组成 + +标准文章页必须包含: + +- 跳至正文链接(skip link); +- 集合、稳定编号和文章状态; +- 双语标题与摘要; +- 发布日期、更新日期、阅读时间和最近核验日期; +- 可保持滚动位置的语言切换器; +- 文章目录与证据标签说明; +- 按语义章节配对的正文; +- 来源、审计记录、canonical URL 和版本信息。 + +单语模式正文最大阅读宽度应约为 `48rem–54rem`;双语模式可以扩展至约 `72rem–80rem`。移动端必须将同一章节的中英文上下排列,而不是缩成难以阅读的双窄栏。 + +### 7.2 语义 Token + +样式必须使用语义 token,不得在组件内反复写具体色值或字体名。至少定义: + +```css +:root { + --color-page: ...; + --color-surface: ...; + --color-surface-muted: ...; + --color-ink: ...; + --color-ink-muted: ...; + --color-rule: ...; + --color-accent: ...; + --color-accent-muted: ...; + --color-focus: ...; + + --color-evidence-paper: ...; + --color-evidence-claim: ...; + --color-evidence-explanation: ...; + --color-evidence-interpretation: ...; + --color-evidence-unverified: ...; + + --font-ui: ...; + --font-body-zh: ...; + --font-body-en: ...; + --font-math: ...; + --font-mono: ...; + + --measure-single: 52rem; + --canvas-bilingual: 76rem; + --toc-width: 14rem; + + --space-1: ...; + --space-2: ...; + --space-3: ...; + --radius-card: ...; + --shadow-card: ...; +} +``` + +中文与英文必须使用明确的语言字体栈,而不是让中文偶然落入系统 fallback: + +```css +:lang(zh-CN) { font-family: var(--font-body-zh); } +:lang(en) { font-family: var(--font-body-en); } +``` + +视觉体系应当保持项目自身身份。可以借鉴编辑部式排版、暖纸张背景、编号栏、衬线正文与无衬线元信息的层级;不得复制其他站点的精确 token、DOM、类名、wordmark、头像、文案或固定布局尺寸。 + +## 8. 语言切换行为 + +页面支持以下模式: + +- `zh`:仅中文; +- `en`:仅英文; +- `both`:中英对照。 + +语言切换器必须: + +- 使用原生按钮; +- 通过 `aria-pressed` 暴露当前状态; +- 支持 Tab、方向键、Home 和 End; +- 更新根元素的 `lang`:中文为 `zh-CN`,英文为 `en`,对照为 `mul`; +- 不得在切换后主动滚回页面顶部,并应尽量保留当前章节上下文; +- 可以通过 URL 查询参数分享,例如 `?lang=zh`; +- 可以在本地保存读者偏好,但 URL 参数优先; +- 当前发布契约要求每篇文章同时具备 `zh-CN` 与 `en`;若未来支持单语文章,必须先升级 schema 与切换器行为。 + +使用 `[hidden]` 隐藏另一语言时,隐藏内容不得继续进入键盘焦点顺序或辅助技术阅读树。 + +## 9. 可访问性与渐进增强 + +所有已发布页面必须满足以下最低标准: + +- 没有 JavaScript 时仍能阅读完整正文、访问目录和来源; +- 正文静态 HTML 包含正确的 `lang` 属性; +- 键盘可以完成导航、语言切换和链接访问; +- 所有交互控件具有可见焦点样式; +- 普通文字与背景对比度至少为 4.5:1,大字号至少为 3:1; +- 主要触控目标建议不小于 `44 × 44px`; +- 支持 `prefers-reduced-motion`; +- 表格有表头,复杂表格提供说明;移动端不得通过无限横向滚动隐藏关键结论; +- 图片有语义准确的 alt;装饰图片使用空 alt; +- 链接文字能够脱离上下文理解,避免多个“点击这里”; +- 数学公式必须由锁定版本的 KaTeX 在构建期输出 HTML 与 MathML;即使样式未加载,辅助技术仍可读取 MathML,邻近正文仍需解释公式含义; +- 颜色不是状态、证据类型或错误提示的唯一表达方式。 + +构建期必须把 Markdown 与公式转换为静态 HTML/MathML。JavaScript 可以增强语言切换、语义块配对和目录高亮,但不得成为读取正文的前置条件。 + +## 10. README 自动索引 + +### 10.1 根 README + +根 `README.md` 应当包含: + +1. 项目定位与内容边界; +2. 从 manifest 自动生成的全部已发布内容及其中英文、网页直达链接; +3. 三个内容集合的入口; +4. 写作、审计和发布流程; +5. Markdown、网站与构建产物之间的 source-of-truth 关系。 + +### 10.2 集合 README + +每个集合的 README 按发布日期倒序列出全部已发布文章,至少包含: + +| ID | 日期 | 中文标题 / English title | Topics | Web | +|---|---|---|---|---| + +### 10.3 生成边界 + +自动生成内容必须放在固定注释之间: + +```markdown + + + +``` + +`content:index` 只允许替换上述区域,不得覆盖 README 中的人工说明;`check:index` 只读检查漂移。CI 依次运行 `check` 与 `build`,不会在构建时静默改写 README。 + +## 11. 构建、SEO 与发布产物 + +构建流程必须: + +1. 校验全部 manifest 和目录结构; +2. 校验双语锚点、来源、公式和资源引用; +3. 验证 README 索引与 manifest 一致,并生成网站 registry; +4. 将 Markdown 和数学公式在构建期预渲染为静态 HTML/MathML; +5. 生成首页、文章页、档案页、Sitemap 和 RSS; +6. 验证站内链接、canonical URL 和静态资源; +7. 将完整站点输出到 `_site/`。 + +首次发布前,仓库必须在 GitHub **Settings → Pages** 中把 Source 设为 **GitHub Actions**;此后只有 `main` 的成功工作流可以部署生产站点。 + +每篇公开页面应当生成: + +- 唯一 `` 与 meta description; +- canonical URL; +- Open Graph 和社交分享信息; +- `Article` 或 `TechArticle` 结构化数据; +- 正确的 `inLanguage`; +- 发布、更新和核验日期; +- 可用时的原论文 citation。 + +除非内容和同行评审状态确实满足定义,不得仅因文章讨论学术论文就标记为 `ScholarlyArticle`。 + +Markdown 渲染使用 Astro 的 Unified adapter、`remark-math` 与 `rehype-katex`。若未来允许不受信任的作者输入,构建阶段必须清理危险 HTML;即使目前只有可信内容,也禁止在 manifest 和 Markdown 中嵌入运行时脚本。 + +## 12. 发布前检查清单 + +### 12.1 自动检查 + +- [ ] manifest schema 有效,必填字段完整。 +- [ ] `id`、集合内 `seriesNo` 和 `slug` 唯一。 +- [ ] 状态、日期和语言声明合法。 +- [ ] 双语锚点集合、顺序和标题层级一致。 +- [ ] 显示公式数量与数学结构、双语外链顺序和 `SOURCES.yaml` registry 一致。 +- [ ] 所有内部链接和静态资源路径有效;外部链接可达性由人工抽查。 +- [ ] 静态 HTML 在无 JavaScript 环境中包含完整正文。 +- [ ] 首页、集合页和 README 索引与 registry 一致。 +- [ ] 草稿与评审稿未进入 Sitemap、RSS 和公开索引。 +- [ ] Sitemap、RSS、canonical 和结构化数据通过检查。 +- [ ] 构建可重复执行,且工作区不会产生未解释的差异。 + +项目至少提供以下命令语义: + +```powershell +npm run check +npm run build +``` + +`check` 必须只读验证,不得静默修复源文件;`build` 可以重建 `_site/` 等明确的生成目录。 + +### 12.2 人工审阅 + +- [ ] 中文和英文的关键结论、证据边界与数字一致。 +- [ ] 文章开头能让非专家迅速理解“解决了什么问题”。 +- [ ] 公式前有直觉,公式后有变量解释和实际含义。 +- [ ] 论文证据、作者主张、本文解释和个人推断已区分。 +- [ ] “首创”“首次”“开山”等历史判断有明确比较范围和来源。 +- [ ] 优点和缺点没有把短期性能差距误写成长期结构性结论。 +- [ ] 手机端、单语模式、对照模式和键盘操作均已人工检查。 +- [ ] 图表、表格、公式、脚注和长链接在窄屏下可读。 +- [ ] `AUDIT.md` 记录检查结果、已知限制、例外和审阅日期。 + +## 13. 版本与变更治理 + +- 本标准的破坏性修改必须提升 `schemaVersion`,并提供已有文章的迁移说明。 +- 已发布文章的内容更正必须更新 `updatedAt`,重大更正还应在页面与 `AUDIT.md` 中说明。 +- 路由、anchor 和 source ID 均视为公共接口;发布后优先兼容,不得仅为命名美观随意更改。 +- 新组件或新内容类型只有在至少两篇文章确有共同需求时,才应进入共享系统。 +- 对本标准的临时例外必须记录原因、影响范围和计划处理方式,不得成为未说明的永久分叉。 + +--- + +本标准的判断基线是:新增下一篇文章时,作者只需要添加文章目录和内容文件;首页、文章外壳、双语配对、README 索引、SEO 信息和发布检查均由共享系统完成,而不需要复制并修改上一篇文章的代码。 diff --git a/README.md b/README.md index e868386..51a80bd 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,68 @@ # Theodore Ouyang — Portfolio -A public archive of my essays, research notes, and reflections. +A public, bilingual archive of essays, research notes, and reflections. Each +published entry is listed below with direct links to its Chinese source, English +source, and generated reading page. + +## Published writing / 已整理文章 + +<!-- portfolio:index:start --> +| ID | Date | Article / 文章 | Read | +|---|---|---|---| +| R-001 | 2026-08-02 | π₀:机器人怎样把“看懂任务”变成连续动作<br>π₀: How a Robot Turns Understanding into Continuous Action | [中文](research/2026-08-02-pi0-vla-flow/README.zh-CN.md) · [English](research/2026-08-02-pi0-vla-flow/README.en.md) · [Web](https://130u.github.io/portfolio/pi0/) | +<!-- portfolio:index:end --> ## Collections - [Articles](articles/) — polished long-form writing. - [Research](research/) — evidence-led research notes and working papers. -- [Reflections](reflections/) — shorter observations and personal thinking. -- [Assets](assets/) — original diagrams and images, grouped by post slug. +- [Reflections](reflections/) — shorter observations and evolving ideas. -## Source-of-truth rule +## Publishing model + +Every piece lives in its own folder with one validated `post.json` manifest and +canonical Markdown for each language. Astro scans those sources at build time +to generate the homepage, collection archives, article pages, RSS, sitemap, and +structured metadata. The browser receives complete static HTML; JavaScript only +enhances language switching and paired-section layout. -The Markdown file in this repository is the canonical editable source. When a -piece is published on [theodoreoy.com](https://www.theodoreoy.com/), the website -may adapt it to its current Next.js/TSX presentation, but substantive edits -should begin here so the two copies do not drift. +```powershell +npm.cmd ci +npm.cmd run content:index +npm.cmd run check +npm.cmd run build +``` -For a published piece: +Create a new draft with: -1. keep the text in one Markdown file; -2. keep original media under `assets/<slug>/`; -3. copy only optimized website derivatives into the website repository; -4. record the public `canonical_url` and the source commit in the post metadata. +```powershell +npm.cmd run content:new -- --collection research --slug short-slug --title-zh "中文标题" --title-en "English title" +``` -Start new work from [the post template](templates/post.md). +The scaffold serializes ID allocation and advances `content-sequence.json`, so +stable collection numbers are never reused; gaps are valid and can record a +failed or abandoned allocation. The counter is flushed and atomically replaced +before the draft directory is published. Do not hand-edit it downward or delete +a published manifest to recycle its ID. CI remains the authoritative guard +against conflicts created in separate worktrees or on separately synced devices. + +The operational rules are documented in [Publishing Standard](PUBLISHING_STANDARD.md) +and the framework decision in [ADR-001](ARCHITECTURE.md). + +GitHub Pages must use **Settings → Pages → Source: GitHub Actions** once for the +repository. After that one-time repository setting, every successful push to +`main` validates, builds, and deploys the site automatically. + +## Source-of-truth rule -## File naming +The Markdown files in this repository are the canonical editable sources. +Generated HTML under `_site/` is disposable. If a separate website adapts an +article, substantive edits begin here so copies do not drift. Original media +belongs under the article folder; published routes and section anchors are +treated as stable public interfaces. -Use `YYYY-MM-DD-short-slug.md` for dated writing. Research projects that need -multiple files can use a folder named after the same short slug. +`130U/130U.github.io` is not part of this build and is not modified by this +repository. ## Rights diff --git a/articles/README.md b/articles/README.md index 816a1bb..60a398c 100644 --- a/articles/README.md +++ b/articles/README.md @@ -1,4 +1,10 @@ # Articles -Polished long-form writing. Use `YYYY-MM-DD-short-slug.md` and start from -[`templates/post.md`](../templates/post.md). +Polished long-form writing. Each article uses a dated folder, a validated +`post.json`, and canonical Markdown sources. + +## Published / 已发布 + +<!-- portfolio:index:start --> +_No published writing in this collection yet._ +<!-- portfolio:index:end --> diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..c0e8efb --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,39 @@ +import sitemap from "@astrojs/sitemap"; +import { unified } from "@astrojs/markdown-remark"; +import { defineConfig } from "astro/config"; +import rehypeKatex from "rehype-katex"; +import remarkMath from "remark-math"; +import { basePath, siteUrl } from "./site.config.mjs"; + +function rehypeRemoveDocumentTitle() { + return (tree) => { + let removed = false; + function visit(node) { + if (!node.children) return; + node.children = node.children.filter((child) => { + if (!removed && child.type === "element" && child.tagName === "h1") { + removed = true; + return false; + } + visit(child); + return true; + }); + } + visit(tree); + }; +} + +export default defineConfig({ + site: siteUrl, + base: basePath, + output: "static", + outDir: "./_site", + trailingSlash: "always", + integrations: [sitemap()], + markdown: { + processor: unified({ + remarkPlugins: [remarkMath], + rehypePlugins: [rehypeRemoveDocumentTitle, rehypeKatex], + }), + }, +}); diff --git a/content-sequence.json b/content-sequence.json new file mode 100644 index 0000000..0fd8911 --- /dev/null +++ b/content-sequence.json @@ -0,0 +1,8 @@ +{ + "schemaVersion": 1, + "collections": { + "article": 0, + "research": 1, + "reflection": 0 + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e23a004 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7048 @@ +{ + "name": "theodore-portfolio-notes", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "theodore-portfolio-notes", + "version": "0.0.0", + "dependencies": { + "@astrojs/markdown-remark": "7.2.1", + "@astrojs/rss": "4.0.19", + "@astrojs/sitemap": "3.7.3", + "ajv": "8.20.0", + "ajv-formats": "3.0.1", + "astro": "7.1.4", + "gray-matter": "4.0.3", + "katex": "0.18.1", + "rehype-katex": "7.0.1", + "remark-math": "6.0.0", + "yaml": "2.9.0" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "cross-env": "10.1.0", + "typescript": "6.0.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^18.0.0" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", + "integrity": "sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.2", + "@astrojs/compiler-binding-darwin-x64": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", + "integrity": "sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", + "integrity": "sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", + "integrity": "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.1.tgz", + "integrity": "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.1.1", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.16.13", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.13.tgz", + "integrity": "sha512-ekOa+CYprEq5n4EJC1qTIAhLk49HZIUQuFwrEuF+3JK/pdMaYnWoREFUI2A0KEPOJiFA2kamBzKzbYljDvUxLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.1", + "@astrojs/yaml2ts": "^0.2.4", + "@jridgewell/sourcemap-codec": "^1.5.5", + "@volar/kit": "~2.4.28", + "@volar/language-core": "~2.4.28", + "@volar/language-server": "~2.4.28", + "@volar/language-service": "~2.4.28", + "muggle-string": "^0.4.1", + "tinyglobby": "^0.2.16", + "volar-service-css": "0.0.71", + "volar-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.1.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.1.tgz", + "integrity": "sha512-jPVNIqTvk+yKviikszv/Y1U4jGUSKpp/Nw48QZV4qjWgp70j4Lkq3lhSDRbWwCfgKvEyO9GHuVbV1dM2WYXy1w==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.1.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.4.tgz", + "integrity": "sha512-6Lvt/bQZEBW+zzdhPblvfZEy5PGEYJaUsUqaCgwHeRPxZJL1gc9I+DRLKWJjjYTWDzVUTzXlMq4WwSK+X34CVw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/rss": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.19.tgz", + "integrity": "sha512-e+z5wYeYtffQdHQO8c2tkSd2JEBdAuRXJV4ZEU5IxkYeE6e39woDd7nw1PH1Kk2tEYNCYuKdylnnbhGmt61awA==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^5.5.7", + "piccolore": "^0.1.3", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz", + "integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "2.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@shikijs/core": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.1.tgz", + "integrity": "sha512-VeR2CY6Nn9/WbisoYLOQZ7HZOnwTrpBuOw4wExjqLnBCi62BNWynBUO6K2uPIASPFJwAv7cX1fUu+LrPlSstcw==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.4.1", + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.1.tgz", + "integrity": "sha512-6U4lJBh8LTvIkEVqRHv/rr3ruwtO6IweFQt1ME1ntHJMGHS+6N86vfYGO1o8c/DtOCTia2lfhdQBtBrps1sDfQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.1.tgz", + "integrity": "sha512-p23RugMKss0r5DAtRJW1yAXUDl60JvhQYV20yuxei//26JyDSJefV3umyWzzwep2weblMnJGDYahuti6XkcMgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.1.tgz", + "integrity": "sha512-xb2kCMloBCIraIy2fS5MW0t/BxVY3q2nDyQKBoeSeq6KNrQbShHetCFlw2n35fGIJ6t3+hXDLQogP5ir9O9bvA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.1.tgz", + "integrity": "sha512-ko2OfDoG89YuQ7xL5LtcQiWKb7NIv1Ephb7g48TVU198OzAMLC8lXVEwaJGHK4sUMYrfAGJDqYmNLOLiW/Kz8w==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.1.tgz", + "integrity": "sha512-wudOaoFro+/Zl9gQv2W1Ur5XlVduqvTuYLI483Xi0wgc1A+cy1hfB2r6ac6ufBgF+ID7KJEW7L41MHrzQ4wH+w==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.1.tgz", + "integrity": "sha512-GOwCLQDHM5EjGUWNPrhzJbr6JP8V/Dx/CDVkWvbZ1Avw5JFnNUckrgbLmE07qtg4WlW7Q7QFndhjIkeU9XMPvw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astro": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.1.4.tgz", + "integrity": "sha512-e0gkBReJECAZuuTgpEB5JMUc6J4mM6boD6wuVE1pBf/fMywG47f8qm9XQoA6kZB1RWHiHmUlLuQ2jd9Q5+72HQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.1", + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/markdown-satteri": "0.3.4", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.1" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/katex": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.1.tgz", + "integrity": "sha512-Td8GCYSxDAoMhHOlKmCFMJ/hz5qlAAb71n66Dryw9nfCVfumLo7nhuotbvKom/XPADmrYC3O5QR71EPq4DarJQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", + "integrity": "sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/micromark-extension-math/node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/rehype-katex/node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.3.tgz", + "integrity": "sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rolldown": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.142.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" + } + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.1.tgz", + "integrity": "sha512-rFP+iYKzjLEIqiMiKANhARqiAbk4deDhWnBtnUO/K0D0dPxMGDH4N0FVfBY/VeI+lPrV4wNGCHQZp7EOr7NNBw==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.4.1", + "@shikijs/engine-javascript": "4.4.1", + "@shikijs/engine-oniguruma": "4.4.1", + "@shikijs/langs": "4.4.1", + "@shikijs/themes": "4.4.1", + "@shikijs/types": "4.4.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/unstorage/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", + "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", + "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.1", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..17deea2 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "theodore-portfolio-notes", + "private": true, + "version": "0.0.0", + "type": "module", + "engines": { + "node": ">=22.12.0" + }, + "scripts": { + "content:index": "node scripts/generate-indexes.mjs", + "content:new": "node scripts/new-article.mjs", + "check:content": "node scripts/check-content.mjs", + "check:index": "node scripts/generate-indexes.mjs --check", + "check:types": "cross-env ASTRO_TELEMETRY_DISABLED=1 astro check", + "check": "npm run check:content && npm run check:index && npm run check:types", + "build": "cross-env ASTRO_TELEMETRY_DISABLED=1 astro build && node scripts/copy-public-sources.mjs && node scripts/check-built-site.mjs", + "dev": "cross-env ASTRO_TELEMETRY_DISABLED=1 astro dev", + "preview": "cross-env ASTRO_TELEMETRY_DISABLED=1 astro preview" + }, + "dependencies": { + "@astrojs/markdown-remark": "7.2.1", + "@astrojs/rss": "4.0.19", + "@astrojs/sitemap": "3.7.3", + "ajv": "8.20.0", + "ajv-formats": "3.0.1", + "astro": "7.1.4", + "gray-matter": "4.0.3", + "katex": "0.18.1", + "rehype-katex": "7.0.1", + "remark-math": "6.0.0", + "yaml": "2.9.0" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "cross-env": "10.1.0", + "typescript": "6.0.3" + } +} diff --git a/reflections/README.md b/reflections/README.md index e47ebfd..1c49b96 100644 --- a/reflections/README.md +++ b/reflections/README.md @@ -1,4 +1,10 @@ # Reflections -Shorter observations, personal notes, and evolving ideas. Use -`YYYY-MM-DD-short-slug.md`. +Shorter observations, personal notes, and evolving ideas, published through the +same manifest and review workflow as the longer collections. + +## Published / 已发布 + +<!-- portfolio:index:start --> +_No published writing in this collection yet._ +<!-- portfolio:index:end --> diff --git a/research/2026-08-02-pi0-vla-flow/AUDIT.md b/research/2026-08-02-pi0-vla-flow/AUDIT.md new file mode 100644 index 0000000..c001c71 --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/AUDIT.md @@ -0,0 +1,95 @@ +# π₀ 双语读书笔记审计 / Bilingual Reading Note Audit + +审计日期 / Audit date: 2026-08-02 +状态 / Status: passed, ready for publication from `main` + +## 1. 交付边界 / Delivery boundary + +- 唯一被修改的远程目标是 `130U/portfolio`;参考仓库 `130U/130U.github.io` 仅作只读设计观察。 +- The only writable target is `130U/portfolio`. `130U/130U.github.io` was used as a read-only design reference. +- 中文与英文 Markdown 是 canonical sources;HTML/CSS/JavaScript 是派生阅读界面。 +- The Chinese and English Markdown files are canonical. HTML, CSS, and JavaScript are derived presentation layers. + +## 2. 双语结构审计 / Bilingual structure audit + +| Check | 中文 | English | Result | +|---|---:|---:|---| +| Total lines | 806 | 806 | Pass | +| Ordered section anchors | 57 | 57 | Exact order match | +| Display-equation blocks | 31 | 31 | Count and structure match | +| External source links | 27 | 27 | URL order match | +| Main chapters | 15 | 15 | One-to-one | +| Source and verification appendices | 2 | 2 | One-to-one | + +Translation policy: one-to-one means semantic-block alignment, not rigid +sentence-for-sentence translation. Every paired block preserves the same facts, +numbers, formulas, evidence identity, causal direction, and qualifications. + +## 3. 关键事实一致性 / Critical-fact consistency + +The following were checked in both languages: + +- roughly 3B PaliGemma + roughly 300M action expert + roughly 3.3B total; +- \(H=50\) physical action steps and \(K=10\) Euler integration steps; +- 20 Hz: execute 16 steps and replan after about 0.8 s; +- 50 Hz: execute 25 steps and replan after about 0.5 s; +- more than 10,000 hours, roughly 903M proprietary timesteps, seven robot configurations, and 68 broad tasks; +- 9.1% as a sampling-mixture weight, not the raw share of elapsed data; +- 1/5/10-hour adaptation experiments and 5–20-minute long-task demonstrations. + +## 4. 必须保留的证据边界 / Mandatory evidence boundaries + +1. π₀ is Physical Intelligence’s first generalist policy and the defining start + of its π-series; it is not the first VLA or embodied-intelligence paper. +2. Claims such as “first,” “largest,” or “new SOTA” remain author claims and + retain “to our knowledge” or equivalent wording. +3. The 0.75–1.00 direct-prompting range is an approximate reading of Figure 7, + not an exact numerical table. +4. “Above 50%” in the complex-task discussion is partial progress, not complete + task success. +5. `zero-shot` in v1 does not mean the broader task family was absent from + pretraining; v4 uses `direct prompting` / `out-of-box`. +6. Forward Euler numerically integrates the learned vector field. It does not + prove that ten steps recover a paired demonstration action. +7. The paper uses \(\tau=0\) for noise and \(\tau=1\) for action. Current openpi + code uses the reverse time convention and negative integration steps; the two + are equivalent under \(t=1-\tau\). +8. Eighteen dimensions describe the paper-era padded cross-robot interface. + Current `Pi0Config` defaults to `action_dim=32`; the main derivation therefore + keeps the action dimension generic as \(d\). +9. WAM is treated as an emerging, nonstandard label. The π₀-versus-WM/WAM + section is marked as interpretation, not as a result established by π₀. +10. The experiments validate the complete recipe but do not cleanly identify + the causal contribution of VLM scale, Flow Matching, data scale, + post-training, and systems engineering separately. + +## 5. 网页与无障碍审计 / Web and accessibility audit + +Local production build checks: + +- `npm run check`: pass; +- `npm run build`: pass; +- the Draft 2020-12 manifest schema is executed by AJV, while Astro applies a strict matching Zod contract; +- publication status is read only from `post.json`; canonical Markdown does not duplicate a stale draft/review label; +- all 20 unique external URLs are registered in `SOURCES.yaml`, and every registered URL appears in both language sources; +- stable collection numbers use a strict committed high-water registry, fsynced atomic replacement, and an exclusive local allocation lock; gaps are retained, while CI blocks cross-worktree or cross-device ID conflicts; +- 58 rendered rows including the preamble, 116 language panes, and 17 TOC entries; +- Chinese, English, and bilingual modes update `?lang=`, `lang`, `aria-pressed`, visibility, and persisted state; +- Arrow keys, Home, and End move focus within the language-control group; activating a focused control changes the reading mode; +- language controls remain hidden when JavaScript is unavailable, while both complete documents and the no-JavaScript TOC remain readable; +- 114 language-prefixed source anchors and 58 enhanced paired rows retain unique DOM IDs; TOC links resolve to the visible paired row; +- no browser console warnings or errors; +- desktop and 390 × 844 responsive checks show no horizontal page overflow; +- all three language controls retain a 44 px minimum height on mobile; +- the sticky header keeps all three collection links and the language switcher available while reading; a real-pointer mode change preserved the active paired section within one CSS pixel; +- bilingual desktop mode aligns each semantic block side by side; mobile mode stacks each Chinese block immediately before its English pair; +- 62 display equations and 162 total math elements are pre-rendered as KaTeX HTML + MathML with no runtime math CDN; +- the root and collection README indexes are generated from `post.json` and match the published registry; +- build QA validates local fragment targets, canonical URLs, and `TechArticle` JSON-LD identity, dates, languages, and citation; +- `prefers-reduced-motion`, increased-contrast, forced-colors, skip link, focus-visible, and semantic headings are covered. + +## 6. Publication decision + +- The reader's interpretive π₀-versus-WM/WAM analysis remains in the public note and is explicitly labeled as interpretation rather than paper evidence. +- The default reading mode remains `中英对照`; Chinese-only and English-only states remain shareable through `?lang=zh` and `?lang=en`. +- The GitHub Pages workflow validates pull requests and publishes only after a successful push to `main`. diff --git a/research/2026-08-02-pi0-vla-flow/README.en.md b/research/2026-08-02-pi0-vla-flow/README.en.md new file mode 100644 index 0000000..34a57c4 --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/README.en.md @@ -0,0 +1,806 @@ +--- +postId: research.001 +lang: en +--- + +# π₀: How a Robot Turns “Understanding the Task” into Continuous Action + +> A bilingual reading note that combines intuition, equations, and a critical comparison of technical approaches. +> Paired version: [中文](./README.zh-CN.md) +> Core paper: [π₀: A Vision-Language-Action Flow Model for General Robot Control](https://arxiv.org/abs/2410.24164) +> Verification basis: [arXiv v4, January 8, 2026](https://arxiv.org/html/2410.24164v4); historical wording: [arXiv v1, October 31, 2024](https://arxiv.org/html/2410.24164v1) + +This note uses five content labels: + +- **Evidence from the paper:** reported directly by the π₀ paper, but not necessarily independently reproduced; +- **Author claim:** judgments such as “first,” “largest,” or “state of the art,” with the authors’ original qualifications preserved; +- **Code snapshot:** the state of the current public openpi implementation, which may differ from the paper-era interface; +- **Plain-language explanation:** an explanatory restatement, not a quotation from the paper; +- **My interpretation:** a route-level judgment derived from the evidence, not a claim made by the π₀ paper. + +--- + +<a id="en-quick-take" data-pair-id="quick-take"></a> +## 1. π₀ in One Minute + +<a id="en-quick-take-what" data-pair-id="quick-take-what"></a> +### 1.1 What is π₀? + +π₀ (pronounced “pi-zero”) is Physical Intelligence’s first-generation generalist robot policy. “Physical Intelligence Zero” is not its formal name, and π₀ is not a world model that explicitly predicts future video. It is a vision-language-action (VLA) policy: given visual observations, a language instruction, and the robot’s proprioceptive state, it directly generates continuous actions. + +The shortest architectural description is: + +$$ +\pi_0 += +\underbrace{\text{VLM semantic backbone}}_{\text{encodes what is observed and what the task requires}} ++ +\underbrace{\text{continuous Flow action expert}}_{\text{determines how the robot should move next}} +$$ + +Flow Matching, however, is only the action-generation mechanism. The full π₀ recipe also includes: + +$$ +\boxed{ +\pi_0 += +\text{VLM semantics} ++ +\text{continuous action expert} ++ +\text{cross-embodiment data} ++ +\text{pretraining/post-training} ++ +\text{receding-horizon execution} +} +$$ + +**Evidence from the paper:** + +- The vision-language backbone is PaliGemma, with roughly 3 billion parameters. +- The action expert has roughly 300 million parameters, bringing the total to about 3.3 billion. +- The model generates $H=50$ continuous physical actions at a time. +- At inference time, it applies $K=10$ Euler updates to the same action chunk. + +<a id="en-quick-take-gap" data-pair-id="quick-take-gap"></a> +### 1.2 What design gap does it bridge? + +Before π₀, two model families had each solved a different part of the problem: + +| Approach | What it did well | Main gap | +|---|---|---| +| VLMs and early VLAs | Understanding images, language, objects, and open-vocabulary instructions | Discrete autoregressive action outputs were not naturally suited to high-frequency, fine-grained control | +| Diffusion and continuous-control policies | Generating smooth, multimodal continuous actions | Limited access to the semantic knowledge and language transfer of large VLMs | +| π₀ | Connecting a large VLM to a continuous action generator | Remaining dependence on the coverage of robot demonstration data | + +π₀ makes a deliberate division of labor. It does not force one language model to perform both semantic interpretation and low-level motor control. The VLM supplies task-relevant visual-language context, while the action expert generates continuous motion. This semantic-backbone–action-expert interface is the paper’s most important and most durable design choice. + +--- + +<a id="en-execution-loop" data-pair-id="execution-loop"></a> +## 2. What Actually Happens When the Robot Acts + +Suppose the instruction is: “Put the plates and cups on the table into the bus tub.” + +<a id="en-execution-loop-observe" data-pair-id="execution-loop-observe"></a> +### 2.1 Observe the real scene + +The robot receives: + +- two or three RGB views, such as workspace and wrist-camera images; +- a natural-language instruction; +- its current proprioceptive state, including joint angles and gripper state. + +<a id="en-execution-loop-context" data-pair-id="execution-loop-context"></a> +### 2.2 Form task-relevant context + +PaliGemma encodes task-relevant visual and linguistic information: which objects are plates, cups, and the tub; what goal is being requested; and how the objects relate to the instruction. The VLM does not first produce a readable sentence for an action decoder. Instead, the action expert attends to internal hidden representations. + +<a id="en-execution-loop-generate" data-pair-id="execution-loop-generate"></a> +### 2.3 Generate actions from noise + +The action expert begins with a block of Gaussian noise. Ten Flow updates transform it into an action chunk containing 50 continuous actions. For teaching purposes, one might describe the sequence as: + +> approach the plate → adjust the wrist → close the gripper → lift → move toward the tub → release. + +The actual output consists of continuous numerical values, not natural-language steps. + +<a id="en-execution-loop-prefix" data-pair-id="execution-loop-prefix"></a> +### 2.4 Execute only a prefix of the chunk + +Although π₀ predicts 50 actions, it does not blindly execute all of them: + +- on a 20 Hz platform, it executes the first 16 actions and replans after roughly 0.8 seconds; +- on a 50 Hz platform, it executes the first 25 actions and replans after roughly 0.5 seconds. + +<a id="en-execution-loop-reobserve" data-pair-id="execution-loop-reobserve"></a> +### 2.5 Reobserve and replan + +The robot captures the real scene again. If an object moved, a grasp failed, or the pose drifted, the next action chunk is generated from the new observation: + +~~~text +observe the real scene +→ form task-relevant context +→ generate a 50-step action chunk +→ execute part of the chunk +→ observe the real scene again +→ generate again +~~~ + +**Important boundary:** π₀ observes the real world again after acting. It does not explicitly generate a future video before acting in order to predict how the world will change. + +--- + +<a id="en-inputs-outputs" data-pair-id="inputs-outputs"></a> +## 3. Model Inputs and Outputs + +<a id="en-inputs-outputs-observation" data-pair-id="inputs-outputs-observation"></a> +### 3.1 Current observation + +The observation at physical time $t$ can be written as: + +$$ +o_t=[I_t^1,\ldots,I_t^n,\ell_t,q_t] +$$ + +where: + +- $I_t^i$ is the image from camera $i$; +- $\ell_t$ is the language instruction; +- $q_t$ is the robot’s proprioceptive state, such as its joint angles and gripper state. + +$q_t$ is neither the model’s internal state nor an “action state.” It describes the robot’s actual physical state at that moment. + +<a id="en-inputs-outputs-vector" data-pair-id="inputs-outputs-vector"></a> +### 3.2 Single-step action vector + +$$ +a_t\in\mathbb R^d +$$ + +$a_t$ is the continuous control vector for one physical time step. The single-step action dimension $d$ can differ across robots. + +<a id="en-inputs-outputs-chunk" data-pair-id="inputs-outputs-chunk"></a> +### 3.3 Action chunk + +$$ +A_t=[a_t,a_{t+1},\ldots,a_{t+H-1}] +\in\mathbb R^{H\times d} +$$ + +π₀ uses $H=50$. When flattened: + +$$ +\operatorname{vec}(A_t)\in\mathbb R^D, +\qquad D=Hd +$$ + +Four easily confused concepts must remain distinct: + +| Concept | Notation | Meaning | +|---|---|---| +| action vector | $a_t$ | the action at one physical time step | +| action chunk | $A_t$ | $H$ consecutive physical actions | +| action slot | a Transformer sequence position | an internal slot carrying one continuous action vector | +| discrete action token | a vocabulary ID | a discretized action representation used by some VLAs | + +An action slot in π₀ carries a continuous vector. It is not a discrete token from a language vocabulary. + +<a id="en-inputs-outputs-distribution" data-pair-id="inputs-outputs-distribution"></a> +### 3.4 What the model actually learns + +$$ +\boxed{ +p_{\mathrm{data}}(A_t\mid o_t) +} +$$ + +In plain language: given the current images, language goal, and robot pose, generate a plausible sequence of future actions. The output is a future action sequence, not a future image or future world state. + +--- + +<a id="en-architecture" data-pair-id="architecture"></a> +## 4. Architecture: How the VLM and Action Expert Work Together + +<a id="en-architecture-specialization" data-pair-id="architecture-specialization"></a> +### 4.1 Two specialized parameter sets + +π₀ can be understood as two specializations within one system: + +1. **PaliGemma VLM, approximately 3B parameters:** processes images and language and supplies object, scene, and instruction semantics; +2. **action expert, approximately 300M parameters:** processes proprioceptive state, noisy actions, and Flow time, then predicts how the continuous action should change. + +As a rough analogy, the two components resemble a supervisor and a choreographer. The supervisor tracks the scene and objective; the choreographer converts that intent into coordinated joint motion. The analogy describes only the division of labor. Internally, the components exchange hidden representations rather than readable language. + +<a id="en-architecture-not-moe" data-pair-id="architecture-not-moe"></a> +### 4.2 It is not a conventional sparse MoE + +Images and language are assigned to the VLM parameters, while proprioceptive state and actions are assigned to the action-expert parameters. The two exchange information through self-attention. There is no learned router that dynamically sends each token to a selected expert. + +A more precise description is that π₀ uses fixed modality specialization, allowing a semantic backbone and an action expert to cooperate within a Transformer-style attention system. + +<a id="en-architecture-continuous" data-pair-id="architecture-continuous"></a> +### 4.3 Why not treat actions as language tokens? + +Robot actions are inherently continuous and require coordination across joints and time. Quantizing them into a discrete vocabulary and emitting them token by token can introduce: + +- quantization error; +- autoregressive latency; +- difficulty coordinating multiple joints across multiple time steps. + +π₀ instead generates the entire continuous action chunk jointly, allowing all 50 action positions to coordinate with one another. + +--- + +<a id="en-flow-matching" data-pair-id="flow-matching"></a> +## 5. Conditional Flow Matching: Learning a Flow from Noise to Action + +<a id="en-flow-matching-noise" data-pair-id="flow-matching-noise"></a> +### 5.1 Why start from noise? + +Gaussian noise is a simple distribution from which samples are easy to draw: + +$$ +\epsilon\sim\mathcal N(0,I) +$$ + +$\epsilon$ has the same shape as the action chunk $A_t$. This noise is neither a physical disturbance injected into the robot nor ordinary regularization noise. It is the generative model’s base distribution. + +The model learns to transport samples from: + +$$ +\text{a simple Gaussian distribution} +\longrightarrow +\text{the action distribution conditioned on the observation} +$$ + +<a id="en-flow-matching-path" data-pair-id="flow-matching-path"></a> +### 5.2 Construct the training path + +First sample a Flow time: + +$$ +\tau\in[0,1] +$$ + +Then linearly interpolate between noise and a demonstrated action chunk: + +$$ +\boxed{ +A_t^\tau=(1-\tau)\epsilon+\tau A_t +} +$$ + +The endpoints are: + +$$ +A_t^0=\epsilon, +\qquad +A_t^1=A_t +$$ + +At $\tau=0$, the sample is pure noise. At $\tau=1$, it is the demonstrated action chunk. Intermediate values are partially noised action candidates. This straight line exists in generation space; it does not mean that the robot’s end effector follows a straight line in physical space. + +<a id="en-flow-matching-target" data-pair-id="flow-matching-target"></a> +### 5.3 Derive the paired training target + +Rewrite the interpolation as: + +$$ +A_t^\tau=\epsilon+\tau(A_t-\epsilon) +$$ + +Differentiating with respect to $\tau$ gives: + +$$ +\boxed{ +\frac{dA_t^\tau}{d\tau}=A_t-\epsilon +} +$$ + +For each sampled pair $(\epsilon,A_t)$, the target velocity is therefore: + +$$ +u=A_t-\epsilon +$$ + +In plain language, the target tells the model which direction and magnitude should be used to update the unfinished action candidate. + +<a id="en-flow-matching-loss" data-pair-id="flow-matching-loss"></a> +### 5.4 The action expert learns a conditional vector field + +The model predicts: + +$$ +v_\theta(A_t^\tau,o_t,\tau) +$$ + +Its training loss is: + +$$ +\boxed{ +\mathcal L(\theta) += +\mathbb E +\left[ +\left\| +v_\theta(A_t^\tau,o_t,\tau) +-(A_t-\epsilon) +\right\|_2^2 +\right] +} +$$ + +<a id="en-flow-matching-qualification" data-pair-id="flow-matching-qualification"></a> +### 5.5 A necessary technical qualification + +During training, each noise–demonstration pair supplies a target $A_t-\epsilon$. At inference time, however, the model does not know a predetermined correct action $A_t$. + +Under the idealization of infinite data and mean-squared-error optimization, the learned field is the conditional vector field: + +$$ +v^*(x,o,\tau) += +\mathbb E +\left[ +A_t-\epsilon +\mid +A_t^\tau=x,\ o_t=o,\ \tau +\right] +$$ + +π₀ therefore learns distributional transport from Gaussian noise to an action distribution conditioned on the observation. It is not retrieving one particular training demonstration. Different initial noise samples can still yield different plausible actions; a conditional expectation vector field does not reduce the policy to one simple “average action.” + +--- + +<a id="en-inference" data-pair-id="inference"></a> +## 6. Inference: What the Ten Euler Steps Actually Do + +<a id="en-inference-start" data-pair-id="inference-start"></a> +### 6.1 Start from fresh noise + +$$ +\hat A_t^{(0)}\sim\mathcal N(0,I) +$$ + +At inference time, the current observation $o_t$ is available, but the correct action answer $A_t$ is not. + +<a id="en-inference-ode" data-pair-id="inference-ode"></a> +### 6.2 The learned ODE + +$$ +\frac{d\hat A_t^\tau}{d\tau} += +v_\theta(\hat A_t^\tau,o_t,\tau) +$$ + +<a id="en-inference-euler" data-pair-id="inference-euler"></a> +### 6.3 Discretize it with the forward Euler method + +π₀ uses: + +$$ +K=10, +\qquad +\delta=\frac{1}{K}=0.1 +$$ + +The update is: + +$$ +\boxed{ +\hat A_t^{(k+1)} += +\hat A_t^{(k)} ++ +\frac{1}{K} +v_\theta +\left( +\hat A_t^{(k)},o_t,\frac{k}{K} +\right) +} +$$ + +for $k=0,1,\ldots,9$. The final output is: + +$$ +\hat A_t=\hat A_t^{(10)} +$$ + +<a id="en-inference-no-proof" data-pair-id="inference-no-proof"></a> +### 6.4 Euler does not prove that ten steps must recover a real action + +Consider a teaching example in which the velocity is assumed to be a known constant: + +$$ +v_\theta=A_t-\epsilon +$$ + +Then: + +$$ +\hat A_t^{(k)} += +\epsilon+\frac{k}{K}(A_t-\epsilon) +$$ + +At $k=K$: + +$$ +\hat A_t^{(K)} += +\epsilon+(A_t-\epsilon) +=A_t +$$ + +This algebra verifies only a known, constant-velocity path paired with one demonstration. It does not prove that ten steps are theoretically necessary or always sufficient in the real model, nor that every noise sample converges to a designated demonstration action. If the velocity were truly constant and the endpoint known, one step with $\delta=1$ would already reach it. + +Multiple steps are useful because the learned vector field changes with the current action candidate, the observation, and $\tau$. The system also has network-approximation and Euler-discretization error. Ten steps are therefore an engineering trade-off among generation quality, numerical accuracy, and inference cost—not a “ten-step convergence theorem.” + +<a id="en-inference-h-vs-k" data-pair-id="inference-h-vs-k"></a> +### 6.5 $H=50$ and $K=10$ mean different things + +$$ +\boxed{ +10\text{ Flow updates} +\longrightarrow +1\text{ action chunk containing 50 physical actions} +} +$$ + +- $H=50$ is the number of physical time steps in the action chunk. +- $K=10$ is the number of numerical integration steps used to generate that one chunk. + +--- + +<a id="en-training-deployment" data-pair-id="training-deployment"></a> +## 7. Training and Deployment, Side by Side + +<a id="en-training-deployment-training" data-pair-id="training-deployment-training"></a> +### Training + +~~~text +real observation o_t and demonstrated action chunk A_t +→ sample noise ε +→ sample Flow time τ +→ construct intermediate action A_t^τ +→ train the action expert to predict A_t - ε +→ update model parameters +~~~ + +<a id="en-training-deployment-deployment" data-pair-id="training-deployment-deployment"></a> +### Deployment + +~~~text +current observation o_t and fresh noise +→ apply 10 Euler updates +→ obtain a 50-step action chunk +→ execute a prefix of the chunk +→ observe the real world again +→ replan +~~~ + +The demonstrated action is available as supervision during training. It is absent at deployment, where the policy must sample along the learned conditional vector field. + +--- + +<a id="en-data-recipe" data-pair-id="data-recipe"></a> +## 8. Beyond the Equations: Data and the Training Recipe + +<a id="en-data-recipe-scale" data-pair-id="data-recipe-scale"></a> +### 8.1 Data scale + +The paper reports: + +- more than 10,000 hours of robot-manipulation data; +- approximately 903M in-house timesteps; +- seven robot configurations; +- 68 broad tasks; +- open data representing roughly 9.1% of the training sampling mixture; +- state and action interfaces padded to a maximum of 18 dimensions across robots. + +The 903M figure counts timesteps, not complete trajectories. The 9.1% figure is a share of the sampling mixture, not a share of raw timesteps. + +<a id="en-data-recipe-pretraining" data-pair-id="data-recipe-pretraining"></a> +### 8.2 Broad pretraining + +The pretraining data spans robots, tasks, objects, and scenes. It also includes imperfect actions, off-nominal states, and recoveries. Its purpose is not to make every behavior immediately expert-level, but to expand the range of states and behaviors that the policy has encountered. + +<a id="en-data-recipe-posttraining" data-pair-id="data-recipe-posttraining"></a> +### 8.3 High-quality post-training + +Task-specific post-training uses more consistent, skilled, and targeted demonstrations to make behavior stable and fluid. A simple task may require about five hours of specialized data, while a complex task may require more than 100 hours. + +π₀ does not eliminate task-specific data collection. It changes the role of that data from “learn the entire capability from scratch” to “calibrate and refine an existing capability.” + +<a id="en-data-recipe-system" data-pair-id="data-recipe-system"></a> +### 8.4 The complete recipe + +$$ +\boxed{ +\text{broad pretraining for capability coverage} +\quad+\quad +\text{high-quality post-training for behavioral proficiency} +} +$$ + +This is methodologically similar to broad pretraining followed by targeted adaptation. π₀’s task post-training, however, should not be equated directly with language-model RLHF or alignment. + +--- + +<a id="en-evidence" data-pair-id="evidence"></a> +## 9. What the Experiments Actually Establish + +The paper evaluates the system at four levels: + +| Evidence level | Main result | Necessary qualification | +|---|---|---| +| Direct prompting | Across five tasks, π₀ obtained normalized progress scores of roughly 0.75–1.00 and substantially exceeded the paper’s baselines | These task families were present in pretraining; they were not strictly unseen tasks | +| Language following | The policy could use intermediate language instructions supplied by a person or a high-level VLM | π₀-small changes scale, initialization, and architecture together, so it is not a clean one-factor ablation | +| New-task adaptation | With 1, 5, or 10 hours of fine-tuning data, pretraining usually improved data efficiency | π₀ did not win on every task at every data point | +| Complex tasks | The paper demonstrated 5–20 minute tasks such as laundry handling, table clearing, and box assembly | “Above 50%” refers to partial progress scores, not full-task success rates | + +The 0.75–1.00 range is an approximate reading of Figure 7, not an exact numerical table reported by the paper. + +<a id="en-evidence-zero-shot" data-pair-id="evidence-zero-shot"></a> +### 9.1 The zero-shot wording in v1 versus v4 + +The initial v1 used the term zero-shot, while also stating that the five basic evaluation task families appeared in pretraining. The current v4 instead uses direct prompting and out-of-box. + +The accurate interpretation is that the evaluated task instance received no task-specific post-training. It does not follow that the model had never encountered the related task family, robot, or behavior distribution. + +<a id="en-evidence-long-horizon" data-pair-id="evidence-long-horizon"></a> +### 9.2 Long tasks are not the same as fully autonomous planning + +Some long-horizon tasks rely on intermediate instructions supplied by a person or a separate high-level VLM. π₀ primarily demonstrates a general low-level policy. A single π₀ model does not simultaneously provide long-horizon decomposition, persistent memory, success verification, safety judgment, and continuous low-level control. + +--- + +<a id="en-innovation" data-pair-id="innovation"></a> +## 10. What Was Genuinely Novel About π₀? + +π₀ did not originate any one of the following ideas: + +- [RT-2](https://arxiv.org/abs/2307.15818) introduced the VLA framing earlier; +- [Diffusion Policy](https://arxiv.org/abs/2303.04137) explored continuous generative control earlier; +- action chunking predates π₀; +- [Octo](https://arxiv.org/abs/2405.12213) was an earlier cross-robot generalist policy; +- [OpenVLA](https://arxiv.org/abs/2406.09246) earlier released a VLA built on an Internet-pretrained VLM. + +π₀’s contribution is the system-level integration of: + +1. a large pretrained VLM that preserves visual-language semantics; +2. a separate action expert for proprioceptive state and continuous actions; +3. Flow Matching that jointly generates high-frequency action chunks; +4. foundation-policy pretraining on more than 10,000 hours of cross-embodiment data; +5. high-quality post-training that turns broad capability into proficient task behavior; +6. extensive real-robot demonstrations involving deformable objects, bimanual coordination, and long tasks. + +The paper itself frames the contribution as an integration and qualifies its novelty claim with “to our knowledge”: according to the authors, it was the first flow-matching VLA to generate high-frequency action chunks for dexterous control. + +The most defensible historical assessment is: + +> π₀ is a defining starting point for Physical Intelligence’s π-series and the continuous action-expert VLA approach, but it is not the first work in VLA robotics or embodied intelligence as a whole. + +--- + +<a id="en-route-analysis" data-pair-id="route-analysis"></a> +## 11. My Interpretation: π₀ versus World-Model and WAM Approaches + +> This section is route-level analysis, not a conclusion established by the π₀ paper. WAM remains an emerging, nonstandard label; researchers do not yet share one definition of its boundaries or coupling mechanisms. + +<a id="en-route-analysis-information" data-pair-id="route-analysis-information"></a> +### 11.1 The key distinction is not “language medium versus video medium” + +My initial intuition was that π₀ is more closely related to the LLM/VLM lineage, that VLA information is more language-like, and that WM/WAM information is more video- or latent-like. This identifies a difference in emphasis, but oversimplifies the intermediate representations. + +A more precise distinction is: + +- π₀ inherits its semantic backbone from PaliGemma and therefore emphasizes objects, instructions, and task semantics; +- the VLM does not pass readable language to the action expert, but a hidden context formed from images, language, and state; +- WM/WAM approaches make future-world structure or predictive supervision materially participate in action learning. + +A π₀-style direct policy learns: + +$$ +p(A_t\mid o_t) +$$ + +Its central question is: “Given the current observation, how should I act now?” + +A world model may learn: + +$$ +p(z_{t+1:t+H}\mid z_t,A_t) +$$ + +Its central question is: “If these actions are taken, how might the world change?” Here $z$ may be an image, a visual latent, a state, or another world representation. It need not be a human-viewable video. + +One teaching abstraction of a joint WAM is: + +$$ +p(A_t,z_{t+1:t+H}\mid o_t) +$$ + +This is not a canonical definition. Specific systems may use other factorizations, prediction only as training supervision, or an action-only output at deployment. + +<a id="en-route-analysis-before-after" data-pair-id="route-analysis-before-after"></a> +### 11.2 “Observe after acting” versus “simulate before acting” is a teaching contrast + +π₀ follows a loop of: + +~~~text +observe → act directly → let the real world change → observe again +~~~ + +A world-model approach may instead use: + +~~~text +observe → predict possible futures internally → compare outcomes → choose an action +~~~ + +This is a useful representative contrast, not a universal rule. Not every world-model or WAM system explicitly renders video or searches multiple futures at deployment. + +The absence of an explicit world model also does not mean that π₀ contains no physical knowledge. To generate effective actions from demonstrations, its weights may encode action-relevant regularities of contact, objects, and robot dynamics. The distinction is that π₀ does not train those regularities through a separately inspectable future-prediction objective, nor does it naturally expose a counterfactual simulator. It may know how to act without explicitly showing how the world will change after each action. + +<a id="en-route-analysis-latency" data-pair-id="route-analysis-latency"></a> +### 11.3 A shorter decision path may be faster, but speed is not guaranteed + +A direct policy need not first generate a future world, evaluate candidate trajectories, and then select an action. Its decision path may therefore be shorter. However: + +- π₀ still performs ten action-expert Flow updates; +- some WAMs use future prediction only during training and deploy an action-only policy; +- engineering speed must be measured through end-to-end p50/p95 latency, control rate, hardware, and replanning behavior. + +π₀ tends to trade a more direct action interface for efficient execution. WM/WAM approaches tend to trade richer dynamic representations for stronger consequence modeling. Their actual speed must be measured rather than inferred from the label. + +<a id="en-route-analysis-tradeoff" data-pair-id="route-analysis-tradeoff"></a> +### 11.4 The practical trade-off + +| Dimension | π₀-style direct VLA | WM/WAM-first approach | +|---|---|---| +| Primary learning target | conditional action distribution | coupling among world transitions, future representations, and actions | +| Main pre-action information | hidden context formed from the current observation | current observation plus future structure or predictive supervision | +| Must it generate video? | no | also no; it may use a latent or training-only supervision | +| Counterfactual rollouts | not explicitly provided | more naturally supports “what if we do this?” | +| Control interface | directly generates continuous action chunks | may generate jointly or use a policy/action-only head | +| Typical strength | clear semantic interface, short execution chain, continuous control | richer dynamics, planning, and consequence evaluation | +| Typical risk | may fail smoothly without explicit success verification | potentially higher compute, memory, data requirements, and model error | + +<a id="en-route-analysis-fusion" data-pair-id="route-analysis-fusion"></a> +### 11.5 The approaches are more likely to merge than eliminate one another + +My expectation is that world models are better suited to slow deliberation, long-horizon planning, consequence evaluation, and anomaly detection, while VLA action experts are better suited to fast, continuous low-level execution. This is an architectural inference from their complementary strengths, not a finding of the π₀ paper. + +~~~text +language and goal understanding +→ world-dynamics simulation +→ task and subgoal planning +→ continuous action generation +→ feedback from the real world +~~~ + +“VLA versus WAM” may therefore become less a product category than a distinction among capabilities inside one robotic system. + +--- + +<a id="en-strengths-limitations" data-pair-id="strengths-limitations"></a> +## 12. Greatest Strength and Most Durable Limitation + +<a id="en-strengths-limitations-strength" data-pair-id="strengths-limitations-strength"></a> +### 12.1 Greatest strength: a semantic-to-motor interface + +π₀’s most durable contribution is neither a benchmark score nor the assumption that Flow Matching will remain the best generator forever. It connects the general semantic capabilities of a large VLM to a continuous action expert that can be replaced and extended. + +Future systems may change the VLM backbone, action encoding, Flow solver, chunk length, or robot platform while preserving the division between a general semantic backbone and a continuous-control expert. + +<a id="en-strengths-limitations-structural" data-pair-id="strengths-limitations-structural"></a> +### 12.2 Greatest structural limitation: capability remains bounded by demonstration support + +π₀ remains, at its core, an offline demonstration-driven conditional behavior-cloning system. It does not explicitly provide: + +- calibrated uncertainty about whether the state is outside its training distribution; +- a refusal mechanism when the instruction or situation is not understood; +- a verifier that determines whether an action actually completed the task; +- persistent world state and long-term memory; +- a mechanism for continual learning through online interaction. + +It may therefore fail smoothly and confidently in unfamiliar states. More data can expand coverage, but it does not automatically create awareness of the unknown. + +<a id="en-strengths-limitations-evidence" data-pair-id="strengths-limitations-evidence"></a> +### 12.3 An enduring limitation of the paper’s evidence: weak causal attribution + +- The core 10,000-hour dataset is not fully available to third parties. +- Baselines do not always share identical training budgets and action interfaces. +- π₀ and π₀-small change parameter count, initialization, and architecture together. +- Most conditions use roughly ten real-robot trials, and the paper does not report confidence intervals. +- Complex tasks use author-designed partial-progress metrics. + +[openpi](https://github.com/Physical-Intelligence/openpi) now provides public code and base weights, so describing π₀ as “entirely closed source” is no longer accurate. The original 10,000-hour pretraining run, however, still cannot be reproduced in full by a third party. + +The paper provides strong evidence that the complete recipe worked in the authors’ environments. It does not cleanly isolate how much of the gain came from the VLM, Flow Matching, data scale, post-training, or systems engineering. + +--- + +<a id="en-misconceptions" data-pair-id="misconceptions"></a> +## 13. Common Misconceptions + +1. **π₀ is formally short for “Physical Intelligence Zero.”** + No. It should simply be called π₀ or pi-zero. + +2. **π₀ is a world model.** + No. It does not explicitly predict future images, states, or rewards. + +3. **The VLM first emits a sentence that is passed to the action model.** + No. The action expert reads internal hidden context. + +4. **$H=50$ means that the action is 50-dimensional.** + No. It denotes 50 physical time steps. + +5. **Ten Flow updates generate ten action chunks.** + No. The ten updates jointly generate one action chunk. + +6. **Forward Euler proves that ten steps must reach the correct action.** + No. Euler is a numerical solver, and ten steps are an engineering choice. + +7. **50 Hz means that the full visual model replans 50 times per second.** + No. It is the action-command rate; the system replans roughly every 0.5–0.8 seconds. + +8. **Direct prompting means that the model has never seen the task.** + No. The basic task families appeared in pretraining. + +9. **π₀ alone performs all planning for the long tasks.** + Not in every case. Some tasks use intermediate instructions from a person or a high-level VLM. + +10. **The experiments prove that Flow Matching alone caused π₀’s success.** + No. The paper primarily validates the complete system recipe. + +--- + +<a id="en-recap" data-pair-id="recap"></a> +## 14. One-Minute Recap + +> π₀ is Physical Intelligence’s first-generation generalist robot policy. It uses a roughly 3B-parameter PaliGemma backbone to encode images and language, then a roughly 300M-parameter action expert to generate an action chunk containing 50 continuous physical actions through Conditional Flow Matching. During training, the model constructs a straight path between Gaussian noise and a demonstrated action and learns how an intermediate action candidate should change. During inference, it begins from fresh noise, applies ten forward Euler updates, executes only part of the resulting chunk, and then observes the real world again. π₀’s central innovation is not one isolated equation. It is the integration of VLM semantics, a continuous action expert, more than 10,000 hours of cross-embodiment pretraining, and high-quality post-training into a foundation-policy recipe. The fundamental difference from a world model is that π₀ directly learns a conditional action distribution, whereas a world model explicitly learns how the world may change. Over time, the two approaches are more likely to combine across planning and execution layers. + +--- + +<a id="en-self-check" data-pair-id="self-check"></a> +## 15. Review Questions + +If you can answer these ten questions, you have captured the core of π₀: + +1. What are π₀’s inputs and outputs? +2. What do $a_t$, $A_t$, $H$, $d$, and $D$ represent? +3. How does an action chunk differ from a discrete action token? +4. Why does generation begin from Gaussian noise? +5. Why is the paired training target $A_t-\epsilon$? +6. Why is no known correct $A_t$ available at inference time? +7. What does forward Euler do in this system, and what does it not establish? +8. Why are $H=50$ and $K=10$ conceptually independent? +9. Why is π₀’s innovation a system recipe rather than one Flow equation? +10. How do the learning objectives of π₀ and world-model/WAM approaches differ? + +--- + +<a id="en-sources" data-pair-id="sources"></a> +## Primary Sources + +- Core paper: [π₀ v4](https://arxiv.org/html/2410.24164v4), [π₀ v1](https://arxiv.org/html/2410.24164v1) +- Author materials: [PI π₀ project page](https://www.pi.website/blog/pi0), [openpi](https://github.com/Physical-Intelligence/openpi) +- Methodological lineage: [PaliGemma technical report](https://arxiv.org/abs/2407.07726), [Flow Matching](https://arxiv.org/abs/2210.02747), [Rectified Flow](https://arxiv.org/abs/2209.14577) +- Prior work: [RT-2](https://arxiv.org/abs/2307.15818), [Diffusion Policy](https://arxiv.org/abs/2303.04137), [Octo](https://arxiv.org/abs/2405.12213), [OpenVLA](https://arxiv.org/abs/2406.09246) +- Subsequent boundaries: [π₀.5](https://arxiv.org/abs/2504.16054), [Real-Time Chunking](https://arxiv.org/abs/2506.07339) +- WAM route references: [WAM Survey](https://arxiv.org/abs/2605.12090), [VPP](https://arxiv.org/abs/2412.14803), [DreamZero](https://arxiv.org/abs/2602.15922), [Fast-WAM](https://arxiv.org/abs/2603.16666), [GigaWorld-Policy](https://arxiv.org/abs/2603.17240) +- Paper index: [Hugging Face paper page](https://huggingface.co/papers/2410.24164) + +<a id="en-verification" data-pair-id="verification"></a> +## Versioning and Verification Notes + +- Equations, architecture, data, and experimental claims primarily follow π₀ arXiv v4. +- Historical zero-shot wording follows v1, with the v4 revision stated explicitly. +- The paper’s derivation uses $\tau=0$ for noise and $\tau=1$ for action. The current openpi code uses the opposite time convention—$t=1$ for noise, $t=0$ for action, and $dt<0$. The substitution $t=1-\tau$ makes them equivalent; this is not a contradiction. +- The main text therefore keeps the action dimension generic as $d$. Eighteen dimensions are the paper’s padded cross-robot data interface; the current open-source `Pi0Config` defaults to `action_dim=32`. They describe different implementation layers and must not be conflated. +- The openpi repository was confirmed as public through the GitHub connector on August 2, 2026. +- The Hugging Face page uses a snapshot verified on August 1, 2026 and serves only as metadata and an ecosystem index. +- “My interpretation” is explanatory analysis and should be read separately from claims established by the paper. diff --git a/research/2026-08-02-pi0-vla-flow/README.md b/research/2026-08-02-pi0-vla-flow/README.md new file mode 100644 index 0000000..d5c0ac6 --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/README.md @@ -0,0 +1,15 @@ +# π₀ bilingual reading note + +This folder contains the canonical, section-aligned sources for the π₀ deep reading. + +- [中文全文](README.zh-CN.md) +- [English full text](README.en.md) +- [Content and interface audit / 内容与界面审计](AUDIT.md) +- [Structured source registry](SOURCES.yaml) +- [Generated bilingual reader](https://130u.github.io/portfolio/pi0/) + +The two language files use the same 57 ordered section anchors. Equations, +evidence qualifiers, numerical claims, and source links are audited as a pair. +Substantive edits should be made in both files before rebuilding the site. The +page shell, homepage card, README indexes, RSS, and sitemap are generated from +[`post.json`](post.json); they are not maintained as separate prose copies. diff --git a/research/2026-08-02-pi0-vla-flow/README.zh-CN.md b/research/2026-08-02-pi0-vla-flow/README.zh-CN.md new file mode 100644 index 0000000..10fe888 --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/README.zh-CN.md @@ -0,0 +1,806 @@ +--- +postId: research.001 +lang: zh-CN +--- + +# π₀:机器人怎样把“看懂任务”变成连续动作 + +> 一份兼顾直觉、公式与路线思辨的双语读书笔记。 +> 配对版本:[English](./README.en.md) +> 核心论文:[π₀: A Vision-Language-Action Flow Model for General Robot Control](https://arxiv.org/abs/2410.24164) +> 核验口径:[arXiv v4,2026-01-08](https://arxiv.org/html/2410.24164v4);历史措辞参考:[arXiv v1,2024-10-31](https://arxiv.org/html/2410.24164v1) + +这篇笔记使用五种内容标签: + +- **论文事实**:由 π₀ 论文直接报告,但不等于已有第三方独立复现; +- **作者声明**:作者关于“首次、最大、SOTA”等判断,必须保留“据作者所知”等限定; +- **代码快照**:当前 openpi 公开实现呈现的状态,可能与论文时期接口不同; +- **通俗解释**:为了帮助理解而做的转述,不是论文原句; +- **我的思考**:基于证据形成的路线判断,不冒充论文结论。 + +--- + +<a id="zh-quick-take" data-pair-id="quick-take"></a> +## 一、先用一分钟抓住 π₀ + +<a id="zh-quick-take-what" data-pair-id="quick-take-what"></a> +### 1. π₀ 是什么 + +π₀(读作 pi-zero)是 Physical Intelligence 发布的第一代通用机器人策略。它不是“Physical Intelligence Zero”的正式全称,也不是一个显式预测未来视频的 World Model。它是一种 Vision-Language-Action policy:根据视觉、语言指令和机器人本体状态,直接生成连续动作。 + +最简洁的架构表达是: + +$$ +\pi_0 += +\underbrace{\text{VLM 语义骨干}}_{\text{编码看到了什么、任务要求什么}} ++ +\underbrace{\text{连续 Flow 动作专家}}_{\text{决定接下来怎样运动}} +$$ + +但 Flow Matching 只是动作生成机制,不是 π₀ 的全部。完整配方还包括: + +$$ +\boxed{ +\pi_0 += +\text{VLM 语义} ++ +\text{连续动作专家} ++ +\text{跨机器人数据} ++ +\text{pretraining/post-training} ++ +\text{滚动执行} +} +$$ + +**论文事实:** + +- 视觉语言骨干采用约 30 亿参数的 PaliGemma; +- action expert 约 3 亿参数,总参数量约 33 亿; +- 模型一次生成 $H=50$ 个连续物理动作; +- 推理时对同一个动作块执行 $K=10$ 次 Euler 更新。 + +<a id="zh-quick-take-gap" data-pair-id="quick-take-gap"></a> +### 2. 它弥合了什么设计缺口 + +π₀ 出现之前,两类模型各自解决了问题的一部分: + +| 技术路线 | 擅长什么 | 主要缺口 | +|---|---|---| +| VLM / 早期 VLA | 理解图像、语言、物体和开放词汇指令 | 离散、自回归动作输出不天然适合高频精细控制 | +| Diffusion / 连续控制策略 | 生成平滑、多模态的连续动作 | 缺少大型 VLM 的语义知识和语言迁移能力 | +| π₀ | 将大型 VLM 与连续动作生成器接在一起 | 仍受机器人示范数据覆盖范围约束 | + +π₀ 的关键选择是:不要求一个语言模型同时勉强承担语义理解和底层马达控制,而是让 VLM 提供任务相关的视觉语言上下文,让 action expert 生成连续动作。这一“语义骨干—动作专家”接口,是论文最重要且最耐久的设计。 + +--- + +<a id="zh-execution-loop" data-pair-id="execution-loop"></a> +## 二、机器人实际执行时发生了什么 + +假设指令是:“把桌上的盘子和杯子收进周转箱。” + +<a id="zh-execution-loop-observe" data-pair-id="execution-loop-observe"></a> +### 1. 观察现实 + +机器人接收: + +- 桌面相机、腕部相机等 2–3 路 RGB 图像; +- 自然语言指令; +- 当前关节角、夹爪状态等本体状态。 + +<a id="zh-execution-loop-context" data-pair-id="execution-loop-context"></a> +### 2. 形成任务上下文 + +PaliGemma 编码与任务有关的视觉语言信息:哪些物体是盘子、杯子和箱子,当前目标是什么,物体与指令有什么关系。这里不是“VLM 先说出一句话,再交给动作解码器”;action expert 通过 attention 读取内部隐藏表示。 + +<a id="zh-execution-loop-generate" data-pair-id="execution-loop-generate"></a> +### 3. 从噪声生成动作 + +action expert 从一块高斯噪声开始,经过 10 次 Flow 更新,得到一段包含 50 个连续动作的 action chunk。教学上可以把它描述为: + +> 靠近盘子 → 调整手腕 → 闭合夹爪 → 抬起 → 移向箱子 → 放下。 + +真实输出是连续数值,而不是自然语言步骤。 + +<a id="zh-execution-loop-prefix" data-pair-id="execution-loop-prefix"></a> +### 4. 只执行动作块前缀 + +π₀ 虽然预测 50 步,却不会盲目执行全部动作: + +- 20 Hz 平台执行前 16 步,约 0.8 秒后重新规划; +- 50 Hz 平台执行前 25 步,约 0.5 秒后重新规划。 + +<a id="zh-execution-loop-reobserve" data-pair-id="execution-loop-reobserve"></a> +### 5. 重新观察并滚动规划 + +机器人重新拍摄真实场景。如果物体移动、夹持失败或姿态偏离,下一轮动作会根据新观察重新生成: + +~~~text +观察现实 +→ 形成任务上下文 +→ 生成 50 步动作块 +→ 执行其中一部分 +→ 重新观察现实 +→ 再生成 +~~~ + +**关键边界:**π₀ 在行动之后重新观察真实世界;它没有在行动之前显式生成一段未来视频来预测世界变化。 + +--- + +<a id="zh-inputs-outputs" data-pair-id="inputs-outputs"></a> +## 三、模型的输入和输出 + +<a id="zh-inputs-outputs-observation" data-pair-id="inputs-outputs-observation"></a> +### 1. 当前观察 + +物理时间 $t$ 的观察可写为: + +$$ +o_t=[I_t^1,\ldots,I_t^n,\ell_t,q_t] +$$ + +其中: + +- $I_t^i$:第 $i$ 路相机图像; +- $\ell_t$:语言指令; +- $q_t$:机器人本体状态,例如关节角和夹爪状态。 + +$q_t$ 不是模型内部状态,也不是所谓“action 状态”,而是机器人当前真实的物理状态。 + +<a id="zh-inputs-outputs-vector" data-pair-id="inputs-outputs-vector"></a> +### 2. 单步动作向量 + +$$ +a_t\in\mathbb R^d +$$ + +$a_t$ 表示一个物理时间步的连续控制向量;$d$ 是单步动作维数,不同机器人可以不同。 + +<a id="zh-inputs-outputs-chunk" data-pair-id="inputs-outputs-chunk"></a> +### 3. 动作块 + +$$ +A_t=[a_t,a_{t+1},\ldots,a_{t+H-1}] +\in\mathbb R^{H\times d} +$$ + +π₀ 使用 $H=50$。若展平: + +$$ +\operatorname{vec}(A_t)\in\mathbb R^D, +\qquad D=Hd +$$ + +几个容易混淆的概念必须分开: + +| 概念 | 表示 | 含义 | +|---|---|---| +| action vector | $a_t$ | 一个物理时间步的动作 | +| action chunk | $A_t$ | 连续 $H$ 个物理动作 | +| action slot | Transformer 序列位置 | 承载一个连续动作向量的内部槽位 | +| discrete action token | 词表 ID | 某些 VLA 使用的离散动作表示 | + +π₀ 的 action slot 承载连续向量,不等于语言词表中的离散 token。 + +<a id="zh-inputs-outputs-distribution" data-pair-id="inputs-outputs-distribution"></a> +### 4. 模型真正学习的对象 + +$$ +\boxed{ +p_{\mathrm{data}}(A_t\mid o_t) +} +$$ + +通俗地说:给定当前画面、语言目标和机器人姿态,生成一段合理的未来动作。生成的是动作,不是未来图像或未来世界状态。 + +--- + +<a id="zh-architecture" data-pair-id="architecture"></a> +## 四、架构:VLM 与 action expert 怎样协作 + +<a id="zh-architecture-specialization" data-pair-id="architecture-specialization"></a> +### 1. 两套专长 + +π₀ 可以理解为同一系统中的两套专长权重: + +1. **PaliGemma VLM,约 3B 参数**:处理图像和语言,提供物体、场景和指令语义; +2. **action expert,约 300M 参数**:处理机器人状态、带噪动作和 Flow 时间,预测连续动作的修改方向。 + +一个粗略比喻是“领班 + 编舞师”:领班掌握现场与目标,编舞师把任务意图转成各关节的协调运动。这个比喻只描述分工;模型内部交换的是隐藏表示,并非可读语言。 + +<a id="zh-architecture-not-moe" data-pair-id="architecture-not-moe"></a> +### 2. 它不是普通的稀疏 MoE + +图像和语言固定进入 VLM 权重,本体状态和动作固定进入 action-expert 权重,两者通过 self-attention 交换信息。系统没有学习一个路由器,临时决定每个 token 应该进入哪个专家。 + +更准确地说,π₀ 使用固定的模态分工,让语义骨干与动作专家在同一 Transformer 式注意力系统中协作。 + +<a id="zh-architecture-continuous" data-pair-id="architecture-continuous"></a> +### 3. 为什么不把动作直接当作语言 token + +机器人动作天然是连续数值,并要求多个关节在时间上协调。把动作量化成离散词表并逐 token 输出,可能引入: + +- 量化误差; +- 自回归延迟; +- 多关节和多时间步之间的协调困难。 + +π₀ 一次联合生成整个连续动作块,使 50 个动作位置能够相互协调。 + +--- + +<a id="zh-flow-matching" data-pair-id="flow-matching"></a> +## 五、Conditional Flow Matching:从噪声学习到动作的流 + +<a id="zh-flow-matching-noise" data-pair-id="flow-matching-noise"></a> +### 1. 为什么从噪声开始 + +高斯噪声是容易采样的简单分布: + +$$ +\epsilon\sim\mathcal N(0,I) +$$ + +$\epsilon$ 与动作块 $A_t$ 形状相同。这里的噪声不是注入真实机器人的物理干扰,也不是普通正则化噪声;它是生成模型的起始分布。 + +模型要学习的是: + +$$ +\text{简单高斯分布} +\longrightarrow +\text{给定观察条件后的动作分布} +$$ + +<a id="zh-flow-matching-path" data-pair-id="flow-matching-path"></a> +### 2. 构造训练路径 + +采样 Flow 时间: + +$$ +\tau\in[0,1] +$$ + +在噪声和真实示范动作之间做线性插值: + +$$ +\boxed{ +A_t^\tau=(1-\tau)\epsilon+\tau A_t +} +$$ + +端点为: + +$$ +A_t^0=\epsilon, +\qquad +A_t^1=A_t +$$ + +$\tau=0$ 是纯噪声,$\tau=1$ 是真实动作块,中间状态是一块“半噪声、半动作”的候选动作。这条直线位于生成空间,不代表机械臂在物理空间中沿直线移动。 + +<a id="zh-flow-matching-target" data-pair-id="flow-matching-target"></a> +### 3. 得到配对训练目标 + +将插值式写成: + +$$ +A_t^\tau=\epsilon+\tau(A_t-\epsilon) +$$ + +对 $\tau$ 求导: + +$$ +\boxed{ +\frac{dA_t^\tau}{d\tau}=A_t-\epsilon +} +$$ + +因此,对每一组采样的 $(\epsilon,A_t)$,监督目标速度是: + +$$ +u=A_t-\epsilon +$$ + +通俗地说,它告诉模型这块尚未完成的候选动作应该朝什么方向、以多大幅度修改。 + +<a id="zh-flow-matching-loss" data-pair-id="flow-matching-loss"></a> +### 4. action expert 学习条件向量场 + +模型预测: + +$$ +v_\theta(A_t^\tau,o_t,\tau) +$$ + +训练损失为: + +$$ +\boxed{ +\mathcal L(\theta) += +\mathbb E +\left[ +\left\| +v_\theta(A_t^\tau,o_t,\tau) +-(A_t-\epsilon) +\right\|_2^2 +\right] +} +$$ + +<a id="zh-flow-matching-qualification" data-pair-id="flow-matching-qualification"></a> +### 5. 必须保留的专业限定 + +训练时,每个噪声—示范动作配对都有目标 $A_t-\epsilon$;实际推理时,模型并不知道某个预先指定的真实动作 $A_t$。 + +在理想的无限数据和均方误差优化下,模型学习的是条件向量场: + +$$ +v^*(x,o,\tau) += +\mathbb E +\left[ +A_t-\epsilon +\mid +A_t^\tau=x,\ o_t=o,\ \tau +\right] +$$ + +这意味着 π₀ 学习的是怎样把高斯噪声分布运输成当前条件下的动作分布,而不是检索某一条训练示范。不同初始噪声仍可对应不同的合理动作;条件期望向量场不等于简单输出一条“平均动作”。 + +--- + +<a id="zh-inference" data-pair-id="inference"></a> +## 六、推理:10 次 Euler 更新究竟做了什么 + +<a id="zh-inference-start" data-pair-id="inference-start"></a> +### 1. 从新噪声开始 + +$$ +\hat A_t^{(0)}\sim\mathcal N(0,I) +$$ + +推理时有当前观察 $o_t$,但没有真实动作答案 $A_t$。 + +<a id="zh-inference-ode" data-pair-id="inference-ode"></a> +### 2. 学到的 ODE + +$$ +\frac{d\hat A_t^\tau}{d\tau} += +v_\theta(\hat A_t^\tau,o_t,\tau) +$$ + +<a id="zh-inference-euler" data-pair-id="inference-euler"></a> +### 3. 使用 Forward Euler 离散求解 + +π₀ 设置: + +$$ +K=10, +\qquad +\delta=\frac{1}{K}=0.1 +$$ + +更新公式: + +$$ +\boxed{ +\hat A_t^{(k+1)} += +\hat A_t^{(k)} ++ +\frac{1}{K} +v_\theta +\left( +\hat A_t^{(k)},o_t,\frac{k}{K} +\right) +} +$$ + +其中 $k=0,1,\ldots,9$,最后得到: + +$$ +\hat A_t=\hat A_t^{(10)} +$$ + +<a id="zh-inference-no-proof" data-pair-id="inference-no-proof"></a> +### 4. Euler 法没有证明“10 步必然得到真实动作” + +在一个教学特例中,若假设速度始终为已知常数: + +$$ +v_\theta=A_t-\epsilon +$$ + +则: + +$$ +\hat A_t^{(k)} += +\epsilon+\frac{k}{K}(A_t-\epsilon) +$$ + +当 $k=K$ 时,确实有: + +$$ +\hat A_t^{(K)} += +\epsilon+(A_t-\epsilon) +=A_t +$$ + +这只验证了一条已知、恒速的配对直线路径,不能证明真实模型中 10 步理论上必需、一定足够,或每个噪声样本都会逼近某个指定示范动作。事实上,如果速度恒定且终点已知,一步 $\delta=1$ 也能到达终点。 + +真实模型需要多步,因为学到的向量场随候选动作位置、当前观察和 $\tau$ 改变,同时存在网络近似误差与 Euler 离散误差。因此,10 步是生成质量、数值精度和推理计算之间的工程折中,不是“十步收敛定理”。 + +<a id="zh-inference-h-vs-k" data-pair-id="inference-h-vs-k"></a> +### 5. $H=50$ 与 $K=10$ 完全不同 + +$$ +\boxed{ +10\text{ 次 Flow 更新} +\longrightarrow +1\text{ 个包含 50 步物理动作的 action chunk} +} +$$ + +- $H=50$:动作块包含的物理时间步数; +- $K=10$:生成同一个动作块时的数值积分步数。 + +--- + +<a id="zh-training-deployment" data-pair-id="training-deployment"></a> +## 七、训练与运行不要混在一起 + +<a id="zh-training-deployment-training" data-pair-id="training-deployment-training"></a> +### 训练阶段 + +~~~text +真实观察 o_t 与真实动作块 A_t +→ 采样噪声 ε +→ 采样 Flow 时间 τ +→ 构造中间动作 A_t^τ +→ 监督 action expert 预测 A_t - ε +→ 更新模型参数 +~~~ + +<a id="zh-training-deployment-deployment" data-pair-id="training-deployment-deployment"></a> +### 运行阶段 + +~~~text +当前观察 o_t 与一块新噪声 +→ 做 10 次 Euler 更新 +→ 得到 50 步动作块 +→ 执行动作块前缀 +→ 重新观察真实世界 +→ 滚动重规划 +~~~ + +训练时存在真实动作监督;运行时没有真实答案,只能沿学到的条件向量场采样。 + +--- + +<a id="zh-data-recipe" data-pair-id="data-recipe"></a> +## 八、公式之外:数据与训练配方同样是核心 + +<a id="zh-data-recipe-scale" data-pair-id="data-recipe-scale"></a> +### 1. 数据规模 + +论文报告: + +- 超过 10,000 小时机器人操作数据; +- 约 903M 自有 timesteps; +- 7 类 robot configurations; +- 68 个宽任务; +- 开放数据占训练采样混合的约 9.1%; +- 不同机器人的状态与动作统一填充到最大 18 维接口。 + +903M 指时间步,不是 903M 条完整轨迹;9.1% 是采样混合占比,不是原始时间步占比。 + +<a id="zh-data-recipe-pretraining" data-pair-id="data-recipe-pretraining"></a> +### 2. 广泛预训练 + +预训练数据覆盖多机器人、多任务、多物体和多场景,也包含不完美动作、偏离状态与恢复过程。它的目标不是让每项任务立即达到最高熟练度,而是扩大模型见过的状态和行为范围。 + +<a id="zh-data-recipe-posttraining" data-pair-id="data-recipe-posttraining"></a> +### 3. 高质量 post-training + +任务后训练使用更一致、更熟练、更有针对性的示范,使动作趋于稳定和流畅。简单任务可能需要约 5 小时专项数据,复杂任务可能需要 100 小时以上。 + +π₀ 没有消灭任务数据收集,而是把专项数据的作用从“从零学习全部能力”转变为“校准和精修已有能力”。 + +<a id="zh-data-recipe-system" data-pair-id="data-recipe-system"></a> +### 4. 整套配方 + +$$ +\boxed{ +\text{广泛预训练负责能力覆盖} +\quad+\quad +\text{高质量后训练负责动作熟练度} +} +$$ + +这在方法论上类似“先广泛预训练,再有针对性地适配”,但 π₀ 的任务后训练不应简单等同于语言模型的 RLHF 或 alignment。 + +--- + +<a id="zh-evidence" data-pair-id="evidence"></a> +## 九、实验究竟证明了什么 + +论文从四个层级验证系统: + +| 证据层级 | 主要结果 | 必须保留的限定 | +|---|---|---| +| Direct prompting | 五个任务中 π₀ 的归一化进度分约为 0.75–1.00,明显高于论文基线 | 这些任务族存在于预训练中,不是严格未见任务 | +| 语言跟随 | 能利用人类或高层 VLM 给出的中间语言指令 | π₀-small 同时改变规模、初始化和架构,不是干净的单因素消融 | +| 新任务适配 | 使用 1/5/10 小时数据微调时,预训练通常提高样本效率 | 并非每个任务、每个数据点都获胜 | +| 复杂任务 | 展示洗衣、收桌、装盒等 5–20 分钟任务 | “超过 50%”是部分进度分,不等于完整成功率 | + +其中 0.75–1.00 是对论文图 7 的近似读图范围,并非论文表格给出的精确数值。 + +<a id="zh-evidence-zero-shot" data-pair-id="evidence-zero-shot"></a> +### 1. v1 与 v4 的 zero-shot 口径 + +首发 v1 使用了 zero-shot,但同一版本说明五个基础评测任务族存在于预训练中。当前 v4 已改为 direct prompting / out-of-box。 + +准确的解释是:模型没有针对相应测试版本做任务专门 post-training,不等于它从未见过相关任务族、机器人或行为分布。 + +<a id="zh-evidence-long-horizon" data-pair-id="evidence-long-horizon"></a> +### 2. 长任务不等于完整自主规划 + +部分长任务依赖人类或独立高层 VLM 提供中间指令。π₀ 主要证明了通用底层策略能力,并没有由单个模型同时包办长期目标分解、持久记忆、成功验证、安全判断和底层连续控制。 + +--- + +<a id="zh-innovation" data-pair-id="innovation"></a> +## 十、π₀ 的核心创新究竟是什么 + +π₀ 不是下列任何单项概念的发明者: + +- [RT-2](https://arxiv.org/abs/2307.15818) 更早提出 VLA; +- [Diffusion Policy](https://arxiv.org/abs/2303.04137) 更早探索连续生成式控制; +- action chunking 并非由 π₀ 首创; +- [Octo](https://arxiv.org/abs/2405.12213) 是更早的跨机器人通用策略; +- [OpenVLA](https://arxiv.org/abs/2406.09246) 更早公开了基于 Internet 预训练 VLM 的 VLA。 + +π₀ 的贡献是系统整合: + +1. 用大型预训练 VLM 保留视觉语言语义; +2. 用独立 action expert 处理本体状态与连续动作; +3. 用 Flow Matching 联合生成高频 action chunk; +4. 用万小时跨机器人数据进行基础预训练; +5. 用高质量 post-training 把广泛能力变成熟练行为; +6. 在柔性物体、双臂协调和长任务上做大规模真实机器人展示。 + +论文将贡献定位为整合型创新,只作了带 “to our knowledge” 限定的首创声明:据作者所知,它是首个用于灵巧控制、生成高频动作块的 flow-matching VLA。 + +因此,更稳妥的历史评价是: + +> π₀ 是 Physical Intelligence 的 π 系列和连续 action-expert VLA 路线的定义性起点,但不是整个 VLA 或具身智能领域的第一篇工作。 + +--- + +<a id="zh-route-analysis" data-pair-id="route-analysis"></a> +## 十一、我的思考:π₀ 与 WM/WAM 路线有什么区别 + +> 本章属于路线分析,不是 π₀ 论文已经证明的结论。WAM 是仍在形成中的非标准化术语,不同研究对其边界和耦合方式并没有统一定义。 + +<a id="zh-route-analysis-information" data-pair-id="route-analysis-information"></a> +### 1. 根本区别不是“语言介质 vs 视频介质” + +我原来的直觉是:π₀ 与 LLM/VLM 的关联更深,VLA 的信息更像语言,而 WM/WAM 的信息更像视频或 latent。这个直觉抓住了信息侧重点,却把中间表示说得过于简单。 + +更准确的区分是: + +- π₀ 的语义骨干来自 PaliGemma,因此重视物体、指令和任务语义; +- VLM 交给 action expert 的不是一段可读语言,而是图像、语言和状态形成的隐藏上下文; +- WM/WAM 路线让未来世界的时空结构或预测监督实质参与动作学习。 + +π₀ 式直接策略学习: + +$$ +p(A_t\mid o_t) +$$ + +它问:“根据当前观察,我现在应该怎样行动?” + +World Model 可以学习: + +$$ +p(z_{t+1:t+H}\mid z_t,A_t) +$$ + +它问:“如果执行这些动作,未来世界可能怎样变化?”其中 $z$ 可以是图像、视觉 latent、状态或其他世界表征,不必是人类可读视频。 + +联合型 WAM 的一种教学抽象是: + +$$ +p(A_t,z_{t+1:t+H}\mid o_t) +$$ + +但这不是统一定义;具体系统也可以采用其他分解方式、训练期预测监督或部署期 action-only 输出。 + +<a id="zh-route-analysis-before-after" data-pair-id="route-analysis-before-after"></a> +### 2. “行动后看结果”与“行动前推演”是教学性对比 + +π₀ 的闭环是: + +~~~text +观察 → 直接行动 → 世界真实变化 → 再观察 +~~~ + +World Model 路线可以采用: + +~~~text +观察 → 内部预测若干未来 → 比较后果 → 选择行动 +~~~ + +这是一种帮助理解的典型对比,并不意味着所有 World Model 或 WAM 都会在部署时显式生成视频并搜索多个未来。 + +没有显式 World Model,也不等于 π₀ 毫无物理知识。为了从示范中生成有效动作,其参数可能编码与接触、物体和机器人动力学有关的行动规律。区别在于:π₀ 没有使用一个可单独检查的未来预测目标来训练这些规律,也不天然提供反事实模拟器。它可能“会做”,却不一定显式展示动作之后世界会怎样变化。 + +<a id="zh-route-analysis-latency" data-pair-id="route-analysis-latency"></a> +### 3. 决策路径可能更短,但不保证更快 + +直接策略不必先生成未来世界、评估候选轨迹再选择动作,因此可能拥有较短的决策路径。但: + +- π₀ 自己仍需 10 次 action-expert Flow 更新; +- 某些 WAM 只在训练时使用未来预测监督,部署时可以 action-only; +- 工程速度必须比较端到端 p50/p95 延迟、控制频率、硬件与重规划方式。 + +因此,π₀ 倾向于以更直接的动作接口换取执行效率;WM/WAM 倾向于以更丰富的动态表征换取后果推演能力。具体快慢需要实测。 + +<a id="zh-route-analysis-tradeoff" data-pair-id="route-analysis-tradeoff"></a> +### 4. 两条路线的真实权衡 + +| 维度 | π₀ 式直接 VLA | WM/WAM-first 路线 | +|---|---|---| +| 主要学习对象 | 条件动作分布 | 世界转移、未来表征与动作的耦合 | +| 动作前主要信息 | 当前观察形成的 hidden context | 当前观察加未来结构或预测监督 | +| 是否必须生成视频 | 否 | 也不一定,可以是 latent 或仅训练期监督 | +| 反事实推演 | 不显式提供 | 更容易支持“如果这样做会怎样” | +| 控制接口 | 直接生成连续动作块 | 可联合生成,也可通过策略或 action-only 头输出 | +| 典型优势 | 语义接口清晰、执行链短、适合连续控制 | 动态信息丰富,适合规划和后果判断 | +| 典型风险 | 可能流畅地做错,却缺少显式成功验证 | 计算、内存、数据要求和模型误差可能更高 | + +<a id="zh-route-analysis-fusion" data-pair-id="route-analysis-fusion"></a> +### 5. 长期更可能融合 + +我的判断是:World Model 更适合慢速推演、长期规划、后果判断和异常检测;VLA action expert 更适合快速、连续的底层执行。这是由两条路线的互补性推导出的架构判断,不是论文结论。 + +~~~text +语言与目标理解 +→ 世界动态推演 +→ 任务与子目标规划 +→ 连续动作生成 +→ 真实世界反馈 +~~~ + +因此,“VLA vs WAM”未来可能不是产品分类,而是同一机器人系统内部不同层次的能力。 + +--- + +<a id="zh-strengths-limitations" data-pair-id="strengths-limitations"></a> +## 十二、最大的优点与最耐久的缺点 + +<a id="zh-strengths-limitations-strength" data-pair-id="strengths-limitations-strength"></a> +### 1. 最大优点:建立“语义—运动接口” + +π₀ 最耐久的贡献不是某个榜单分数,也不一定是 Flow Matching 永远最好,而是把大型 VLM 的通用语义能力与一个可以替换和扩展的连续动作专家接在一起。 + +未来可以更换 VLM 主干、动作编码、Flow 求解器、chunk 长度或机器人平台,但“通用语义骨干 + 连续控制专家”的分工仍可能保留。 + +<a id="zh-strengths-limitations-structural" data-pair-id="strengths-limitations-structural"></a> +### 2. 最大结构性缺点:能力边界受示范支持域约束 + +π₀ 本质上仍是离线示范驱动的条件行为克隆。它没有显式提供: + +- 自己是否处于训练分布外的不确定性; +- 不理解时的拒绝执行; +- 动作之后任务是否成功的验证器; +- 持久世界状态与长期记忆; +- 通过在线交互持续学习的机制。 + +因此,它可能在陌生状态下流畅而自信地做错。更多数据能扩大覆盖面,但不会自动带来对未知状态的自知。 + +<a id="zh-strengths-limitations-evidence" data-pair-id="strengths-limitations-evidence"></a> +### 3. 论文证据的耐久限制:难以因果归因 + +- 核心万小时数据无法被第三方完整获得; +- 基线训练预算和动作接口不完全一致; +- π₀ 与 π₀-small 同时改变参数量、初始化和架构; +- 多数条件约进行 10 次真实机器人试验,论文未报告置信区间; +- 复杂任务使用作者自建的部分进度量表。 + +[openpi](https://github.com/Physical-Intelligence/openpi) 现已公开代码和基础权重,因此“完全不开源”已经不准确;但原始 10,000 小时预训练仍不能被第三方完整复刻。 + +论文有力证明的是整套系统配方在作者环境中有效,但没有干净分离 VLM、Flow、数据规模、post-training 与系统工程各自贡献了多少。 + +--- + +<a id="zh-misconceptions" data-pair-id="misconceptions"></a> +## 十三、最容易出现的误读 + +1. **π₀ 是 Physical Intelligence Zero 的正式全称。** + 不是;直接称 π₀ 或 pi-zero。 + +2. **π₀ 是 World Model。** + 不是;它不显式预测未来图像、状态或奖励。 + +3. **VLM 先输出一句语言,再交给动作模型。** + 不是;action expert 读取内部隐藏上下文。 + +4. **$H=50$ 表示动作是 50 维。** + 不是;它表示 50 个物理时间步。 + +5. **10 次 Flow 更新会生成 10 个动作块。** + 不是;10 次更新共同生成一个动作块。 + +6. **Forward Euler 证明 10 步必然到达真实动作。** + 不是;Euler 是数值求解器,10 步是工程选择。 + +7. **50 Hz 表示模型每秒重新看图并完整推理 50 次。** + 不是;这是动作命令频率,系统约每 0.5–0.8 秒重规划。 + +8. **Direct prompting 等于从未见过任务。** + 不是;基础任务族存在于预训练中。 + +9. **长任务完全由 π₀ 单模型自主规划。** + 不完整;部分任务使用人类或高层 VLM 的中间指令。 + +10. **实验已经证明 Flow Matching 是成功的唯一原因。** + 没有;论文主要证明整套 recipe 有效。 + +--- + +<a id="zh-recap" data-pair-id="recap"></a> +## 十四、一分钟复述 + +> π₀ 是 Physical Intelligence 的第一代通用机器人策略。它用约 30 亿参数的 PaliGemma 编码图像和语言,再用约 3 亿参数的 action expert,通过 Conditional Flow Matching 生成包含 50 个连续物理动作的 action chunk。训练时,模型在高斯噪声和真实动作之间构造直线路径,学习候选动作应该怎样修改;推理时从新噪声开始,使用 10 次 Forward Euler 更新得到动作块,然后只执行其中一部分并重新观察现实。π₀ 真正的创新不是一条孤立公式,而是将 VLM 语义、连续动作专家、万小时跨机器人预训练和高质量 post-training 整合成一套 foundation-policy 配方。它与 World Model 的根本区别是:π₀ 直接学习条件动作分布,而 World Model 显式学习世界可能怎样变化。长期看,两条路线更可能在规划层和执行层融合。 + +--- + +<a id="zh-self-check" data-pair-id="self-check"></a> +## 十五、复盘自测 + +如果能够回答下面十个问题,就基本掌握了 π₀: + +1. π₀ 的输入和输出分别是什么? +2. $a_t$、$A_t$、$H$、$d$、$D$ 分别表示什么? +3. action chunk 与 discrete action token 有什么区别? +4. 为什么使用高斯噪声作为起点? +5. 为什么配对训练目标是 $A_t-\epsilon$? +6. 推理时为什么不存在一个已知的真实 $A_t$? +7. Forward Euler 在系统中负责什么,又不负责什么? +8. 为什么 $H=50$ 与 $K=10$ 完全不同? +9. π₀ 的创新为什么是系统配方,而不是单一 Flow 公式? +10. π₀ 与 World Model/WAM 的学习目标有什么区别? + +--- + +<a id="zh-sources" data-pair-id="sources"></a> +## 主要来源 + +- 核心论文:[π₀ v4](https://arxiv.org/html/2410.24164v4)、[π₀ v1](https://arxiv.org/html/2410.24164v1) +- 作者材料:[PI π₀ 项目页](https://www.pi.website/blog/pi0)、[openpi](https://github.com/Physical-Intelligence/openpi) +- 方法前史:[PaliGemma 技术报告](https://arxiv.org/abs/2407.07726)、[Flow Matching](https://arxiv.org/abs/2210.02747)、[Rectified Flow](https://arxiv.org/abs/2209.14577) +- 前置工作:[RT-2](https://arxiv.org/abs/2307.15818)、[Diffusion Policy](https://arxiv.org/abs/2303.04137)、[Octo](https://arxiv.org/abs/2405.12213)、[OpenVLA](https://arxiv.org/abs/2406.09246) +- 后续边界:[π₀.5](https://arxiv.org/abs/2504.16054)、[Real-Time Chunking](https://arxiv.org/abs/2506.07339) +- WAM 路线参考:[WAM Survey](https://arxiv.org/abs/2605.12090)、[VPP](https://arxiv.org/abs/2412.14803)、[DreamZero](https://arxiv.org/abs/2602.15922)、[Fast-WAM](https://arxiv.org/abs/2603.16666)、[GigaWorld-Policy](https://arxiv.org/abs/2603.17240) +- 论文索引:[Hugging Face paper page](https://huggingface.co/papers/2410.24164) + +<a id="zh-verification" data-pair-id="verification"></a> +## 版本与核验说明 + +- 公式、模型、数据与实验事实主要使用 π₀ arXiv v4; +- “zero-shot”历史措辞使用 v1,并明确当前 v4 已改写; +- 论文推导使用 $\tau=0$ 为噪声、$\tau=1$ 为动作;当前 openpi 代码采用相反的时间方向($t=1$ 为噪声、$t=0$ 为动作,$dt<0$),两者通过 $t=1-\tau$ 等价,并不矛盾; +- 正文始终用一般动作维数 $d$。18 维是论文跨机器人数据接口的补齐维数;当前 openpi 的 `Pi0Config` 默认 `action_dim=32`,属于当前开源实现配置,不能混成同一个数字; +- openpi 公共仓库状态于 2026-08-02 通过 GitHub connector 确认为公开仓库; +- Hugging Face 页面沿用 2026-08-01 已核验快照,仅用于元数据与生态索引; +- “我的思考”属于解释性推断,应与论文事实分开阅读。 diff --git a/research/2026-08-02-pi0-vla-flow/SOURCES.yaml b/research/2026-08-02-pi0-vla-flow/SOURCES.yaml new file mode 100644 index 0000000..99611ec --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/SOURCES.yaml @@ -0,0 +1,124 @@ +# Stable source registry for the π₀ bilingual reading note. +# `primary: true` means an original paper, project page, or official repository. + +- id: pi0-paper + type: paper + title: "π₀: A Vision-Language-Action Flow Model for General Robot Control" + url: "https://arxiv.org/abs/2410.24164" + primary: true + +- id: pi0-paper-v4 + type: paper + title: "π₀: A Vision-Language-Action Flow Model for General Robot Control" + version: "arXiv v4" + url: "https://arxiv.org/html/2410.24164v4" + primary: true + +- id: pi0-paper-v1 + type: paper + title: "π₀: A Vision-Language-Action Flow Model for General Robot Control" + version: "arXiv v1" + url: "https://arxiv.org/html/2410.24164v1" + primary: true + +- id: pi0-project + type: project + title: "π₀ — Physical Intelligence" + url: "https://www.pi.website/blog/pi0" + primary: true + +- id: openpi + type: code + title: "Physical-Intelligence/openpi" + url: "https://github.com/Physical-Intelligence/openpi" + primary: true + +- id: huggingface-pi0 + type: index + title: "Hugging Face Papers — 2410.24164" + url: "https://huggingface.co/papers/2410.24164" + primary: false + +- id: paligemma + type: paper + title: "PaliGemma: A versatile 3B VLM for transfer" + url: "https://arxiv.org/abs/2407.07726" + primary: true + +- id: flow-matching + type: paper + title: "Flow Matching for Generative Modeling" + url: "https://arxiv.org/abs/2210.02747" + primary: true + +- id: rectified-flow + type: paper + title: "Flow Straight and Fast: Learning to Generate and Transfer Data with Rectified Flow" + url: "https://arxiv.org/abs/2209.14577" + primary: true + +- id: rt2 + type: paper + title: "RT-2: Vision-Language-Action Models Transfer Web Knowledge to Robotic Control" + url: "https://arxiv.org/abs/2307.15818" + primary: true + +- id: diffusion-policy + type: paper + title: "Diffusion Policy: Visuomotor Policy Learning via Action Diffusion" + url: "https://arxiv.org/abs/2303.04137" + primary: true + +- id: octo + type: paper + title: "Octo: An Open-Source Generalist Robot Policy" + url: "https://arxiv.org/abs/2405.12213" + primary: true + +- id: openvla + type: paper + title: "OpenVLA: An Open-Source Vision-Language-Action Model" + url: "https://arxiv.org/abs/2406.09246" + primary: true + +- id: pi0-5 + type: paper + title: "π₀.₅: a Vision-Language-Action Model with Open-World Generalization" + url: "https://arxiv.org/abs/2504.16054" + primary: true + +- id: real-time-chunking + type: paper + title: "Real-Time Execution of Action Chunking Flow Policies" + url: "https://arxiv.org/abs/2506.07339" + primary: true + +- id: wam-survey + type: paper + title: "World Action Models: The Next Frontier in Embodied AI" + url: "https://arxiv.org/abs/2605.12090" + primary: true + +- id: video-prediction-policy + type: paper + title: "Video Prediction Policy: A Generalist Robot Policy with Predictive Visual Representations" + url: "https://arxiv.org/abs/2412.14803" + primary: true + +- id: dreamzero + type: paper + title: "World Action Models are Zero-shot Policies" + url: "https://arxiv.org/abs/2602.15922" + primary: true + +- id: fast-wam + type: paper + title: "Fast-WAM: Do World Action Models Need Test-time Future Imagination?" + url: "https://arxiv.org/abs/2603.16666" + primary: true + +- id: gigaworld-policy + type: paper + title: "GigaWorld-Policy: An Efficient Action-Centered World--Action Model" + url: "https://arxiv.org/abs/2603.17240" + primary: true diff --git a/research/2026-08-02-pi0-vla-flow/post.json b/research/2026-08-02-pi0-vla-flow/post.json new file mode 100644 index 0000000..b426993 --- /dev/null +++ b/research/2026-08-02-pi0-vla-flow/post.json @@ -0,0 +1,40 @@ +{ + "schemaVersion": 1, + "id": "research.001", + "seriesNo": 1, + "slug": "pi0", + "collection": "research", + "status": "published", + "publishedAt": "2026-08-02T00:00:00-04:00", + "updatedAt": "2026-08-02T00:00:00-04:00", + "featured": true, + "readingMinutes": 35, + "sourceLanguage": "zh-CN", + "languages": { + "zh-CN": { + "file": "README.zh-CN.md", + "title": "π₀:机器人怎样把“看懂任务”变成连续动作", + "summary": "从 VLM 语义骨干、连续动作专家到 Conditional Flow Matching,逐层解释 π₀ 如何把视觉、语言与本体状态变成可执行的动作块。" + }, + "en": { + "file": "README.en.md", + "title": "π₀: How a Robot Turns Understanding into Continuous Action", + "summary": "A layered account of the VLM backbone, continuous action expert, and Conditional Flow Matching that turn vision, language, and proprioception into executable action chunks." + } + }, + "topics": ["Robotics", "Embodied AI", "Vision-Language-Action", "Flow Matching"], + "paper": { + "title": "π₀: A Vision-Language-Action Flow Model for General Robot Control", + "publishedAt": "2024-10-31", + "venue": "arXiv", + "url": "https://arxiv.org/abs/2410.24164", + "arxivId": "2410.24164" + }, + "audit": { + "status": "passed", + "file": "AUDIT.md", + "reviewedAt": "2026-08-02T00:00:00-04:00", + "reviewedBy": "Codex structural audit", + "openIssueCount": 0 + } +} diff --git a/research/README.md b/research/README.md index 1b60ab3..ae8aa9c 100644 --- a/research/README.md +++ b/research/README.md @@ -1,5 +1,12 @@ # Research -Evidence-led research notes, working papers, and source-backed investigations. -Use one Markdown file for a compact note or one slug-named folder for a -multi-file project. +Evidence-led deep readings, working papers, and source-backed investigations. +Every published project includes paired sources and an audit record. + +## Published / 已发布 + +<!-- portfolio:index:start --> +| ID | Date | 中文标题 / English title | Topics | Web | +|---|---|---|---|---| +| R-001 | 2026-08-02 | [π₀:机器人怎样把“看懂任务”变成连续动作](2026-08-02-pi0-vla-flow/README.zh-CN.md)<br>[π₀: How a Robot Turns Understanding into Continuous Action](2026-08-02-pi0-vla-flow/README.en.md) | Robotics, Embodied AI, Vision-Language-Action, Flow Matching | [Web](https://130u.github.io/portfolio/pi0/) | +<!-- portfolio:index:end --> diff --git a/schemas/content-sequence.schema.json b/schemas/content-sequence.schema.json new file mode 100644 index 0000000..c690d28 --- /dev/null +++ b/schemas/content-sequence.schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/130U/portfolio/main/schemas/content-sequence.schema.json", + "title": "Portfolio content sequence high-water marks", + "type": "object", + "additionalProperties": false, + "required": ["schemaVersion", "collections"], + "properties": { + "schemaVersion": { "const": 1 }, + "collections": { + "type": "object", + "additionalProperties": false, + "required": ["article", "research", "reflection"], + "properties": { + "article": { "$ref": "#/$defs/highWatermark" }, + "research": { "$ref": "#/$defs/highWatermark" }, + "reflection": { "$ref": "#/$defs/highWatermark" } + } + } + }, + "$defs": { + "highWatermark": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + } + } +} diff --git a/schemas/post.schema.json b/schemas/post.schema.json new file mode 100644 index 0000000..2f4183f --- /dev/null +++ b/schemas/post.schema.json @@ -0,0 +1,262 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/130U/portfolio/main/schemas/post.schema.json", + "title": "Portfolio post metadata", + "description": "Metadata contract for a bilingual article, research note, or reflection in the portfolio.", + "type": "object", + "additionalProperties": false, + "required": [ + "schemaVersion", + "id", + "seriesNo", + "slug", + "collection", + "status", + "publishedAt", + "updatedAt", + "featured", + "readingMinutes", + "sourceLanguage", + "languages", + "topics" + ], + "properties": { + "schemaVersion": { + "description": "Version of this metadata contract.", + "const": 1 + }, + "id": { + "description": "Stable identifier that does not change when the title or URL changes.", + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[a-z0-9]+(?:[.-][a-z0-9]+)*$" + }, + "seriesNo": { + "description": "Positive sequence number used to order the public body of work.", + "type": "integer", + "minimum": 1 + }, + "slug": { + "description": "URL-safe, lowercase, kebab-case post slug.", + "type": "string", + "minLength": 1, + "maxLength": 120, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" + }, + "collection": { + "type": "string", + "enum": ["article", "research", "reflection"] + }, + "status": { + "type": "string", + "enum": ["draft", "review", "published"] + }, + "publishedAt": { + "description": "Publication timestamp, or null until the post is published.", + "oneOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "featured": { + "type": "boolean" + }, + "readingMinutes": { + "type": "integer", + "minimum": 1 + }, + "sourceLanguage": { + "description": "Language in which the post was originally written.", + "type": "string", + "enum": ["zh-CN", "en"] + }, + "languages": { + "description": "Required localized source files and display metadata.", + "type": "object", + "additionalProperties": false, + "required": ["zh-CN", "en"], + "properties": { + "zh-CN": { + "allOf": [ + { "$ref": "#/$defs/languageEntry" }, + { "type": "object", "properties": { "file": { "const": "README.zh-CN.md" } } } + ] + }, + "en": { + "allOf": [ + { "$ref": "#/$defs/languageEntry" }, + { "type": "object", "properties": { "file": { "const": "README.en.md" } } } + ] + } + } + }, + "topics": { + "type": "array", + "minItems": 1, + "maxItems": 20, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 60 + } + }, + "paper": { + "$ref": "#/$defs/paper" + }, + "audit": { + "$ref": "#/$defs/audit" + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "const": "published" + } + }, + "required": ["status"] + }, + "then": { + "type": "object", + "required": ["audit"], + "properties": { + "publishedAt": { + "type": "string", + "format": "date-time" + }, + "audit": { + "allOf": [ + { "$ref": "#/$defs/audit" }, + { + "type": "object", + "required": ["reviewedAt", "openIssueCount"], + "properties": { + "status": { "const": "passed" }, + "reviewedAt": { "type": "string", "format": "date-time" }, + "openIssueCount": { "const": 0 } + } + } + ] + } + } + } + } + ], + "$defs": { + "relativeMarkdownPath": { + "type": "string", + "minLength": 4, + "pattern": "^(?!/)(?![A-Za-z]:/)(?!.*(?:^|/)\\.\\.(?:/|$))[^\\\\]+\\.md$" + }, + "languageEntry": { + "type": "object", + "additionalProperties": false, + "required": ["file", "title", "summary"], + "properties": { + "file": { + "$ref": "#/$defs/relativeMarkdownPath" + }, + "title": { + "type": "string", + "minLength": 1, + "maxLength": 240 + }, + "summary": { + "type": "string", + "minLength": 1, + "maxLength": 1200 + } + } + }, + "paper": { + "type": "object", + "additionalProperties": false, + "required": ["title", "url"], + "properties": { + "title": { + "type": "string", + "minLength": 1, + "maxLength": 500 + }, + "authors": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 160 + } + }, + "publishedAt": { + "type": "string", + "format": "date" + }, + "venue": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "url": { + "type": "string", + "format": "uri" + }, + "arxivId": { + "type": "string", + "pattern": "^[0-9]{4}\\.[0-9]{4,5}(?:v[0-9]+)?$" + }, + "doi": { + "type": "string", + "minLength": 3, + "maxLength": 200 + } + } + }, + "audit": { + "type": "object", + "additionalProperties": false, + "required": ["status", "file"], + "properties": { + "status": { + "type": "string", + "enum": ["pending", "passed", "needs-revision"] + }, + "file": { + "const": "AUDIT.md" + }, + "reviewedAt": { + "oneOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "reviewedBy": { + "type": "string", + "minLength": 1, + "maxLength": 160 + }, + "openIssueCount": { + "type": "integer", + "minimum": 0 + } + } + } + } +} diff --git a/scripts/check-built-site.mjs b/scripts/check-built-site.mjs new file mode 100644 index 0000000..759e681 --- /dev/null +++ b/scripts/check-built-site.mjs @@ -0,0 +1,140 @@ +import { readFile, readdir } from "node:fs/promises"; +import { dirname, extname, join, relative, resolve, sep } from "node:path"; +import { publicUrl } from "../site.config.mjs"; +import { loadPosts, repoRoot } from "./lib/content.mjs"; + +const outputDir = join(repoRoot, "_site"); +const failures = []; + +async function filesUnder(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const path = join(directory, entry.name); + if (entry.isDirectory()) files.push(...(await filesUnder(path))); + else files.push(path); + } + return files; +} + +async function targetExists(target) { + try { + const stats = await import("node:fs/promises").then(({ stat }) => stat(target)); + if (stats.isDirectory()) await readFile(join(target, "index.html")); + return true; + } catch { + return false; + } +} + +const files = await filesUnder(outputDir); +const htmlFiles = files.filter((file) => extname(file) === ".html"); + +for (const file of htmlFiles) { + const html = await readFile(file, "utf8"); + if (!html.includes('id="main-content"')) failures.push(`${file}: missing main-content landmark`); + if (/<script[^>]+src=["']https?:\/\//i.test(html)) failures.push(`${file}: runtime script uses an external CDN`); + const ids = [...html.matchAll(/\sid=["']([^"']+)["']/g)].map((match) => match[1]); + const duplicateIds = [...new Set(ids.filter((id, index) => ids.indexOf(id) !== index))]; + if (duplicateIds.length) failures.push(`${file}: duplicate DOM ids: ${duplicateIds.join(", ")}`); + + const relativeHtml = relative(outputDir, file).split(sep).join("/"); + if (relativeHtml !== "404.html") { + const route = relativeHtml === "index.html" + ? "" + : relativeHtml.endsWith("/index.html") + ? relativeHtml.slice(0, -"index.html".length) + : relativeHtml; + const expectedCanonical = publicUrl(route); + const canonical = html.match(/<link\b(?=[^>]*\brel=["']canonical["'])[^>]*\bhref=["']([^"']+)["'][^>]*>/i)?.[1]; + if (canonical !== expectedCanonical) failures.push(`${file}: canonical is ${canonical ?? "missing"}; expected ${expectedCanonical}`); + } + + for (const match of html.matchAll(/(?:href|src)=["']([^"']+)["']/g)) { + const raw = match[1]; + if (/^(?:https?:|mailto:|tel:|data:)/.test(raw)) continue; + if (raw.startsWith("#")) { + const fragment = decodeURIComponent(raw.slice(1)); + if (fragment && !ids.includes(fragment)) failures.push(`${file}: broken local fragment ${raw}`); + continue; + } + const clean = decodeURIComponent(raw.split(/[?#]/)[0]); + if (!clean) continue; + const target = clean.startsWith("/portfolio/") + ? join(outputDir, clean.slice("/portfolio/".length)) + : resolve(dirname(file), clean); + if (!(await targetExists(target))) failures.push(`${file}: broken local reference ${raw}`); + } +} + +const posts = (await loadPosts({ includeDrafts: false })).filter((post) => post.data.status === "published"); +for (const post of posts) { + const path = join(outputDir, post.data.slug, "index.html"); + try { + const html = await readFile(path, "utf8"); + if (!html.includes(`data-post-id="${post.data.id}"`)) failures.push(`${path}: missing post identity`); + if ((html.match(/<div class="article-prose" data-language-column=/g) ?? []).length !== 2) failures.push(`${path}: missing static bilingual documents`); + if (!html.includes('class="katex"')) failures.push(`${path}: build-time math rendering is missing`); + if ((html.match(/<h1(?:\s|>)/g) ?? []).length !== 1) failures.push(`${path}: article page must contain exactly one h1`); + if ((html.match(/data-pair-id=/g) ?? []).length !== 114 && post.data.id === "research.001") { + failures.push(`${path}: expected 114 language-specific pair anchors`); + } + if (!html.includes("Conditional Flow Matching") && post.data.id === "research.001") { + failures.push(`${path}: article prose was not rendered at build time`); + } + + const expectedCanonical = publicUrl(`${post.data.slug}/`); + const jsonLdSource = html.match(/<script\b(?=[^>]*\btype=["']application\/ld\+json["'])[^>]*>([\s\S]*?)<\/script>/i)?.[1]; + if (!jsonLdSource) { + failures.push(`${path}: missing JSON-LD`); + } else { + try { + const jsonLd = JSON.parse(jsonLdSource); + if (jsonLd["@type"] !== "TechArticle") failures.push(`${path}: JSON-LD type must be TechArticle`); + if (jsonLd.mainEntityOfPage !== expectedCanonical) failures.push(`${path}: JSON-LD mainEntityOfPage is incorrect`); + if (jsonLd.datePublished !== post.data.publishedAt) failures.push(`${path}: JSON-LD datePublished is incorrect`); + if (jsonLd.dateModified !== post.data.updatedAt) failures.push(`${path}: JSON-LD dateModified is incorrect`); + if (JSON.stringify(jsonLd.inLanguage) !== JSON.stringify(["zh-CN", "en"])) failures.push(`${path}: JSON-LD inLanguage is incorrect`); + if (post.data.paper?.url && jsonLd.citation !== post.data.paper.url) failures.push(`${path}: JSON-LD citation is incorrect`); + } catch (error) { + failures.push(`${path}: invalid JSON-LD: ${error.message}`); + } + } + } catch { + failures.push(`${path}: published route was not generated`); + } + + for (const language of Object.values(post.data.languages)) { + const sourcePath = join(outputDir, post.data.slug, language.file); + if (!(await targetExists(sourcePath))) failures.push(`${sourcePath}: canonical Markdown source was not copied`); + } + for (const supplemental of [post.data.audit?.file, post.data.collection === "research" ? "SOURCES.yaml" : undefined].filter(Boolean)) { + const supplementalPath = join(outputDir, post.data.slug, supplemental); + if (!(await targetExists(supplementalPath))) failures.push(`${supplementalPath}: publication supplement was not copied`); + } +} + +for (const required of ["index.html", "posts.json", "rss.xml", "sitemap-index.xml"]) { + if (!(await targetExists(join(outputDir, required)))) failures.push(`Missing generated artifact: ${required}`); +} + +try { + const rss = await readFile(join(outputDir, "rss.xml"), "utf8"); + if (!rss.includes(`<link>${publicUrl()}</link>`)) failures.push("rss.xml: channel link is missing the configured base path"); +} catch { + failures.push("rss.xml: could not verify channel link"); +} + +try { + const notFound = await readFile(join(outputDir, "404.html"), "utf8"); + if (!/<meta name="robots" content="noindex">/.test(notFound)) failures.push("404.html: missing noindex directive"); +} catch { + failures.push("404.html: could not verify error-page metadata"); +} + +if (failures.length) { + console.error(failures.map((failure) => `- ${failure}`).join("\n")); + process.exitCode = 1; +} else { + console.log(`Built-site audit passed: ${htmlFiles.length} HTML pages, ${posts.length} published article route(s).`); +} diff --git a/scripts/check-content.mjs b/scripts/check-content.mjs new file mode 100644 index 0000000..f2984fb --- /dev/null +++ b/scripts/check-content.mjs @@ -0,0 +1,220 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import matter from "gray-matter"; +import { parse as parseYaml } from "yaml"; +import { + collectionFolders, + exists, + loadPosts, + readLanguageSource, + repoRoot, + safeChildPath, +} from "./lib/content.mjs"; +import { validatePostData } from "./lib/validate-post.mjs"; +import { validateSequenceData } from "./lib/validate-sequence.mjs"; + +const failures = []; +const posts = await loadPosts(); +const seen = { id: new Set(), slug: new Set(), series: new Set() }; +let firstAnchorCount = 0; + +try { + const sequence = JSON.parse(await readFile(join(repoRoot, "content-sequence.json"), "utf8")); + const validation = validateSequenceData(sequence); + if (!validation.valid) throw new Error(validation.errors.join("; ")); + for (const collection of Object.keys(collectionFolders)) { + const highWatermark = sequence.collections[collection]; + const observed = Math.max(0, ...posts.filter((post) => post.data.collection === collection).map((post) => post.data.seriesNo)); + if (highWatermark < observed) { + throw new Error(`${collection} high-water mark ${highWatermark} is below the observed series number ${observed}`); + } + } +} catch (error) { + failures.push(`content-sequence.json: ${error.message}`); +} + +function fail(post, message) { + failures.push(`${post.data?.id ?? post.relativeDir}: ${message}`); +} + +function attribute(attributes, name) { + return attributes.match(new RegExp(`\\b${name}=["']([^"']+)["']`, "i"))?.[1]; +} + +function anchors(source) { + return [...source.matchAll(/<a\b([^>]*)><\/a>/gi)].flatMap((match) => { + const pairId = attribute(match[1], "data-pair-id"); + if (!pairId) return []; + return [{ pairId, id: attribute(match[1], "id"), index: match.index ?? 0, length: match[0].length }]; + }); +} + +function externalLinks(source) { + return [...source.matchAll(/\]\((https?:\/\/[^)]+)\)/g)].map((match) => match[1]); +} + +function displayEquations(source) { + return [...source.matchAll(/^\$\$\s*\n([\s\S]*?)\n\$\$\s*$/gm)].map((match) => match[1]); +} + +function equationStructure(equation) { + return equation.replace(/\\text\{[^{}]*\}/g, "\\text{…}").replace(/\s+/g, " ").trim(); +} + +function headingLevels(source) { + const matches = anchors(source); + return matches.map((match, index) => { + const start = match.index + match.length; + const end = matches[index + 1]?.index ?? source.length; + const heading = source.slice(start, end).match(/^(#{2,6})\s+/m); + return { id: match.pairId, level: heading?.[1].length ?? 0 }; + }); +} + +function time(value) { + return value ? Date.parse(value) : Number.NaN; +} + +for (const post of posts) { + const schema = validatePostData(post.data); + if (!schema.valid) { + for (const error of schema.errors) fail(post, `manifest schema: ${error}`); + continue; + } + + for (const [kind, value] of [ + ["id", post.data.id], + ["slug", post.data.slug], + ["series", `${post.data.collection}:${post.data.seriesNo}`], + ]) { + if (seen[kind].has(value)) fail(post, `duplicate ${kind}: ${value}`); + seen[kind].add(value); + } + + const expectedId = `${post.data.collection}.${String(post.data.seriesNo).padStart(3, "0")}`; + if (post.data.id !== expectedId) fail(post, `id must match collection and seriesNo: ${expectedId}`); + + const [folder, articleDirectory, ...extra] = post.relativeDir.split("/"); + if (extra.length || collectionFolders[post.data.collection] !== folder) { + fail(post, `collection does not match its folder: ${post.data.collection}`); + } + if (!/^\d{4}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(articleDirectory ?? "")) { + fail(post, "article directory must use YYYY-MM-DD-short-slug"); + } else { + const directoryDate = articleDirectory.slice(0, 10); + const parsedDirectoryDate = new Date(`${directoryDate}T00:00:00Z`); + if (Number.isNaN(parsedDirectoryDate.getTime()) || parsedDirectoryDate.toISOString().slice(0, 10) !== directoryDate) { + fail(post, `article directory contains an invalid calendar date: ${directoryDate}`); + } + } + + if (!(await exists(join(post.sourceDir, "README.md")))) fail(post, "missing article README.md entrypoint"); + if (post.data.status === "published") { + if (!post.data.publishedAt) fail(post, "published content requires publishedAt"); + if (post.data.audit?.status !== "passed") fail(post, "published content must have a passed audit"); + if (!post.data.audit?.reviewedAt) fail(post, "published content requires audit.reviewedAt"); + if (post.data.audit?.openIssueCount !== 0) fail(post, "published content requires zero open audit issues"); + } + if (post.data.publishedAt && time(post.data.updatedAt) < time(post.data.publishedAt)) { + fail(post, "updatedAt cannot precede publishedAt"); + } + if (post.data.audit?.reviewedAt && time(post.data.audit.reviewedAt) < time(post.data.updatedAt)) { + fail(post, "audit.reviewedAt cannot predate updatedAt"); + } + if (post.data.audit?.file && !(await exists(safeChildPath(post.sourceDir, post.data.audit.file)))) { + fail(post, `missing audit file: ${post.data.audit.file}`); + } + + let zh; + let en; + try { + [zh, en] = await Promise.all([readLanguageSource(post, "zh-CN"), readLanguageSource(post, "en")]); + } catch (error) { + fail(post, error.message); + continue; + } + + for (const [language, shortLanguage, source] of [["zh-CN", "zh", zh], ["en", "en", en]]) { + let parsed; + try { + parsed = matter(source); + } catch (error) { + fail(post, `${language} frontmatter is invalid: ${error.message}`); + continue; + } + if (parsed.data.postId !== post.data.id) fail(post, `${language} source has the wrong postId`); + if (parsed.data.lang !== language) fail(post, `${language} source has the wrong lang`); + if (/<script\b/i.test(parsed.content)) fail(post, `${language} source contains a script tag`); + for (const image of parsed.content.matchAll(/!\[([^\]]*)\]\([^)]+\)/g)) { + if (!image[1].trim()) fail(post, `${language} source contains an image without alt text`); + } + + const records = anchors(parsed.content); + if (records.some((record) => record.id !== `${shortLanguage}-${record.pairId}`)) { + fail(post, `${language} anchors must use id="${shortLanguage}-<pair-id>"`); + } + const renderedIds = records.map((record) => record.id); + if (new Set(renderedIds).size !== renderedIds.length) fail(post, `${language} source contains duplicate anchor ids`); + } + + const zhAnchors = anchors(zh).map((anchor) => anchor.pairId); + const enAnchors = anchors(en).map((anchor) => anchor.pairId); + firstAnchorCount ||= zhAnchors.length; + if (!zhAnchors.length) fail(post, "bilingual sources contain no stable section anchors"); + if (new Set(zhAnchors).size !== zhAnchors.length) fail(post, "Chinese source contains duplicate pair ids"); + if (new Set(enAnchors).size !== enAnchors.length) fail(post, "English source contains duplicate pair ids"); + if (JSON.stringify(zhAnchors) !== JSON.stringify(enAnchors)) fail(post, "bilingual pair-id order differs"); + if (JSON.stringify(headingLevels(zh)) !== JSON.stringify(headingLevels(en))) fail(post, "paired heading levels differ"); + if (JSON.stringify(externalLinks(zh)) !== JSON.stringify(externalLinks(en))) fail(post, "external source links differ"); + + const zhEquations = displayEquations(zh); + const enEquations = displayEquations(en); + if (zhEquations.length !== enEquations.length) { + fail(post, `display-equation count differs: zh=${zhEquations.length}, en=${enEquations.length}`); + } else if (JSON.stringify(zhEquations.map(equationStructure)) !== JSON.stringify(enEquations.map(equationStructure))) { + fail(post, "display-equation mathematical structure differs"); + } + + if (post.data.collection === "research" && post.data.status === "published") { + const sourcesPath = join(post.sourceDir, "SOURCES.yaml"); + if (!(await exists(sourcesPath))) { + fail(post, "published research requires SOURCES.yaml"); + } else { + try { + const sources = parseYaml(await readFile(sourcesPath, "utf8")); + if (!Array.isArray(sources) || !sources.length) throw new Error("source registry must be a non-empty list"); + const zhExternalLinks = new Set(externalLinks(zh)); + const enExternalLinks = new Set(externalLinks(en)); + const sourceIds = new Set(); + const sourceUrls = new Set(); + let primaryCount = 0; + for (const source of sources) { + if (!source || typeof source !== "object") throw new Error("each source must be an object"); + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(source.id ?? "")) throw new Error(`invalid source id: ${source.id}`); + if (sourceIds.has(source.id)) throw new Error(`duplicate source id: ${source.id}`); + sourceIds.add(source.id); + if (!source.title || !source.type || typeof source.primary !== "boolean") throw new Error(`${source.id} is missing title, type, or primary`); + const url = new URL(source.url); + if (!/^https?:$/.test(url.protocol)) throw new Error(`${source.id} must use an HTTP(S) URL`); + if (sourceUrls.has(source.url)) throw new Error(`duplicate source URL: ${source.url}`); + sourceUrls.add(source.url); + if (!zhExternalLinks.has(source.url) || !enExternalLinks.has(source.url)) throw new Error(`${source.id} is not linked from both language sources`); + if (source.primary) primaryCount += 1; + } + if (!primaryCount) throw new Error("source registry must include at least one primary source"); + for (const url of zhExternalLinks) { + if (!sourceUrls.has(url)) throw new Error(`external source is not registered: ${url}`); + } + } catch (error) { + fail(post, `SOURCES.yaml: ${error.message}`); + } + } + } +} + +if (failures.length) { + console.error(failures.map((failure) => `- ${failure}`).join("\n")); + process.exitCode = 1; +} else { + console.log(`Content audit passed: ${posts.length} post(s); first bilingual pair has ${firstAnchorCount} aligned sections.`); +} diff --git a/scripts/copy-public-sources.mjs b/scripts/copy-public-sources.mjs new file mode 100644 index 0000000..5ca8a82 --- /dev/null +++ b/scripts/copy-public-sources.mjs @@ -0,0 +1,24 @@ +import { copyFile, mkdir } from "node:fs/promises"; +import { join } from "node:path"; +import { exists, loadPosts, repoRoot, safeChildPath } from "./lib/content.mjs"; + +const outputDir = join(repoRoot, "_site"); +const posts = (await loadPosts({ includeDrafts: false })).filter((post) => post.data.status === "published"); + +for (const post of posts) { + const routeDir = join(outputDir, post.data.slug); + await mkdir(routeDir, { recursive: true }); + for (const language of Object.values(post.data.languages)) { + await copyFile(safeChildPath(post.sourceDir, language.file), join(routeDir, language.file)); + } + const supplementalFiles = new Set([ + post.data.audit?.file, + post.data.collection === "research" ? "SOURCES.yaml" : undefined, + ].filter(Boolean)); + for (const file of supplementalFiles) { + const source = safeChildPath(post.sourceDir, file); + if (await exists(source)) await copyFile(source, join(routeDir, file)); + } +} + +console.log(`Copied canonical Markdown sources for ${posts.length} published post(s).`); diff --git a/scripts/generate-indexes.mjs b/scripts/generate-indexes.mjs new file mode 100644 index 0000000..2ac0451 --- /dev/null +++ b/scripts/generate-indexes.mjs @@ -0,0 +1,81 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { collectionFolders, loadPosts, repoRoot, webUrl } from "./lib/content.mjs"; + +const checkOnly = process.argv.includes("--check"); +const startMarker = "<!-- portfolio:index:start -->"; +const endMarker = "<!-- portfolio:index:end -->"; + +function displayId(post) { + return `${post.data.collection[0].toUpperCase()}-${String(post.data.seriesNo).padStart(3, "0")}`; +} +function dateOnly(value) { + return value ? value.slice(0, 10) : "—"; +} + +function replaceGenerated(source, generated, file) { + const start = source.indexOf(startMarker); + const end = source.indexOf(endMarker); + if (start === -1 || end === -1 || end < start) { + throw new Error(`${file} is missing a valid generated index boundary.`); + } + return `${source.slice(0, start + startMarker.length)}\n${generated.trim()}\n${source.slice(end)}`; +} + +function rootIndex(posts) { + if (!posts.length) return "\n_No published writing yet._\n"; + const rows = posts.map((post) => { + const zh = `${post.relativeDir}/${post.data.languages["zh-CN"].file}`; + const en = `${post.relativeDir}/${post.data.languages.en.file}`; + return `| ${displayId(post)} | ${dateOnly(post.data.publishedAt)} | ${post.data.languages["zh-CN"].title}<br>${post.data.languages.en.title} | [中文](${zh}) · [English](${en}) · [Web](${webUrl(post)}) |`; + }); + return [ + "", + "| ID | Date | Article / 文章 | Read |", + "|---|---|---|---|", + ...rows, + "", + ].join("\n"); +} + +function collectionIndex(posts, folder) { + if (!posts.length) return "\n_No published writing in this collection yet._\n"; + const rows = posts.map((post) => { + const articleFolder = post.relativeDir.slice(folder.length + 1); + return `| ${displayId(post)} | ${dateOnly(post.data.publishedAt)} | [${post.data.languages["zh-CN"].title}](${articleFolder}/${post.data.languages["zh-CN"].file})<br>[${post.data.languages.en.title}](${articleFolder}/${post.data.languages.en.file}) | ${post.data.topics.join(", ")} | [Web](${webUrl(post)}) |`; + }); + return [ + "", + "| ID | Date | 中文标题 / English title | Topics | Web |", + "|---|---|---|---|---|", + ...rows, + "", + ].join("\n"); +} + +async function updateFile(path, generated, drift) { + const source = await readFile(path, "utf8"); + const next = replaceGenerated(source, generated, path); + if (next === source) return; + if (checkOnly) drift.push(path); + else await writeFile(path, next, "utf8"); +} + +const posts = (await loadPosts({ includeDrafts: false })).filter((post) => post.data.status === "published"); +const drift = []; +await updateFile(join(repoRoot, "README.md"), rootIndex(posts), drift); + +for (const [collection, folder] of Object.entries(collectionFolders)) { + await updateFile( + join(repoRoot, folder, "README.md"), + collectionIndex(posts.filter((post) => post.data.collection === collection), folder), + drift, + ); +} + +if (drift.length) { + console.error(`Generated README indexes are stale:\n${drift.map((path) => `- ${path}`).join("\n")}\nRun npm run content:index.`); + process.exitCode = 1; +} else { + console.log(checkOnly ? "README indexes are current." : "README indexes generated."); +} diff --git a/scripts/lib/content.mjs b/scripts/lib/content.mjs new file mode 100644 index 0000000..121ee71 --- /dev/null +++ b/scripts/lib/content.mjs @@ -0,0 +1,78 @@ +import { access, readFile, readdir } from "node:fs/promises"; +import { dirname, join, relative, resolve, sep } from "node:path"; +import { fileURLToPath } from "node:url"; +import { publicUrl } from "../../site.config.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +export const repoRoot = dirname(dirname(scriptDir)); +export const collectionFolders = { + article: "articles", + research: "research", + reflection: "reflections", +}; + +async function findManifests(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const path = join(directory, entry.name); + if (entry.isDirectory()) files.push(...(await findManifests(path))); + else if (entry.isFile() && entry.name === "post.json") files.push(path); + } + return files; +} + +export async function loadPosts({ includeDrafts = true } = {}) { + const manifestPaths = []; + for (const folder of Object.values(collectionFolders)) { + manifestPaths.push(...(await findManifests(join(repoRoot, folder)))); + } + + const posts = await Promise.all( + manifestPaths.map(async (manifestPath) => { + const data = JSON.parse(await readFile(manifestPath, "utf8")); + return { + data, + manifestPath, + sourceDir: dirname(manifestPath), + relativeDir: relative(repoRoot, dirname(manifestPath)).split(sep).join("/"), + }; + }), + ); + + return posts + .filter((post) => includeDrafts || post.data.status === "published") + .sort((a, b) => { + const dateOrder = Date.parse(b.data.publishedAt ?? "") - Date.parse(a.data.publishedAt ?? ""); + return dateOrder || a.data.id.localeCompare(b.data.id); + }); +} + +export function safeChildPath(parent, child) { + if (typeof child !== "string" || !child || child.includes("\\")) { + throw new Error(`Invalid repository-relative path: ${String(child)}`); + } + const target = resolve(parent, child); + const prefix = `${resolve(parent)}${sep}`; + if (!target.startsWith(prefix)) throw new Error(`Path escapes article directory: ${child}`); + return target; +} + +export async function readLanguageSource(post, language) { + const entry = post.data.languages?.[language]; + if (!entry) throw new Error(`${post.data.id} has no ${language} metadata.`); + return readFile(safeChildPath(post.sourceDir, entry.file), "utf8"); +} + +export async function exists(path) { + try { + await access(path); + return true; + } catch { + return false; + } +} + +export function webUrl(post) { + return publicUrl(`${post.data.slug}/`); +} diff --git a/scripts/lib/validate-post.mjs b/scripts/lib/validate-post.mjs new file mode 100644 index 0000000..1835d1e --- /dev/null +++ b/scripts/lib/validate-post.mjs @@ -0,0 +1,21 @@ +import { readFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import Ajv2020 from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; + +const here = dirname(fileURLToPath(import.meta.url)); +const schema = JSON.parse(await readFile(join(here, "../../schemas/post.schema.json"), "utf8")); +const ajv = new Ajv2020({ allErrors: true, strict: true }); +addFormats(ajv); +const validate = ajv.compile(schema); + +export function validatePostData(data) { + const valid = validate(data); + return { + valid, + errors: valid + ? [] + : (validate.errors ?? []).map((error) => `${error.instancePath || "/"} ${error.message}`), + }; +} diff --git a/scripts/lib/validate-sequence.mjs b/scripts/lib/validate-sequence.mjs new file mode 100644 index 0000000..cca31b9 --- /dev/null +++ b/scripts/lib/validate-sequence.mjs @@ -0,0 +1,19 @@ +import { readFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import Ajv2020 from "ajv/dist/2020.js"; + +const here = dirname(fileURLToPath(import.meta.url)); +const schema = JSON.parse(await readFile(join(here, "../../schemas/content-sequence.schema.json"), "utf8")); +const ajv = new Ajv2020({ allErrors: true, strict: true }); +const validate = ajv.compile(schema); + +export function validateSequenceData(data) { + const valid = validate(data); + return { + valid, + errors: valid + ? [] + : (validate.errors ?? []).map((error) => `${error.instancePath || "/"} ${error.message}`), + }; +} diff --git a/scripts/new-article.mjs b/scripts/new-article.mjs new file mode 100644 index 0000000..770330f --- /dev/null +++ b/scripts/new-article.mjs @@ -0,0 +1,164 @@ +import { randomUUID } from "node:crypto"; +import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { setTimeout as delay } from "node:timers/promises"; +import { collectionFolders, exists, loadPosts, repoRoot } from "./lib/content.mjs"; +import { validatePostData } from "./lib/validate-post.mjs"; +import { validateSequenceData } from "./lib/validate-sequence.mjs"; + +const transientWindowsErrors = new Set(["EACCES", "EBUSY", "ENOTEMPTY", "EPERM"]); + +async function withFilesystemRetry(operation) { + let lastError; + for (let attempt = 0; attempt < 6; attempt += 1) { + try { + return await operation(); + } catch (error) { + lastError = error; + if (!transientWindowsErrors.has(error.code) || attempt === 5) throw error; + await delay(40 * (2 ** attempt)); + } + } + throw lastError; +} + +async function removeWithRetry(path, options) { + return withFilesystemRetry(() => rm(path, options)); +} + +async function atomicWriteJson(path, data, temporaryRoot) { + const temporaryPath = join(temporaryRoot, `sequence-${randomUUID()}.json`); + let handle; + try { + handle = await open(temporaryPath, "wx"); + await handle.writeFile(`${JSON.stringify(data, null, 2)}\n`, "utf8"); + await handle.sync(); + await handle.close(); + handle = undefined; + await withFilesystemRetry(() => rename(temporaryPath, path)); + } catch (error) { + await handle?.close().catch(() => {}); + await removeWithRetry(temporaryPath, { force: true }).catch((cleanupError) => { + console.warn(`Warning: could not remove temporary sequence file ${temporaryPath}: ${cleanupError.message}`); + }); + throw error; + } +} + +const args = Object.fromEntries( + process.argv.slice(2).reduce((pairs, token, index, source) => { + if (token.startsWith("--")) pairs.push([token.slice(2), source[index + 1]]); + return pairs; + }, []), +); +const collection = args.collection; +const slug = args.slug; +const titleZh = args["title-zh"]; +const titleEn = args["title-en"]; + +if (!collectionFolders[collection] || !slug || !titleZh || !titleEn) { + console.error('Usage: npm run content:new -- --collection research --slug short-slug --title-zh "标题" --title-en "Title"'); + process.exit(1); +} +if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) throw new Error("slug must be lowercase kebab-case"); + +const today = new Date().toISOString().slice(0, 10); +const directory = join(repoRoot, collectionFolders[collection], `${today}-${slug}`); +const temporaryRoot = join(repoRoot, ".content-tmp"); +const sequencePath = join(repoRoot, "content-sequence.json"); +const lockPath = join(temporaryRoot, "allocation.lock"); +await mkdir(temporaryRoot, { recursive: true }); + +let allocationLock; +try { + allocationLock = await open(lockPath, "wx"); + await allocationLock.writeFile(`${process.pid} ${new Date().toISOString()}\n`, "utf8"); +} catch (error) { + if (allocationLock) { + await allocationLock.close().catch(() => {}); + await removeWithRetry(lockPath, { force: true }).catch(() => {}); + } + if (error.code === "EEXIST") { + throw new Error("Another content:new allocation is active. If no process is running, remove .content-tmp/allocation.lock and retry."); + } + throw error; +} + +try { + const posts = await loadPosts(); + if (posts.some((post) => post.data.slug === slug)) throw new Error(`slug is already in use: ${slug}`); + if (await exists(directory)) throw new Error(`article directory already exists: ${directory}`); + + const sequence = JSON.parse(await readFile(sequencePath, "utf8")); + const sequenceValidation = validateSequenceData(sequence); + if (!sequenceValidation.valid) throw new Error(`Invalid content-sequence.json:\n${sequenceValidation.errors.join("\n")}`); + const observedMax = Math.max(0, ...posts.filter((post) => post.data.collection === collection).map((post) => post.data.seriesNo)); + const recordedMax = sequence.collections[collection]; + if (Math.max(observedMax, recordedMax) >= Number.MAX_SAFE_INTEGER) { + throw new Error(`${collection} sequence is exhausted`); + } + const seriesNo = Math.max(observedMax, recordedMax) + 1; + const id = `${collection}.${String(seriesNo).padStart(3, "0")}`; + const post = { + schemaVersion: 1, + id, + seriesNo, + slug, + collection, + status: "draft", + publishedAt: null, + updatedAt: `${today}T00:00:00Z`, + featured: false, + readingMinutes: 1, + sourceLanguage: "zh-CN", + languages: { + "zh-CN": { file: "README.zh-CN.md", title: titleZh, summary: "待填写摘要。" }, + en: { file: "README.en.md", title: titleEn, summary: "Summary to be written." }, + }, + topics: ["Uncategorized"], + audit: { status: "pending", file: "AUDIT.md", reviewedAt: null, openIssueCount: 0 }, + }; + + const validation = validatePostData(post); + if (!validation.valid) throw new Error(`Generated manifest is invalid:\n${validation.errors.join("\n")}`); + + sequence.collections[collection] = seriesNo; + await atomicWriteJson(sequencePath, sequence, temporaryRoot); + + const temporaryDirectory = join(temporaryRoot, randomUUID()); + await mkdir(temporaryDirectory, { recursive: true }); + try { + const writes = await Promise.allSettled([ + writeFile(join(temporaryDirectory, "post.json"), `${JSON.stringify(post, null, 2)}\n`, "utf8"), + writeFile(join(temporaryDirectory, "README.md"), `# ${titleEn}\n\n- [中文](README.zh-CN.md)\n- [English](README.en.md)\n- [Audit](AUDIT.md)\n`, "utf8"), + writeFile(join(temporaryDirectory, "README.zh-CN.md"), `---\npostId: ${id}\nlang: zh-CN\n---\n\n# ${titleZh}\n\n<a id="zh-quick-take" data-pair-id="quick-take"></a>\n## 一分钟结论\n\n待写。\n`, "utf8"), + writeFile(join(temporaryDirectory, "README.en.md"), `---\npostId: ${id}\nlang: en\n---\n\n# ${titleEn}\n\n<a id="en-quick-take" data-pair-id="quick-take"></a>\n## One-minute takeaway\n\nTo be written.\n`, "utf8"), + writeFile(join(temporaryDirectory, "SOURCES.yaml"), "# Add primary sources with stable IDs.\n[]\n", "utf8"), + writeFile(join(temporaryDirectory, "AUDIT.md"), `# Audit\n\nStatus: pending\n\n- [ ] Metadata\n- [ ] Bilingual anchors\n- [ ] Sources and evidence boundaries\n- [ ] Build and visual QA\n`, "utf8"), + ]); + const writeFailures = writes.filter((result) => result.status === "rejected").map((result) => result.reason); + if (writeFailures.length) throw new AggregateError(writeFailures, "Could not create every draft file"); + const latestPosts = await loadPosts(); + const conflict = latestPosts.find((candidate) => + candidate.data.slug === slug || + candidate.data.id === id || + (candidate.data.collection === collection && candidate.data.seriesNo === seriesNo) + ); + if (conflict) throw new Error(`content changed during allocation; conflict with ${conflict.data.id}`); + await withFilesystemRetry(() => rename(temporaryDirectory, directory)); + } catch (error) { + await removeWithRetry(temporaryDirectory, { recursive: true, force: true }).catch((cleanupError) => { + console.warn(`Warning: could not remove temporary article directory ${temporaryDirectory}: ${cleanupError.message}`); + }); + throw error; + } + + console.log(`Created ${id} at ${directory}. Run npm run content:index after completing metadata.`); +} finally { + await allocationLock.close().catch((error) => { + console.warn(`Warning: could not close the allocation lock: ${error.message}`); + }); + await removeWithRetry(lockPath, { force: true }).catch((error) => { + console.warn(`Warning: could not remove ${lockPath}; verify that no content:new process is running before deleting it manually: ${error.message}`); + }); +} diff --git a/site.config.mjs b/site.config.mjs new file mode 100644 index 0000000..47d6524 --- /dev/null +++ b/site.config.mjs @@ -0,0 +1,7 @@ +export const siteUrl = "https://130u.github.io"; +export const basePath = "/portfolio"; + +export function publicUrl(path = "") { + const clean = path.replace(/^\/+/, ""); + return new URL(`${basePath}/${clean}`, siteUrl).href; +} diff --git a/src/components/CollectionPage.astro b/src/components/CollectionPage.astro new file mode 100644 index 0000000..121a83b --- /dev/null +++ b/src/components/CollectionPage.astro @@ -0,0 +1,42 @@ +--- +import type { PostEntry } from "../lib/site"; +import { collectionCopy } from "../lib/site"; +import BaseLayout from "../layouts/BaseLayout.astro"; +import PostCard from "./PostCard.astro"; + +interface Props { + collection: "article" | "research" | "reflection"; + posts: PostEntry[]; +} + +const { collection, posts } = Astro.props; +const copy = collectionCopy[collection]; +const description = `${copy.en} / ${copy.zh} — Theodore Ouyang's bilingual writing archive.`; +--- + +<BaseLayout title={`${copy.en} — Theodore Ouyang`} description={description} canonicalPath={`${copy.path}/`}> + <main id="main-content"> + <section class="collection-hero"> + <p class="eyebrow">Collection · 内容集合</p> + <div class="parallel-copy"> + <div data-content-lang="zh" lang="zh-CN"> + <h1>{copy.zh}</h1> + <p>可审阅、可引用、可长期维护的写作档案。</p> + </div> + <div data-content-lang="en" lang="en"> + <h1>{copy.en}</h1> + <p>Reviewable, citable writing designed to remain maintainable over time.</p> + </div> + </div> + </section> + <section class="notes-section" aria-label={`${copy.en} archive`}> + <div class="section-heading"> + <h2>Archive · 归档</h2> + <p>{String(posts.length).padStart(2, "0")}</p> + </div> + <div class="card-stack"> + {posts.length ? posts.map((post) => <PostCard post={post} />) : <p class="empty-state">No published notes yet · 暂无已发布内容</p>} + </div> + </section> + </main> +</BaseLayout> diff --git a/src/components/PostCard.astro b/src/components/PostCard.astro new file mode 100644 index 0000000..95388c0 --- /dev/null +++ b/src/components/PostCard.astro @@ -0,0 +1,41 @@ +--- +import type { PostEntry } from "../lib/site"; +import { articleHref, collectionCopy, formatDate } from "../lib/site"; + +interface Props { + post: PostEntry; +} + +const { post } = Astro.props; +const { data } = post; +const number = String(data.seriesNo).padStart(2, "0"); +--- + +<article class="note-card"> + <a class="note-card-link" href={articleHref(data.slug)}> + <div class="note-card-meta"> + <span>{collectionCopy[data.collection].en} · {data.topics.slice(0, 2).join(" · ")}</span> + <time datetime={data.publishedAt ?? undefined}>{formatDate(data.publishedAt)}</time> + </div> + <div class="note-card-body"> + <div class="note-index" aria-hidden="true">{number}</div> + <div class="note-card-copy"> + <div data-content-lang="zh" lang="zh-CN"> + <p class="note-kicker">{collectionCopy[data.collection].zh} · {data.readingMinutes} 分钟</p> + <h3>{data.languages["zh-CN"].title}</h3> + <p>{data.languages["zh-CN"].summary}</p> + </div> + <div data-content-lang="en" lang="en"> + <p class="note-kicker">{collectionCopy[data.collection].en} · {data.readingMinutes} min</p> + <h3>{data.languages.en.title}</h3> + <p>{data.languages.en.summary}</p> + </div> + </div> + </div> + <div class="note-card-footer"> + <span data-content-lang="zh" lang="zh-CN">阅读文章</span> + <span data-content-lang="en" lang="en">Read the note</span> + <span class="arrow" aria-hidden="true">↗</span> + </div> + </a> +</article> diff --git a/src/components/SiteHeader.astro b/src/components/SiteHeader.astro new file mode 100644 index 0000000..b1118c1 --- /dev/null +++ b/src/components/SiteHeader.astro @@ -0,0 +1,25 @@ +--- +import { collectionHref } from "../lib/site"; + +const home = import.meta.env.BASE_URL; +--- + +<header class="site-header"> + <a class="wordmark" href={home} aria-label="Theodore Ouyang, home"> + <span class="wordmark-mark" aria-hidden="true">T·O</span> + <span>Theodore Ouyang</span> + </a> + + <nav class="site-nav" aria-label="Primary / 主导航"> + <a href={collectionHref("article")}>Articles</a> + <a href={collectionHref("research")}>Research</a> + <a href={collectionHref("reflection")}>Reflections</a> + </nav> + + <div class="language-control" role="group" aria-label="Reading language / 阅读语言"> + <span class="language-control-label" aria-hidden="true">Language</span> + <button type="button" data-language-control="zh" aria-pressed="false">中文</button> + <button type="button" data-language-control="en" aria-pressed="false">English</button> + <button type="button" data-language-control="both" aria-pressed="true">中英对照</button> + </div> +</header> diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..93150f7 --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,86 @@ +import { defineCollection } from "astro:content"; +import { glob } from "astro/loaders"; +import { z } from "astro/zod"; + +const languageEntry = (file: "README.zh-CN.md" | "README.en.md") => z.object({ + file: z.literal(file), + title: z.string().min(1).max(240), + summary: z.string().min(1).max(1200), +}).strict(); + +const uniqueStrings = (values: string[]) => new Set(values).size === values.length; + +const posts = defineCollection({ + loader: glob({ + base: ".", + pattern: "{articles,research,reflections}/**/post.json", + }), + schema: z.object({ + schemaVersion: z.literal(1), + id: z.string().max(100).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/), + seriesNo: z.number().int().positive(), + slug: z.string().max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), + collection: z.enum(["article", "research", "reflection"]), + status: z.enum(["draft", "review", "published"]), + publishedAt: z.iso.datetime({ offset: true }).nullable(), + updatedAt: z.iso.datetime({ offset: true }), + featured: z.boolean(), + readingMinutes: z.number().int().positive(), + sourceLanguage: z.enum(["zh-CN", "en"]), + languages: z.object({ + "zh-CN": languageEntry("README.zh-CN.md"), + en: languageEntry("README.en.md"), + }).strict(), + topics: z.array(z.string().min(1).max(60)).min(1).max(20).refine(uniqueStrings, "Topics must be unique"), + paper: z + .object({ + title: z.string().min(1).max(500), + authors: z.array(z.string().min(1).max(160)).min(1).refine(uniqueStrings, "Authors must be unique").optional(), + publishedAt: z.iso.date().optional(), + venue: z.string().min(1).max(200).optional(), + url: z.url(), + arxivId: z.string().regex(/^[0-9]{4}\.[0-9]{4,5}(?:v[0-9]+)?$/).optional(), + doi: z.string().min(3).max(200).optional(), + }).strict() + .optional(), + audit: z + .object({ + status: z.enum(["pending", "passed", "needs-revision"]), + file: z.literal("AUDIT.md"), + reviewedAt: z.iso.datetime({ offset: true }).nullable().optional(), + reviewedBy: z.string().min(1).max(160).optional(), + openIssueCount: z.number().int().nonnegative().optional(), + }).strict() + .optional(), + }).strict().superRefine((data, context) => { + const issue = (path: (string | number)[], message: string) => context.addIssue({ code: "custom", path, message }); + if (data.status === "published") { + if (data.publishedAt === null) issue(["publishedAt"], "Published posts require a publication timestamp"); + if (!data.audit) issue(["audit"], "Published posts require an audit"); + else { + if (data.audit.status !== "passed") issue(["audit", "status"], "Published posts require a passed audit"); + if (!data.audit.reviewedAt) issue(["audit", "reviewedAt"], "Published posts require an audit timestamp"); + if (data.audit.openIssueCount !== 0) issue(["audit", "openIssueCount"], "Published posts require zero open issues"); + } + } + if (data.publishedAt && Date.parse(data.updatedAt) < Date.parse(data.publishedAt)) { + issue(["updatedAt"], "updatedAt cannot precede publishedAt"); + } + if (data.audit?.reviewedAt && Date.parse(data.audit.reviewedAt) < Date.parse(data.updatedAt)) { + issue(["audit", "reviewedAt"], "The audit cannot predate the current content revision"); + } + }), +}); + +const documents = defineCollection({ + loader: glob({ + base: ".", + pattern: "{articles,research,reflections}/**/README.{zh-CN,en}.md", + }), + schema: z.object({ + postId: z.string().regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/), + lang: z.enum(["zh-CN", "en"]), + }).strict(), +}); + +export const collections = { posts, documents }; diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..f964fe0 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1 @@ +/// <reference types="astro/client" /> diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..d41a8d9 --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,97 @@ +--- +import SiteHeader from "../components/SiteHeader.astro"; +import "katex/dist/katex.min.css"; +import "../styles/site.css"; + +interface Props { + title: string; + description: string; + canonicalPath?: string | null; + noindex?: boolean; + article?: boolean; + publishedAt?: string | null; + updatedAt?: string; + topics?: string[]; + paperUrl?: string; +} + +const { + title, + description, + canonicalPath = "", + noindex = false, + article = false, + publishedAt, + updatedAt, + topics = [], + paperUrl, +} = Astro.props; +const base = import.meta.env.BASE_URL; +const canonical = canonicalPath === null + ? undefined + : new URL(`${base}${canonicalPath.replace(/^\/+/, "")}`, Astro.site ?? Astro.url).href; +const structuredData = article + ? { + "@context": "https://schema.org", + "@type": "TechArticle", + headline: title, + description, + datePublished: publishedAt ?? undefined, + dateModified: updatedAt, + author: { "@type": "Person", name: "Theodore Ouyang" }, + inLanguage: ["zh-CN", "en"], + keywords: topics, + mainEntityOfPage: canonical, + citation: paperUrl, + } + : undefined; +--- + +<!doctype html> +<html lang="mul" data-language-mode="both"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="description" content={description} /> + <meta name="color-scheme" content="light" /> + <meta name="theme-color" content="#f4ede1" /> + {canonical && <link rel="canonical" href={canonical} />} + {noindex && <meta name="robots" content="noindex" />} + <link rel="alternate" type="application/rss+xml" title="Theodore Ouyang — Portfolio" href={`${base}rss.xml`} /> + <meta property="og:type" content={article ? "article" : "website"} /> + <meta property="og:title" content={title} /> + <meta property="og:description" content={description} /> + {canonical && <meta property="og:url" content={canonical} />} + <meta property="og:site_name" content="Theodore Ouyang — Portfolio" /> + <meta name="twitter:card" content="summary" /> + <title>{title} + + {structuredData && + + diff --git a/src/lib/site.ts b/src/lib/site.ts new file mode 100644 index 0000000..84eacd8 --- /dev/null +++ b/src/lib/site.ts @@ -0,0 +1,36 @@ +import type { CollectionEntry } from "astro:content"; + +export type PostEntry = CollectionEntry<"posts">; + +export const collectionCopy = { + article: { zh: "文章", en: "Articles", path: "articles" }, + research: { zh: "研究", en: "Research", path: "research" }, + reflection: { zh: "思考", en: "Reflections", path: "reflections" }, +} as const; + +export function published(posts: PostEntry[]) { + return posts + .filter((post) => post.data.status === "published") + .sort((a, b) => { + const dateOrder = Date.parse(b.data.publishedAt ?? "") - Date.parse(a.data.publishedAt ?? ""); + return dateOrder || a.data.id.localeCompare(b.data.id); + }); +} + +export function formatDate(value: string | null, locale: "zh-CN" | "en" = "en") { + if (!value) return locale === "zh-CN" ? "尚未发布" : "Not published"; + return new Intl.DateTimeFormat(locale, { + year: "numeric", + month: locale === "en" ? "short" : "long", + day: "2-digit", + timeZone: "UTC", + }).format(new Date(value)); +} + +export function articleHref(slug: string) { + return `${import.meta.env.BASE_URL}${slug}/`; +} + +export function collectionHref(collection: keyof typeof collectionCopy) { + return `${import.meta.env.BASE_URL}${collectionCopy[collection].path}/`; +} diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..cde4456 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,14 @@ +--- +import BaseLayout from "../layouts/BaseLayout.astro"; +--- + +
+
+

404

+
+

这一页不存在。

返回首页

+

This page does not exist.

Return home

+
+
+
+
diff --git a/src/pages/[slug]/index.astro b/src/pages/[slug]/index.astro new file mode 100644 index 0000000..902f6b3 --- /dev/null +++ b/src/pages/[slug]/index.astro @@ -0,0 +1,131 @@ +--- +import { getCollection, render, type CollectionEntry } from "astro:content"; +import { dirname, resolve } from "node:path"; +import BaseLayout from "../../layouts/BaseLayout.astro"; +import { collectionCopy, collectionHref, formatDate, published } from "../../lib/site"; + +export async function getStaticPaths() { + const posts = published(await getCollection("posts")); + const documents = await getCollection("documents"); + + return posts.map((post) => { + if (!post.filePath) throw new Error(`Missing manifest path for ${post.data.id}`); + const sourceDirectory = dirname(post.filePath); + const selectDocument = (language: "zh-CN" | "en") => { + const expectedPath = resolve(sourceDirectory, post.data.languages[language].file); + const matches = documents.filter((document) => + document.filePath && + resolve(document.filePath) === expectedPath && + document.data.postId === post.data.id && + document.data.lang === language + ); + if (matches.length !== 1) throw new Error(`Expected exactly one ${language} document for ${post.data.id}`); + return matches[0]; + }; + const zh = selectDocument("zh-CN"); + const en = selectDocument("en"); + return { params: { slug: post.data.slug }, props: { post, zh, en } }; + }); +} + +interface Props { + post: CollectionEntry<"posts">; + zh: CollectionEntry<"documents">; + en: CollectionEntry<"documents">; +} + +const { post, zh, en } = Astro.props; +const { data } = post; +const [{ Content: ZhContent }, { Content: EnContent }] = await Promise.all([render(zh), render(en)]); + +function pairedHeadings(body = "") { + const matches = [...body.matchAll(/]*\bdata-pair-id=["']([^"']+)["'][^>]*><\/a>/gi)]; + return matches.flatMap((match, index) => { + const start = (match.index ?? 0) + match[0].length; + const end = matches[index + 1]?.index ?? body.length; + const heading = body.slice(start, end).match(/^(#{2})\s+(.+)$/m); + return heading ? [{ id: match[1], label: heading[2].replace(/[*_`]/g, "").trim() }] : []; + }); +} + +const zhToc = pairedHeadings(zh.body); +const enToc = pairedHeadings(en.body); +const toc = zhToc.map((item, index) => ({ ...item, en: enToc[index]?.label ?? item.label })); +const copy = collectionCopy[data.collection]; +const publicSourceBase = `${import.meta.env.BASE_URL}${data.slug}/`; +--- + + +
+
+ ← {copy.en} / {copy.zh} +

{String(data.seriesNo).padStart(2, "0")} · {data.topics.join(" · ")}

+

+ {data.languages["zh-CN"].title} + {data.languages.en.title} +

+
+

{data.languages["zh-CN"].summary}

+

{data.languages.en.summary}

+
+ +
+ +
+ + +
+
+ +
+
+ +
+
+
+
+ +
diff --git a/src/pages/articles/index.astro b/src/pages/articles/index.astro new file mode 100644 index 0000000..0873483 --- /dev/null +++ b/src/pages/articles/index.astro @@ -0,0 +1,8 @@ +--- +import { getCollection } from "astro:content"; +import CollectionPage from "../../components/CollectionPage.astro"; +import { published } from "../../lib/site"; + +const posts = published(await getCollection("posts")).filter((post) => post.data.collection === "article"); +--- + diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..8049f77 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,61 @@ +--- +import { getCollection } from "astro:content"; +import PostCard from "../components/PostCard.astro"; +import BaseLayout from "../layouts/BaseLayout.astro"; +import { collectionCopy, collectionHref, published } from "../lib/site"; + +const posts = published(await getCollection("posts")); +const latest = posts.slice(0, 6); +const counts = Object.fromEntries( + Object.keys(collectionCopy).map((collection) => [ + collection, + posts.filter((post) => post.data.collection === collection).length, + ]), +); +--- + + +
+
+

Research notes · 研究笔记

+
+
+

把复杂问题,写到真正看懂为止。

+

关于人工智能、机器人与技术路径的研究笔记:保留证据,也保留推理发生的过程。

+
+
+

Follow a difficult question until it becomes legible.

+

Research on AI, robotics, and technical strategy—preserving both the evidence and the reasoning that connects it.

+
+
+
+ +
+ { + Object.entries(collectionCopy).map(([key, copy]) => ( + + {copy.zh} + {copy.en} + {String(counts[key]).padStart(2, "0")} + + )) + } +
+ +
+
+
+

Latest writing · 最新整理

+

Published notes

+
+

{String(posts.length).padStart(2, "0")}

+
+
+ {latest.map((post) => )} +
+
+
+
diff --git a/src/pages/posts.json.js b/src/pages/posts.json.js new file mode 100644 index 0000000..7f47012 --- /dev/null +++ b/src/pages/posts.json.js @@ -0,0 +1,15 @@ +import { getCollection } from "astro:content"; +import { published } from "../lib/site"; + +export async function GET(context) { + const posts = published(await getCollection("posts")); + const base = import.meta.env.BASE_URL; + const registry = posts.map((post) => ({ + ...post.data, + url: new URL(`${base}${post.data.slug}/`, context.site).href, + })); + + return new Response(`${JSON.stringify(registry, null, 2)}\n`, { + headers: { "Content-Type": "application/json; charset=utf-8" }, + }); +} diff --git a/src/pages/reflections/index.astro b/src/pages/reflections/index.astro new file mode 100644 index 0000000..cea4bc1 --- /dev/null +++ b/src/pages/reflections/index.astro @@ -0,0 +1,8 @@ +--- +import { getCollection } from "astro:content"; +import CollectionPage from "../../components/CollectionPage.astro"; +import { published } from "../../lib/site"; + +const posts = published(await getCollection("posts")).filter((post) => post.data.collection === "reflection"); +--- + diff --git a/src/pages/research/index.astro b/src/pages/research/index.astro new file mode 100644 index 0000000..b4a3a47 --- /dev/null +++ b/src/pages/research/index.astro @@ -0,0 +1,8 @@ +--- +import { getCollection } from "astro:content"; +import CollectionPage from "../../components/CollectionPage.astro"; +import { published } from "../../lib/site"; + +const posts = published(await getCollection("posts")).filter((post) => post.data.collection === "research"); +--- + diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js new file mode 100644 index 0000000..8c98453 --- /dev/null +++ b/src/pages/rss.xml.js @@ -0,0 +1,22 @@ +import rss from "@astrojs/rss"; +import { getCollection } from "astro:content"; +import { published } from "../lib/site"; + +export async function GET(context) { + const posts = published(await getCollection("posts")); + const base = import.meta.env.BASE_URL; + + return rss({ + title: "Theodore Ouyang — Portfolio", + description: "Bilingual research notes, essays, and reflections.", + site: new URL(base, context.site), + items: posts.map((post) => ({ + title: post.data.languages.en.title, + description: post.data.languages.en.summary, + pubDate: new Date(post.data.publishedAt), + link: new URL(`${base}${post.data.slug}/`, context.site).href, + categories: post.data.topics, + customData: `zh-CN, en`, + })), + }); +} diff --git a/src/scripts/article.ts b/src/scripts/article.ts new file mode 100644 index 0000000..552c0ec --- /dev/null +++ b/src/scripts/article.ts @@ -0,0 +1,70 @@ +interface Segment { + id: string; + nodes: Element[]; +} + +function anchorFor(node: Element) { + if (node.matches("a[data-pair-id]")) return node as HTMLAnchorElement; + if (node.children.length === 1 && node.firstElementChild?.matches("a[data-pair-id]")) { + return node.firstElementChild as HTMLAnchorElement; + } + return null; +} + +function segmentColumn(column: Element): Segment[] { + const segments: Segment[] = [{ id: "preamble", nodes: [] }]; + for (const node of [...column.children]) { + const anchor = anchorFor(node); + if (anchor?.dataset.pairId) segments.push({ id: anchor.dataset.pairId, nodes: [node] }); + else segments.at(-1)?.nodes.push(node); + } + return segments.filter((segment) => segment.nodes.length > 0); +} + +function enhancePairedReader() { + const reader = document.querySelector("[data-paired-reader]"); + if (!reader || reader.dataset.enhanced === "true") return; + + const zhColumn = reader.querySelector('[data-language-column="zh"]'); + const enColumn = reader.querySelector('[data-language-column="en"]'); + if (!zhColumn || !enColumn) return; + + const zhSegments = segmentColumn(zhColumn); + const enSegments = segmentColumn(enColumn); + if (zhSegments.length !== enSegments.length) return; + if (zhSegments.some((segment, index) => segment.id !== enSegments[index]?.id)) return; + + const grid = document.createElement("div"); + grid.className = "paired-grid"; + + zhSegments.forEach((zhSegment, index) => { + const row = document.createElement("section"); + row.className = "paired-section"; + row.dataset.pairId = zhSegment.id; + row.id = zhSegment.id; + + const zhPane = document.createElement("div"); + zhPane.className = "paired-pane article-prose"; + zhPane.dataset.contentLang = "zh"; + zhPane.lang = "zh-CN"; + zhPane.append(...zhSegment.nodes); + + const enPane = document.createElement("div"); + enPane.className = "paired-pane article-prose"; + enPane.dataset.contentLang = "en"; + enPane.lang = "en"; + enPane.append(...(enSegments[index]?.nodes ?? [])); + + row.append(zhPane, enPane); + grid.append(row); + }); + + reader.replaceChildren(grid); + reader.dataset.enhanced = "true"; + document.querySelectorAll("[data-pair-target]").forEach((link) => { + link.href = `#${link.dataset.pairTarget}`; + }); + window.dispatchEvent(new CustomEvent("portfolio:contentready")); +} + +enhancePairedReader(); diff --git a/src/scripts/site.ts b/src/scripts/site.ts new file mode 100644 index 0000000..63b4def --- /dev/null +++ b/src/scripts/site.ts @@ -0,0 +1,98 @@ +type LanguageMode = "zh" | "en" | "both"; + +const modes: LanguageMode[] = ["zh", "en", "both"]; +const root = document.documentElement; + +function isMode(value: string | null | undefined): value is LanguageMode { + return modes.includes(value as LanguageMode); +} + +function currentMode(): LanguageMode { + const value = root.dataset.languageMode; + return isMode(value) ? value : "both"; +} + +function captureReadingPosition() { + const reader = document.querySelector("[data-paired-reader]"); + if (!reader) return null; + const readerBox = reader.getBoundingClientRect(); + if (readerBox.bottom <= 0 || readerBox.top >= window.innerHeight) return null; + + const referenceLine = Math.min(window.innerHeight * 0.25, 160); + const rows = [...reader.querySelectorAll(".paired-section[id]")]; + const row = rows.find((candidate) => candidate.getBoundingClientRect().bottom > referenceLine) ?? rows.at(-1); + return row ? { id: row.id, top: row.getBoundingClientRect().top } : null; +} + +function restoreReadingPosition(position: { id: string; top: number } | null) { + if (!position) return; + requestAnimationFrame(() => { + const target = document.getElementById(position.id); + if (!target) return; + root.dataset.restoringScroll = "true"; + const correct = () => window.scrollBy({ top: target.getBoundingClientRect().top - position.top, behavior: "auto" }); + correct(); + window.setTimeout(() => { + correct(); + delete root.dataset.restoringScroll; + }, 100); + }); +} + +function applyLanguage(mode: LanguageMode, persist = false) { + const readingPosition = persist ? captureReadingPosition() : null; + root.dataset.languageMode = mode; + root.lang = mode === "zh" ? "zh-CN" : mode === "en" ? "en" : "mul"; + + document.querySelectorAll("[data-content-lang]").forEach((element) => { + const language = element.dataset.contentLang; + element.hidden = mode !== "both" && language !== mode; + }); + + document.querySelectorAll("[data-language-control]").forEach((button) => { + button.setAttribute("aria-pressed", String(button.dataset.languageControl === mode)); + }); + + if (persist) { + try { + localStorage.setItem("portfolio-language", mode); + } catch {} + + const url = new URL(window.location.href); + if (mode === "both") url.searchParams.delete("lang"); + else url.searchParams.set("lang", mode); + history.replaceState(null, "", `${url.pathname}${url.search}${url.hash}`); + } + + restoreReadingPosition(readingPosition); + window.dispatchEvent(new CustomEvent("portfolio:languagechange", { detail: { mode } })); +} + +function bindLanguageControls() { + const buttons = [...document.querySelectorAll("[data-language-control]")]; + buttons.forEach((button) => { + button.addEventListener("click", () => { + const mode = button.dataset.languageControl; + if (isMode(mode)) applyLanguage(mode, true); + }); + + button.addEventListener("keydown", (event) => { + if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return; + event.preventDefault(); + const index = buttons.indexOf(button); + const nextIndex = event.key === "Home" + ? 0 + : event.key === "End" + ? buttons.length - 1 + : event.key === "ArrowRight" + ? (index + 1) % buttons.length + : (index - 1 + buttons.length) % buttons.length; + buttons[nextIndex]?.focus(); + }); + }); +} + +bindLanguageControls(); +applyLanguage(currentMode()); +root.classList.add("language-controls-ready"); +window.addEventListener("portfolio:contentready", () => applyLanguage(currentMode())); diff --git a/src/styles/site.css b/src/styles/site.css new file mode 100644 index 0000000..5bd8182 --- /dev/null +++ b/src/styles/site.css @@ -0,0 +1,1007 @@ +:root { + color-scheme: light; + --color-page: #f4ede1; + --color-surface: #fbf7ef; + --color-surface-muted: #e9dfd0; + --color-ink: #201b18; + --color-ink-muted: #62574e; + --color-rule: #cfc1af; + --color-accent: #94472f; + --color-accent-muted: #b96547; + --color-focus: #62319a; + --color-evidence-paper: #356b58; + --color-evidence-claim: #8b5d22; + --color-evidence-code: #6b5a8a; + --color-evidence-explanation: #496f91; + --color-evidence-interpretation: #824c78; + --color-evidence-unverified: #735f53; + --font-ui: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif; + --font-body-zh: "Noto Serif CJK SC", "Source Han Serif SC", "Songti SC", SimSun, serif; + --font-body-en: Georgia, "Times New Roman", serif; + --font-math: "STIX Two Math", "Cambria Math", serif; + --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; + --measure-single: 52rem; + --canvas-bilingual: 78rem; + --toc-width: 14rem; + --header-offset: 6rem; + --space-1: 0.5rem; + --space-2: 1rem; + --space-3: 1.5rem; + --space-4: 2.5rem; + --space-5: 4rem; + --radius-card: 0.125rem; + --shadow-card: 0 18px 45px rgb(57 40 28 / 9%); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + background: var(--color-page); + color: var(--color-ink); + font-family: var(--font-ui); + line-height: 1.62; + scroll-behavior: smooth; + text-rendering: optimizeLegibility; +} + +html[data-restoring-scroll="true"] { + scroll-behavior: auto; +} + +body { + min-width: 20rem; + margin: 0; + background: + linear-gradient(90deg, transparent 0, transparent calc(100% - 1px), rgb(92 67 49 / 4%) 100%), + var(--color-page); +} + +[data-content-lang][lang="zh-CN"], +:lang(zh-CN) .article-prose, +.article-prose:lang(zh-CN) { + font-family: var(--font-body-zh); +} + +[data-content-lang][lang="en"], +:lang(en) .article-prose, +.article-prose:lang(en) { + font-family: var(--font-body-en); +} + +a { + color: inherit; +} + +button, +a { + -webkit-tap-highlight-color: transparent; +} + +button:focus-visible, +a:focus-visible { + outline: 3px solid var(--color-focus); + outline-offset: 4px; +} + +[hidden] { + display: none !important; +} + +html[data-language-mode="zh"] [data-content-lang="en"], +html[data-language-mode="en"] [data-content-lang="zh"] { + display: none !important; +} + +.skip-link { + position: fixed; + z-index: 100; + inset-block-start: 0.75rem; + inset-inline-start: 0.75rem; + min-height: 2.75rem; + padding: 0.55rem 0.85rem; + border: 2px solid var(--color-ink); + background: var(--color-surface); + font-weight: 750; + transform: translateY(-160%); +} + +.skip-link:focus { + transform: translateY(0); +} + +.site-header, +main, +.site-footer { + width: min(calc(100% - 2.5rem), var(--canvas-bilingual)); + margin-inline: auto; +} + +.site-header { + position: sticky; + z-index: 20; + inset-block-start: 0; + display: grid; + grid-template-columns: auto 1fr auto; + min-height: 6rem; + align-items: center; + gap: 2rem; + border-bottom: 1px solid var(--color-rule); + background: color-mix(in srgb, var(--color-page), transparent 4%); + backdrop-filter: blur(14px); +} + +.wordmark { + display: inline-flex; + min-height: 2.75rem; + align-items: center; + gap: 0.75rem; + font-family: var(--font-body-en); + font-weight: 700; + text-decoration: none; +} + +.wordmark-mark { + display: grid; + width: 2.5rem; + height: 2.5rem; + place-items: center; + border: 1px solid var(--color-ink); + border-radius: 50%; + font: 700 0.7rem/1 var(--font-ui); + letter-spacing: 0.08em; +} + +.site-nav { + display: flex; + justify-content: center; + gap: clamp(1rem, 3vw, 2rem); + font-size: 0.75rem; + font-weight: 720; + letter-spacing: 0.04em; +} + +.site-nav a { + min-height: 2.75rem; + display: inline-flex; + align-items: center; + color: var(--color-ink-muted); + text-decoration-color: transparent; + text-underline-offset: 0.28rem; +} + +.site-nav a:hover { + color: var(--color-accent); + text-decoration-color: currentColor; +} + +.language-control { + display: none; + align-items: center; + padding: 0.2rem; + gap: 0.15rem; + border: 1px solid var(--color-rule); + border-radius: 999px; + background: rgb(251 247 239 / 65%); +} + +.language-controls-ready .language-control { + display: inline-flex; +} + +.language-control-label { + padding-inline: 0.65rem 0.4rem; + color: var(--color-ink-muted); + font-size: 0.68rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.language-control button { + min-width: 2.75rem; + min-height: 2.75rem; + padding: 0.45rem 0.82rem; + border: 0; + border-radius: 999px; + background: transparent; + color: var(--color-ink-muted); + cursor: pointer; + font: 700 0.76rem/1 var(--font-ui); +} + +.language-control button[aria-pressed="true"] { + background: var(--color-ink); + color: var(--color-surface); +} + +h1, +h2, +h3, +p { + margin-block-start: 0; +} + +h1, +h2, +h3 { + font-family: var(--font-body-en); + font-weight: 500; + text-wrap: balance; +} + +[data-content-lang][lang="zh-CN"] h1, +[data-content-lang][lang="zh-CN"] h2, +[data-content-lang][lang="zh-CN"] h3, +h1:lang(zh-CN), +h2:lang(zh-CN), +h3:lang(zh-CN) { + font-family: var(--font-body-zh); +} + +.eyebrow, +.note-kicker, +.rail-heading { + margin: 0 0 1rem; + color: var(--color-accent); + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.13em; + text-transform: uppercase; +} + +.hero, +.collection-hero { + padding-block: clamp(5rem, 10vw, 9rem) clamp(4rem, 8vw, 7rem); + border-bottom: 1px solid var(--color-rule); +} + +.parallel-copy { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(2.5rem, 7vw, 7rem); +} + +.parallel-copy > * + * { + padding-inline-start: clamp(2rem, 4vw, 4rem); + border-inline-start: 1px solid var(--color-rule); +} + +html[data-language-mode="zh"] .parallel-copy, +html[data-language-mode="en"] .parallel-copy { + grid-template-columns: minmax(0, var(--measure-single)); +} + +html[data-language-mode="zh"] .parallel-copy > *, +html[data-language-mode="en"] .parallel-copy > * { + padding-inline-start: 0; + border-inline-start: 0; +} + +.hero h1, +.collection-hero h1 { + max-width: 18ch; + margin-bottom: 1.5rem; + font-size: clamp(2.5rem, 5vw, 5.25rem); + line-height: 1.04; + letter-spacing: -0.045em; +} + +.hero-deck, +.collection-hero p:not(.eyebrow) { + max-width: 36rem; + margin-bottom: 0; + color: var(--color-ink-muted); + font-family: var(--font-body-en); + font-size: clamp(1.05rem, 1.7vw, 1.35rem); +} + +.collection-strip { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + border-bottom: 1px solid var(--color-rule); +} + +.collection-strip a { + display: grid; + min-height: 7rem; + align-content: center; + padding: 1.5rem; + gap: 0.15rem; + border-inline-end: 1px solid var(--color-rule); + text-decoration: none; +} + +.collection-strip a:last-child { + border-inline-end: 0; +} + +.collection-strip span { + color: var(--color-ink-muted); + font-size: 0.78rem; +} + +.collection-strip strong { + grid-column: 2; + grid-row: 1 / 3; + align-self: center; + justify-self: end; + color: var(--color-accent-muted); + font-family: var(--font-body-en); + font-size: 2rem; + font-weight: 500; +} + +.notes-section { + padding-block: clamp(4.5rem, 8vw, 7.5rem); +} + +.section-heading { + display: flex; + align-items: end; + justify-content: space-between; + margin-bottom: 2rem; + gap: 2rem; +} + +.section-heading .eyebrow { + margin-bottom: 0.4rem; +} + +.section-heading h2 { + margin: 0; + font-size: clamp(1.8rem, 3vw, 2.7rem); + letter-spacing: -0.025em; +} + +.section-heading > p { + margin: 0; + color: var(--color-ink-muted); + font-size: 0.78rem; + font-variant-numeric: tabular-nums; +} + +.card-stack { + display: grid; + gap: 1.25rem; +} + +.note-card { + border: 1px solid var(--color-rule); + border-radius: var(--radius-card); + background: var(--color-surface); + box-shadow: var(--shadow-card); +} + +.note-card-link { + display: block; + padding: clamp(1.4rem, 4vw, 3.25rem); + text-decoration: none; +} + +.note-card-meta, +.note-card-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + color: var(--color-ink-muted); + font-size: 0.72rem; + font-weight: 750; + letter-spacing: 0.055em; + text-transform: uppercase; +} + +.note-card-body { + display: grid; + grid-template-columns: minmax(5rem, 0.2fr) minmax(0, 1fr); + gap: clamp(1.75rem, 5vw, 5rem); + padding-block: clamp(2.5rem, 5vw, 5rem); +} + +.note-index { + color: var(--color-accent-muted); + font-family: var(--font-body-en); + font-size: clamp(4rem, 9vw, 8rem); + font-style: italic; + line-height: 0.85; +} + +.note-card-copy { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(2rem, 5vw, 5rem); +} + +.note-card-copy > * + * { + padding-inline-start: clamp(1.75rem, 3.5vw, 3.5rem); + border-inline-start: 1px solid var(--color-rule); +} + +html[data-language-mode="zh"] .note-card-copy, +html[data-language-mode="en"] .note-card-copy { + grid-template-columns: minmax(0, 46rem); +} + +html[data-language-mode="zh"] .note-card-copy > *, +html[data-language-mode="en"] .note-card-copy > * { + padding-inline-start: 0; + border-inline-start: 0; +} + +.note-card h3 { + max-width: 21ch; + margin-bottom: 1rem; + font-size: clamp(1.7rem, 3vw, 3.15rem); + line-height: 1.13; + letter-spacing: -0.035em; +} + +.note-card-copy p:last-child { + margin-bottom: 0; + color: var(--color-ink-muted); + font-family: var(--font-body-en); + font-size: 1rem; +} + +.note-card-footer { + justify-content: flex-start; + min-height: 2.75rem; + padding-top: 1.25rem; + border-top: 1px solid var(--color-rule); + color: var(--color-ink); +} + +.note-card-footer [data-content-lang="zh"]::after { + margin-inline: 0.65rem; + color: var(--color-rule); + content: "/"; +} + +html[data-language-mode="zh"] .note-card-footer [data-content-lang="zh"]::after, +html[data-language-mode="en"] .note-card-footer [data-content-lang="zh"]::after { + content: none; +} + +.arrow { + margin-inline-start: auto; + color: var(--color-accent); + font-size: 1.15rem; +} + +.empty-state { + padding: 3rem; + border: 1px solid var(--color-rule); + color: var(--color-ink-muted); + text-align: center; +} + +.article-hero { + padding-block: clamp(3.5rem, 8vw, 7rem) clamp(2.5rem, 6vw, 5rem); + border-bottom: 1px solid var(--color-rule); +} + +.back-link { + display: inline-flex; + min-height: 2.75rem; + align-items: center; + margin-bottom: 2rem; + color: var(--color-ink-muted); + font-size: 0.78rem; + font-weight: 750; + text-underline-offset: 0.25rem; +} + +.article-hero h1 { + max-width: 22ch; + margin-bottom: 1.5rem; + font-size: clamp(2.6rem, 6vw, 5.8rem); + line-height: 1.04; + letter-spacing: -0.045em; +} + +.article-hero h1 span { + display: block; +} + +html[data-language-mode="both"] .article-hero h1 [data-content-lang="en"] { + max-width: 25ch; + margin-top: 0.35em; + color: var(--color-ink-muted); + font-size: 0.56em; + line-height: 1.12; + letter-spacing: -0.025em; +} + +.article-deck { + max-width: 50rem; + color: var(--color-ink-muted); + font-family: var(--font-body-en); + font-size: clamp(1.05rem, 2vw, 1.32rem); +} + +.article-deck p { + margin-bottom: 0.5rem; +} + +.article-meta { + display: flex; + flex-wrap: wrap; + gap: 0.4rem 0.75rem; + color: var(--color-ink-muted); + font-size: 0.72rem; + font-weight: 750; + letter-spacing: 0.045em; + text-transform: uppercase; +} + +.article-meta > * + *::before { + margin-inline-end: 0.75rem; + color: var(--color-rule); + content: "·"; +} + +.reading-layout { + display: grid; + grid-template-columns: minmax(11rem, var(--toc-width)) minmax(0, 1fr); + align-items: start; + gap: clamp(2.5rem, 5vw, 5rem); + padding-block: clamp(3rem, 6vw, 6rem); +} + +.article-rail { + position: sticky; + inset-block-start: calc(var(--header-offset) + 1.5rem); + max-height: calc(100vh - var(--header-offset) - 3rem); + overflow: auto; + padding-inline-end: 1rem; + scrollbar-width: thin; +} + +.article-toc ol { + margin: 0; + padding: 0; + list-style: none; + counter-reset: toc; +} + +.article-toc li { + counter-increment: toc; +} + +.article-toc a { + display: grid; + grid-template-columns: 1.8rem 1fr; + min-height: 2.75rem; + align-items: start; + padding-block: 0.52rem; + color: var(--color-ink-muted); + font-size: 0.76rem; + line-height: 1.35; + text-decoration: none; +} + +.article-toc a::before { + padding-top: 0.04rem; + color: var(--color-rule); + content: counter(toc, decimal-leading-zero); + font-variant-numeric: tabular-nums; +} + +html[data-language-mode="both"] .article-toc [data-content-lang="en"] { + display: none; +} + +.article-toc a:hover { + color: var(--color-accent); +} + +.evidence-legend { + margin-top: 1.5rem; + padding-top: 1.2rem; + border-top: 1px solid var(--color-rule); + color: var(--color-ink-muted); + font-size: 0.68rem; +} + +.evidence-legend p { + display: flex; + align-items: center; + margin-bottom: 0.45rem; + gap: 0.5rem; +} + +.legend-dot { + width: 0.55rem; + height: 0.55rem; + flex: 0 0 auto; + border-radius: 50%; +} + +.legend-paper { background: var(--color-evidence-paper); } +.legend-claim { background: var(--color-evidence-claim); } +.legend-code { background: var(--color-evidence-code); } +.legend-explanation { background: var(--color-evidence-explanation); } +.legend-interpretation { background: var(--color-evidence-interpretation); } + +.paired-reader { + min-width: 0; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(2rem, 4vw, 4.5rem); +} + +.paired-reader > .article-prose + .article-prose { + padding-inline-start: clamp(1.5rem, 3vw, 3.5rem); + border-inline-start: 1px solid var(--color-rule); +} + +.paired-reader[data-enhanced="true"] { + display: block; +} + +.paired-grid { + display: grid; + gap: 0; +} + +.paired-section { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(2rem, 4vw, 4.5rem); + padding-block: 0 2.5rem; + scroll-margin-top: calc(var(--header-offset) + 1.5rem); +} + +.paired-section + .paired-section { + padding-top: 2.5rem; + border-top: 1px solid color-mix(in srgb, var(--color-rule), transparent 28%); +} + +.paired-pane + .paired-pane { + padding-inline-start: clamp(1.5rem, 3vw, 3.5rem); + border-inline-start: 1px solid var(--color-rule); +} + +html[data-language-mode="zh"] .paired-reader, +html[data-language-mode="en"] .paired-reader, +html[data-language-mode="zh"] .paired-section, +html[data-language-mode="en"] .paired-section { + grid-template-columns: minmax(0, var(--measure-single)); +} + +html[data-language-mode="zh"] .paired-pane, +html[data-language-mode="en"] .paired-pane { + padding-inline-start: 0; + border-inline-start: 0; +} + +.article-prose { + min-width: 0; + color: var(--color-ink); + font-size: 1.01rem; + line-height: 1.78; + overflow-wrap: anywhere; +} + +.article-prose > h1:first-child, +.paired-pane > h1:first-child { + display: none; +} + +.article-prose h2, +.article-prose h3, +.article-prose h4 { + scroll-margin-top: calc(var(--header-offset) + 1.5rem); + color: var(--color-ink); + font-family: inherit; + line-height: 1.24; + letter-spacing: -0.02em; +} + +.article-prose h2 { + margin: 0 0 1.25rem; + font-size: clamp(1.7rem, 2.6vw, 2.35rem); +} + +.article-prose h3 { + margin: 2.2rem 0 0.85rem; + font-size: clamp(1.28rem, 1.8vw, 1.62rem); +} + +.article-prose h4 { + margin: 1.8rem 0 0.7rem; + font-size: 1.08rem; +} + +.article-prose p, +.article-prose ul, +.article-prose ol, +.article-prose blockquote, +.article-prose pre, +.article-prose table { + margin-block: 0 1.15rem; +} + +.article-prose a { + color: var(--color-accent); + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; +} + +.article-prose ul, +.article-prose ol { + padding-inline-start: 1.35rem; +} + +.article-prose li + li { + margin-top: 0.38rem; +} + +.article-prose blockquote { + padding: 1rem 1.15rem; + border-inline-start: 3px solid var(--color-evidence-explanation); + background: color-mix(in srgb, var(--color-surface), var(--color-evidence-explanation) 6%); + color: var(--color-ink-muted); +} + +.article-prose blockquote p:last-child { + margin-bottom: 0; +} + +.article-prose code { + padding: 0.08em 0.3em; + border: 1px solid color-mix(in srgb, var(--color-rule), transparent 25%); + border-radius: 0.2rem; + background: var(--color-surface-muted); + font: 0.86em/1.45 var(--font-mono); +} + +.article-prose pre { + max-width: 100%; + overflow: auto; + padding: 1rem; + border: 1px solid var(--color-rule); + background: #27231f; + color: #fbf7ef; +} + +.article-prose pre code { + padding: 0; + border: 0; + background: transparent; + color: inherit; +} + +.article-prose table { + width: 100%; + border-collapse: collapse; + font-family: var(--font-ui); + font-size: 0.78rem; + line-height: 1.5; +} + +.article-prose th, +.article-prose td { + padding: 0.72rem; + border: 1px solid var(--color-rule); + text-align: start; + vertical-align: top; +} + +.article-prose th { + background: var(--color-surface-muted); +} + +.article-prose math[display="block"], +.article-prose .math-display, +.article-prose .katex-display { + display: block; + width: 100%; + max-width: 100%; + margin-block: 1.4rem; + overflow-y: hidden; + overflow-x: auto; + padding: 0.8rem 0; + font-family: var(--font-math); +} + +.article-prose .katex-display > .katex { + display: inline-block; + min-width: max-content; + text-align: start; +} + +.article-prose hr { + margin-block: 2.5rem; + border: 0; + border-top: 1px solid var(--color-rule); +} + +.site-footer { + display: flex; + justify-content: space-between; + padding-block: 2rem 3.5rem; + gap: 1.5rem; + border-top: 1px solid var(--color-rule); + color: var(--color-ink-muted); + font-size: 0.74rem; +} + +.site-footer p { + margin: 0; +} + +@media (hover: hover) { + .note-card { + transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; + } + + .note-card:hover { + border-color: var(--color-accent-muted); + box-shadow: 0 24px 56px rgb(57 40 28 / 13%); + transform: translateY(-2px); + } +} + +@media (max-width: 68rem) { + :root { --header-offset: 8.75rem; } + + .site-header { + grid-template-columns: auto 1fr; + padding-block: 0.75rem; + } + + .site-nav { + grid-column: 1 / -1; + justify-content: flex-start; + overflow-x: auto; + padding-top: 0.25rem; + border-top: 1px solid color-mix(in srgb, var(--color-rule), transparent 35%); + scrollbar-width: thin; + } + + .language-control { + justify-self: end; + } + + .reading-layout { + grid-template-columns: 1fr; + } + + .article-rail { + position: relative; + inset-block-start: auto; + max-height: none; + padding: 0 0 1.5rem; + border-bottom: 1px solid var(--color-rule); + } + + .article-toc ol { + columns: 2; + column-gap: 2rem; + } +} + +@media (max-width: 48rem) { + :root { --header-offset: 8.5rem; } + + .site-header, + main, + .site-footer { + width: min(calc(100% - 1.5rem), var(--canvas-bilingual)); + } + + .site-header { + grid-template-columns: auto minmax(0, 1fr); + gap: 0.55rem 0.75rem; + padding-block: 0.85rem; + } + + .wordmark { + grid-column: 1; + grid-row: 1; + justify-self: start; + } + + .language-control { + grid-column: 2; + grid-row: 1; + justify-self: end; + } + + .site-nav { + grid-column: 1 / -1; + grid-row: 2; + width: 100%; + justify-content: space-between; + } + + .language-controls-ready .language-control { + display: grid; + grid-template-columns: repeat(3, 1fr); + } + + .language-control-label { + display: none; + } + + .parallel-copy, + .note-card-copy, + .paired-reader, + .paired-section { + grid-template-columns: 1fr; + } + + .parallel-copy > * + *, + .note-card-copy > * + *, + .paired-reader > .article-prose + .article-prose, + .paired-pane + .paired-pane { + padding-block-start: 1.75rem; + padding-inline-start: 0; + border-block-start: 1px solid var(--color-rule); + border-inline-start: 0; + } + + .collection-strip { + grid-template-columns: 1fr; + } + + .collection-strip a { + min-height: 5rem; + border-inline-end: 0; + border-bottom: 1px solid var(--color-rule); + } + + .note-card-body { + grid-template-columns: 1fr; + } + + .note-index { + font-size: 3.5rem; + } + + .article-toc ol { + columns: 1; + } + + .article-meta > * + *::before { + content: none; + } + + .site-footer { + flex-direction: column; + } +} + +@media (max-width: 30rem) { + .wordmark > span:last-child { + display: none; + } +} + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, *::before, *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + } +} + +@media (prefers-contrast: more) { + :root { + --color-rule: #786b60; + --color-ink-muted: #3f3732; + } +} + +@media (forced-colors: active) { + .language-control button[aria-pressed="true"], + .note-card, + .article-prose blockquote { + border: 1px solid CanvasText; + } +} diff --git a/templates/post.md b/templates/post.md index ddf6f82..b36604f 100644 --- a/templates/post.md +++ b/templates/post.md @@ -1,17 +1,45 @@ --- -title: "" -date: YYYY-MM-DD -type: article -status: draft -summary: "" -canonical_url: "" -source_commit: "" +postId: collection.000 +lang: zh-CN --- -# Title +# 标题 / Title -Write the piece here. +> 先说明本文是什么、基于哪些证据、哪些部分属于作者判断。 -## Sources +> 英文源稿保留相同 `data-pair-id`,并把锚点 `id` 的 `zh-` 前缀改为 `en-`。 -- + +## 一分钟结论 / One-minute takeaway + +先给读者三至五个必须记住的结论。 + + +## 问题与缺口 / Problem and gap + +说明已有路线、真实缺口和本文的分析对象。 + + +## 输入、输出与符号 / Inputs, outputs, and notation + +先区分对象,再写公式;明确变量维数和时间下标。 + + +## 方法与机制 / Method and mechanism + +按“结论 → 通俗解释 → 正式定义或公式 → 证据 → 边界”展开。 + + +## 证据与实验 / Evidence and experiments + +区分论文事实、作者主张、读者解释、本文推断和未验证范围。 + + +## 优势、长期缺陷与路线判断 / Strengths, durable limitations, and route judgement + +明确哪些判断来自论文,哪些是本文思辨。 + + +## 来源与核验 / Sources and verification + +- 使用原始论文、项目页和官方仓库;在 `SOURCES.yaml` 中分配稳定 ID。 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9ab00c4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["_site"] +}