diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88ac01f6e..6625075d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,13 +26,13 @@ jobs: - name: Install run: npm ci - name: Build - run: npm run build + run: npm run build:vitepress # Deploy, limited to the main branch - name: Deploy if: success() && github.ref == 'refs/heads/main' uses: peaceiris/actions-gh-pages@v4 with: - publish_dir: ./docs/.vuepress/dist + publish_dir: ./docs/.vitepress/dist github_token: ${{ secrets.GITHUB_TOKEN }} cname: lightningdevkit.org user_name: 'github-actions[bot]' diff --git a/.gitignore b/.gitignore index 3cc642fc8..ce305284e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ # Production /docs/.vuepress/dist +/docs/.vitepress/dist +/docs/.vitepress/cache # Local planning artifacts (Claude /ce workflow) /docs/brainstorms diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 000000000..64913b8c1 --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,225 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { defineConfig, type DefaultTheme } from 'vitepress' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +// VuePress 1 still occupies the top-level `node_modules/vue` slot (Vue 2.7.x). +// Force Vite to resolve `vue` and `vue/*` from VitePress's nested Vue 3. +// Remove this alias once VuePress is dropped in Phase 7b. +const vitepressVueDir = path.resolve( + __dirname, + '../../node_modules/vitepress/node_modules/vue', +) + +const githubUrl = 'https://github.com/lightningdevkit' +const discordUrl = 'https://discord.gg/5AcknnMfBw' + +const blogSidebar: DefaultTheme.SidebarItem[] = [ + { + text: 'Blog', + collapsed: false, + items: [ + { text: 'Articles', link: '/blog/' }, + { text: 'Tags', link: '/blog/tags/' }, + { text: 'Authors', link: '/blog/author/' }, + ], + }, +] + +const docsSidebar: DefaultTheme.SidebarItem[] = [ + { + text: 'Documentation', + collapsed: false, + items: [ + { + text: 'Introduction', + link: '/introduction/', + collapsed: true, + items: [ + { text: 'Use Cases', link: '/introduction/use-cases' }, + { text: 'Architecture', link: '/introduction/architecture' }, + { text: 'Peer Management', link: '/introduction/peer-management' }, + { text: 'Persistent Storage', link: '/introduction/persistent_storage' }, + { text: 'Blockchain Data', link: '/introduction/blockchain_data' }, + { text: 'Wallet Management', link: '/introduction/wallet_management' }, + { text: 'Networking', link: '/introduction/networking' }, + { text: 'Private Key Management', link: '/introduction/private_key_management' }, + { text: 'Transactions', link: '/introduction/transactions' }, + { text: 'Random Number Generation', link: '/introduction/random_number_generation' }, + ], + }, + { + text: 'Building a node with LDK', + collapsed: true, + items: [ + { text: 'Introduction', link: '/building-a-node-with-ldk/introduction' }, + { text: 'Installation', link: '/building-a-node-with-ldk/installation' }, + { text: 'Setting up a Channel Manager', link: '/building-a-node-with-ldk/setting-up-a-channel-manager' }, + { text: 'Handling Events', link: '/building-a-node-with-ldk/handling-events' }, + { text: 'Setting up a Peer Manager', link: '/building-a-node-with-ldk/setting-up-a-peer-manager' }, + { text: 'Connect to Peers', link: '/building-a-node-with-ldk/connect-to-peers' }, + { text: 'Opening a Channel', link: '/building-a-node-with-ldk/opening-a-channel' }, + { text: 'Sending Payments', link: '/building-a-node-with-ldk/sending-payments' }, + { text: 'Receiving Payments', link: '/building-a-node-with-ldk/receiving-payments' }, + { text: 'Closing a Channel', link: '/building-a-node-with-ldk/closing-a-channel' }, + ], + }, + { text: 'Running a sample LDK node', link: '/running-a-sample-ldk-node' }, + { + text: 'Advanced Guides', + collapsed: true, + items: [ + { text: 'Blockchain Data', link: '/blockchain_data/' }, + { text: 'Key Management', link: '/key_management' }, + { text: 'Fee Estimation', link: '/fee_estimation' }, + { text: 'Probing and Path Finding', link: '/probing' }, + ], + }, + { text: 'Examples', link: '/examples' }, + ], + }, + { + text: 'API Reference', + collapsed: false, + items: [ + { + text: 'Rust', + collapsed: true, + items: [ + { text: 'lightning', link: 'https://docs.rs/lightning/*/lightning/' }, + { text: 'lightning-background-processor', link: 'https://docs.rs/lightning-background-processor/*/lightning_background_processor/' }, + { text: 'lightning-block-sync', link: 'https://docs.rs/lightning-block-sync/*/lightning_block_sync/' }, + { text: 'lightning-invoice', link: 'https://docs.rs/lightning-invoice/*/lightning_invoice/' }, + { text: 'lightning-net-tokio', link: 'https://docs.rs/lightning-net-tokio/*/lightning_net_tokio/' }, + { text: 'lightning-persister', link: 'https://docs.rs/lightning-persister/*/lightning_persister/' }, + { text: 'lightning-rapid-gossip-sync', link: 'https://docs.rs/lightning-rapid-gossip-sync/*/lightning_rapid_gossip_sync/' }, + { text: 'lightning-transaction-sync', link: 'https://docs.rs/lightning-transaction-sync/*/lightning_transaction_sync/' }, + { text: 'lightning-custom-message', link: 'https://docs.rs/lightning-custom-message/*/lightning_custom_message/' }, + ], + }, + ], + }, +] + +export default defineConfig({ + title: 'Lightning Dev Kit Documentation', + description: 'LDK is a flexible lightning implementation with supporting batteries (or modules).', + + cleanUrls: true, + lastUpdated: true, + + srcExclude: [ + 'brainstorms/**', + 'plans/**', + 'todos/**', + 'README.md', + ], + + ignoreDeadLinks: true, + + head: [ + ['link', { rel: 'icon', href: '/favicon.ico' }], + ['link', { rel: 'apple-touch-icon', href: '/img/favicon/apple-touch-icon.png' }], + ['link', { rel: 'manifest', href: '/site.webmanifest' }], + ['link', { rel: 'preload', as: 'font', type: 'font/woff2', crossorigin: '', href: '/fonts/ibm-plex-mono-400.woff2' }], + ['meta', { name: 'msapplication-config', content: '/browserconfig.xml' }], + ['meta', { name: 'theme-color', content: '#ffffff' }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:url', content: 'https://lightningdevkit.org/' }], + ['meta', { property: 'og:image', content: 'https://lightningdevkit.org/card.png' }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + ['meta', { name: 'twitter:image', content: 'https://lightningdevkit.org/card.png' }], + ], + + markdown: { + lineNumbers: false, + // Code blocks use a dark navy background in BOTH light and dark mode + // (--vp-code-block-bg is dark in :root and .dark alike), so both Shiki + // themes must be dark-on-navy. github-light/github-dark have muted tokens + // that disappear on the navy; one-dark-pro stays high-contrast and legible. + theme: { + light: 'one-dark-pro', + dark: 'one-dark-pro', + }, + }, + + themeConfig: { + // Hide the right-hand "On this page" outline on docs pages — + // not part of the LDK design. Can still be enabled per-page via + // frontmatter `outline: 'deep'` if a long page benefits from it. + outline: false, + + // Remove the "Previous/Next page" pager at the bottom of docs pages. + docFooter: { + prev: false, + next: false, + }, + + // Logo and wordmark are rendered together via `NavLogo.vue` in the + // `nav-bar-title-before` slot, which uses the original sprite at + // `/img/logo.svg` (with `#small` / `#large` symbols, each a + // composed bolt + wordmark). No `themeConfig.logo` needed; setting + // siteTitle to false hides the default plain-text title. + siteTitle: false, + + editLink: { + pattern: 'https://github.com/lightningdevkit/lightningdevkit.org/edit/main/docs/:path', + text: 'Edit this page on GitHub', + }, + + nav: [ + { text: 'Docs', link: '/introduction/' }, + { text: 'Case Studies', link: '/case-studies' }, + { text: 'Blog', link: '/blog/' }, + { text: 'Discord', link: discordUrl }, + { text: 'GitHub', link: githubUrl }, + ], + + sidebar: { + '/blog/': blogSidebar, + '/': docsSidebar, + }, + + // Search: VitePress's built-in local search (MiniSearch-powered) + // ships immediately and runs entirely client-side. Plan is to swap + // to DocSearch v3 once Algolia approves the application — the + // legacy v2 index (appId `BH4D9OD16A`, indexName `lightningdevkit`) + // is deprecated and not compatible with VitePress's search config + // shape. Apply at https://docsearch.algolia.com/apply (free for + // OSS docs sites; 1–2 week approval typical). + // + // To swap to v3 once credentials arrive, replace the block below + // with: + // + // search: { + // provider: 'algolia', + // options: { + // appId: '', + // apiKey: '', + // indexName: 'lightningdevkit', + // }, + // }, + search: { + provider: 'local', + }, + // No `footer` here: VitePress's default VPFooter would render its own + // copyright above our custom SiteFooter (layout-bottom slot). The + // SiteFooter carries the single copyright line. + }, + + vite: { + resolve: { + // Alias bare `vue` and subpaths to VitePress's nested Vue 3. + // Point at the PACKAGE DIRECTORY (not a specific file) so esbuild's + // prebundler can package-resolve via vue's own package.json + // exports map — pointing at index.mjs directly let the prebundler + // cache Vue 2 from top-level node_modules. + alias: [ + { find: /^vue$/, replacement: vitepressVueDir }, + { find: /^vue\/(.*)$/, replacement: path.join(vitepressVueDir, '$1') }, + ], + dedupe: ['vue'], + }, + }, +}) diff --git a/docs/.vitepress/theme/components/BlogPostHeader.vue b/docs/.vitepress/theme/components/BlogPostHeader.vue new file mode 100644 index 000000000..9b269e3ee --- /dev/null +++ b/docs/.vitepress/theme/components/BlogPostHeader.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/docs/.vitepress/theme/components/BlogPostList.vue b/docs/.vitepress/theme/components/BlogPostList.vue new file mode 100644 index 000000000..69ffccbdd --- /dev/null +++ b/docs/.vitepress/theme/components/BlogPostList.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/docs/.vitepress/theme/components/CaseStudiesPage.vue b/docs/.vitepress/theme/components/CaseStudiesPage.vue new file mode 100644 index 000000000..42d19b6f4 --- /dev/null +++ b/docs/.vitepress/theme/components/CaseStudiesPage.vue @@ -0,0 +1,442 @@ + + + + + diff --git a/docs/.vitepress/theme/components/HomeCaseStudies.vue b/docs/.vitepress/theme/components/HomeCaseStudies.vue new file mode 100644 index 000000000..f082ca114 --- /dev/null +++ b/docs/.vitepress/theme/components/HomeCaseStudies.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/docs/.vitepress/theme/components/HomeCrossPromo.vue b/docs/.vitepress/theme/components/HomeCrossPromo.vue new file mode 100644 index 000000000..2f463890c --- /dev/null +++ b/docs/.vitepress/theme/components/HomeCrossPromo.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/docs/.vitepress/theme/components/HomeFeatures.vue b/docs/.vitepress/theme/components/HomeFeatures.vue new file mode 100644 index 000000000..689311594 --- /dev/null +++ b/docs/.vitepress/theme/components/HomeFeatures.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/docs/.vitepress/theme/components/HomePromo.vue b/docs/.vitepress/theme/components/HomePromo.vue new file mode 100644 index 000000000..27417c691 --- /dev/null +++ b/docs/.vitepress/theme/components/HomePromo.vue @@ -0,0 +1,107 @@ + + + diff --git a/docs/.vitepress/theme/components/HomeServerPromo.vue b/docs/.vitepress/theme/components/HomeServerPromo.vue new file mode 100644 index 000000000..b186cd237 --- /dev/null +++ b/docs/.vitepress/theme/components/HomeServerPromo.vue @@ -0,0 +1,271 @@ + + + diff --git a/docs/.vitepress/theme/components/NavLogo.vue b/docs/.vitepress/theme/components/NavLogo.vue new file mode 100644 index 000000000..12aa567c4 --- /dev/null +++ b/docs/.vitepress/theme/components/NavLogo.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/docs/.vitepress/theme/components/SiteFooter.vue b/docs/.vitepress/theme/components/SiteFooter.vue new file mode 100644 index 000000000..1269b6995 --- /dev/null +++ b/docs/.vitepress/theme/components/SiteFooter.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 000000000..f5b7587a3 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,38 @@ +import DefaultTheme from 'vitepress/theme' +import type { Theme } from 'vitepress' +import { h } from 'vue' + +import './style.css' + +import HomeFeatures from './components/HomeFeatures.vue' +import HomeServerPromo from './components/HomeServerPromo.vue' +import HomeCaseStudies from './components/HomeCaseStudies.vue' +import HomeCrossPromo from './components/HomeCrossPromo.vue' +import SiteFooter from './components/SiteFooter.vue' +import BlogPostList from './components/BlogPostList.vue' +import BlogPostHeader from './components/BlogPostHeader.vue' +import CaseStudiesPage from './components/CaseStudiesPage.vue' +import NavLogo from './components/NavLogo.vue' + +export default { + extends: DefaultTheme, + Layout() { + return h(DefaultTheme.Layout, null, { + 'nav-bar-title-before': () => h(NavLogo), + 'doc-before': () => h(BlogPostHeader), + 'home-features-after': () => [ + h(HomeFeatures), + h(HomeServerPromo), + h(HomeCaseStudies), + h(HomeCrossPromo), + ], + 'layout-bottom': () => h(SiteFooter), + }) + }, + enhanceApp({ app }) { + // Register globally so blog markdown pages can use + // without per-file script setup imports. + app.component('BlogPostList', BlogPostList) + app.component('CaseStudiesPage', CaseStudiesPage) + }, +} satisfies Theme diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 000000000..9cdbd0f27 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,742 @@ +/** + * LDK brand styles for VitePress's default theme. + * + * Brand colors ported from docs/public/css/variables.css (the v1 theme's + * source) into VitePress's --vp-c-brand-* tokens. The v1 theme used a + * single hue per mode; VitePress's default theme expects a 4-step ramp + * (1=base, 2=hover, 3=focus, soft=very-light backdrop) which we derive. + * + * Light mode primary: #0F31F7 (LDK blue) + * Dark mode primary: #76F3CD (LDK mint) + * + * Fonts: Manrope (body) + IBM Plex Mono (code), matching the v1 + * spiralbtc theme's pairing. + */ + +@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap'); + +@font-face { + font-family: 'IBM Plex Mono'; + src: url('/fonts/ibm-plex-mono-400.woff2') format('woff2'); + font-weight: 400; + font-display: swap; +} + +:root { + --vp-c-brand-1: #0f31f7; + --vp-c-brand-2: #2e48f8; + --vp-c-brand-3: #4d5ffa; + --vp-c-brand-soft: #f5f7ff; + + --vp-button-brand-bg: #0f31f7; + --vp-button-brand-hover-bg: #2e48f8; + --vp-button-brand-active-bg: #4d5ffa; + + --vp-code-block-bg: #020a36; + --vp-code-bg: #e6eafe; + + --vp-custom-block-tip-bg: #f5f7ff; + --vp-custom-block-tip-text: var(--vp-c-text-1); + + --vp-font-family-base: 'Manrope', -apple-system, BlinkMacSystemFont, + 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', + 'Helvetica Neue', sans-serif; + --vp-font-family-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, + 'SF Mono', Menlo, Consolas, Liberation Mono, monospace; + + /* Custom tokens used by the inline SVG monograms (illustrations.svg). + Mirror the v1 spiralbtc theme's --docs-primary / --docs-border-color + so the sprite's `fill="var(--docs-primary)"` paths pick up the + brand color and switch with light/dark. */ + --docs-primary: var(--vp-c-brand-1); + --docs-border-color: var(--vp-c-text-1); + + /* Taller navbar for a bit more vertical padding around the logo and + nav items (VitePress default is 64px). */ + --vp-nav-height: 72px; + + /* White sidebar (VitePress default is the grey --vp-c-bg-alt) so docs + pages read on the same surface as the page frame. */ + --vp-sidebar-bg-color: var(--vp-c-bg); + + /* Shared layout widths. The dashed page frame sits at --ldk-frame-width + (centered), with the content column 48px narrower inside it (a 24px + gutter each side). Widen these to pull the frame closer to the window + edges; the navbar, home sections, dividers, footer, and cross-promo + all derive from them. */ + --ldk-content-width: 1320px; + --ldk-frame-width: 1368px; + --ldk-frame-half: 684px; /* --ldk-frame-width / 2 */ + + /* Docs layout matches the frame width so the sidebar + content sit + within the same frame. */ + --vp-layout-max-width: var(--ldk-frame-width); +} + +.dark { + --vp-c-brand-1: #76f3cd; + --vp-c-brand-2: #5bd9b3; + --vp-c-brand-3: #40bf99; + --vp-c-brand-soft: rgba(118, 243, 205, 0.14); + + --vp-button-brand-bg: #76f3cd; + --vp-button-brand-hover-bg: #5bd9b3; + --vp-button-brand-active-bg: #40bf99; + --vp-button-brand-text: #000628; + --vp-button-brand-hover-text: #000628; + + --vp-code-block-bg: #002547; + --vp-code-bg: #06463c; + + --vp-custom-block-tip-bg: rgba(118, 243, 205, 0.08); +} + +/* Slot-mounted home sections: keep them centered and consistent with + VitePress's home layout container width. Each section gets a dashed + bottom divider so the home page reads as a stack of cards inside the + page frame. */ +.vp-home-extras { + position: relative; + max-width: var(--ldk-content-width); + margin: 0 auto; + padding: 0 24px; +} + +/* Bottom divider. Rendered as a pseudo-element rather than a plain + `border-bottom` so it can reach the dashed page-frame lines. The + section is 1152px wide but the frame sits at calc(50% ± 600px) + (1200px apart), so a content-width border would stop 24px short of + each line and read as "cut off". Base spans the content width + (unchanged on mobile/tablet); once the frame is visible it widens to + the 1200px frame span and centers so its ends meet the lines. */ +.vp-home-extras::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + border-bottom: 1px dashed var(--vp-c-divider); + pointer-events: none; +} + +@media (min-width: 1241px) { + .vp-home-extras::after { + left: 50%; + right: auto; + width: var(--ldk-frame-width); + transform: translateX(-50%); + } +} + +/* Typography ramp for home section headings (case studies, etc.) + so they sit between the hero (44px) and body copy in a clean + ratio rather than landing at VitePress's default markdown h2. */ +.vp-home-extras h2 { + font-size: 28px; + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.01em; +} + +/* Drop VitePress's default solid divider above each doc h2 — the design + relies on the dashed page frame, not solid section rules. Keep the + vertical rhythm via margin alone. */ +.vp-doc h2 { + margin-top: 48px; + padding-top: 0; + border-top: none; +} + +/* The default theme leaves the doc page's `.VPDoc .container` at + 1152px max-width too — keep our home content aligned with it so + the dashed frame visually wraps both. */ + +/* Dashed page frame: vertical lines flanking the centered content, + matching the v1 spiralbtc theme's `.wrap-border` aesthetic. + Implemented as `position: absolute` on the Layout root so the + lines extend with content height rather than viewport. Hidden + on narrow viewports where they'd overlap content. */ +.Layout { + position: relative; +} + +.Layout::before, +.Layout::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 0; + border-left: 1px dashed var(--vp-c-divider); + pointer-events: none; + /* Above the docs sidebar (z-index 28) and above the navbar (z-index 30) + so the frame lines run unbroken to the top of the window through the + navbar region. The navbar content is inset 16px from the frame width, + so the 1px lines clear the logo and nav items. */ + z-index: 31; + /* Hidden by default (narrow viewports where they'd overlap content); + revealed once there's room at the wide breakpoint below. */ + display: none; +} + +/* Frame the 1200px content column (center ± 600) — same on home and + docs now that the docs layout is also capped at 1200px. */ +.Layout::before { + left: calc(50% - var(--ldk-frame-half)); +} + +.Layout::after { + left: calc(50% + var(--ldk-frame-half)); +} + +@media (min-width: 1241px) { + .Layout::before, + .Layout::after { + display: block; + } +} + +/* VitePress adds a large bottom margin under the home content before the + footer. Drop it so the BDK cross-promo (our last home section) sits + flush at the bottom of the page frame with the footer directly below. */ +.VPHome { + margin-bottom: 0 !important; +} + +/* Dashed dividers on the structural elements VitePress renders by + default — navbar bottom and hero bottom. The home extras have their + own bottom-border rule above. */ +.VPNavBar { + border-bottom: 1px dashed var(--vp-c-divider); +} + +/* On desktop docs pages VitePress forces the navbar transparent via a + *scoped* rule — `.VPNavBar:not(.home)[data-v-hash]`, specificity + (0,3,0) — which lets the sidebar (z-index 28, below) show through and + overlap the logo as it scrolls. A plain class selector can't outrank + the scoped attribute selector, so use !important to re-assert an opaque + background and let the fixed navbar fully mask the sidebar beneath it. */ +@media (min-width: 960px) { + .VPNavBar.has-sidebar { + background-color: var(--vp-c-bg) !important; + } +} + +/* The content-body is explicitly `height: var(--vp-nav-height)`, but + border-box sizing + our 1px bottom border shrink the navbar's content + area by 1px — so the content-body overflows 1px and, on docs pages + where it has an opaque background, paints over the dashed border on the + nav-items side. Trim it so the border shows full width. */ +.VPNavBar .content-body { + height: calc(var(--vp-nav-height) - 1px); +} + +/* Align the navbar inner content with the dashed page-frame. + VitePress's default `.wrapper` has asymmetric padding (`0 8px 0 24px`) + that shifts the navbar right; zero it out so the container's own + padding is the only inset. The container is sized to the frame width + (1200px) so the logo and nav items sit a symmetric 24px inside the + frame lines — slightly closer to the border than the 1152px content + column below. */ +.VPNavBar .wrapper { + padding: 0 !important; +} + +.VPNavBar .wrapper > .container { + max-width: var(--ldk-frame-width) !important; + margin: 0 auto !important; + padding-left: 16px !important; + padding-right: 16px !important; +} + +/* Keep the navbar identical on docs (sidebar) pages. VitePress's + `has-sidebar` mode pulls the logo into an absolute sidebar-width box + and pads the nav content by the sidebar width; neutralize both so the + logo and nav items flow within the 1200px container exactly like + every other page. */ +@media (min-width: 960px) { + .VPNavBar.has-sidebar .title { + position: static !important; + width: auto !important; + padding: 0 !important; + background: transparent !important; + } + .VPNavBar.has-sidebar .content { + padding-left: 0 !important; + padding-right: 0 !important; + } +} + +/* The sidebar's "curtain" is the sticky mask at the top of the sidebar's + own scroll container — it hides links as they scroll up past the + navbar. Keep it (matched to the page background so it doesn't read as a + distinct box) so sidebar content never overlaps the fixed navbar/logo. */ +.VPSidebar .curtain { + background-color: var(--vp-c-bg); +} + +/* Sidebar sits above the doc content (so content scrolls under it), but + below the page-frame lines (z-index 29) so the left frame line stays + visible in the sidebar's left margin, and below the navbar. */ +@media (min-width: 960px) { + .VPSidebar { + z-index: 28 !important; + } +} + +/* Dashed divider between the sidebar and the content, matching the page + frame, so the docs grid reads as: left frame | sidebar | content | + right frame. */ +@media (min-width: 960px) { + .VPSidebar { + border-right: 1px dashed var(--vp-c-divider); + } +} + +/* Footer sits in the doc-content column on sidebar pages: mirror + VPContent.has-sidebar's sidebar offset and let the inner fill that + column, so the footer matches the VPDoc.has-sidebar.has-aside width and + never sits under the fixed sidebar. */ +@media (min-width: 960px) { + .VPContent.has-sidebar ~ .site-footer { + /* Offset past the fixed sidebar, plus a 32px gutter so the columns + aren't flush against the sidebar/frame edge. */ + padding-left: calc(var(--vp-sidebar-width) + 32px); + /* Padded divider separating the footer from the doc content above. */ + border-top: 1px dashed var(--vp-c-divider); + } + .VPContent.has-sidebar ~ .site-footer .site-footer-inner { + max-width: none; + margin: 0; + } +} + +@media (min-width: 1440px) { + .VPContent.has-sidebar ~ .site-footer { + padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width) + 32px); + padding-right: calc((100vw - var(--vp-layout-max-width)) / 2 + 32px); + } +} + +/* Plain pages (e.g. Case Studies) have no divider above the footer the + way docs pages (the sidebar rule above) and the home page (its last + section's divider) do. Give the footer a dashed top border there. + Drawn as a pseudo-element so it can reach the page-frame lines like + the home dividers, rather than spanning the full window width. */ +.VPContent:not(.has-sidebar):not(.is-home) ~ .site-footer { + position: relative; +} +.VPContent:not(.has-sidebar):not(.is-home) ~ .site-footer::before { + content: ''; + position: absolute; + top: 0; + left: 24px; + right: 24px; + border-top: 1px dashed var(--vp-c-divider); + pointer-events: none; +} + +@media (min-width: 1241px) { + .VPContent:not(.has-sidebar):not(.is-home) ~ .site-footer::before { + left: 50%; + right: auto; + width: var(--ldk-frame-width); + transform: translateX(-50%); + } +} + +/* Hero: tighten the default VitePress hero to read as a single + content column rather than the full-width centered ramp the + default theme produces. Padding lives on the container (not the + `.VPHero` outer) so the inner content edge matches the navbar + and home-extras sections. */ +.VPHome .VPHero { + border-bottom: 1px dashed var(--vp-c-divider); + /* VitePress's default uses a negative `margin-top` equal to the nav + height plus an asymmetric padding (`calc(... + 80px) 48px 64px`) + in media queries — both fight our symmetric layout. Override + fully with `!important` so the visible padding is what we set. */ + margin-top: 0 !important; + padding: 48px 0 !important; +} + +.VPHome .VPHero .container { + max-width: var(--ldk-content-width); + margin: 0 auto; + padding: 0 24px; +} + +.VPHome .VPHero .main { + text-align: left; + display: flex; + flex-direction: column; + gap: 32px; + align-items: flex-start; +} + +.VPHome .VPHero .name, +.VPHome .VPHero .text { + /* Mobile base: smaller heading that's allowed to wrap so it never + overflows on narrow viewports. */ + font-size: 32px; + line-height: 1.15; + font-weight: 700; + letter-spacing: -0.02em; + white-space: normal; +} + +@media (min-width: 961px) { + .VPHome .VPHero .name, + .VPHome .VPHero .text { + /* Wide enough to keep the heading on a single line. */ + font-size: 48px; + white-space: nowrap; + } +} + +.VPHome .VPHero .tagline { + font-size: 1.2rem; + line-height: 1.5; + color: var(--vp-c-text-2); + margin: 0 !important; + padding: 0 !important; + max-width: 48ch; +} + +@media (min-width: 961px) { + .VPHome .VPHero .tagline { + font-size: 1.6rem; + } +} + +.VPHome .VPHero .actions { + margin-top: 0 !important; + padding-top: 0 !important; +} + +.VPHome .VPHero .VPButton.brand { + display: inline-block; + font-size: 1.2rem; + padding: 10px 20px; + border-radius: 8px; + transition: background-color 0.1s ease; + box-sizing: border-box; + color: var(--vp-button-brand-text, var(--vp-c-white)); + background: var(--vp-button-brand-bg); + font-weight: 600; + line-height: 1.4; + height: auto; +} + +.VPHome .VPHero .VPButton.brand:hover { + background: var(--vp-button-brand-hover-bg); +} + +/* Wordmark + bolt rendered as a composed unit via `NavLogo.vue` in + the `nav-bar-title-before` slot. No extra title styling needed + beyond what the component scopes itself. */ + +/* Case studies is a landing-style page (no sidebar/aside), so VitePress + caps its doc column at ~784px and centers it. Let it break out to the + full frame content width instead, so the featured band and project + grids span the page and align with the home content. */ +.VPDoc:has(.cs-page) .container { + max-width: var(--ldk-content-width) !important; +} +.VPDoc:has(.cs-page) .content { + max-width: none !important; +} + +/* Case studies page layout. The page body is a series of inline + `
` grids containing `.case-study-item` + cards. Matching the v1 spiralbtc theme: three-column grid, centered + text, logo on top, title + description below, optional "View case + study" link at the bottom. */ +.case-studies { + display: grid; + grid-template-columns: 1fr; + gap: 24px; + margin-top: 24px; +} + +@media (min-width: 541px) { + .case-studies { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 961px) { + .case-studies { + grid-template-columns: repeat(3, 1fr); + } +} + +.case-study-item { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 24px 16px; +} + +/* Featured row: a dashed-bordered band (matching the page-frame + aesthetic) with dashed cell dividers between the three highlights, + rather than individual rounded cards. */ +.case-studies.cs-featured { + position: relative; + /* Featured highlights render 20% larger than the rest of the page. + zoom (not transform: scale) reflows, so the band still fills its + container width while logos, text, and padding all grow. This + compounds with .cs-page's own zoom:1.25 to an effective 1.5. */ + zoom: 1.2; + margin-top: 0; + gap: 0; + grid-template-columns: 1fr; + border-top: 1px dashed var(--vp-c-divider); + border-bottom: 1px dashed var(--vp-c-divider); +} + +.cs-featured .case-study-item { + padding: 32px 24px; +} + +/* Stacked (narrow): horizontal dashed dividers between cells. */ +.cs-featured .case-study-item:not(:first-child) { + border-top: 1px dashed var(--vp-c-divider); +} + +.cs-featured .case-study-item > a:first-child { + height: 80px; +} + +.cs-featured .case-study-item img { + max-height: 80px; +} + +@media (min-width: 961px) { + /* Three across: swap to vertical dashed dividers. */ + .case-studies.cs-featured { + grid-template-columns: repeat(3, 1fr); + } + .cs-featured .case-study-item:not(:first-child) { + border-top: 0; + border-left: 1px dashed var(--vp-c-divider); + } +} + +/* Once the page frame is visible (≥1241px, matching .vp-home-extras), + the band's own border stops ~24px short of each frame line. Replace + it with frame-spanning pseudo-elements so the top/bottom dashed lines + reach the vertical frame lines. The pseudo-elements sit inside the + band's compounded zoom (cs-page 1.25 × cs-featured 1.2 = 1.5), so the + real-px frame width is divided by that factor to render at the true + frame span, centered on the (centered) band. */ +@media (min-width: 1241px) { + .case-studies.cs-featured { + border-top: 0; + border-bottom: 0; + /* Break the band out to the full page-frame width so the outer cells + meet the frame lines — otherwise they stop ~56px short (the content + column's 32px padding + the 24px content-to-frame gutter), leaving + whitespace beside the first/last cells. `/1.5` undoes the compounded + zoom (cs-page 1.25 × cs-featured 1.2); left:50% + translateX(-50%) + re-centers the wider band on the frame regardless of the narrower + content container it lives in. */ + width: calc(var(--ldk-frame-width) / 1.5); + max-width: none; + left: 50%; + transform: translateX(-50%); + } + .case-studies.cs-featured::before, + .case-studies.cs-featured::after { + content: ''; + position: absolute; + left: 50%; + width: calc(var(--ldk-frame-width) / 1.5); + transform: translateX(-50%); + border-top: 1px dashed var(--vp-c-divider); + pointer-events: none; + } + .case-studies.cs-featured::before { + top: 0; + } + .case-studies.cs-featured::after { + bottom: 0; + } +} + +.case-study-item > a:first-child { + display: inline-flex; + align-items: center; + justify-content: center; + height: 64px; + margin-bottom: 12px; +} + +.case-study-item img { + max-height: 64px; + max-width: 140px; + object-fit: contain; +} + +.case-study-item h3 { + margin: 0 0 8px; + font-size: 18px; + font-weight: 600; + border: 0; + padding: 0; +} + +.case-study-item h3 a { + color: var(--vp-c-text-1); + text-decoration: none; +} + +.case-study-item h3 a:hover { + color: var(--vp-c-brand-1); +} + +.case-study-item p { + margin: 0 0 12px; + color: var(--vp-c-text-2); + font-size: 14px; + line-height: 1.5; + flex-grow: 1; +} + +.case-study-item .nav-link { + display: inline-block; + margin-top: auto; + color: var(--vp-c-brand-1); + font-size: 14px; + font-weight: 500; + text-decoration: none; +} + +.case-study-item .nav-link:hover { + color: var(--vp-c-brand-2); +} + +.more-cases-heading { + margin-top: 32px; + font-size: 28px; + font-weight: 600; +} + +/* Brand-color the nav items (Docs / Case Studies / Blog / Discord / GitHub) + to match the v1 spiralbtc navbar. */ +.VPNavBarMenuLink, +.VPNavBarMenuGroup .text { + color: var(--vp-c-brand-1) !important; +} + +.VPNavBarMenuLink:hover, +.VPNavBarMenuGroup:hover .text { + color: var(--vp-c-brand-2) !important; +} + +/* Larger nav item text on desktop. */ +@media (min-width: 768px) { + .VPNavBarMenuLink, + .VPNavBarMenuGroup .text { + font-size: 18px; + } +} + +/* VitePress renders a divider line between the menu group and the + appearance toggle. With socialLinks removed it's a stray vertical + line next to the toggle — hide it. The same goes for the `::before` + pseudo-element on `.appearance` (and adjacent combinations) that + draws another 1px vertical bar. */ +.VPNavBar .divider { + display: none; +} +.VPNavBar .appearance::before, +.VPNavBar .menu + .social-links::before, +.VPNavBar .translations + .appearance::before, +.VPNavBar .appearance + .social-links::before { + display: none !important; + content: none !important; +} + +/* Visually reorder the navbar items so search sits AFTER the menu + (GitHub) and before the appearance toggle. The DOM order is + Search → Menu → Appearance; flex `order` rearranges them as + Menu → Search → Appearance without touching markup. */ +.VPNavBar .content-body .VPNavBarMenu { + order: 1; +} +.VPNavBar .content-body .VPNavBarSearch { + order: 2; + /* Default `flex-grow: 1` absorbs leftover space and pushes the + menu items left. Disable so everything hugs the right edge. */ + flex-grow: 0; +} +.VPNavBar .content-body .VPNavBarAppearance { + order: 3; + /* Mobile base: surface the dark/light toggle in the navbar itself, + sitting between search (order 2) and the hamburger (order 4). + VitePress hides it here by default (it lives in the hamburger + screen), so we opt it back in. */ + display: flex; +} +/* Push the hamburger past search and appearance (it has no `order`, + so defaults to 0) — order reads Search → Appearance → Hamburger. + The 48px button centers its 16px icon, leaving 16px of internal + padding before the container's 24px edge — so the icon group would + sit ~16px further in than the logo on the left. Cancel that padding + with a negative margin so the icon aligns to the same 24px line as + the LDK symbol (the right-packed group shifts out as a unit). */ +.VPNavBar .content-body .VPNavBarHamburger { + order: 4; + margin-right: -16px; +} + +/* Mobile: space the three visible controls (search, appearance, + hamburger) evenly with a single gap rather than per-item margins, + which previously left an uneven 16px / 0px split. */ +.VPNavBar .content-body { + gap: 8px; +} + +@media (min-width: 768px) { + /* Desktop manages spacing per item, so drop the mobile gap and + restore the search/appearance insets. The "⋯" extra menu carries + the appearance toggle in this range, so hide the standalone one. */ + .VPNavBar .content-body { + gap: 0; + } + .VPNavBar .content-body .VPNavBarSearch { + padding-left: 16px; + } + .VPNavBar .content-body .VPNavBarAppearance { + display: none; + margin-left: 16px; + } + .VPNavBar .content-body .VPNavBarHamburger { + margin-right: 0; + } +} + +/* ≥1280px: VitePress surfaces the standalone toggle again (its + default), so restore it. */ +@media (min-width: 1280px) { + .VPNavBar .content-body .VPNavBarAppearance { + display: flex; + } +} + +/* Theme-aware images: render the light variant by default and swap to the + dark variant under .dark (e.g. the architecture diagram, which ships + two SVGs rather than a single CSS-themed one). */ +img.dark-only { + display: none; +} + +.dark img.light-only { + display: none; +} + +.dark img.dark-only { + display: initial; +} diff --git a/docs/assets/fonts/Manrope-OFL.txt b/docs/assets/fonts/Manrope-OFL.txt new file mode 100644 index 000000000..472064afc --- /dev/null +++ b/docs/assets/fonts/Manrope-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2018 The Manrope Project Authors (https://github.com/sharanda/manrope) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/assets/ldk-architecture-dark.svg b/docs/assets/ldk-architecture-dark.svg new file mode 100644 index 000000000..a86eac2f2 --- /dev/null +++ b/docs/assets/ldk-architecture-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/ldk-peer-management-dark.svg b/docs/assets/ldk-peer-management-dark.svg new file mode 100644 index 000000000..3f31b9dd9 --- /dev/null +++ b/docs/assets/ldk-peer-management-dark.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +RapidGossipSyncOnionMessenger diff --git a/docs/assets/ldk-peer-management.svg b/docs/assets/ldk-peer-management.svg index b3dd28e28..2c33df47a 100644 --- a/docs/assets/ldk-peer-management.svg +++ b/docs/assets/ldk-peer-management.svg @@ -1,4 +1,4 @@ - + @@ -8,22 +8,22 @@ - - - + + + - - + + - - + + - +RapidGossipSyncOnionMessenger diff --git a/docs/blockchain_data/block_source.md b/docs/blockchain_data/block_source.md deleted file mode 100644 index 32523d9ad..000000000 --- a/docs/blockchain_data/block_source.md +++ /dev/null @@ -1,35 +0,0 @@ -# Block Source - -Implementing the `BlockSource` interface requires defining methods for fetching -headers, blocks, and the best block hash. - - - - - - - - -For instance, you may implement this interface by querying Bitcoin Core's JSON -RPC interface, which happens to be a sample implementation provided by -`lightning-block-sync`. - -Let's walk through the use case where LDK receives full blocks. diff --git a/docs/blockchain_data/chain_activity.md b/docs/blockchain_data/chain_activity.md deleted file mode 100644 index e72e2ff02..000000000 --- a/docs/blockchain_data/chain_activity.md +++ /dev/null @@ -1,27 +0,0 @@ -# Chain Activity - -Initially, our node doesn't have any channels and hence has no data to monitor -for on-chain. When a channel is opened with a peer, the `ChannelManager` creates -a `ChannelMonitor` and passes it to the `ChainMonitor` to watch. - -At this point, you need to feed LDK any chain data of interest so that it can -respond accordingly. It supports receiving either full blocks or pre-filtered -blocks using the `chain::Listen` interface. While block data can be sourced from -anywhere, it is your responsibility to call the `block_connected` and -`block_disconnected` methods on `ChannelManager` and `ChainMonitor`. This allows -them to update channel state and respond to on-chain events, respectively. - -LDK comes with a `lightning-block-sync` utility that handles polling a block -source for the best chain tip, detecting chain forks, and notifying listeners -when blocks are connected and disconnected. It can be configured to: - -* Poll a custom `BlockSource` -* Notify `ChannelManager` and `ChainMonitor` of block events - -It is your choice as to whether you use this utility or your own to feed the -required chain data to LDK. If you choose to use it, you will need to implement -the `BlockSource` interface or use one of the samples that it provides. - -::: tip Note -Currently, `lightning-block-sync` is only available in Rust. -::: diff --git a/docs/blockchain_data/confirmed_transactions.md b/docs/blockchain_data/confirmed_transactions.md deleted file mode 100644 index c62e3d124..000000000 --- a/docs/blockchain_data/confirmed_transactions.md +++ /dev/null @@ -1,41 +0,0 @@ -# Confirmed Transactions - -Up until this point, we've explored how to notify LDK of chain activity using -blocks. But what if you're sourcing chain activity from a place that doesn't -provide a block-centric interface, like Electrum? - -LDK's `ChannelManager` and `ChainMonitor` implement a -[`chain::Confirm`](https://docs.rs/lightning/*/lightning/chain/trait.Confirm.html) -interface to support this use case, analogous to the block-oriented -[`chain::Listen`](https://docs.rs/lightning/*/lightning/chain/trait.Listen.html) -interface which we've been using up until now. With this alternative approach, -you still need to give LDK information about chain activity, -but only for transactions of interest. To this end, you must call -`Confirm::transactions_confirmed` when any transactions identified by -[`chain::Filter`](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html)'s -`register_tx`/`register_output` methods are confirmed. - -You also need to notify LDK of any transactions with insufficient confirmations -that have been reorganized out of the chain. Transactions that need to be monitored for such -reorganization are returned by `Confirm::get_relevant_txids`. If any of these transactions become -unconfirmed, you must call `Confirm::transaction_unconfirmed`. - -Lastly, you must notify LDK whenever a new chain tip is available using -the `Confirm::best_block_updated` method. See the documentation for a full -picture of how this interface is intended to be used. - -::: tip Note -Note that the described methods of `Confirm` must be called in accordance with the ordering requirements -described in the [`Confirm` documentation](https://docs.rs/lightning/*/lightning/chain/trait.Confirm.html#order) -::: - -::: tip Note -Note that the described methods of `Confirm` must be called both on the -`ChannelManager` *and* the `ChainMonitor`. -::: - -::: tip Note -Be advised that `chain::Confirm` is a less mature interface than -`chain::Listen`. As such, there is not yet a utility like -`lightning-block-sync` to use for interacting with clients like Electrum. -::: diff --git a/docs/blockchain_data/full_blocks.md b/docs/blockchain_data/full_blocks.md deleted file mode 100644 index 529bdc194..000000000 --- a/docs/blockchain_data/full_blocks.md +++ /dev/null @@ -1,14 +0,0 @@ -# Full Blocks - -If your Lightning node is backed by a Bitcoin full node, the operation is -straight forward: call the appropriate methods on `ChannelManager` and -`ChainMonitor` as blocks are connected and disconnected. LDK will handle the -rest! - -So what happens? The `ChannelManager` examines the block's transactions and -updates the internal channel state as needed. The `ChainMonitor` will detect -any spends of the channel funding transaction or any pertinent transaction -outputs, tracking them as necessary. - -If necessary, LDK will broadcast a transaction on your behalf. More on that -later. For now, let's look at the more interesting case of pre-filtered blocks. diff --git a/docs/blockchain_data/index.md b/docs/blockchain_data/index.md new file mode 100644 index 000000000..3c3dff224 --- /dev/null +++ b/docs/blockchain_data/index.md @@ -0,0 +1,205 @@ +# Blockchain Data + +In this guide, we'll explore how to provide chain data to LDK upon startup and +as new blocks are mined. This allows LDK to maintain channel state and monitor +for on-chain channel activity. + +LDK maintains channels with your node's peers during the course of node +operation. When a new channel is opened, the `ChannelManager` will keep track of +the channel's state and tell the `ChainMonitor` that a new channel should be +watched. The `ChainMonitor` does so by maintaining a `ChannelMonitor` for each +channel. + +When a new block is mined, it is connected to the chain while other blocks may +be disconnected if reorganized out. Transactions are confirmed or unconfirmed +during this process. You are required to feed this activity to LDK which will +process it by: + +* Updating channel state +* Signaling back transactions to filter +* Broadcasting transactions if necessary + +## Chain Activity + +Initially, our node doesn't have any channels and hence has no data to monitor +for on-chain. When a channel is opened with a peer, the `ChannelManager` creates +a `ChannelMonitor` and passes it to the `ChainMonitor` to watch. + +At this point, you need to feed LDK any chain data of interest so that it can +respond accordingly. It supports receiving either full blocks or pre-filtered +blocks using the `chain::Listen` interface. While block data can be sourced from +anywhere, it is your responsibility to call the `block_connected` and +`block_disconnected` methods on `ChannelManager` and `ChainMonitor`. This allows +them to update channel state and respond to on-chain events, respectively. + +LDK comes with a `lightning-block-sync` utility that handles polling a block +source for the best chain tip, detecting chain forks, and notifying listeners +when blocks are connected and disconnected. It can be configured to: + +* Poll a custom `BlockSource` +* Notify `ChannelManager` and `ChainMonitor` of block events + +It is your choice as to whether you use this utility or your own to feed the +required chain data to LDK. If you choose to use it, you will need to implement +the `BlockSource` interface or use one of the samples that it provides. + +::: tip Note +Currently, `lightning-block-sync` is only available in Rust. +::: + +## Block Source + +Implementing the `BlockSource` interface requires defining methods for fetching +headers, blocks, and the best block hash. (`lightning-block-sync` is Rust-only.) + +```rust +impl BlockSource for Blockchain { + fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, _height: Option) -> AsyncBlockSourceResult<'a, BlockHeaderData> { + // + } + + // Note: `get_block` returns `BlockData` (it can yield either a full block + // or just the header, supporting pre-filtered block sources). + fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, BlockData> { + // + } + + fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<'a, (BlockHash, Option)> { + // + } +} +``` + +For instance, you may implement this interface by querying Bitcoin Core's JSON +RPC interface, which happens to be a sample implementation provided by +`lightning-block-sync`. + +Let's walk through the use case where LDK receives full blocks. + +## Full Blocks + +If your Lightning node is backed by a Bitcoin full node, the operation is +straight forward: call the appropriate methods on `ChannelManager` and +`ChainMonitor` as blocks are connected and disconnected. LDK will handle the +rest! + +So what happens? The `ChannelManager` examines the block's transactions and +updates the internal channel state as needed. The `ChainMonitor` will detect +any spends of the channel funding transaction or any pertinent transaction +outputs, tracking them as necessary. + +If necessary, LDK will broadcast a transaction on your behalf. More on that +later. For now, let's look at the more interesting case of pre-filtered blocks. + +## Pre-filtered Blocks + +For environments that are resource constrained, receiving and processing all +transaction data may not be feasible. LDK handles this case by signaling back +which transactions and outputs it is interested in. This information can then be +used to filter blocks prior to sending them to your node. + +For example, if your block source is an Electrum client, you can pass along this +information to it. Or, if you are making use of a BIP 157 client, you can check +if a block contains relevant transactions before fetching it. + +So how does this work in practice? `ChainMonitor` is parameterized by an +optional type that implements `chain::Filter`: + +::: code-group + +```rust [Rust] +impl chain::Filter for Blockchain { + fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { + // + } + + fn register_output(&self, output: WatchedOutput) { + // + } +} +``` + +```kotlin [Kotlin] +val txFilter = Filter.new_impl(object : Filter.FilterInterface { + override fun register_tx(txid: ByteArray, scriptPubkey: ByteArray) { + // + } + + override fun register_output(output: WatchedOutput) { + // + } +}) +``` + +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +const txFilter = ldk.Filter.new_impl({ + register_tx(txid: Uint8Array, scriptPubkey: Uint8Array): void { + // + }, + register_output(output: ldk.WatchedOutput): void { + // + }, +} as ldk.FilterInterface); +``` + +::: + +When this is provided, `ChainMonitor` will call back to the filter as channels +are opened and blocks connected. This gives the opportunity for the source to +pre-filter blocks as desired. + +Regardless, when a block is connected, its header must be processed by LDK. + +## Confirmed Transactions + +Up until this point, we've explored how to notify LDK of chain activity using +blocks. But what if you're sourcing chain activity from a place that doesn't +provide a block-centric interface, like Electrum or Esplora? + +LDK's `ChannelManager` and `ChainMonitor` implement a +[`chain::Confirm`](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Confirm.html) +interface to support this use case, analogous to the block-oriented +[`chain::Listen`](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Listen.html) +interface which we've been using up until now. With this alternative approach, +you still need to give LDK information about chain activity, +but only for transactions of interest. To this end, you must call +`Confirm::transactions_confirmed` when any transactions identified by +[`chain::Filter`](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Filter.html)'s +`register_tx`/`register_output` methods are confirmed. + +You also need to notify LDK of any transactions with insufficient confirmations +that have been reorganized out of the chain. Transactions that need to be monitored for such +reorganization are returned by `Confirm::get_relevant_txids`. If any of these transactions become +unconfirmed, you must call `Confirm::transaction_unconfirmed`. + +Lastly, you must notify LDK whenever a new chain tip is available using +the `Confirm::best_block_updated` method. See the documentation for a full +picture of how this interface is intended to be used. + +::: tip Note +The described methods of `Confirm` must be called both on the `ChannelManager` +*and* the `ChainMonitor`, and in accordance with the ordering requirements +described in the [`Confirm` documentation](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Confirm.html#order). +::: + +::: tip Note +For Electrum and Esplora backends, LDK provides the +[`lightning-transaction-sync`](https://docs.rs/lightning-transaction-sync/0.2.0/lightning_transaction_sync/) +crate, which drives the `Confirm` interface for you via its `EsploraSyncClient` +and `ElectrumSyncClient`. You register interest through the `Filter` it exposes, +then call `sync` with the `ChannelManager` and `ChainMonitor`. +::: + +## Transaction Broadcasting + +Inevitably, LDK will need to broadcast transactions on your behalf. As you +notify it of blocks, it will determine if it should broadcast a transaction and +do so using an implementation of `BroadcasterInterface` that you have provided. + +And as those transactions or those from your peers are confirmed on-chain, they +will be likewise processed when notified of a connected block. Thus, continuing +the cycle. diff --git a/docs/blockchain_data/introduction.md b/docs/blockchain_data/introduction.md deleted file mode 100644 index 8c2ef2f5d..000000000 --- a/docs/blockchain_data/introduction.md +++ /dev/null @@ -1,24 +0,0 @@ -## Introduction - -In this guide, we'll explore how to provide chain data to LDK upon startup and -as new blocks are mined. This allows LDK to maintain channel state and monitor -for on-chain channel activity. - -LDK maintains channels with your node's peers during the course of node -operation. When a new channel is opened, the `ChannelManager` will keep track of -the channel's state and tell the `ChainMonitor` that a new channel should be -watched. The `ChainMonitor` does so by maintaining a `ChannelMonitor` for each -channel. - -When a new block is mined, it is connected to the chain while other blocks may -be disconnected if reorganized out. Transactions are confirmed or unconfirmed -during this process. You are required to feed this activity to LDK which will -process it by: - -* Updating channel state -* Signaling back transactions to filter -* Broadcasting transactions if necessary - -We will walk through this process as depicted here: - -![LDK block processing](../assets/ldk-block-processing.svg) \ No newline at end of file diff --git a/docs/blockchain_data/pre_filtered_blocks.md b/docs/blockchain_data/pre_filtered_blocks.md deleted file mode 100644 index c1d59342f..000000000 --- a/docs/blockchain_data/pre_filtered_blocks.md +++ /dev/null @@ -1,56 +0,0 @@ -# Pre-filtered Blocks - -For environments that are resource constrained, receiving and processing all -transaction data may not be feasible. LDK handles this case by signaling back -which transactions and outputs it is interested in. This information can then be -used to filter blocks prior to sending them to your node. - -For example, if your block source is an Electrum client, you can pass along this -information to it. Or, if you are making use of a BIP 157 client, you can check -if a block contains relevant transactions before fetching it. - -So how does this work in practice? `ChainMonitor` is parameterized by an -optional type that implements `chain::Filter`: - - - - - - -When this is provided, `ChainMonitor` will call back to the filter as channels -are opened and blocks connected. This gives the opportunity for the source to -pre-filter blocks as desired. - -Regardless, when a block is connected, its header must be processed by LDK. diff --git a/docs/blockchain_data/transaction_broadcasting.md b/docs/blockchain_data/transaction_broadcasting.md deleted file mode 100644 index f0e3e870b..000000000 --- a/docs/blockchain_data/transaction_broadcasting.md +++ /dev/null @@ -1,9 +0,0 @@ -# Transaction Broadcasting - -Inevitably, LDK will need to broadcast transactions on your behalf. As you -notify it of blocks, it will determine if it should broadcast a transaction and -do so using an implementation of `BroadcasterInterface` that you have provided. - -And as those transactions or those from your peers are confirmed on-chain, they -will be likewise processed when notified of a connected block. Thus, continuing -the cycle. \ No newline at end of file diff --git a/docs/blog/_taxonomy.ts b/docs/blog/_taxonomy.ts new file mode 100644 index 000000000..f30f7c1c7 --- /dev/null +++ b/docs/blog/_taxonomy.ts @@ -0,0 +1,91 @@ +/** + * Slugify "Self-custody" → "self-custody", "Elias Rohrer" → "elias-rohrer". + * + * Kept in a separate module from `posts.data.mts` because VitePress's + * static-data plugin transforms `.data.{js,ts,mjs,mts}` files so they + * expose only a `data` named export — any other exports (like this + * function) get dropped, and consumers get + * `"slugify" is not exported by "posts.data.mts"` at build time. + */ +export function slugify(value: string): string { + return value + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') +} + +export interface Post { + url: string + title: string + description: string + date: string + authors: string[] + tags: string[] +} + +/** + * Read blog frontmatter directly from disk. Used by `[param].paths.mts` + * files which are loaded by VitePress via `loadConfigFromFile` and + * bypass the static-data plugin pipeline — so they can't use the + * `data` export from `posts.data.mts` (would be undefined). The + * paths files run at build time on Node, hence the `fs` import. + * + * Runtime pages (markdown bodies with ` + +# Posts by {{ $params.name }} + +

No posts found.

+ + + +[← Back to all authors](/blog/author/) diff --git a/docs/blog/author/[author].paths.mts b/docs/blog/author/[author].paths.mts new file mode 100644 index 000000000..414b8a8e2 --- /dev/null +++ b/docs/blog/author/[author].paths.mts @@ -0,0 +1,18 @@ +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from 'node:path' +import { readBlogPosts, slugify } from '../_taxonomy.ts' + +const blogDir = resolve(dirname(fileURLToPath(import.meta.url)), '..') + +export default { + async paths() { + const posts = await readBlogPosts(blogDir) + const authors = new Set() + for (const post of posts) { + for (const author of post.authors) authors.add(author) + } + return [...authors].map((author) => ({ + params: { author: slugify(author), name: author }, + })) + }, +} diff --git a/docs/blog/author/index.md b/docs/blog/author/index.md new file mode 100644 index 000000000..bc3d6aba3 --- /dev/null +++ b/docs/blog/author/index.md @@ -0,0 +1,75 @@ +--- +title: Authors +description: Browse blog posts by author +--- + + + +# Authors + + + + diff --git a/docs/_blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial wallets.md b/docs/blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial-wallets.md similarity index 99% rename from docs/_blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial wallets.md rename to docs/blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial-wallets.md index 0a997eb0d..6993c0a7f 100644 --- a/docs/_blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial wallets.md +++ b/docs/blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial-wallets.md @@ -5,7 +5,7 @@ date: "2024-01-24" authors: - Matt Carvalho tags: - - Case-Studies + - case-studies --- In 2020, the Lightning Network was in its early stages, and most people used custodial wallets. We believe this trend towards custodial wallets contradicts the Bitcoin ethos, so we decided to create a self-custodial alternative that offers a great user experience for all, not just technical users. diff --git a/docs/_blog/bolt12-has-arrived.md b/docs/blog/bolt12-has-arrived.md similarity index 96% rename from docs/_blog/bolt12-has-arrived.md rename to docs/blog/bolt12-has-arrived.md index 790bef1e1..e070031d7 100644 --- a/docs/_blog/bolt12-has-arrived.md +++ b/docs/blog/bolt12-has-arrived.md @@ -5,9 +5,9 @@ date: "2024-04-30" authors: - Jeff Czyz tags: - - Offers - - Onion Messages - - Privacy + - offers + - onion messages + - privacy --- You may have heard of BOLT12—lovingly referred to as Offers—but what is it, and why should you care? In short, BOLT12 is a new payment protocol for Lightning that offers enhanced privacy, reusable payment codes, refunds, and much more, all natively over the Lightning Network. No additional servers are required. This is all possible using new technologies like onion messages and route blinding. Let’s further explore how to make use of BOLT12 in LDK. @@ -24,7 +24,7 @@ That’s where BOLT12 comes in. The specification defines an *offer* that can be

Comparison of typical BOLT11 (left) and BOLT12 (right) QR codes

-So how is this all happening without a dedicated server? The magic comes in with something called [onion messages](../onion-messages-demystified). The Lightning protocol is defined as messages exchanged between direct peers. An onion payment involves an interchange of messages along a path, collecting fees and transferring funds until the preimage is revealed and the funds claimed. +So how is this all happening without a dedicated server? The magic comes in with something called [onion messages](./onion-messages-demystified.md). The Lightning protocol is defined as messages exchanged between direct peers. An onion payment involves an interchange of messages along a path, collecting fees and transferring funds until the preimage is revealed and the funds claimed. An onion message is very similar in that it involves sending messages along a path, encrypted as an onion such that each hop only knows where to send the message next. The difference is that they’re more efficient and don’t require locking any liquidity. Messages are forwarded until they reach the intended recipient. There is no interchange between adjacent peers to sign and revoke commitments or a round-trip along the path. Channel liquidity—or channels, for that matter—aren’t even required. diff --git a/docs/_blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md b/docs/blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md similarity index 99% rename from docs/_blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md rename to docs/blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md index a1efc4177..c24245f89 100644 --- a/docs/_blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md +++ b/docs/blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk.md @@ -5,7 +5,7 @@ date: "2022-11-17" authors: - Ryan Loomba tags: - - Case-Studies + - case-studies --- Cash App allows users to instantly deposit and withdraw bitcoin over the lightning network. As an engineer on the Cash App Lightning Wallet team, I was very involved in the entire implementation process. diff --git a/docs/_blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md b/docs/blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md similarity index 99% rename from docs/_blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md rename to docs/blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md index 1b36f3ef0..7783fc50f 100644 --- a/docs/_blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md +++ b/docs/blog/fedimint-lightning-gateway-uses-ldk-node-to-simplify-deployment-and-liquidity-management.md @@ -5,7 +5,7 @@ date: "2025-01-30" authors: - Justin Moeller tags: - - Case-Studies + - case-studies --- The [Fedimint Lightning Gateway](https://github.com/fedimint/fedimint/blob/master/docs/gateway.md) is a server application that powers the Lightning payments for Fedimints. Lightning Gateways enable Fedimint ecash users access to the broader Lightning network by offering a trustless ecash to Lightning swap service to the federation. A Fedimint user incentivizes the gateway to pay a Lightning invoice on its behalf by locking ecash to a hash time locked contract that is enforced by the federation. The gateway can then claim the locked ecash by revealing the preimage from the paid invoice. To receive ecash, the gateway will fund a Fedimint client as long as it receives a preimage in return that the gateway can use to claim a payment on the Lightning network. If this sounds similar to how HTLCs in Lightning work, that's because it is! However, there is the added caveat that Lightning Gateways must also manage an ecash balance alongside the Lightning channels. diff --git a/docs/_blog/how-we-built-our-sparknodes-using-ldk.md b/docs/blog/how-we-built-our-sparknodes-using-ldk.md similarity index 99% rename from docs/_blog/how-we-built-our-sparknodes-using-ldk.md rename to docs/blog/how-we-built-our-sparknodes-using-ldk.md index 3f77ef07f..7350eb99b 100644 --- a/docs/_blog/how-we-built-our-sparknodes-using-ldk.md +++ b/docs/blog/how-we-built-our-sparknodes-using-ldk.md @@ -5,7 +5,7 @@ date: "2025-02-04" authors: - Lightspark Engineering tags: - - Case-Studies + - case-studies --- When we started building Lightspark's Lightning Network infrastructure, we needed an architecture that could scale as customer demand grew. Our system needed to be flexible and capable of scaling with the growth. diff --git a/docs/blog/index.md b/docs/blog/index.md new file mode 100644 index 000000000..4ed99673a --- /dev/null +++ b/docs/blog/index.md @@ -0,0 +1,12 @@ +--- +title: Blog +description: Updates, deep-dives, and case studies from the LDK team and community +--- + + + +# Articles + + diff --git a/docs/_blog/introducing-phantom-node-payments.md b/docs/blog/introducing-phantom-node-payments.md similarity index 99% rename from docs/_blog/introducing-phantom-node-payments.md rename to docs/blog/introducing-phantom-node-payments.md index 6d9cfd17a..393f59ee6 100644 --- a/docs/_blog/introducing-phantom-node-payments.md +++ b/docs/blog/introducing-phantom-node-payments.md @@ -5,8 +5,8 @@ date: "2022-07-11" authors: - Valentine Wallace tags: - - Enterprise - - Phantom + - enterprise + - phantom --- Introducing phantom node payments, a new-ish feature for LDK. diff --git a/docs/_blog/ldk-an-sdk-for-the-lightning-network.md b/docs/blog/ldk-an-sdk-for-the-lightning-network.md similarity index 99% rename from docs/_blog/ldk-an-sdk-for-the-lightning-network.md rename to docs/blog/ldk-an-sdk-for-the-lightning-network.md index 2e0d80b5b..23349e30a 100644 --- a/docs/_blog/ldk-an-sdk-for-the-lightning-network.md +++ b/docs/blog/ldk-an-sdk-for-the-lightning-network.md @@ -5,8 +5,8 @@ date: "2022-06-20" authors: - Conor Okus tags: - - Bitcoin - - LDK + - bitcoin + - ldk --- We are excited to release our first blog post, we have a lot to share and a lot to build. We’ll be updating this blog regularly with what we’re working on, new features and the details on new releases. diff --git a/docs/_blog/ldk-pathfinding.md b/docs/blog/ldk-pathfinding.md similarity index 99% rename from docs/_blog/ldk-pathfinding.md rename to docs/blog/ldk-pathfinding.md index efa37bd74..d5b4dc19e 100644 --- a/docs/_blog/ldk-pathfinding.md +++ b/docs/blog/ldk-pathfinding.md @@ -5,7 +5,7 @@ date: "2025-02-10" authors: - Matt Corallo tags: - - Pathfinding + - pathfinding --- Some time ago, LDK shipped an overhaul of our pathfinding algorithm to incorporate substantially more learnings over time into each new path over which we sent a payment. At the time, we didn’t have any formal analysis framework for our pathfinder, so we didn’t run to our blog to declare it the most advanced pathfinder in the lightning world. As of Nov, 2024, [we do](https://bluematt.bitcoin.ninja/2024/11/22/ln-routing-replay/). diff --git a/docs/_blog/ldk-roadmap.md b/docs/blog/ldk-roadmap.md similarity index 99% rename from docs/_blog/ldk-roadmap.md rename to docs/blog/ldk-roadmap.md index 8c966ab88..69cda120e 100644 --- a/docs/_blog/ldk-roadmap.md +++ b/docs/blog/ldk-roadmap.md @@ -5,7 +5,7 @@ date: "2023-03-13" authors: - Steve Lee tags: - - Roadmap + - roadmap --- LDK is going to have a big year. Several key features are actively being developed or expected to be developed in the next few quarters. There are also a few projects of interest that don’t yet have any committed developers. If you want to get involved, hop into the [Discord Server](https://discord.gg/xaYE3pDQpm) or [GitHub repo](https://github.com/lightningdevkit). diff --git a/docs/_blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md b/docs/blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md similarity index 99% rename from docs/_blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md rename to docs/blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md index caa7fd350..1f0b4ae34 100644 --- a/docs/_blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md +++ b/docs/blog/lqwd-liquidity-provider-get-liquidity-when-you-need-it.md @@ -5,7 +5,7 @@ date: "2025-01-14" authors: - Aziz Pulatov tags: - - Case-Studies + - case-studies --- The rapid growth of the Lightning Network has created unprecedented demand for liquidity. Traditionally, receiving payments on the Lightning Network required pre-established channels which posed barriers to new users and hindered scalability. With the introduction of [LSPS2 / bLIP-52](https://github.com/lightning/blips/blob/master/blip-0052.md) and its Just-In-Time (JIT) channel negotiation capabilities, the Lightning Network can now provide liquidity exactly when needed, improving both accessibility and efficiency. By leveraging [LDK Node](https://lightningdevkit.org/blog/announcing-ldk-node/) (a higher-level wrapper of LDK) and the [Lightning Liquidity crate](https://github.com/lightningdevkit/rust-lightning/tree/main/lightning-liquidity)—both Rust-based implementations—we developed a specialized LSP Node [(LQwD)](https://lqwdtech.com/). This node enables real-time liquidity provisioning by dynamically creating channels, ensuring payments can be received without prior investments in channel capacity. diff --git a/docs/_blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md b/docs/blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md similarity index 99% rename from docs/_blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md rename to docs/blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md index f513124d4..62a151f52 100644 --- a/docs/_blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md +++ b/docs/blog/mutiny-uses-ldk-the-first-lightning-wallet-for-the-web.md @@ -5,7 +5,7 @@ date: "2023-08-07" authors: - Ben Carman tags: - - Case-Studies + - case-studies --- Mutiny is a new lightning wallet for the web. It is the first self-custodial wallet to run directly in a user's browser. It’s built on top of LDK and [BDK](https://bitcoindevkit.org/), written in Rust, and compiled into WebAssembly. It offers a simple, intuitive interface for sending and receiving payments and other advanced features, such as Nostr Wallet Connect and subscription payments. diff --git a/docs/_blog/onion-messages-demystified.md b/docs/blog/onion-messages-demystified.md similarity index 99% rename from docs/_blog/onion-messages-demystified.md rename to docs/blog/onion-messages-demystified.md index 92cf63f5d..044d1098b 100644 --- a/docs/_blog/onion-messages-demystified.md +++ b/docs/blog/onion-messages-demystified.md @@ -5,8 +5,8 @@ date: "2022-10-04" authors: - Valentine Wallace tags: - - Onion Messages - - Offers + - onion messages + - offers --- We recently merged support for onion message forwarding, a precursor to deprecating today’s Lightning invoice format, in favor of [offers](https://github.com/lightning/bolts/pull/798). Offers bring many improvements to Lightning, including static invoices, refunds, and receiving payments in a way that doesn’t reveal your node ID or UTXOs. Then, once offer extensions are added, support for subscriptions follows. diff --git a/docs/blog/posts.data.mts b/docs/blog/posts.data.mts new file mode 100644 index 000000000..72461a8ea --- /dev/null +++ b/docs/blog/posts.data.mts @@ -0,0 +1,28 @@ +import { createContentLoader } from 'vitepress' +import type { Post } from './_taxonomy.ts' + +declare const data: Post[] +export { data } + +export default createContentLoader('blog/*.md', { + excerpt: false, + transform(raw): Post[] { + return raw + .filter(({ url }) => { + // Skip meta pages — only individual articles get aggregated. + if (url === '/blog/' || url === '/blog/index') return false + if (url.startsWith('/blog/tags/')) return false + if (url.startsWith('/blog/author/')) return false + return true + }) + .map(({ url, frontmatter }) => ({ + url, + title: frontmatter.title ?? '', + description: frontmatter.description ?? '', + date: frontmatter.date ?? '', + authors: Array.isArray(frontmatter.authors) ? frontmatter.authors : [], + tags: Array.isArray(frontmatter.tags) ? frontmatter.tags : [], + })) + .sort((a, b) => +new Date(b.date) - +new Date(a.date)) + }, +}) diff --git a/docs/_blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md b/docs/blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md similarity index 99% rename from docs/_blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md rename to docs/blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md index bc69548dd..dd58f5b5c 100644 --- a/docs/_blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md +++ b/docs/blog/sensei-uses-ldk-to-build-a-multi-node-lightning-server-application.md @@ -5,7 +5,7 @@ date: "2022-12-20" authors: - John Cantrell tags: - - Case-Studies + - case-studies --- [Sensei](https://l2.technology/sensei) is a lightning node application optimized to serve many nodes within a single instance. It offers a beautiful web interface for admins to manage nodes as well as an interface for individual nodes to perform all of the common lightning network tasks such as connecting to peers, opening and closing channels, and creating and paying invoices. Sensei exposes http and grpc interfaces, allowing automation and programmatic control of the nodes. diff --git a/docs/blog/tags/[tag].md b/docs/blog/tags/[tag].md new file mode 100644 index 000000000..24a47ac1e --- /dev/null +++ b/docs/blog/tags/[tag].md @@ -0,0 +1,23 @@ +--- +title: Tag +--- + + + +# Posts tagged "{{ $params.name }}" + +

No posts found.

+ + + +[← Back to all tags](/blog/tags/) diff --git a/docs/blog/tags/[tag].paths.mts b/docs/blog/tags/[tag].paths.mts new file mode 100644 index 000000000..84ba44534 --- /dev/null +++ b/docs/blog/tags/[tag].paths.mts @@ -0,0 +1,18 @@ +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from 'node:path' +import { readBlogPosts, slugify } from '../_taxonomy.ts' + +const blogDir = resolve(dirname(fileURLToPath(import.meta.url)), '..') + +export default { + async paths() { + const posts = await readBlogPosts(blogDir) + const tags = new Set() + for (const post of posts) { + for (const tag of post.tags) tags.add(tag) + } + return [...tags].map((tag) => ({ + params: { tag: slugify(tag), name: tag }, + })) + }, +} diff --git a/docs/blog/tags/index.md b/docs/blog/tags/index.md new file mode 100644 index 000000000..50dd39cbe --- /dev/null +++ b/docs/blog/tags/index.md @@ -0,0 +1,75 @@ +--- +title: Tags +description: Browse blog posts by tag +--- + + + +# Tags + + + + diff --git a/docs/_blog/teos-uses-ldk-to-build-open-source-watchtower.md b/docs/blog/teos-uses-ldk-to-build-open-source-watchtower.md similarity index 99% rename from docs/_blog/teos-uses-ldk-to-build-open-source-watchtower.md rename to docs/blog/teos-uses-ldk-to-build-open-source-watchtower.md index c4e869ecf..bfb4b8e04 100644 --- a/docs/_blog/teos-uses-ldk-to-build-open-source-watchtower.md +++ b/docs/blog/teos-uses-ldk-to-build-open-source-watchtower.md @@ -5,7 +5,7 @@ date: "2023-01-18" authors: - Sergi Delgado tags: - - Case-Studies + - case-studies --- [The Eye of Satoshi (TEOS)](https://github.com/talaia-labs/rust-teos) is a bitcoin watchtower with a specific focus on lightning. Watchtowers are third-party viewing services that help prevent people from cheating on the lightning network by constantly monitoring the bitcoin blockchain for channel breaches. If found, a watchtower will react by penalizing the misbehaving party and sending all funds to its counterpart. diff --git a/docs/_blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md b/docs/blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md similarity index 99% rename from docs/_blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md rename to docs/blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md index ab7d71965..943a6aae3 100644 --- a/docs/_blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md +++ b/docs/blog/the-challenges-of-developing-non-custodial-lightning-on-mobile.md @@ -5,8 +5,8 @@ date: "2023-12-14" authors: - Matt Corallo tags: - - Mobile - - Non-Custodial + - mobile + - non-custodial --- Lightning development is tough. While going custodial simplifies the process, it means sacrificing user privacy, censorship resistance, and self-sovereignty, all of which contradict bitcoin’s ethos. Therefore, many companies and projects have started with or redirected their focus from building custodial to non-custodial Lightning applications. diff --git a/docs/_blog/unleashing-liquidity-on-the-lightning-network-with-lightning-liquidity.md b/docs/blog/unleashing-liquidity-on-the-lightning-network-with-lightning-liquidity.md similarity index 100% rename from docs/_blog/unleashing-liquidity-on-the-lightning-network-with-lightning-liquidity.md rename to docs/blog/unleashing-liquidity-on-the-lightning-network-with-lightning-liquidity.md diff --git a/docs/_blog/zero-confirmation-channels.md b/docs/blog/zero-confirmation-channels.md similarity index 99% rename from docs/_blog/zero-confirmation-channels.md rename to docs/blog/zero-confirmation-channels.md index 823e1611f..c69a98ad1 100644 --- a/docs/_blog/zero-confirmation-channels.md +++ b/docs/blog/zero-confirmation-channels.md @@ -5,7 +5,7 @@ date: "2022-09-15" authors: - John Cantrell tags: - - 0-Conf Channels + - 0-conf channels --- Support for 0-conf channels became available in the Lightning Development Kit’s June 2022, [107 release](https://github.com/lightningdevkit/rust-lightning/releases/tag/v0.0.107). A 0-conf channel allows peers to start using a channel as soon as the funding transaction is broadcast instead of waiting for multiple confirmations. By temporarily trusting the person initiating the channel open, 0-conf channels enable instant onboarding to the Lightning Network. diff --git a/docs/building-a-node-with-ldk/closing-a-channel.md b/docs/building-a-node-with-ldk/closing-a-channel.md index b72284685..7b98394e0 100644 --- a/docs/building-a-node-with-ldk/closing-a-channel.md +++ b/docs/building-a-node-with-ldk/closing-a-channel.md @@ -2,28 +2,32 @@ Begins the process of closing a channel. After this call (plus some timeout), no new HTLCs will be accepted on the given channel, and after additional timeout/the closing of all pending HTLCs, the channel will be closed on chain. - - - - - +::: To claim Funds directly into a custom wallet like BDK wallet using a custom `KeysManager` see the [Key Management](/key_management.md) guide for more info. # SpendableOutputs Event Handling - - - - - - +::: -**References:** [Rust `SpendableOutputs` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html#variant.SpendableOutputs), [Java/Kotlin `SpendableOutputs` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L802) \ No newline at end of file +**References:** [Rust `SpendableOutputs` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.SpendableOutputs), [Rust `OutputSpender` docs](https://docs.rs/lightning/0.2.2/lightning/sign/trait.OutputSpender.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `OutputSpender` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/OutputSpender.mts) \ No newline at end of file diff --git a/docs/building-a-node-with-ldk/connect-to-peers.md b/docs/building-a-node-with-ldk/connect-to-peers.md index 2a000b94a..96e45145b 100644 --- a/docs/building-a-node-with-ldk/connect-to-peers.md +++ b/docs/building-a-node-with-ldk/connect-to-peers.md @@ -4,24 +4,26 @@ In this section you'll learn how to join the lightning network. Firstly we need to have the ability to do high performance I/O operations. LDK provides default implementations for initializing all of your networking needs. If you are using Rust, you can use our simple socket handling library `lightning_net_tokio`. In Kotlin/Java you can use the `NioPeerHandler` which uses Java's NIO I/O interface. +In TypeScript there is no networking module for non-Node.js (browser) environments: WASM cannot open raw TCP sockets. Instead you implement a `SocketDescriptor` that bridges LDK to a transport you do have — typically a `WebSocket` talking to a WebSocket-to-TCP proxy you run server-side — and feed bytes to the `PeerManager` yourself. (Node.js users can use the separate [`lightningdevkit-node-net`](https://www.npmjs.com/package/lightningdevkit-node-net) package instead.) + **What it's used for**: making peer connections, facilitating peer data to and from LDK - - - - - - - - +::: Connections to other peers are established with `PeerManager`. You'll need to know the pubkey and address of another node that you want as a peer. Once the connection is established and the handshake is complete, `PeerManager` will show the peer's pubkey in its list of peers. - - - - - - - - +::: **Dependencies:** `PeerManager` -**References:** [Rust `lightning-net-tokio` docs](https://docs.rs/lightning-net-tokio/*/lightning_net_tokio/), [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Java/Kotlin `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/batteries/NioPeerHandler.java), -[Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java), +**References:** [Rust `lightning-net-tokio` docs](https://docs.rs/lightning-net-tokio/0.2.0/lightning_net_tokio/), [Rust `PeerManager` docs](https://docs.rs/lightning/0.2.2/lightning/ln/peer_handler/struct.PeerManager.html), [Java/Kotlin `NioPeerHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/batteries/NioPeerHandler.java), [Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/PeerManager.java), [TypeScript `SocketDescriptor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/SocketDescriptor.mts) diff --git a/docs/building-a-node-with-ldk/handling-events.md b/docs/building-a-node-with-ldk/handling-events.md index 00b3c3b79..044afcc9f 100644 --- a/docs/building-a-node-with-ldk/handling-events.md +++ b/docs/building-a-node-with-ldk/handling-events.md @@ -4,69 +4,69 @@ LDK requires that you handle many different events throughout your app's life cy To start handling events in your application, run: - - - - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +// There is no BackgroundProcessor in the TypeScript bindings — pull events +// yourself by passing an EventHandler to `process_pending_events`. +const handler = ldk.EventHandler.new_impl({ + handle_event(event: ldk.Event): ldk.Result_NoneReplayEventZ { + if (event instanceof ldk.Event_PaymentSent) { + // Handle successful payment + } else if (event instanceof ldk.Event_PaymentFailed) { + // Handle failed payment + } else if (event instanceof ldk.Event_FundingGenerationReady) { + // Create a funding tx to be broadcast + } + return ldk.Result_NoneReplayEventZ.constructor_ok(); + }, +} as ldk.EventHandlerInterface); + +// Call this whenever the channel manager / chain monitor signals work is pending. +channelManager.as_EventsProvider().process_pending_events(handler); +chainMonitor.as_EventsProvider().process_pending_events(handler); +``` - +::: -References: [Rust `Event` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java) +References: [Rust `Event` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/Event.mts) diff --git a/docs/building-a-node-with-ldk/installation.md b/docs/building-a-node-with-ldk/installation.md index c8ada95cc..29994988c 100644 --- a/docs/building-a-node-with-ldk/installation.md +++ b/docs/building-a-node-with-ldk/installation.md @@ -8,27 +8,23 @@ If you have any questions about anything related to LDK, feel free to ask our co Add LDK to a project by configuring the respective config files: - - - - +```bash [TypeScript] +# Install the LDK WASM bindings from npm, replacing {VERSION} with the version +# you want. Pin an explicit version: the npm `latest` tag lags the releases +# (e.g. use 0.2.0-0). The bindings are pure ESM and require a one-time async +# WASM init before use (see below). - +npm install lightningdevkit@{VERSION} +``` + +::: Example usage after installation is complete: - - - - - - - -::: tip Installing LDK Swift -Add ldk-swift package using the Swift Package Manager. +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; -To import the package, use: `import LightningDevKit` +// Load the WASM once, before using any API: +await ldk.initializeWasmWebFetch("/liblightningjs.wasm"); +``` ::: diff --git a/docs/building-a-node-with-ldk/introduction.md b/docs/building-a-node-with-ldk/introduction.md index a169a58fc..92c965086 100644 --- a/docs/building-a-node-with-ldk/introduction.md +++ b/docs/building-a-node-with-ldk/introduction.md @@ -18,19 +18,19 @@ The following tutorials will show you how to build the simplest lightning node u Let's start by looking at the core components we'll need to make this node work for the tasks we outlined above. -1. A [`ChannelManager`](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManager.html), to open and close channels. -2. A networking stack (https://docs.rs/lightning-net-tokio/*/lightning_net_tokio/index.html), for establishing TCP/IP connections to other nodes on the lightning network. +1. A [`ChannelManager`](https://docs.rs/lightning/0.2.2/lightning/ln/channelmanager/struct.ChannelManager.html), to open and close channels. +2. A networking stack (https://docs.rs/lightning-net-tokio/0.2.0/lightning_net_tokio/index.html), for establishing TCP/IP connections to other nodes on the lightning network. 3. Payments & routing, ability to create and pay invoices. To make the above work we also need to setup a series of supporting modules, including: -1. A [`FeeEstimator`](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.FeeEstimator.html) -2. A [`Logger`](https://docs.rs/lightning/*/lightning/util/logger/index.html) -3. A Transaction [`Broadcaster`](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.BroadcasterInterface.html) -4. A [`NetworkGraph`](https://docs.rs/lightning/*/lightning/routing/gossip/struct.NetworkGraph.html) -5. A [`Persister`](https://docs.rs/lightning/*/lightning/util/persist/trait.Persister.html) -6. An [`EventHandler`](https://docs.rs/lightning/*/lightning/events/trait.EventHandler.html) -7. A Transaction [`Filter`](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html) -8. A [`ChainMonitor`](https://docs.rs/lightning/*/lightning/chain/chainmonitor/index.html) -9. A [`KeysManager`](https://docs.rs/lightning/*/lightning/sign/struct.KeysManager.html) -10. A [`Scorer`](https://docs.rs/lightning/*/lightning/routing/scoring/index.html) +1. A [`FeeEstimator`](https://docs.rs/lightning/0.2.2/lightning/chain/chaininterface/trait.FeeEstimator.html) +2. A [`Logger`](https://docs.rs/lightning/0.2.2/lightning/util/logger/index.html) +3. A Transaction [`Broadcaster`](https://docs.rs/lightning/0.2.2/lightning/chain/chaininterface/trait.BroadcasterInterface.html) +4. A [`NetworkGraph`](https://docs.rs/lightning/0.2.2/lightning/routing/gossip/struct.NetworkGraph.html) +5. A [`Persister`](https://docs.rs/lightning/0.2.2/lightning/util/persist/trait.Persister.html) +6. An [`EventHandler`](https://docs.rs/lightning/0.2.2/lightning/events/trait.EventHandler.html) +7. A Transaction [`Filter`](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Filter.html) +8. A [`ChainMonitor`](https://docs.rs/lightning/0.2.2/lightning/chain/chainmonitor/index.html) +9. A [`KeysManager`](https://docs.rs/lightning/0.2.2/lightning/sign/struct.KeysManager.html) +10. A [`Scorer`](https://docs.rs/lightning/0.2.2/lightning/routing/scoring/index.html) diff --git a/docs/building-a-node-with-ldk/opening-a-channel.md b/docs/building-a-node-with-ldk/opening-a-channel.md index eca962cb7..bab6a2c52 100644 --- a/docs/building-a-node-with-ldk/opening-a-channel.md +++ b/docs/building-a-node-with-ldk/opening-a-channel.md @@ -11,73 +11,66 @@ Now that you have a peer, you can open a channel with them using `ChannelManager Channels can be announced to the network or can remain private, which is controlled via `UserConfig::announced_channel`. - - - - - - - - +::: # FundingGenerationReady Event Handling @@ -89,42 +82,41 @@ Remember that the funding transaction must only spend [SegWit](https://bitcoinop ::: - - - - - - - - +::: -**References:** [Rust `FundingGenerationReady` docs](https://docs.rs/lightning/*/lightning/util/events/enum.Event.html#variant.FundingGenerationReady), [Java `FundingGenerationReady` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L95) +**References:** [Rust `FundingGenerationReady` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.FundingGenerationReady), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/Event.mts) # Broadcasting the Funding Transaction After crafting the funding transaction you'll need to send it to the Bitcoin network where it will hopefully be mined and added to the blockchain. You'll need to watch this transaction and wait for a minimum of 6 confirmations before the channel is ready to use. - - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; - - - +::: -**References:** [Rust `BroadcasterInterface` docs](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.BroadcasterInterface.html), [Java/Kotlin `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/BroadcasterInterface.java) +**References:** [Rust `BroadcasterInterface` docs](https://docs.rs/lightning/0.2.2/lightning/chain/chaininterface/trait.BroadcasterInterface.html), [Java/Kotlin `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/BroadcasterInterface.java), [TypeScript `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/BroadcasterInterface.mts) ::: tip Keep LDK in sync diff --git a/docs/building-a-node-with-ldk/receiving-payments.md b/docs/building-a-node-with-ldk/receiving-payments.md index 1cf525ddd..1b9e6824d 100644 --- a/docs/building-a-node-with-ldk/receiving-payments.md +++ b/docs/building-a-node-with-ldk/receiving-payments.md @@ -5,20 +5,21 @@ amount and description. `ChannelManager` contains the remaining information needed for the invoice. Use the provided utility to generate an invoice and register a pending payment in `ChannelManager`. - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; - - +::: While it is possible to create an invoice without using the utility, `ChannelManager` will reject any incoming HTLCs for unregistered payments to protect your privacy. In this case, use either `create_inbound_payment` or `create_inbound_payment_for_hash` to register a payment with `ChannelManager` before creating the invoice with the returned payment hash and/or secret. -You might also opt to for `inbound_payment`, useful for generating invoices for [phantom node payments](https://docs.rs/lightning/*/lightning/sign/struct.PhantomKeysManager.html) without a ChannelManager. +You might also opt to for `inbound_payment`, useful for generating invoices for [phantom node payments](https://docs.rs/lightning/0.2.2/lightning/sign/struct.PhantomKeysManager.html) without a ChannelManager. # PaymentClaimable Event Handling @@ -98,61 +85,52 @@ As with sending a payment, LDK will generate an event once a payment is received. It is your responsibility to handle the `PaymentClaimable` event by using `ChannelManager` to release the preimage and claim the funds. - - - - - - +::: -**References:** [Rust `PaymentClaimable` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html#variant.PaymentClaimable), [Java/Kotlin `PaymentClaimable` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L261) +**References:** [Rust `PaymentClaimable` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.PaymentClaimable), [Rust `PaymentPurpose` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.PaymentPurpose.html), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/Event.mts) diff --git a/docs/building-a-node-with-ldk/sending-payments.md b/docs/building-a-node-with-ldk/sending-payments.md index 74e16cbd2..e4adff0b5 100644 --- a/docs/building-a-node-with-ldk/sending-payments.md +++ b/docs/building-a-node-with-ldk/sending-payments.md @@ -5,93 +5,88 @@ string in accordance with BOLT 11. After parsing the invoice, you'll need to find a route from your node to the recipient and then make the payment using `ChannelManager`. - - - - - - - +::: # PaymentSent & PaymentFailed Event Handling @@ -100,51 +95,46 @@ in a `PaymentSent` event with the preimage of the payment hash. Be sure to look out for a `PaymentFailed` event, if the payment fails for some reason, and act accordingly. - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; - - - +::: -**References:** [Rust `PaymentSent` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html#variant.PaymentSent),[Rust `PaymentFailed` docs](https://docs.rs/lightning/*/lightning/events/enum.Event.html#variant.PaymentFailed), [Java/Kotlin `PaymentSent` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L464), [Java/Kotlin `PaymentFailed` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Event.java#L512) \ No newline at end of file +**References:** [Rust `PaymentSent` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.PaymentSent), [Rust `PaymentFailed` docs](https://docs.rs/lightning/0.2.2/lightning/events/enum.Event.html#variant.PaymentFailed), [Java/Kotlin `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Event.java), [TypeScript `Event` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/Event.mts) \ No newline at end of file diff --git a/docs/building-a-node-with-ldk/setting-up-a-channel-manager.md b/docs/building-a-node-with-ldk/setting-up-a-channel-manager.md index 47b917f47..11c6e0cef 100644 --- a/docs/building-a-node-with-ldk/setting-up-a-channel-manager.md +++ b/docs/building-a-node-with-ldk/setting-up-a-channel-manager.md @@ -6,33 +6,31 @@ The `ChannelManager` is responsible for several tasks related to managing channe Adding a `ChannelManager` to your application should look something like this: - - - - - - +// The TypeScript bindings have no ChannelManagerConstructor helper — call the +// raw constructor. (The sections below build each dependency.) +const params = ldk.ChainParameters.constructor_new( + ldk.Network.LDKNetwork_Regtest, + ldk.BestBlock.constructor_from_network(ldk.Network.LDKNetwork_Regtest) +); +const messageRouter = ldk.DefaultMessageRouter.constructor_new( + networkGraph, + keysManager.as_EntropySource() +); - +const channelManager = ldk.ChannelManager.constructor_new( + feeEstimator, + chainMonitor.as_Watch(), + txBroadcaster, + router.as_Router(), + messageRouter.as_MessageRouter(), + logger, + keysManager.as_EntropySource(), + keysManager.as_NodeSigner(), + keysManager.as_SignerProvider(), + userConfig, + params, + Math.floor(Date.now() / 1000) // current_timestamp (seconds) +); +``` + +::: There are a few dependencies needed to get this working. Let's walk through setting up each one so we can plug them into our `ChannelManager`. @@ -92,88 +89,71 @@ There are a few dependencies needed to get this working. Let's walk through sett **What it's used for:** estimating fees for on-chain transactions that LDK wants broadcasted. - - - - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +const feeEstimator = ldk.FeeEstimator.new_impl({ + get_est_sat_per_1000_weight(target: ldk.ConfirmationTarget): number { + // Return your own feerates per ConfirmationTarget (floor is 253). + switch (target) { + case ldk.ConfirmationTarget.LDKConfirmationTarget_UrgentOnChainSweep: + return 5000; + case ldk.ConfirmationTarget.LDKConfirmationTarget_ChannelCloseMinimum: + return 253; + default: + return 2000; + } + }, +} as ldk.FeeEstimatorInterface); +``` - +::: **Implementation notes:** 1. Fees must be returned in: satoshis per 1000 weight units @@ -183,67 +163,68 @@ retrieving fresh ones every time **Dependencies:** *none* -**References:** [Rust `FeeEstimator` docs](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.FeeEstimator.html), [Java/Kotlin `FeeEstimator` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/FeeEstimator.java) +**References:** [Rust `FeeEstimator` docs](https://docs.rs/lightning/0.2.2/lightning/chain/chaininterface/trait.FeeEstimator.html), [Java/Kotlin `FeeEstimator` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/FeeEstimator.java) ### Initialize the `Router` **What it's used for:** Finds a Route for a payment between the given payer and a payee. - - +::: code-group - +```kotlin [Kotlin] +// If you use the ChannelManagerConstructor it builds the router for you. The +// NetworkGraph it needs is created like so: +val networkGraph = NetworkGraph.of(Network.LDKNetwork_Regtest, logger) +``` - +const router = ldk.DefaultRouter.constructor_new( + networkGraph, + logger, + keysManager.as_EntropySource(), + multiThreadedScorer.as_LockableScore(), + ldk.ProbabilisticScoringFeeParameters.constructor_default() +); +``` - +::: **Dependencies:** `P2PGossipSync`, `Logger`, `KeysManager`, `Scorer` -**References:** [Rust `Router` docs](https://docs.rs/lightning/*/lightning/routing/router/trait.Router.html), [Java/Kotlin `Router` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Router.java) +**References:** [Rust `Router` docs](https://docs.rs/lightning/0.2.2/lightning/routing/router/trait.Router.html), [Java/Kotlin `Router` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Router.java) ### Initialize the `Logger` **What it's used for:** LDK logging - - - - - - +const logger = ldk.Logger.new_impl({ + log(record: ldk.Record): void { + console.log(`${record.get_level()} [${record.get_module_path()}] ${record.get_args()}`); + }, +} as ldk.LoggerInterface); +``` - +::: **Implementation notes:** you'll likely want to write the logs to a file for debugging purposes. **Dependencies:** *none* -**References:** [Rust `Logger` docs](https://docs.rs/lightning/*/lightning/util/logger/trait.Logger.html), [Java/Kotlin `Logger` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Logger.java) +**References:** [Rust `Logger` docs](https://docs.rs/lightning/0.2.2/lightning/util/logger/trait.Logger.html), [Java/Kotlin `Logger` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Logger.java) ### Initialize the `BroadcasterInterface` **What it's used for:** broadcasting various transactions to the bitcoin network - - - - - - +const txBroadcaster = ldk.BroadcasterInterface.new_impl({ + broadcast_transactions(txs: Uint8Array[]): void { + // + }, +} as ldk.BroadcasterInterfaceInterface); +``` - +::: **Dependencies:** _none_ -**References:** [Rust `BroadcasterInterface` docs](https://docs.rs/lightning/*/lightning/chain/chaininterface/trait.BroadcasterInterface.html), [Java/Kotlin `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/BroadcasterInterface.java) +**References:** [Rust `BroadcasterInterface` docs](https://docs.rs/lightning/0.2.2/lightning/chain/chaininterface/trait.BroadcasterInterface.html), [Java/Kotlin `BroadcasterInterface` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/BroadcasterInterface.java) ### Initialize `Persist` **What it's used for:** persisting `ChannelMonitor`s, which contain crucial channel data, in a timely manner - - - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +const persister = ldk.Persist.new_impl({ + persist_new_channel(id: ldk.OutPoint, data: ldk.ChannelMonitor): ldk.ChannelMonitorUpdateStatus { + // + return ldk.ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed; + }, + update_persisted_channel(id: ldk.OutPoint, update: ldk.ChannelMonitorUpdate, data: ldk.ChannelMonitor): ldk.ChannelMonitorUpdateStatus { + return ldk.ChannelMonitorUpdateStatus.LDKChannelMonitorUpdateStatus_Completed; + }, + get_and_clear_completed_updates(): ldk.TwoTuple_ChannelIdu64Z[] { + return []; + }, +} as ldk.PersistInterface); +``` - +::: - - - +::: **Implementation notes:** @@ -452,46 +409,89 @@ let persister = FilesystemPersister::new(ldk_data_dir_path); **Dependencies:** _none_ -**References:** [Rust `Persister` docs](https://docs.rs/lightning/*/lightning/chain/chainmonitor/trait.Persist.html), [Java/Kotlin `Persister` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Persist.java) +**References:** [Rust `Persister` docs](https://docs.rs/lightning/0.2.2/lightning/chain/chainmonitor/trait.Persist.html), [Java/Kotlin `Persister` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Persist.java) ### Start Background Processing **What it's used for:** running tasks periodically in the background to keep LDK operational. - - - +::: -**Dependencies:** `ChannelManager`, `ChainMonitor`, `PeerManager`, `Logger` +**Dependencies:** `ChannelManager`, `ChainMonitor`, `PeerManager`, `Logger`, `Persister`/`KVStore` (plus, in Rust, the `OnionMessenger`, `GossipSync`, and optionally `Scorer`/`OutputSweeper`) -**References:** [Rust `BackgroundProcessor::Start` docs](https://docs.rs/lightning-background-processor/*/lightning_background_processor/struct.BackgroundProcessor.html#method.start) +**References:** [Rust `process_events_async` docs](https://docs.rs/lightning-background-processor/0.2.0/lightning_background_processor/fn.process_events_async.html), [Java/Kotlin `ChannelManagerConstructor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/batteries/ChannelManagerConstructor.java) ### Regularly Broadcast Node Announcement **What it's used for:** if you have 1 or more public channels, you may need to announce your node and its channels occasionally. LDK will automatically announce channels when they are created, but there are no guarantees you have connected peers at that time or that your peers will propagate such announcements. The broader node-announcement message is not automatically broadcast. - - - +::: **Dependencies:** `Peer Manager` -**References:** [`PeerManager::broadcast_node_announcement` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html#method.broadcast_node_announcement) +**References:** [`PeerManager::broadcast_node_announcement` docs](https://docs.rs/lightning/0.2.2/lightning/ln/peer_handler/struct.PeerManager.html#method.broadcast_node_announcement) ### Optional: Initialize the Transaction `Filter` @@ -514,10 +513,9 @@ i.e. if you're using BIP 157/158 or Electrum as your chain backend **What it's used for:** if you are not providing full blocks, LDK uses this object to tell you what transactions and outputs to watch for on-chain. - - - - - - - - +::: -**Implementation notes:** see the [Blockchain Data](/blockchain_data/introduction.md) guide for more info +**Implementation notes:** see the [Blockchain Data](/blockchain_data/) guide for more info **Dependencies:** _none_ -**References:** [Rust `Filter` docs](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html), [Java/Kotlin `Filter` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/Filter.java) +**References:** [Rust `Filter` docs](https://docs.rs/lightning/0.2.2/lightning/chain/trait.Filter.html), [Java/Kotlin `Filter` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/Filter.java) ### Initialize the `ChainMonitor` **What it's used for:** tracking one or more `ChannelMonitor`s and using them to monitor the chain for lighting transactions that are relevant to our node, and broadcasting transactions if need be. - - - - - - - - +::: **Implementation notes:** `Filter` must be non-`None` if you're using Electrum or BIP 157/158 as your chain backend @@ -631,16 +627,15 @@ let chainMonitor = ChainMonitor( **Optional dependency:** `Filter` -**References:** [Rust `ChainMonitor` docs](https://docs.rs/lightning/*/lightning/chain/chainmonitor/struct.ChainMonitor.html), [Java/Kotlin `ChainMonitor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/ChainMonitor.java) +**References:** [Rust `ChainMonitor` docs](https://docs.rs/lightning/0.2.2/lightning/chain/chainmonitor/struct.ChainMonitor.html), [Java/Kotlin `ChainMonitor` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/ChainMonitor.java) ### Initialize the `KeysManager` **What it's used for:** providing keys for signing Lightning transactions - - - - - - - - +::: **Implementation notes:** @@ -718,32 +712,30 @@ let keysManager = KeysManager(seed: seed, startingTimeSecs: timestampSeconds, st **Dependencies:** random bytes -**References:** [Rust `KeysManager` docs](https://docs.rs/lightning/*/lightning/sign/struct.KeysManager.html), [Java/Kotlin `KeysManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/KeysManager.java) +**References:** [Rust `KeysManager` docs](https://docs.rs/lightning/0.2.2/lightning/sign/struct.KeysManager.html), [Java/Kotlin `KeysManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/KeysManager.java) ### Read `ChannelMonitor` state from disk **What it's used for:** if LDK is restarting and has at least 1 channel, its `ChannelMonitor`s will need to be (1) fed to the `ChannelManager` and (2) synced to chain. - - - - - - - +::: **Dependencies:** `KeysManager` -**References:** [Rust `load_outputs_to_watch` docs](https://docs.rs/lightning/*/lightning/chain/channelmonitor/struct.ChannelMonitor.html#method.load_outputs_to_watch) +**References:** [Rust `load_outputs_to_watch` docs](https://docs.rs/lightning/0.2.2/lightning/chain/channelmonitor/struct.ChannelMonitor.html#method.load_outputs_to_watch) ### Initialize the `ChannelManager` **What it's used for:** managing channel state - - - - - - - - +::: **Implementation notes:** No methods should be called on `ChannelManager` until _after_ the `ChannelMonitor`s and `ChannelManager` are synced to the chain tip (next step). **Dependencies:** `KeysManager`, `FeeEstimator`, `ChainMonitor`, `BroadcasterInterface`, `Logger` -**References:** [Rust `ChannelManager` docs](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManager.html), [Java/Kotlin `ChannelManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/ChannelManager.java) +**References:** [Rust `ChannelManager` docs](https://docs.rs/lightning/0.2.2/lightning/ln/channelmanager/struct.ChannelManager.html), [Java/Kotlin `ChannelManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/ChannelManager.java) ### Sync `ChannelMonitor`s and `ChannelManager` to chain tip @@ -937,10 +914,9 @@ _after_ the `ChannelMonitor`s and `ChannelManager` are synced to the chain tip ( **Example:** - - - - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +// The TypeScript bindings have no block-sync helper — drive the `Confirm` +// interface yourself. Both the ChannelManager and ChainMonitor implement it +// (via `as_Confirm()`); as you learn of confirmed/unconfirmed transactions and +// new tips from your chain source, forward them to both: +// +// const confirm = channelManager.as_Confirm(); +// confirm.transactions_confirmed(header, txdata, height); +// confirm.transaction_unconfirmed(txid); +// confirm.best_block_updated(header, height); +// +// Use `confirm.get_relevant_txids()` to learn which transactions to monitor for +// reorgs. Repeat for `chainMonitor.as_Confirm()`. +``` - +::: **Implementation notes:** @@ -1130,7 +1062,7 @@ If you are connecting full blocks or using BIP 157/158, then it is recommended t LDK's `lightning_block_sync` crate as in the example above: the high-level steps that must be done for both `ChannelManager` and each `ChannelMonitor` are as follows: 1. Get the last blockhash that each object saw. - - Receive the latest block hash when through [deserializtion](https://docs.rs/lightning/*/lightning/ln/channelmanager/struct.ChannelManagerReadArgs.html) of the `ChannelManager` via `read()` + - Receive the latest block hash when through [deserializtion](https://docs.rs/lightning/0.2.2/lightning/ln/channelmanager/struct.ChannelManagerReadArgs.html) of the `ChannelManager` via `read()` - Each `ChannelMonitor`'s is in `channel_manager.channel_monitors`, as the 2nd element in each tuple 2. For each object, if its latest known blockhash has been reorged out of the chain, then disconnect blocks using `channel_manager.as_Listen().block_disconnected(..)` or `channel_monitor.block_disconnected(..)` until you reach the last common ancestor with the main chain. 3. For each object, reconnect blocks starting from the common ancestor until it gets to your best known chain tip using `channel_manager.as_Listen().block_connected(..)` and/or `channel_monitor.block_connected(..)`. @@ -1146,98 +1078,62 @@ Alternatively, you can use LDK's `lightning-transaction-sync` crate. This provid **What it's used for:** generating routes to send payments over - - - - - - - - - +::: **Implementation notes:** this struct is not required if you are providing your own routes. It will be used internally in `ChannelManager` to build a `NetworkGraph`. Network options include: `Mainnet`,`Regtest`,`Testnet`,`Signet` **Dependencies:** `Logger` -**Optional dependency:** `Access`, a source of chain information. Recommended to be able to verify channels before adding them to the internal network graph. +**Optional dependency:** `UtxoLookup` (formerly `Access`), a source of chain information. Recommended to be able to verify channels before adding them to the internal network graph. -**References:** [Rust `P2PGossipSync` docs](https://docs.rs/lightning/*/lightning/routing/gossip/struct.P2PGossipSync.html), [`Access` docs](https://docs.rs/lightning/*/lightning/chain/trait.Access.html), [Java/Kotlin `P2PGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/P2PGossipSync.java), [Rust `RapidGossipSync` docs](https://docs.rs/lightning-rapid-gossip-sync/*/lightning_rapid_gossip_sync/), [Java/Kotlin `RapidGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RapidGossipSync.java) +**References:** [Rust `P2PGossipSync` docs](https://docs.rs/lightning/0.2.2/lightning/routing/gossip/struct.P2PGossipSync.html), [`UtxoLookup` docs](https://docs.rs/lightning/0.2.2/lightning/routing/utxo/trait.UtxoLookup.html), [Java/Kotlin `P2PGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/P2PGossipSync.java), [Rust `RapidGossipSync` docs](https://docs.rs/lightning-rapid-gossip-sync/0.2.0/lightning_rapid_gossip_sync/), [Java/Kotlin `RapidGossipSync` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/RapidGossipSync.java) ### Optional: Initialize `Probabilistic Scorer` **What it's used for**: to find a suitable payment path to reach the destination. - - - - - - - - - +::: **Dependencies:** `NetworkGraph` -**References:** [Rust `ProbabilisticScorer` docs](https://docs.rs/lightning/*/lightning/routing/scoring/struct.ProbabilisticScorer.html), [Java/Kotlin `ProbabilisticScorer` docs](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/ProbabilisticScorer.java) \ No newline at end of file +**References:** [Rust `ProbabilisticScorer` docs](https://docs.rs/lightning/0.2.2/lightning/routing/scoring/struct.ProbabilisticScorer.html), [Java/Kotlin `ProbabilisticScorer` docs](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/ProbabilisticScorer.java) \ No newline at end of file diff --git a/docs/building-a-node-with-ldk/setting-up-a-peer-manager.md b/docs/building-a-node-with-ldk/setting-up-a-peer-manager.md index 0c4619b61..061c4ef80 100644 --- a/docs/building-a-node-with-ldk/setting-up-a-peer-manager.md +++ b/docs/building-a-node-with-ldk/setting-up-a-peer-manager.md @@ -6,60 +6,65 @@ The `PeerManager` is responsible for managing a set of peer connections and data To add a `PeerManager` to your application, run: - - +```java [Kotlin] +import org.ldk.structs.PeerManager - - - +```typescript [TypeScript] +import * as ldk from "lightningdevkit"; + +// In TypeScript there is no MessageHandler wrapper — pass each handler to +// PeerManager.constructor_new directly. Use IgnoringMessageHandler for any +// role you don't need (here: routing, onion and custom messages). +const ignorer = ldk.IgnoringMessageHandler.constructor_new(); + +const peerManager = ldk.PeerManager.constructor_new( + channelManager.as_ChannelMessageHandler(), + ignorer.as_RoutingMessageHandler(), // or gossipSync.as_RoutingMessageHandler() + ignorer.as_OnionMessageHandler(), // or onionMessenger.as_OnionMessageHandler() + ignorer.as_CustomMessageHandler(), + chainMonitor.as_SendOnlyMessageHandler(), + Math.floor(Date.now() / 1000), // current time / nonce + keysManager.as_EntropySource().get_secure_random_bytes(), // 32 ephemeral bytes + logger, + keysManager.as_NodeSigner() +); +``` - +::: -**Implementation notes:** if you did not initialize `P2PGossipSync` in the previous step, you can initialize your own struct (which can be a dummy struct) that implements `RoutingMessageHandler` +**Implementation notes:** if you did not initialize `P2PGossipSync` in the previous step, you can pass an `IgnoringMessageHandler` (as shown in the TypeScript example) or your own struct implementing `RoutingMessageHandler` in its place. -**Dependencies:** `ChannelManager`, `RoutingMessageHandler`, `KeysManager`, random bytes, `Logger` +**Dependencies:** `ChannelManager`, `RoutingMessageHandler`, `ChainMonitor`, `KeysManager`, random bytes, `Logger` -**References:** [Rust `PeerManager` docs](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html), [Rust `RoutingMessageHandler` docs](https://docs.rs/lightning/*/lightning/ln/msgs/trait.RoutingMessageHandler.html), [Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/PeerManager.java), [Java/Kotlin `RoutingMessageHandler` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/main/src/main/java/org/ldk/structs/RoutingMessageHandler.java) +**References:** [Rust `PeerManager` docs](https://docs.rs/lightning/0.2.2/lightning/ln/peer_handler/struct.PeerManager.html), [Rust `MessageHandler` docs](https://docs.rs/lightning/0.2.2/lightning/ln/peer_handler/struct.MessageHandler.html), [Java/Kotlin `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/src/main/java/org/ldk/structs/PeerManager.java), [TypeScript `PeerManager` bindings](https://github.com/lightningdevkit/ldk-garbagecollected/blob/v0.2.0.0/ts/structs/PeerManager.mts) diff --git a/docs/case-studies.md b/docs/case-studies.md index 8460707cb..76965c3a4 100644 --- a/docs/case-studies.md +++ b/docs/case-studies.md @@ -1,435 +1,10 @@ --- -cases: true +title: Case Studies +description: Bitcoin applications and services building with LDK sidebar: false -tagline: "Bitcoin applications building with LDK" -description: "A list of bitcoin applications and services building with LDK" -actionText: "Add your project" -actionLink: "https://github.com/orgs/lightningdevkit/discussions/1554" -features: - - title: "Alby Hub" - details: "Equipped with its own lightning node, ready to connect you to numerous applications" - image: "/img/alby-hub-featured.svg" - imageAlt: "alby hub logo" - caseStudyLink: "/blog/alby-hub-uses-ldk-to-offer-a-self-custodial-lightning-wallet-for-everyone/" - - title: "Cash App" - details: "Send and spend, bank, and buy stocks or bitcoin " - image: "/img/cashapp-badge.svg" - imageAlt: "cash app logo" - caseStudyLink: "/blog/cashapp-enables-lightning-withdrawals-and-deposits-using-ldk/" - - title: "Bitkit" - details: "Hands you the keys to your money, profile, contacts, and web accounts" - image: "/img/bitkit.svg" - imageAlt: "" - caseStudyLink: "/blog/bitkit-uses-ldk-to-build-the-ultimate-alternative-to-custodial-wallets/" +aside: false editLink: false lastUpdated: false --- -

- Meet the projects building with LDK -

- - - - - - - - - - - - - - - - - + diff --git a/docs/examples.md b/docs/examples.md index c332fab6a..5f5386563 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -7,14 +7,6 @@ Click the links below and learn from community-built example projects This sample serves as a complete reference for constructing a lightning node with LDK. This is a good starting point if you want a self-guided tour. -### [Kotlin/Android Sample App](https://github.com/conorokus/umlando-wallet) - -This sample uses the Kotlin bindings provided by [ldk-garbagecollected](https://github.com/lightningdevkit/ldk-garbagecollected) to create a self-custodial mobile lightning wallet on Android. - -### [Swift/iOS Sample App](https://github.com/Roy0Anonymous/Vajra-Wallet) - -This sample uses the Swift bindings provided by [ldk-swift](https://github.com/lightningdevkit/ldk-swift) to create a self-custodial mobile lightning wallet on iOS. - ### [Rust node with sample Lightning Signer integration](https://gitlab.com/lightning-signer/lnrod/) A Rust lightning node implementation based on LDK and the Lightning Signer projects. Aims to be production ready at some point. @@ -22,31 +14,3 @@ A Rust lightning node implementation based on LDK and the Lightning Signer proje ### [Rust node with sample Tor integration](https://github.com/TonyGiorgio/ldk-sample-tor) A Rust lightning node sample implementation based on LDK with an embedded Tor daemon. - -### [LDK Node](https://github.com/lightningdevkit/ldk-node) - -A ready-to-go Lightning node library built using LDK and [BDK](https://bitcoindevkit.org/). - -### [LDK Node Rust Sample](https://github.com/optout21/ldk-node-sample) - -A sample lightning node command-line app built on top of LDK Node (similar to ldk-sample). - -### [LDK Node Swift Sample App](https://github.com/reez/Monday/tree/main/LDKNodeMonday) - -This sample uses [ldk-node-swift](https://github.com/lightningdevkit/ldk-node/blob/main/Package.swift) bindings to quickly build a self custodial lightning mobile wallet on iOS. Watch the video tutorial [here](https://www.youtube.com/watch?v=rcU3LU6iZCs). - -### [LDK Node Swift Sample app using Bitcoin Design Guide](https://github.com/bdgwallet/dailywallet) - -This sample is a good starting point for building a sample iOS wallet with a strong focus on user experience. - -### [LDK Node Flutter Sample App](https://github.com/LtbLightning/ldk-node-flutter-demo) - -This sample is a starting point for an LDK Node Flutter app. - -### [LDK Node React Native Sample App](https://github.com/LtbLightning/ldk-node-rn-demo) - -This sample is a starting point for an LDK Node React Native app. - -### [LDK Node Sample Desktop App](https://github.com/jbesraa/ldk-node-desktop) - -This sample is a desktop node manager for LDK Node. diff --git a/docs/fee_estimation.md b/docs/fee_estimation.md index eef33ee56..00b76d7cf 100644 --- a/docs/fee_estimation.md +++ b/docs/fee_estimation.md @@ -106,16 +106,16 @@ Once you've created the directory, open the ```Cargo.toml``` file, which Cargo u [package] name = "ldk-fee-estimator" version = "0.1.0" -edition = "2024" +edition = "2021" [dependencies] -lightning = { version = "0.0.124", features = ["max_level_trace"] } -lightning-block-sync = { version = "0.0.124", features = [ "rpc-client" ] } -lightning-invoice = { version = "0.31.0" } -lightning-net-tokio = { version = "0.0.124" } -lightning-persister = { version = "0.0.124" } -lightning-background-processor = { version = "0.0.124" } -lightning-rapid-gossip-sync = { version = "0.0.124" } +lightning = { version = "0.2.2", features = ["max_level_trace"] } +lightning-block-sync = { version = "0.2.0", features = [ "rpc-client" ] } +lightning-invoice = { version = "0.34.0" } +lightning-net-tokio = { version = "0.2.0" } +lightning-persister = { version = "0.2.0" } +lightning-background-processor = { version = "0.2.0" } +lightning-rapid-gossip-sync = { version = "0.2.0" } reqwest = { version = "0.11", features = ["json", "blocking"] } serde = { version = "1.0", features = ["derive"] } tokio = { version = "1", features = ["full"] } # Async runtime, required for reqwest diff --git a/docs/index.md b/docs/index.md index d456dccd6..586001662 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,49 +1,11 @@ --- -home: true -heroText: Build Lightning applications your way with LDK -tagline: A complete Lightning implementation packaged as an SDK -actionText: Get started -actionLink: /introduction/ -features: - - title: "Flexible and customizable " - details: "Persistence. Networking. Chain sourcing. Routing. Key management. You name it. LDK easily configures to your application’s needs." - image: "customizable" - - title: "High performance" - details: "As lightweight and compact as you need it to be, LDK performs on small-footprint devices and scales in the cloud." - image: "focus" - - title: "Any language, any platform" - details: "LDK provides a multi-language native API. Run a Lightning node on mobile, web, HSMs, LSPs, or your existing infrastructure." - image: "mobile" -promo: true -promoImage: "ldk-node" -promoTitle: "LDK Node" -promoDescription: "Build a mobile Lightning wallet fast with our ready-to-go solution" -promoActionText: "Get Started" -promoActionLink: "https://docs.rs/ldk-node/*/ldk_node/" ---- - -
-
-
- - - - -
-
-

Trusted by the best

-

Innovative projects are building with LDK

- All case studies -> -
-
-
+layout: home - +hero: + text: Build Lightning applications your way with LDK + tagline: A complete Lightning implementation packaged as an SDK + actions: + - theme: brand + text: Get started + link: /introduction/ +--- diff --git a/docs/introduction/architecture.md b/docs/introduction/architecture.md index ff56cfb3b..5f8188341 100644 --- a/docs/introduction/architecture.md +++ b/docs/introduction/architecture.md @@ -1,5 +1,6 @@ # Architecture -![Architecture](../assets/ldk-architecture.svg) +Architecture +Architecture Firstly, LDK is not a daemon, but rather its own implementation of the Lightning protocol written as an SDK and designed to be compiled and executed in your application. @@ -9,4 +10,4 @@ LDK also uses an event-driven architecture which allows for asynchronous result ::: tip Language Bindings -Although the core SDK is written in Rust, LDK supports many other programming languages. These include Java/Kotlin, Swift, JavaScript/TypeScript (Beta), C++ (Alpha). Check out [examples](../examples.md) to see some of the implementations out in the wild! +Although the core SDK is written in Rust, LDK supports many other programming languages. These include Java/Kotlin, JavaScript/TypeScript, C++ (Alpha). Check out [examples](../examples.md) to see some of the implementations out in the wild! diff --git a/docs/introduction/blockchain_data.md b/docs/introduction/blockchain_data.md index 79930cd44..4676b3587 100644 --- a/docs/introduction/blockchain_data.md +++ b/docs/introduction/blockchain_data.md @@ -2,6 +2,6 @@ LDK provides a simple `block_connected`/`block_disconnected` API which you pass block headers and transaction information to. LDK also provides an API for getting information about transactions it wishes to be informed of, which is compatible with Electrum server requests/neutrino filtering/etc. -[Blockchain Data Guide](https://lightningdevkit.org/blockchain_data/introduction/) +[Blockchain Data Guide](/blockchain_data/) [Sample module in Rust](https://github.com/rust-bitcoin/rust-lightning/tree/main/lightning-block-sync) diff --git a/docs/introduction/index.md b/docs/introduction/index.md index ec73f5fc5..effa1e0bb 100644 --- a/docs/introduction/index.md +++ b/docs/introduction/index.md @@ -16,11 +16,6 @@ These provide the most searchable and comprehensive documentation on LDK. If you're using Java and want more information on any method/struct/etc., searching the Rust docs for the Rust version of that struct/method is your best bet. -### [Swift LDK Documentation](https://github.com/arik-so/SwiftLightning/tree/master/Documentation) - -These docs are mainly geared towards how Swift could call LDK C bindings directly, but still may -provide a useful overview of Rust Lightning in the context of language bindings. - ### [Rust Sample Node](https://github.com/lightningdevkit/ldk-sample) The sample serves as a complete reference for constructing a Lightning node with diff --git a/docs/introduction/peer-management.md b/docs/introduction/peer-management.md index f43bfbccb..0b25ac375 100644 --- a/docs/introduction/peer-management.md +++ b/docs/introduction/peer-management.md @@ -1,5 +1,6 @@ # Peer Management -![Peer Management](../assets/ldk-peer-management.svg) +Peer Management +Peer Management One of the first things you'll need to do when building your own Lightning node is connect to a peer. In LDK this is handled by the [`PeerManager`](https://docs.rs/lightning/*/lightning/ln/peer_handler/struct.PeerManager.html). diff --git a/docs/introduction/transactions.md b/docs/introduction/transactions.md index 8988745f5..298d59829 100644 --- a/docs/introduction/transactions.md +++ b/docs/introduction/transactions.md @@ -6,7 +6,7 @@ Clients running a light client may wish to filter for transactions on a separate [LDK's `Filter` API reference](https://docs.rs/lightning/*/lightning/chain/trait.Filter.html) -More information is available in the [Blockchain Data guide](/blockchain_data/introduction). +More information is available in the [Blockchain Data guide](/blockchain_data/). ## Fee Estimation diff --git a/docs/introduction/use-cases.md b/docs/introduction/use-cases.md index 4208e3fc7..2776782cd 100644 --- a/docs/introduction/use-cases.md +++ b/docs/introduction/use-cases.md @@ -17,7 +17,7 @@ supports routing data being fetched via the Lightning P2P protocol, an external service, or routes can be calculated off-device. It also provides cross-platform compatibility for free, allowing synchronization of Lightning state across devices and, as long as there is protection from simultaneous updates, users to -access their wallet on any device. See the [architecure](../architecture) page for more +access their wallet on any device. See the [architecture](./architecture.md) page for more details on the interfaces LDK provides for integration. ## HSMs (Hardware Security Modules) diff --git a/docs/key_management.md b/docs/key_management.md index 8f012932b..83442a38d 100644 --- a/docs/key_management.md +++ b/docs/key_management.md @@ -1,53 +1,46 @@ # Key Management -LDK provides a simple default `KeysManager` implementation that takes a 32-byte seed for use as a BIP 32 extended key and derives keys from that. Check out the [Rust docs](https://docs.rs/lightning/*/lightning/sign/struct.KeysManager.html). +LDK provides a simple default `KeysManager` implementation that takes a 32-byte seed for use as a BIP 32 extended key and derives keys from that. Check out the [Rust docs](https://docs.rs/lightning/0.2.2/lightning/sign/struct.KeysManager.html). However, LDK also allows to customize the way key material and entropy are sourced through custom implementations of the `NodeSigner`, `SignerProvider`, and `EntropySource` traits located in `sign`. These traits include basic methods to provide public and private key material, as well as pseudorandom numbers. A `KeysManager` can be constructed simply with only a 32-byte seed and some random integers which ensure uniqueness across restarts (defined as `starting_time_secs` and `starting_time_nanos`): - - - - - - - - +::: # Creating a Unified Wallet @@ -60,10 +53,9 @@ Using a [BDK](https://bitcoindevkit.org/)-based wallet the steps would be as fol 3. Derive the private key at `m/535h` (or some other custom path). That's 32 bytes and is your starting entropy for your LDK wallet. 4. Optional: use a custom `SignerProvider` implementation to have the BDK wallet provide the destination and shutdown scripts (see [Spending On-Chain Funds](#spending-on-chain-funds)). - - - - - - - +::: ::: tip Protection for on-chain wallet @@ -139,7 +117,7 @@ An advantage to this approach is that the LDK entropy is contained within your i # Spending On-Chain Funds -When a channel has been closed and some outputs on chain are spendable only by us, LDK provides a `util::events::Event::SpendableOutputs` event in return from `ChannelMonitor::get_and_clear_pending_events()`. It contains a list of `sign::SpendableOutputDescriptor` objects which describe the output and provide all necessary information to spend it. +When a channel has been closed and some outputs on chain are spendable only by us, LDK provides a `events::Event::SpendableOutputs` event in return from `ChannelMonitor::get_and_clear_pending_events()`. It contains a list of `sign::SpendableOutputDescriptor` objects which describe the output and provide all necessary information to spend it. If you're using `KeysManager` directly, a utility method is provided which can generate a signed transaction given a list of ` SpendableOutputDescriptor` objects. `KeysManager::spend_spendable_outputs` can be called any time after receiving the `SpendableOutputDescriptor` objects to build a spending transaction, including delaying until sending funds to an external destination or opening a new channel. Note that if you open new channels directly with `SpendableOutputDescriptor` objects, you must ensure all closing/destination scripts provided to LDK are SegWit (either native or P2SH-wrapped). @@ -150,10 +128,18 @@ In order to make the outputs from channel closing spendable by a third-party wal For example, a wrapper based on BDK's [`Wallet`](https://docs.rs/bdk/*/bdk/wallet/struct.Wallet.html) could look like this: - - - - - - - +::: diff --git a/docs/probing.md b/docs/probing.md index 8baa414c0..920fb67a9 100644 --- a/docs/probing.md +++ b/docs/probing.md @@ -235,12 +235,15 @@ To instantiate the background processor, you need to pass in the required compon // by sending a signal that can be checked at appropriate intervals. let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(()); let mut background_processor = tokio::spawn(process_events_async( - Arc::clone(&persister), // Persister for writing/reading from file system. + Arc::clone(&persister), // KVStore for writing/reading from the file system. event_handler, // Handler for processing various LDK events. chain_monitor.clone(), // Monitors blocks for relevant activity. channel_manager.clone(), // Responsible for managing tasks related to channel state. + Some(onion_messenger.clone()), // Onion messenger (or None). GossipSync::p2p(gossip_sync.clone()), // Handles P2P network gossip. peer_manager.clone(), // Manages peer connections and associated data. + None, // Liquidity manager (NO_LIQUIDITY_MANAGER). + Some(output_sweeper.clone()), // Output sweeper (or None). logger.clone(), // Logger for recording events and errors. Some(scorer.clone()), // Scorer for updating and persisting scoring updates. move |t| { @@ -287,7 +290,7 @@ fn update_scorer<'a, S: 'static + Deref + Send + Sync, SC: 'a + Wri let mut score = scorer.write_lock(); score.probe_successful(path, duration_since_epoch); }, - Event::ailed { path, short_channel_id: Some(scid), .. } => { + Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => { let mut score = scorer.write_lock(); score.probe_failed(path, *scid, duration_since_epoch); }, diff --git a/docs/.vuepress/public/browserconfig.xml b/docs/public/browserconfig.xml similarity index 100% rename from docs/.vuepress/public/browserconfig.xml rename to docs/public/browserconfig.xml diff --git a/docs/.vuepress/public/card.png b/docs/public/card.png similarity index 100% rename from docs/.vuepress/public/card.png rename to docs/public/card.png diff --git a/docs/.vuepress/public/css/variables.css b/docs/public/css/variables.css similarity index 100% rename from docs/.vuepress/public/css/variables.css rename to docs/public/css/variables.css diff --git a/docs/.vuepress/public/favicon.ico b/docs/public/favicon.ico similarity index 100% rename from docs/.vuepress/public/favicon.ico rename to docs/public/favicon.ico diff --git a/docs/.vuepress/public/fonts/ibm-plex-mono-400.woff2 b/docs/public/fonts/ibm-plex-mono-400.woff2 similarity index 100% rename from docs/.vuepress/public/fonts/ibm-plex-mono-400.woff2 rename to docs/public/fonts/ibm-plex-mono-400.woff2 diff --git a/docs/public/img/10101.png b/docs/public/img/10101.png new file mode 100644 index 000000000..3daa50096 Binary files /dev/null and b/docs/public/img/10101.png differ diff --git a/docs/public/img/OM-dm-image-1.png b/docs/public/img/OM-dm-image-1.png new file mode 100644 index 000000000..c8f7ca9d1 Binary files /dev/null and b/docs/public/img/OM-dm-image-1.png differ diff --git a/docs/public/img/OM-dm-image-2.png b/docs/public/img/OM-dm-image-2.png new file mode 100644 index 000000000..2fe817099 Binary files /dev/null and b/docs/public/img/OM-dm-image-2.png differ diff --git a/docs/public/img/OM-dm-image-3.png b/docs/public/img/OM-dm-image-3.png new file mode 100644 index 000000000..66ffb916f Binary files /dev/null and b/docs/public/img/OM-dm-image-3.png differ diff --git a/docs/public/img/OM-dm-image-4.png b/docs/public/img/OM-dm-image-4.png new file mode 100644 index 000000000..ab01cb95d Binary files /dev/null and b/docs/public/img/OM-dm-image-4.png differ diff --git a/docs/public/img/OM-image-1.png b/docs/public/img/OM-image-1.png new file mode 100644 index 000000000..d3a79c48f Binary files /dev/null and b/docs/public/img/OM-image-1.png differ diff --git a/docs/public/img/OM-image-2.png b/docs/public/img/OM-image-2.png new file mode 100644 index 000000000..0bb41a34f Binary files /dev/null and b/docs/public/img/OM-image-2.png differ diff --git a/docs/public/img/OM-image-3.png b/docs/public/img/OM-image-3.png new file mode 100644 index 000000000..9197f1754 Binary files /dev/null and b/docs/public/img/OM-image-3.png differ diff --git a/docs/public/img/OM-image-4.png b/docs/public/img/OM-image-4.png new file mode 100644 index 000000000..431c454e1 Binary files /dev/null and b/docs/public/img/OM-image-4.png differ diff --git a/docs/public/img/abcd_ln_path.png b/docs/public/img/abcd_ln_path.png new file mode 100644 index 000000000..be266a005 Binary files /dev/null and b/docs/public/img/abcd_ln_path.png differ diff --git a/docs/public/img/aciedo.svg b/docs/public/img/aciedo.svg new file mode 100644 index 000000000..2fd57142a --- /dev/null +++ b/docs/public/img/aciedo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/public/img/alby-architecture.png b/docs/public/img/alby-architecture.png new file mode 100644 index 000000000..090353f64 Binary files /dev/null and b/docs/public/img/alby-architecture.png differ diff --git a/docs/.vuepress/public/img/alby-hub-featured.svg b/docs/public/img/alby-hub-featured.svg similarity index 100% rename from docs/.vuepress/public/img/alby-hub-featured.svg rename to docs/public/img/alby-hub-featured.svg diff --git a/docs/public/img/alby-logo-dark.png b/docs/public/img/alby-logo-dark.png new file mode 100644 index 000000000..725da09f3 Binary files /dev/null and b/docs/public/img/alby-logo-dark.png differ diff --git a/docs/public/img/alby-logo.webp b/docs/public/img/alby-logo.webp new file mode 100644 index 000000000..6f290dc3d Binary files /dev/null and b/docs/public/img/alby-logo.webp differ diff --git a/docs/public/img/alby.svg b/docs/public/img/alby.svg new file mode 100644 index 000000000..27c457374 --- /dev/null +++ b/docs/public/img/alby.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/public/img/atomic.png b/docs/public/img/atomic.png new file mode 100644 index 000000000..621c22e9f Binary files /dev/null and b/docs/public/img/atomic.png differ diff --git a/docs/public/img/atomicdex.svg b/docs/public/img/atomicdex.svg new file mode 100644 index 000000000..3235dbe9b --- /dev/null +++ b/docs/public/img/atomicdex.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/img/bdk-logo.svg b/docs/public/img/bdk-logo.svg new file mode 100644 index 000000000..93916d4aa --- /dev/null +++ b/docs/public/img/bdk-logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/public/img/bdk-mark.svg b/docs/public/img/bdk-mark.svg new file mode 100644 index 000000000..778c98a77 --- /dev/null +++ b/docs/public/img/bdk-mark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/public/img/bitkit-architecture.png b/docs/public/img/bitkit-architecture.png new file mode 100644 index 000000000..317e2a5ca Binary files /dev/null and b/docs/public/img/bitkit-architecture.png differ diff --git a/docs/public/img/bitkit.png b/docs/public/img/bitkit.png new file mode 100644 index 000000000..1a25ce0af Binary files /dev/null and b/docs/public/img/bitkit.png differ diff --git a/docs/.vuepress/public/img/bitkit.svg b/docs/public/img/bitkit.svg similarity index 100% rename from docs/.vuepress/public/img/bitkit.svg rename to docs/public/img/bitkit.svg diff --git a/docs/public/img/blinded-path.svg b/docs/public/img/blinded-path.svg new file mode 100644 index 000000000..7939fcca5 --- /dev/null +++ b/docs/public/img/blinded-path.svg @@ -0,0 +1 @@ + diff --git a/docs/.vuepress/public/img/border.svg b/docs/public/img/border.svg similarity index 100% rename from docs/.vuepress/public/img/border.svg rename to docs/public/img/border.svg diff --git a/docs/public/img/c=.png b/docs/public/img/c=.png new file mode 100644 index 000000000..1c75d7db6 Binary files /dev/null and b/docs/public/img/c=.png differ diff --git a/docs/public/img/cash-app-architecture.svg b/docs/public/img/cash-app-architecture.svg new file mode 100644 index 000000000..66ee48454 --- /dev/null +++ b/docs/public/img/cash-app-architecture.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/img/cash-app-logo.png b/docs/public/img/cash-app-logo.png new file mode 100644 index 000000000..1a410b5a9 Binary files /dev/null and b/docs/public/img/cash-app-logo.png differ diff --git a/docs/.vuepress/public/img/cashapp-badge.svg b/docs/public/img/cashapp-badge.svg similarity index 100% rename from docs/.vuepress/public/img/cashapp-badge.svg rename to docs/public/img/cashapp-badge.svg diff --git a/docs/public/img/cashapp.svg b/docs/public/img/cashapp.svg new file mode 100644 index 000000000..3e2ee39e8 --- /dev/null +++ b/docs/public/img/cashapp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/img/etta.png b/docs/public/img/etta.png new file mode 100644 index 000000000..d5714a130 Binary files /dev/null and b/docs/public/img/etta.png differ diff --git a/docs/.vuepress/public/img/favicon/android-chrome-192x192.png b/docs/public/img/favicon/android-chrome-192x192.png similarity index 100% rename from docs/.vuepress/public/img/favicon/android-chrome-192x192.png rename to docs/public/img/favicon/android-chrome-192x192.png diff --git a/docs/.vuepress/public/img/favicon/android-chrome-512x512.png b/docs/public/img/favicon/android-chrome-512x512.png similarity index 100% rename from docs/.vuepress/public/img/favicon/android-chrome-512x512.png rename to docs/public/img/favicon/android-chrome-512x512.png diff --git a/docs/.vuepress/public/img/favicon/apple-touch-icon.png b/docs/public/img/favicon/apple-touch-icon.png similarity index 100% rename from docs/.vuepress/public/img/favicon/apple-touch-icon.png rename to docs/public/img/favicon/apple-touch-icon.png diff --git a/docs/.vuepress/public/img/favicon/mstile-144x144.png b/docs/public/img/favicon/mstile-144x144.png similarity index 100% rename from docs/.vuepress/public/img/favicon/mstile-144x144.png rename to docs/public/img/favicon/mstile-144x144.png diff --git a/docs/.vuepress/public/img/favicon/mstile-150x150.png b/docs/public/img/favicon/mstile-150x150.png similarity index 100% rename from docs/.vuepress/public/img/favicon/mstile-150x150.png rename to docs/public/img/favicon/mstile-150x150.png diff --git a/docs/.vuepress/public/img/favicon/mstile-310x150.png b/docs/public/img/favicon/mstile-310x150.png similarity index 100% rename from docs/.vuepress/public/img/favicon/mstile-310x150.png rename to docs/public/img/favicon/mstile-310x150.png diff --git a/docs/.vuepress/public/img/favicon/mstile-310x310.png b/docs/public/img/favicon/mstile-310x310.png similarity index 100% rename from docs/.vuepress/public/img/favicon/mstile-310x310.png rename to docs/public/img/favicon/mstile-310x310.png diff --git a/docs/.vuepress/public/img/favicon/mstile-70x70.png b/docs/public/img/favicon/mstile-70x70.png similarity index 100% rename from docs/.vuepress/public/img/favicon/mstile-70x70.png rename to docs/public/img/favicon/mstile-70x70.png diff --git a/docs/.vuepress/public/img/fedimint.png b/docs/public/img/fedimint.png similarity index 100% rename from docs/.vuepress/public/img/fedimint.png rename to docs/public/img/fedimint.png diff --git a/docs/public/img/gatewayd-arch-ldk2.png b/docs/public/img/gatewayd-arch-ldk2.png new file mode 100644 index 000000000..83a5e292e Binary files /dev/null and b/docs/public/img/gatewayd-arch-ldk2.png differ diff --git a/docs/public/img/gatewayd-arch.png b/docs/public/img/gatewayd-arch.png new file mode 100644 index 000000000..0e26454a9 Binary files /dev/null and b/docs/public/img/gatewayd-arch.png differ diff --git a/docs/public/img/github-white.png b/docs/public/img/github-white.png new file mode 100644 index 000000000..9e7c3cf66 Binary files /dev/null and b/docs/public/img/github-white.png differ diff --git a/docs/public/img/github.png b/docs/public/img/github.png new file mode 100644 index 000000000..342ca9c1c Binary files /dev/null and b/docs/public/img/github.png differ diff --git a/docs/public/img/hydranet.png b/docs/public/img/hydranet.png new file mode 100644 index 000000000..d38d848b3 Binary files /dev/null and b/docs/public/img/hydranet.png differ diff --git a/docs/.vuepress/public/img/illustrations.svg b/docs/public/img/illustrations.svg similarity index 98% rename from docs/.vuepress/public/img/illustrations.svg rename to docs/public/img/illustrations.svg index f7ad9610e..9a377e27a 100644 --- a/docs/.vuepress/public/img/illustrations.svg +++ b/docs/public/img/illustrations.svg @@ -11,7 +11,7 @@ - + diff --git a/docs/public/img/kumuly.png b/docs/public/img/kumuly.png new file mode 100644 index 000000000..9a56a7f25 Binary files /dev/null and b/docs/public/img/kumuly.png differ diff --git a/docs/public/img/kuutamo.png b/docs/public/img/kuutamo.png new file mode 100644 index 000000000..d99e6ec88 Binary files /dev/null and b/docs/public/img/kuutamo.png differ diff --git a/docs/public/img/ldk-block-processing.svg b/docs/public/img/ldk-block-processing.svg new file mode 100644 index 000000000..657f91127 --- /dev/null +++ b/docs/public/img/ldk-block-processing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/img/ldk-roadmap.png b/docs/public/img/ldk-roadmap.png new file mode 100644 index 000000000..0e3347223 Binary files /dev/null and b/docs/public/img/ldk-roadmap.png differ diff --git a/docs/public/img/lexe-logo.jpg b/docs/public/img/lexe-logo.jpg new file mode 100644 index 000000000..86c62c1bc Binary files /dev/null and b/docs/public/img/lexe-logo.jpg differ diff --git a/docs/public/img/lexe.png b/docs/public/img/lexe.png new file mode 100644 index 000000000..706f26896 Binary files /dev/null and b/docs/public/img/lexe.png differ diff --git a/docs/public/img/lightspark-logo.svg b/docs/public/img/lightspark-logo.svg new file mode 100644 index 000000000..d287b2e4e --- /dev/null +++ b/docs/public/img/lightspark-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/docs/public/img/lightspark.png b/docs/public/img/lightspark.png new file mode 100644 index 000000000..d871be6fa Binary files /dev/null and b/docs/public/img/lightspark.png differ diff --git a/docs/public/img/lightspark.svg b/docs/public/img/lightspark.svg new file mode 100644 index 000000000..8fb8325d1 --- /dev/null +++ b/docs/public/img/lightspark.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/public/img/lipa.png b/docs/public/img/lipa.png new file mode 100644 index 000000000..9f3392608 Binary files /dev/null and b/docs/public/img/lipa.png differ diff --git a/docs/public/img/lndk.png b/docs/public/img/lndk.png new file mode 100644 index 000000000..2889039e6 Binary files /dev/null and b/docs/public/img/lndk.png differ diff --git a/docs/.vuepress/public/img/logo.svg b/docs/public/img/logo.svg similarity index 98% rename from docs/.vuepress/public/img/logo.svg rename to docs/public/img/logo.svg index 998b74fb0..9f2e2bfb7 100644 --- a/docs/.vuepress/public/img/logo.svg +++ b/docs/public/img/logo.svg @@ -1,12 +1,12 @@ - + - + diff --git a/docs/public/img/lqwd.png b/docs/public/img/lqwd.png new file mode 100644 index 000000000..a86785aa3 Binary files /dev/null and b/docs/public/img/lqwd.png differ diff --git a/docs/public/img/lqwd_lsp_node.png b/docs/public/img/lqwd_lsp_node.png new file mode 100644 index 000000000..86561cbbb Binary files /dev/null and b/docs/public/img/lqwd_lsp_node.png differ diff --git a/docs/public/img/mercury.png b/docs/public/img/mercury.png new file mode 100644 index 000000000..2dff472f4 Binary files /dev/null and b/docs/public/img/mercury.png differ diff --git a/docs/.vuepress/public/img/mutiny-featured.png b/docs/public/img/mutiny-featured.png similarity index 100% rename from docs/.vuepress/public/img/mutiny-featured.png rename to docs/public/img/mutiny-featured.png diff --git a/docs/public/img/mutiny.png b/docs/public/img/mutiny.png new file mode 100644 index 000000000..33bf684b6 Binary files /dev/null and b/docs/public/img/mutiny.png differ diff --git a/docs/public/img/mutiny.svg b/docs/public/img/mutiny.svg new file mode 100644 index 000000000..c3a51afc3 --- /dev/null +++ b/docs/public/img/mutiny.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/public/img/offer-metadata.svg b/docs/public/img/offer-metadata.svg new file mode 100644 index 000000000..f28c04b54 --- /dev/null +++ b/docs/public/img/offer-metadata.svg @@ -0,0 +1 @@ + diff --git a/docs/public/img/offers-flow.svg b/docs/public/img/offers-flow.svg new file mode 100644 index 000000000..cd21e16fe --- /dev/null +++ b/docs/public/img/offers-flow.svg @@ -0,0 +1 @@ + diff --git a/docs/public/img/phantom-hints.png b/docs/public/img/phantom-hints.png new file mode 100644 index 000000000..86a50ed0e Binary files /dev/null and b/docs/public/img/phantom-hints.png differ diff --git a/docs/public/img/phantom-pmt.png b/docs/public/img/phantom-pmt.png new file mode 100644 index 000000000..503c62b47 Binary files /dev/null and b/docs/public/img/phantom-pmt.png differ diff --git a/docs/public/img/qr-codes.png b/docs/public/img/qr-codes.png new file mode 100644 index 000000000..0ee808ec6 Binary files /dev/null and b/docs/public/img/qr-codes.png differ diff --git a/docs/public/img/scoring_initial_histogram.png b/docs/public/img/scoring_initial_histogram.png new file mode 100644 index 000000000..d3daf4228 Binary files /dev/null and b/docs/public/img/scoring_initial_histogram.png differ diff --git a/docs/public/img/scoring_initial_rene_cdf.png b/docs/public/img/scoring_initial_rene_cdf.png new file mode 100644 index 000000000..8ab4bcc66 Binary files /dev/null and b/docs/public/img/scoring_initial_rene_cdf.png differ diff --git a/docs/public/img/scoring_modern_nonlinear_cdf.png b/docs/public/img/scoring_modern_nonlinear_cdf.png new file mode 100644 index 000000000..0b72b824e Binary files /dev/null and b/docs/public/img/scoring_modern_nonlinear_cdf.png differ diff --git a/docs/public/img/scoring_og_nonlinear_cdf.png b/docs/public/img/scoring_og_nonlinear_cdf.png new file mode 100644 index 000000000..bd8c17512 Binary files /dev/null and b/docs/public/img/scoring_og_nonlinear_cdf.png differ diff --git a/docs/public/img/scoring_real_histogram.png b/docs/public/img/scoring_real_histogram.png new file mode 100644 index 000000000..dae28b47a Binary files /dev/null and b/docs/public/img/scoring_real_histogram.png differ diff --git a/docs/public/img/scoring_results.png b/docs/public/img/scoring_results.png new file mode 100644 index 000000000..c10671ba7 Binary files /dev/null and b/docs/public/img/scoring_results.png differ diff --git a/docs/public/img/scoring_second_histogram.png b/docs/public/img/scoring_second_histogram.png new file mode 100644 index 000000000..cff94689c Binary files /dev/null and b/docs/public/img/scoring_second_histogram.png differ diff --git a/docs/public/img/scoring_truncated_og_cdf.png b/docs/public/img/scoring_truncated_og_cdf.png new file mode 100644 index 000000000..5667c5c0d Binary files /dev/null and b/docs/public/img/scoring_truncated_og_cdf.png differ diff --git a/docs/public/img/sensei-architecture.svg b/docs/public/img/sensei-architecture.svg new file mode 100644 index 000000000..355821928 --- /dev/null +++ b/docs/public/img/sensei-architecture.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/.vuepress/public/img/sensei-main.png b/docs/public/img/sensei-main.png similarity index 100% rename from docs/.vuepress/public/img/sensei-main.png rename to docs/public/img/sensei-main.png diff --git a/docs/public/img/sensei.png b/docs/public/img/sensei.png new file mode 100644 index 000000000..2c730f41a Binary files /dev/null and b/docs/public/img/sensei.png differ diff --git a/docs/public/img/teos-architecture-diagram.png b/docs/public/img/teos-architecture-diagram.png new file mode 100644 index 000000000..0c83143cc Binary files /dev/null and b/docs/public/img/teos-architecture-diagram.png differ diff --git a/docs/.vuepress/public/img/teos-main.png b/docs/public/img/teos-main.png similarity index 100% rename from docs/.vuepress/public/img/teos-main.png rename to docs/public/img/teos-main.png diff --git a/docs/public/img/teos.png b/docs/public/img/teos.png new file mode 100644 index 000000000..473931834 Binary files /dev/null and b/docs/public/img/teos.png differ diff --git a/docs/public/img/teos.svg b/docs/public/img/teos.svg new file mode 100644 index 000000000..650b4f1a5 --- /dev/null +++ b/docs/public/img/teos.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/img/valera.png b/docs/public/img/valera.png new file mode 100644 index 000000000..d6b1d47fc Binary files /dev/null and b/docs/public/img/valera.png differ diff --git a/docs/public/img/velas.png b/docs/public/img/velas.png new file mode 100644 index 000000000..930542ec7 Binary files /dev/null and b/docs/public/img/velas.png differ diff --git a/docs/public/img/vls.png b/docs/public/img/vls.png new file mode 100644 index 000000000..7146754f3 Binary files /dev/null and b/docs/public/img/vls.png differ diff --git a/docs/public/img/voltage.png b/docs/public/img/voltage.png new file mode 100644 index 000000000..ed10bca6d Binary files /dev/null and b/docs/public/img/voltage.png differ diff --git a/docs/public/img/vss-data-flow-simplified.svg b/docs/public/img/vss-data-flow-simplified.svg new file mode 100644 index 000000000..5e8f0085d --- /dev/null +++ b/docs/public/img/vss-data-flow-simplified.svg @@ -0,0 +1 @@ +VSS High Level Data FlowVSS High Level Data FlowMobileDeviceVSSStorageMobileDeviceMobileDeviceVSSVSSStorageStorageAuthentication Requestsome(authToken)loop[for every payment sent/received]Update LN Channel StateAtomic Update LN Channel StateSuccess \ No newline at end of file diff --git a/docs/.vuepress/public/llms.txt b/docs/public/llms.txt similarity index 100% rename from docs/.vuepress/public/llms.txt rename to docs/public/llms.txt diff --git a/docs/.vuepress/public/site.webmanifest b/docs/public/site.webmanifest similarity index 100% rename from docs/.vuepress/public/site.webmanifest rename to docs/public/site.webmanifest diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 000000000..5c331a40c --- /dev/null +++ b/netlify.toml @@ -0,0 +1,9 @@ +# Build the VitePress site for Netlify deploy previews (and production). +# Overrides any build command/publish dir configured in the Netlify UI, +# which still pointed at the old VuePress pipeline. +[build] + command = "npm run build:vitepress" + publish = "docs/.vitepress/dist" + +[build.environment] + NODE_VERSION = "22" diff --git a/package-lock.json b/package-lock.json index 08077378e..a23a9e1b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,321 @@ "broken-link-checker": "0.7.8", "serve": "14.2.6", "start-server-and-test": "3.0.5", + "vitepress": "^1.5.0", "vue": "2.7.16", "vue-server-renderer": "2.7.16", "vuepress": "1.9.10" } }, + "node_modules/@algolia/abtesting": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.18.1.tgz", + "integrity": "sha512-aehCadlWOGvrT91KUIZpC0MbB8KBW9yUuvTJFd2xesR7le/IsT4nJUnjCCZ4ZqZCeTcPHPV5mo//fZ5oxcSVYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-core/node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-core/node_modules/algoliasearch": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", + "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/abtesting": "1.18.1", + "@algolia/client-abtesting": "5.52.1", + "@algolia/client-analytics": "5.52.1", + "@algolia/client-common": "5.52.1", + "@algolia/client-insights": "5.52.1", + "@algolia/client-personalization": "5.52.1", + "@algolia/client-query-suggestions": "5.52.1", + "@algolia/client-search": "5.52.1", + "@algolia/ingestion": "1.52.1", + "@algolia/monitoring": "1.52.1", + "@algolia/recommend": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights/node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights/node_modules/algoliasearch": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", + "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/abtesting": "1.18.1", + "@algolia/client-abtesting": "5.52.1", + "@algolia/client-analytics": "5.52.1", + "@algolia/client-common": "5.52.1", + "@algolia/client-insights": "5.52.1", + "@algolia/client-personalization": "5.52.1", + "@algolia/client-query-suggestions": "5.52.1", + "@algolia/client-search": "5.52.1", + "@algolia/ingestion": "1.52.1", + "@algolia/monitoring": "1.52.1", + "@algolia/recommend": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.52.1.tgz", + "integrity": "sha512-HmXOGBOAOJPounpBzBpuY0zDYeiCpxgHnQmuA7JO6ScukcBdGp3/XM9zJk5pJx/xNGD68mbPGXWpDxGtl6BwDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.52.1.tgz", + "integrity": "sha512-5oo4+I8iixie9vXhCyNFCzeIr8pqA3FQ//VsLHTDvZAV4ttYOPGvYHGQq5NSalrLx5Jc3dRro/5uDOlnUMcBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.52.1.tgz", + "integrity": "sha512-qCDoZfx5MpX7XQzvQ3bC4tSEMkQWQMaF/ABtLuoze03Y/flR563CCSws02qIJ23oX7lxl92LsilZjINVyTdtLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.52.1.tgz", + "integrity": "sha512-hnGs0/lsFJ2PWDxNBz7pxreXo/Xz7gxYRcfePBUjsH26ad0kU/sgnVZd9LwWBpsQv65z2jlb5dkyaB9WE9M9FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.52.1.tgz", + "integrity": "sha512-2VxxNc/uBysyKvGeBdSM5n9eIDKH8kWD7wd9/yqbJAiVwU4Yv6tU1LSJusHKrXV/aCu1KW7t9Gug9QyeEmtn/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.52.1.tgz", + "integrity": "sha512-O6mPtsw3xEfNOe6gWFpYLeAZAIljNa4Hgna3bq15PwyN7nbjTY0wXJFRbzs/0YVf75Br+SbOQUmjKxXYjDiSiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.52.1.tgz", + "integrity": "sha512-gA8oJOV1LnQQkDf91iebNnFInHuW0gRPEgLSOQ7EfipCEjYTHm5swm1DlH9H5RaRw4RrHuzHBegnlzc0MAstcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.52.1.tgz", + "integrity": "sha512-U9zZfc5xIu9wRxZkt+HceJUAD4VKHKbAyLSloJdEyMRmphXeibfrY9cxqIXBcmPeZzGhn3Imb35Dq8l19PkJhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.52.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.52.1.tgz", + "integrity": "sha512-a3SGNceHmkQfq77iG8Ka+w1pvwfZa/0lzEIgse30fL0kD+yKnd/dg0dQvSfFPAEt2f21DMcGkDSSeJlO3KdQjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.52.1.tgz", + "integrity": "sha512-z98QEguCFDpxb4S/PyrUK1igqF8tPsdbqOUUO6ON91vJ58w+Gwa6ncrI0oNXSFcrkxA5EqPKPQ2A1PBCn08TYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.52.1.tgz", + "integrity": "sha512-CI7+/0I11QeZM59Uc8whd2or0kqzFVjpaPn9Qpwll/krHcBAxk24WkAQ6WX+IwDVMfpont4YGbKwAmCre3vE8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.52.1.tgz", + "integrity": "sha512-S6bDuw9byfOvm3T71cgdoZgrgnZq6hpdMLkx52Louh57nUAmvGQESz2aojOynQHjbTiV55smvAFbgn0qT4tJrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.52.1.tgz", + "integrity": "sha512-tqZXM+54rWo4mk5jL5Z/flE11nPmNEdXwFBM5py9DkOmbjeCNemfVd45FyM97XdzfZ0dl9uOJC6PYn1FpkeyQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.52.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -1848,139 +2158,1099 @@ "deprecated": "Potential XSS vulnerability patched in v6.0.0.", "dev": true }, - "node_modules/@hapi/address": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", - "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^11.0.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@hapi/formula": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", - "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/hoek": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", - "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/pinpoint": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", - "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT" }, - "node_modules/@hapi/tlds": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz", - "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==", + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=14.0.0" + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" } }, - "node_modules/@hapi/topo": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", - "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@hapi/hoek": "^11.0.2" + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@docsearch/react/node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@docsearch/react/node_modules/algoliasearch": { + "version": "5.52.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", + "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", "dev": true, "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.18.1", + "@algolia/client-abtesting": "5.52.1", + "@algolia/client-analytics": "5.52.1", + "@algolia/client-common": "5.52.1", + "@algolia/client-insights": "5.52.1", + "@algolia/client-personalization": "5.52.1", + "@algolia/client-query-suggestions": "5.52.1", + "@algolia/client-search": "5.52.1", + "@algolia/ingestion": "1.52.1", + "@algolia/monitoring": "1.52.1", + "@algolia/recommend": "5.52.1", + "@algolia/requester-browser-xhr": "5.52.1", + "@algolia/requester-fetch": "5.52.1", + "@algolia/requester-node-http": "5.52.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">= 14.0.0" } }, - "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==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz", + "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.82", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.82.tgz", + "integrity": "sha512-4p978qHx8eD/QBOhgBzp/p7uS3OO2KCnVpFPJTUvuhuDXv1Hr4RcxcZ5MWc6ptkf/3Dlb1xb23068OtPyx10mA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", "dev": true, + "license": "MIT", "dependencies": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - }, - "engines": { - "node": ">=4" + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" } }, - "node_modules/@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", "dev": true, - "engines": { - "node": ">= 6" + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -2063,6 +3333,13 @@ "@types/node": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/express": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", @@ -2097,6 +3374,16 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/highlight.js": { "version": "9.12.4", "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", @@ -2126,10 +3413,11 @@ "license": "MIT" }, "node_modules/@types/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "10.0.3", @@ -2143,11 +3431,22 @@ "highlight.js": "^9.7.0" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.2", @@ -2228,6 +3527,20 @@ "source-map": "^0.6.1" } }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/webpack": { "version": "4.41.33", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz", @@ -2275,6 +3588,13 @@ "node": ">= 8" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@vssue/api-github-v3": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/@vssue/api-github-v3/-/api-github-v3-1.4.7.tgz", @@ -2697,6 +4017,76 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.34.tgz", + "integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.34.tgz", + "integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.34", + "@vue/shared": "3.5.34" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz", + "integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.34", + "@vue/runtime-core": "3.5.34", + "@vue/shared": "3.5.34", + "csstype": "^3.2.3" + } + }, "node_modules/@vue/shared": { "version": "3.5.34", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.34.tgz", @@ -3130,6 +4520,153 @@ "webpack-chain": "^6.0.0" } }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/@vue/compiler-sfc": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz", + "integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.34", + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.14", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vueuse/core/node_modules/@vue/server-renderer": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.34.tgz", + "integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "vue": "3.5.34" + } + }, + "node_modules/@vueuse/core/node_modules/vue": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", + "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-sfc": "3.5.34", + "@vue/runtime-dom": "3.5.34", + "@vue/server-renderer": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/@vue/compiler-sfc": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz", + "integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.34", + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.14", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vueuse/shared/node_modules/@vue/server-renderer": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.34.tgz", + "integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "vue": "3.5.34" + } + }, + "node_modules/@vueuse/shared/node_modules/vue": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", + "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-sfc": "3.5.34", + "@vue/runtime-dom": "3.5.34", + "@vue/server-renderer": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -4328,6 +5865,16 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/bluebird": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", @@ -5251,6 +6798,17 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5356,6 +6914,28 @@ "integrity": "sha512-acv43vqJ0+N0rD+Uw3pDHSxP30FHrywu2NO6/wBaHChJIizpDeBUd6NjqhNhy9LGaEAhZAXn46QzmlAvIWd16g==", "dev": true }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -6082,6 +7662,17 @@ "stream-length": "^1.0.1" } }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -6296,6 +7887,22 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -7096,10 +8703,11 @@ "license": "CC0-1.0" }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" }, "node_modules/cyclist": { "version": "1.0.2", @@ -8295,6 +9903,16 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -8330,6 +9948,20 @@ "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==", "dev": true }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -8630,6 +10262,13 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -9984,6 +11623,16 @@ "readable-stream": "^2.3.6" } }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, "node_modules/follow-redirects": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", @@ -10812,6 +12461,44 @@ "node": ">= 0.4" } }, + "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==", + "dev": true, + "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-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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -10864,6 +12551,13 @@ "hulk": "bin/hulk" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -10937,6 +12631,17 @@ "node": ">=4" } }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/htmlparser2": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", @@ -12248,6 +13953,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -12832,6 +14550,13 @@ "node": ">=0.10.0" } }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/markdown-it": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", @@ -12982,6 +14707,28 @@ "safe-buffer": "^5.1.2" } }, + "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==", + "dev": true, + "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/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -13097,6 +14844,100 @@ "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", "dev": true }, + "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==", + "dev": true, + "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-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==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "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==", + "dev": true, + "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-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "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==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -13261,6 +15102,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, "node_modules/mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -13304,6 +15152,13 @@ "node": ">=0.4" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -14082,6 +15937,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -14573,6 +16440,13 @@ ], "license": "MIT" }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -16262,6 +18136,17 @@ "dev": true, "license": "MIT" }, + "node_modules/preact": { + "version": "10.29.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", + "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -16341,6 +18226,17 @@ "dev": true, "license": "ISC" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -16872,6 +18768,16 @@ "node": ">=4" } }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -16910,6 +18816,23 @@ "node": ">=0.10.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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -17329,6 +19252,13 @@ "node": ">= 4" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rgb-regex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", @@ -17445,6 +19375,66 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", "dev": true }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -17572,6 +19562,14 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -18018,6 +20016,23 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -18414,6 +20429,17 @@ "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -18511,6 +20537,16 @@ "dev": true, "license": "MIT" }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/speech-rule-engine": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", @@ -18954,6 +20990,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "dev": true, + "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.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -19148,6 +21199,19 @@ "node": ">= 8" } }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -19220,6 +21284,13 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true, + "license": "MIT" + }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -19775,6 +21846,17 @@ "tree-kill": "cli.js" } }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -20058,26 +22140,99 @@ "unique-slug": "^2.0.0" } }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.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==", + "dev": true, + "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==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" + "@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/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "crypto-random-string": "^2.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/universalify": { @@ -20693,6 +22848,408 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.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==", + "dev": true, + "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/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vitepress/node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/vitepress/node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/vitepress/node_modules/@vue/compiler-sfc": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz", + "integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.34", + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.14", + "source-map-js": "^1.2.1" + } + }, + "node_modules/vitepress/node_modules/@vue/server-renderer": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.34.tgz", + "integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "vue": "3.5.34" + } + }, + "node_modules/vitepress/node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/vitepress/node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/vitepress/node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/vitepress/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitepress/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vitepress/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitepress/node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vitepress/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress/node_modules/vue": { + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", + "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.34", + "@vue/compiler-sfc": "3.5.34", + "@vue/runtime-dom": "3.5.34", + "@vue/server-renderer": "3.5.34", + "@vue/shared": "3.5.34" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -22907,6 +25464,17 @@ "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", "dev": true + }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index eb1756161..598e93ac3 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "prestart": "rm -rf $npm_package_config_dist", "start": "NODE_ENV=development vuepress dev docs -p $npm_package_config_port", "build": "NODE_ENV=production vuepress build docs", + "start:vitepress": "vitepress dev docs", + "build:vitepress": "vitepress build docs", "linkcheck": "start-server-and-test linkcheck:serve $npm_package_config_port linkcheck:local", "linkcheck:serve": "serve -p $npm_package_config_port $npm_package_config_dist", "linkcheck:local": "$npm_package_config_linkcheck http://localhost:$npm_package_config_port" @@ -34,6 +36,7 @@ "broken-link-checker": "0.7.8", "serve": "14.2.6", "start-server-and-test": "3.0.5", + "vitepress": "^1.5.0", "vue": "2.7.16", "vue-server-renderer": "2.7.16", "vuepress": "1.9.10" diff --git a/scripts/migrate-codeswitcher.mjs b/scripts/migrate-codeswitcher.mjs new file mode 100644 index 000000000..6c3f40fd4 --- /dev/null +++ b/scripts/migrate-codeswitcher.mjs @@ -0,0 +1,93 @@ +#!/usr/bin/env node +// One-shot migration script: (VuePress 1 spiralbtc theme) +// -> ::: code-group (VitePress built-in). +// +// Usage: +// node scripts/migrate-codeswitcher.mjs +// +// Idempotent: skips files with no CodeSwitcher blocks. Safe to run twice. +// +// This script is one-shot and can be deleted after the migration lands. + +import fs from 'node:fs' + +const files = process.argv.slice(2) +if (files.length === 0) { + console.error('Usage: node scripts/migrate-codeswitcher.mjs ') + process.exit(1) +} + +const codeSwitcherRe = + /([\s\S]*?)<\/CodeSwitcher>/g +const templateRe = + /([\s\S]*?)<\/template>/g + +function parseLanguagesMap(raw) { + // raw looks like `{rust:'Rust', kotlin:'Kotlin', swift:'Swift'}`. + const inner = raw.replace(/^\{|\}$/g, '') + const labels = {} + for (const pair of inner.split(',')) { + const m = pair.match(/^\s*['"]?([\w-]+)['"]?\s*:\s*['"]([^'"]+)['"]\s*$/) + if (m) labels[m[1]] = m[2] + } + return labels +} + +function dedent(text) { + const lines = text.split('\n') + // Strip pure leading/trailing blank lines for measurement. + const nonBlank = lines.filter((l) => l.trim().length > 0) + if (nonBlank.length === 0) return '' + const indents = nonBlank.map((l) => l.match(/^[ \t]*/)[0].length) + const minIndent = Math.min(...indents) + if (minIndent === 0) return lines.join('\n') + return lines + .map((l) => (l.length >= minIndent ? l.slice(minIndent) : l)) + .join('\n') +} + +function transformTemplate(slotName, slotContent, labels) { + const label = labels[slotName] || slotName + const dedented = dedent(slotContent).trim() + + // Inject `[Label]` after the first opening code-fence language tag. + // Don't change the language token itself — the original may legitimately + // use a different fence (e.g. kotlin slot rendering as ```java). + const withLabel = dedented.replace( + /^(```[\w-]*)\s*$/m, + (_, fence) => `${fence} [${label}]`, + ) + return withLabel +} + +let totalBlocks = 0 +let filesChanged = 0 + +for (const file of files) { + const original = fs.readFileSync(file, 'utf8') + let blocksInFile = 0 + + const transformed = original.replace(codeSwitcherRe, (_match, langMap, body) => { + blocksInFile++ + const labels = parseLanguagesMap(langMap) + const blocks = [] + let templateMatch + templateRe.lastIndex = 0 + while ((templateMatch = templateRe.exec(body)) !== null) { + const [, slot, content] = templateMatch + blocks.push(transformTemplate(slot, content, labels)) + } + return `::: code-group\n\n${blocks.join('\n\n')}\n\n:::` + }) + + if (blocksInFile > 0) { + fs.writeFileSync(file, transformed) + console.log(`✓ ${file}: ${blocksInFile} block(s)`) + filesChanged++ + totalBlocks += blocksInFile + } +} + +console.log( + `\n${totalBlocks} CodeSwitcher block(s) converted across ${filesChanged} file(s).`, +) diff --git a/todos/001-pending-p3-add-node-engines-field.md b/todos/001-pending-p3-add-node-engines-field.md new file mode 100644 index 000000000..977f5d7a9 --- /dev/null +++ b/todos/001-pending-p3-add-node-engines-field.md @@ -0,0 +1,67 @@ +--- +status: pending +priority: p3 +issue_id: 001 +tags: [code-review, quality, build-tooling] +dependencies: [] +--- + +# Add `engines.node` field to package.json + +## Problem Statement + +After bumping the CI runtime to Node 22 (required by `start-server-and-test` v3), `package.json` still has no `engines` field. A contributor installing on Node 16 or 18 will get a successful `npm install` followed by confusing failures at build time. An `engines` declaration would surface the mismatch at install time with a clear message. + +This is not a blocker for the upgrade PR — it's a small follow-up that closes a paper cut introduced by the runtime change. + +## Findings + +- `package.json` currently has no `engines` block (lines 30–38 contain only `devDependencies`). +- CI pins `node-version: 22` in `.github/workflows/build.yml`. +- `start-server-and-test@3.0.5` declares `engines: { node: "^22 || >=24" }`. +- npm respects `engines` strictly only when `engine-strict=true` is set in `.npmrc`; without it the field is advisory (still prints a warning, still useful). + +## Proposed Solutions + +### Option A: Add `engines` to package.json (minimal) + +```json +"engines": { + "node": ">=22" +} +``` + +**Pros:** One-line change, surfaces version mismatch at install. **Cons:** Advisory only without `.npmrc`. **Effort:** Small. **Risk:** None. + +### Option B: Option A + `.nvmrc` file containing `22` + +**Pros:** `nvm use` in repo root auto-selects the right Node. Standard convention. +**Cons:** Two files instead of one. **Effort:** Small. **Risk:** None. + +### Option C: Option B + `.npmrc` with `engine-strict=true` + +**Pros:** Hard-fails install on wrong Node version. **Cons:** Strict enforcement may surprise contributors. **Effort:** Small. **Risk:** Low — easy to opt out. + +## Recommended Action + +_(Triage decision — leave blank initially)_ + +## Technical Details + +- **Files:** `package.json` (Option A); add `.nvmrc` (Option B); add `.npmrc` (Option C). +- **No build or CI changes required** (CI already uses Node 22). + +## Acceptance Criteria + +- [ ] `engines.node` field present in `package.json` +- [ ] `npm install` on Node <22 produces a clear warning or error +- [ ] If `.nvmrc` added: `nvm use` in repo root selects Node 22 + +## Work Log + +_(populate as work progresses)_ + +## Resources + +- Plan: `docs/plans/2026-05-13-001-refactor-upgrade-dev-dependencies-plan.md` (local, gitignored) +- npm docs: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines diff --git a/todos/002-pending-p3-replace-broken-link-checker.md b/todos/002-pending-p3-replace-broken-link-checker.md new file mode 100644 index 000000000..044b235b0 --- /dev/null +++ b/todos/002-pending-p3-replace-broken-link-checker.md @@ -0,0 +1,71 @@ +--- +status: pending +priority: p3 +issue_id: 002 +tags: [code-review, dependencies, technical-debt] +dependencies: [] +--- + +# Replace unmaintained `broken-link-checker` + +## Problem Statement + +`broken-link-checker@0.7.8` is the only tool wired into `npm run linkcheck`. The package has had **no releases since 2017** and depends on an ancient pre-fetch stack (`bhttp`, `request`, etc. — visible in the npm deprecation warnings during install). It is the largest single source of the 142 transitive vulnerabilities reported by `npm audit`, and the only realistic mitigation path is replacement. + +This is not blocking the dep-upgrade PR — `broken-link-checker` was already at 0.7.8 on `main`, and the PR even reduced the vuln count by leaving its tree untouched while modernizing other deps. But it should be tracked as a follow-up. + +## Findings + +- Latest release of `broken-link-checker`: `0.7.8` — published 2017-09-19. +- The GitHub repo (https://github.com/stevenvachon/broken-link-checker) shows the last commit was years ago; no active maintenance. +- Used only by the CI link-check step, which has `continue-on-error: true` — so the immediate functional risk is low, but the supply-chain risk (audit noise, transitive vulns) is high. +- `npm run linkcheck` runs successfully today against the current install. + +## Proposed Solutions + +### Option A: `lychee` (recommended for docs sites) + +[lychee](https://github.com/lycheeverse/lychee) is a fast Rust-based link checker with a dedicated GitHub Action (`lycheeverse/lychee-action`). Replaces both `broken-link-checker` and the `start-server-and-test`/`serve` plumbing if used against the built static site (or directly against URLs in markdown). + +**Pros:** Actively maintained, fast, no npm transitive vulns, GitHub Action integrates into workflow with a few lines. +**Cons:** Different output format — the workflow's `grep "Getting links from\|BROKEN"` filter would need to change. +**Effort:** Medium. **Risk:** Low — linkcheck is already non-blocking in CI. + +### Option B: `linkinator` + +[linkinator](https://github.com/JustinBeckwith/linkinator) is a Node-based crawler maintained by Google. Drop-in API similar to `blc` but with active maintenance. + +**Pros:** Stays in the npm ecosystem; CLI shape close to `blc`. +**Cons:** Still ships its own transitive deps; less of a reduction in npm audit noise than lychee. +**Effort:** Small. **Risk:** Low. + +### Option C: Status quo (defer) + +Leave `broken-link-checker` as-is until it actually breaks. Acceptable since linkcheck is `continue-on-error: true`. + +**Pros:** Zero work. **Cons:** Tech debt accrues; npm audit noise stays elevated. **Effort:** None. + +## Recommended Action + +_(Triage decision — leave blank initially)_ + +## Technical Details + +- **Files affected (Option A):** `.github/workflows/build.yml` (replace linkcheck step + remove sst/serve deps when no longer needed); `package.json` config block. +- **Files affected (Option B):** `package.json` (swap dep); workflow needs minor command change. + +## Acceptance Criteria + +- [ ] CI link-check step runs successfully against the deployed/staged site +- [ ] `npm audit` vulnerability count reduced (concrete number depends on chosen replacement) +- [ ] No regression in detection coverage for the docs site's external links + +## Work Log + +_(populate as work progresses)_ + +## Resources + +- Brainstorm: `docs/brainstorms/2026-05-13-dependency-upgrade-brainstorm.md` (local, gitignored) — `broken-link-checker` retention was an explicit decision deferred to follow-up +- lychee: https://github.com/lycheeverse/lychee +- linkinator: https://github.com/JustinBeckwith/linkinator diff --git a/todos/003-complete-p1-routeparametersconfig-wrong-import.md b/todos/003-complete-p1-routeparametersconfig-wrong-import.md new file mode 100644 index 000000000..42f602268 --- /dev/null +++ b/todos/003-complete-p1-routeparametersconfig-wrong-import.md @@ -0,0 +1,59 @@ +--- +status: pending +priority: p1 +issue_id: 003 +tags: [code-review, docs, rust, api-correctness] +dependencies: [] +--- + +# Rust `RouteParametersConfig` imported from the wrong module (won't compile) + +## Problem Statement + +The Rust "send a payment" sample imports `RouteParametersConfig` from +`lightning::ln::channelmanager`, where it does not exist in 0.2.2. A reader who +copies the snippet gets a compile error (`unresolved import`). This is the most +severe correctness issue from the review — shipped sample code that does not +build. + +## Findings + +- `docs/building-a-node-with-ldk/sending-payments.md`, Rust tab: + ```rust + use lightning::ln::channelmanager::{PaymentId, Retry, RouteParametersConfig}; + ``` +- In lightning 0.2.2, `RouteParametersConfig` lives in + `lightning::routing::router` (it implements `Default`). `PaymentId` and + `Retry` are correctly in `lightning::ln::channelmanager`. +- Source: https://docs.rs/lightning/0.2.2/lightning/routing/router/struct.RouteParametersConfig.html + and https://docs.rs/lightning/0.2.2/lightning/ln/channelmanager/index.html + (RouteParametersConfig absent there). +- Confidence: Certain (verified against docs.rs). + +## Proposed Solutions + +### Option A: Split the import (recommended) + +```rust +use lightning::ln::channelmanager::{PaymentId, Retry}; +use lightning::routing::router::RouteParametersConfig; +``` + +**Pros:** Correct, minimal. **Cons:** none. **Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage)_ + +## Technical Details + +- Affected file: `docs/building-a-node-with-ldk/sending-payments.md` (first code-group, Rust tab). + +## Acceptance Criteria + +- [ ] `RouteParametersConfig` is imported from `lightning::routing::router`. +- [ ] `PaymentId`/`Retry` remain imported from `lightning::ln::channelmanager`. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (API-correctness agent). diff --git a/todos/004-complete-p2-kotlin-peer-node-id-accessor.md b/todos/004-complete-p2-kotlin-peer-node-id-accessor.md new file mode 100644 index 000000000..cb6bb6fd5 --- /dev/null +++ b/todos/004-complete-p2-kotlin-peer-node-id-accessor.md @@ -0,0 +1,55 @@ +--- +status: pending +priority: p2 +issue_id: 004 +tags: [code-review, docs, kotlin, api-correctness] +dependencies: [] +--- + +# Kotlin `it._counterparty_node_id` does not resolve on `PeerDetails` + +## Problem Statement + +The Kotlin "connect to peers" sample reads the peer node id with a property +accessor that the ldk-java binding doesn't synthesize, so the snippet won't +compile as written. + +## Findings + +- `docs/building-a-node-with-ldk/connect-to-peers.md`, Kotlin tab: + ```kotlin + val peerNodeIds = peerManager.list_peers().map { it._counterparty_node_id } + ``` +- The v0.2.0.0 binding exposes `PeerDetails.get_counterparty_node_id()`. Kotlin + only synthesizes property-style access for JavaBean getters of the form + `getXxx()` (capital letter after `get`); `get_counterparty_node_id()` is not + converted to a `_counterparty_node_id` property. +- Source: https://raw.githubusercontent.com/lightningdevkit/ldk-garbagecollected/v0.2.0.0/src/main/java/org/ldk/structs/PeerDetails.java +- Confidence: Likely. (Note: elsewhere the docs use the `_field` convention for + generated getters that DO follow the JavaBean shape; this one does not.) + +## Proposed Solutions + +### Option A: Use the explicit getter (recommended) + +```kotlin +val peerNodeIds = peerManager.list_peers().map { it.get_counterparty_node_id() } +``` + +**Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage)_ + +## Technical Details + +- Affected file: `docs/building-a-node-with-ldk/connect-to-peers.md` (second code-group, Kotlin tab). + +## Acceptance Criteria + +- [ ] Kotlin sample uses `get_counterparty_node_id()`. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (API-correctness agent). diff --git a/todos/005-complete-p2-footer-dead-blockchain-data-link.md b/todos/005-complete-p2-footer-dead-blockchain-data-link.md new file mode 100644 index 000000000..5b38f586b --- /dev/null +++ b/todos/005-complete-p2-footer-dead-blockchain-data-link.md @@ -0,0 +1,56 @@ +--- +status: pending +priority: p2 +issue_id: 005 +tags: [code-review, docs, vitepress, dead-link] +dependencies: [] +--- + +# Site footer links to deleted `/blockchain_data/introduction/` + +## Problem Statement + +Consolidating the seven Blockchain Data pages into `/blockchain_data/` updated +the sidebar (`config.mts`) but missed the site footer, which still points at the +now-deleted `/blockchain_data/introduction/`. This is a broken link rendered on +**every page** of the site. VitePress's build-time dead-link check only covers +markdown relative links, not Vue component `href`s, so the build passed despite +this. + +## Findings + +- `docs/.vitepress/theme/components/SiteFooter.vue:26`: + ```js + { text: 'Blockchain Data', link: '/blockchain_data/introduction/' }, + ``` +- The page now lives at `/blockchain_data/` (see `docs/blockchain_data/index.md`); + the `introduction.md` sub-page was deleted in commit 925dd716. +- Confidence: Certain (grep-verified). + +## Proposed Solutions + +### Option A: Point the footer at the consolidated page (recommended) + +```js +{ text: 'Blockchain Data', link: '/blockchain_data/' }, +``` + +**Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage)_ + +## Technical Details + +- Affected file: `docs/.vitepress/theme/components/SiteFooter.vue:26`. +- Worth a quick grep of the footer for any other stale links while here. + +## Acceptance Criteria + +- [ ] Footer "Blockchain Data" link resolves to `/blockchain_data/`. +- [ ] No other footer links point at removed pages. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (simplicity agent). Verified at SiteFooter.vue:26. diff --git a/todos/006-complete-p2-invalid-ts-ldkseed-placeholder.md b/todos/006-complete-p2-invalid-ts-ldkseed-placeholder.md new file mode 100644 index 000000000..ca406bf40 --- /dev/null +++ b/todos/006-complete-p2-invalid-ts-ldkseed-placeholder.md @@ -0,0 +1,58 @@ +--- +status: pending +priority: p2 +issue_id: 006 +tags: [code-review, docs, typescript, api-correctness] +dependencies: [] +--- + +# Invalid TypeScript placeholder in Key Management (comment is not an expression) + +## Problem Statement + +The TypeScript "Creating a Unified Wallet" sample assigns a block comment as a +value, which is not valid TypeScript — a reader copying it gets a syntax error. + +## Findings + +- `docs/key_management.md`, TypeScript tab of the Unified Wallet code-group: + ```ts + const ldkSeed: Uint8Array = /* 32 bytes derived at m/535h */; + ``` +- A `/* ... */` comment is not an expression, so the assignment doesn't parse. +- Confidence: Certain. + +## Proposed Solutions + +### Option A: Use a real placeholder value (recommended) + +```ts +// 32 bytes derived from your HD wallet at m/535h +const ldkSeed = new Uint8Array(32); +``` + +**Pros:** Compiles; intent preserved via comment. **Effort:** Small. **Risk:** None. + +### Option B: Declare with a clearly-stubbed function + +```ts +const ldkSeed: Uint8Array = deriveLdkSeedAtPath("m/535h"); // your derivation +``` + +**Pros:** Reads as real code. **Cons:** introduces an undefined helper. **Effort:** Small. + +## Recommended Action + +_(triage)_ + +## Technical Details + +- Affected file: `docs/key_management.md` (Unified Wallet code-group, TS tab). + +## Acceptance Criteria + +- [ ] The TS snippet parses as valid TypeScript (no comment-as-value). + +## Work Log + +- 2026-06-04: Found during `/ce:review` (docs-quality agent). diff --git a/todos/007-complete-p3-install-bash-typescript-label.md b/todos/007-complete-p3-install-bash-typescript-label.md new file mode 100644 index 000000000..0a535de0b --- /dev/null +++ b/todos/007-complete-p3-install-bash-typescript-label.md @@ -0,0 +1,47 @@ +--- +status: pending +priority: p3 +issue_id: 007 +tags: [code-review, docs, quality] +dependencies: [] +--- + +# Installation TypeScript tab uses `bash` highlighting + +## Problem Statement + +In `installation.md` the TypeScript install tab is fenced as ```` ```bash [TypeScript] ```` +— a shell `npm install` command highlighted as bash but labelled TypeScript. +Likely intentional (the install step is a shell command, grouped under the +TypeScript ecosystem tab), but flagged for consistency. + +## Findings + +- `docs/building-a-node-with-ldk/installation.md`, installation code-group: + ```` ```bash [TypeScript] ```` + `npm install lightningdevkit@{VERSION}` +- The other install tabs use ecosystem-appropriate languages (`toml [Rust]`, + `java [Kotlin]`). + +## Proposed Solutions + +### Option A: Leave as-is (likely correct) + +`npm install` is a shell command; bash highlighting is accurate, and the tab +label correctly groups it under TypeScript. **Effort:** none. + +### Option B: Relabel the fence language to `sh`/`shell` + +Cosmetic only. **Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage — may be dismissed as intentional)_ + +## Acceptance Criteria + +- [ ] Decision recorded: keep `bash` highlighting or switch. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (docs-quality agent). Likely won't-fix. diff --git a/todos/008-complete-p3-rapid-gossip-sync-wildcard-link.md b/todos/008-complete-p3-rapid-gossip-sync-wildcard-link.md new file mode 100644 index 000000000..40ad4f44b --- /dev/null +++ b/todos/008-complete-p3-rapid-gossip-sync-wildcard-link.md @@ -0,0 +1,46 @@ +--- +status: pending +priority: p3 +issue_id: 008 +tags: [code-review, docs, quality, links] +dependencies: [] +--- + +# Sibling-crate reference links still use `/*/` wildcard version + +## Problem Statement + +The 0.2 migration pinned `lightning` doc links to `/0.2.2/`, but some +sibling-crate reference links still use the `/*/` wildcard, which is an +internal inconsistency. (`/*/` resolves to "latest" on docs.rs, so the links +work — this is consistency polish, not a broken link.) + +## Findings + +- `docs/building-a-node-with-ldk/setting-up-a-channel-manager.md` (~line 1128): + `https://docs.rs/lightning-rapid-gossip-sync/*/lightning_rapid_gossip_sync/` +- Other sibling crates (`lightning-net-tokio`, `lightning-background-processor`) + may have similar `/*/` links; a grep will surface them. +- Sibling crates are at 0.2.0 (not 0.2.2). + +## Proposed Solutions + +### Option A: Pin siblings to `/0.2.0/` + +Consistent with how `lightning` was pinned to `/0.2.2/`. **Effort:** Small. + +### Option B: Standardize on `/latest/` for all docs.rs links + +Auto-tracks future releases; less version drift to maintain. **Effort:** Small. + +## Recommended Action + +_(triage — pick one convention and apply repo-wide)_ + +## Acceptance Criteria + +- [ ] docs.rs link versioning is consistent across the changed docs. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (docs-quality agent). diff --git a/todos/009-complete-p3-blockchain-data-note-trim.md b/todos/009-complete-p3-blockchain-data-note-trim.md new file mode 100644 index 000000000..b97042f24 --- /dev/null +++ b/todos/009-complete-p3-blockchain-data-note-trim.md @@ -0,0 +1,48 @@ +--- +status: pending +priority: p3 +issue_id: 009 +tags: [code-review, docs, quality, prose] +dependencies: [] +--- + +# Trim redundant notes in consolidated Blockchain Data page + +## Problem Statement + +The consolidated `blockchain_data/index.md` has minor verbosity introduced +during the merge: a duplicated "only available in Rust" statement and three +stacked `::: tip Note` callouts in the Confirmed Transactions section. + +## Findings + +- `docs/blockchain_data/index.md`: + - The `lightning-block-sync` "only available in Rust" point appears in a tip + box (~line 47) and is then restated a few lines later (~line 53, + "As noted above, ..."). + - Three consecutive `::: tip Note` blocks in Confirmed Transactions (~lines + 184–200); two open with "Note that ..." inside a box already titled "Note". + +## Proposed Solutions + +### Option A: Light trim (recommended) + +- Drop the "As noted above, `lightning-block-sync` is only available in Rust" + restatement. +- Merge the `Confirm` ordering note and the "call on both ChannelManager and + ChainMonitor" note into one callout; drop redundant "Note that" prefixes. + +**Effort:** Small. **Risk:** None (prose only). + +## Recommended Action + +_(triage)_ + +## Acceptance Criteria + +- [ ] No duplicated "only available in Rust" sentence. +- [ ] Confirmed Transactions has at most two note callouts. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (simplicity agent). diff --git a/todos/010-pending-p2-ghost-cta-darkmode-contrast.md b/todos/010-pending-p2-ghost-cta-darkmode-contrast.md new file mode 100644 index 000000000..a188b0357 --- /dev/null +++ b/todos/010-pending-p2-ghost-cta-darkmode-contrast.md @@ -0,0 +1,53 @@ +--- +status: pending +priority: p2 +issue_id: 010 +tags: [code-review, frontend, dark-mode, accessibility, home] +dependencies: [] +--- + +# LDK Node ghost CTA hover is unreadable in dark mode + +## Problem Statement + +In the new Feature hero, the compact LDK Node strip's outline ("ghost") CTA +turns white text on a mint fill on hover in dark mode — near-invisible. + +## Findings + +- `docs/.vitepress/theme/components/HomeServerPromo.vue`, `.fh-node-cta:hover`: + ```css + .fh-node-cta:hover { background: var(--vp-c-brand-1); color: var(--vp-c-white); } + ``` +- In **dark mode** `--vp-c-brand-1` is the mint accent (~`#76f3cd`) while + `--vp-c-white` stays `#ffffff` → white-on-mint contrast ≈ 1.2:1 (the label + effectively disappears on hover). In light mode it's white-on-blue (fine). +- The primary `.fh-cta` already handles this correctly with + `color: var(--vp-button-brand-text, var(--vp-c-white))` (resolves to a dark + ink in dark mode). +- Source: frontend review agent. + +## Proposed Solutions + +### Option A: Use the brand-text token for hover (recommended) + +```css +.fh-node-cta:hover { + background: var(--vp-c-brand-1); + color: var(--vp-button-brand-text, var(--vp-c-white)); +} +``` + +Matches `.fh-cta`; readable in both themes. **Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage)_ + +## Acceptance Criteria + +- [ ] Node strip CTA hover label is legible in both light and dark mode. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (frontend agent). diff --git a/todos/011-pending-p2-cta-focus-visible.md b/todos/011-pending-p2-cta-focus-visible.md new file mode 100644 index 000000000..c3cb20f94 --- /dev/null +++ b/todos/011-pending-p2-cta-focus-visible.md @@ -0,0 +1,49 @@ +--- +status: pending +priority: p2 +issue_id: 011 +tags: [code-review, frontend, accessibility, home] +dependencies: [] +--- + +# Feature hero CTAs have no visible focus state + +## Problem Statement + +The two custom-styled anchor CTAs in the Feature hero (`.fh-cta`, `.fh-node-cta`) +define no `:focus-visible` styling, and the theme's `style.css` has no global +focus rule, so keyboard focus falls back to the browser default outline only — +which can be low-contrast on the brand-soft band. WCAG 2.4.7 (Focus Visible). + +## Findings + +- `docs/.vitepress/theme/components/HomeServerPromo.vue`: `.fh-cta` and + `.fh-node-cta` style `:hover` but not `:focus-visible`. +- Source: frontend review agent (grep found no focus rule in `style.css`). + +## Proposed Solutions + +### Option A: Add an explicit focus-visible outline (recommended) + +```css +.fh-cta:focus-visible, +.fh-node-cta:focus-visible { + outline: 2px solid var(--vp-c-brand-1); + outline-offset: 2px; +} +``` + +**Effort:** Small. **Risk:** None. (Consider whether a site-wide focus style is +warranted instead — out of scope here.) + +## Recommended Action + +_(triage)_ + +## Acceptance Criteria + +- [ ] Both CTAs show a clear, on-brand focus ring when tabbed to. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (frontend agent). diff --git a/todos/012-pending-p3-node-strip-tablet-crowding.md b/todos/012-pending-p3-node-strip-tablet-crowding.md new file mode 100644 index 000000000..f996e521e --- /dev/null +++ b/todos/012-pending-p3-node-strip-tablet-crowding.md @@ -0,0 +1,46 @@ +--- +status: pending +priority: p3 +issue_id: 012 +tags: [code-review, frontend, responsive, home] +dependencies: [] +--- + +# Node strip can crowd at tablet widths (721–900px) + +## Problem Statement + +The compact Node strip uses `grid-template-columns: 84px 1fr auto` from 721px +up, with the only stack breakpoint at `max-width: 720px`. Between ~721–900px the +icon + copy + nowrap "Get Started" pill compete for space and the copy can wrap +awkwardly. + +## Findings + +- `docs/.vitepress/theme/components/HomeServerPromo.vue`: `.fh-node` grid is + three-column above 721px; `.fh-node-cta` has `white-space: nowrap`. +- Source: frontend review agent. + +## Proposed Solutions + +### Option A: Raise the stacking breakpoint + +Change the `@media (max-width: 720px)` stack rule for `.fh-node` to +`max-width: 860px` so the strip collapses to a centered single column on small +tablets. **Effort:** Small. **Risk:** None. + +### Option B: Let the icon drop sooner / allow CTA wrap + +More targeted but fiddlier. **Effort:** Small. + +## Recommended Action + +_(triage)_ + +## Acceptance Criteria + +- [ ] Node strip reads cleanly across 721–900px (no cramped/overlapping copy). + +## Work Log + +- 2026-06-04: Found during `/ce:review` (frontend agent). diff --git a/todos/013-pending-p3-delete-orphaned-homepromo.md b/todos/013-pending-p3-delete-orphaned-homepromo.md new file mode 100644 index 000000000..2ec4dfb2c --- /dev/null +++ b/todos/013-pending-p3-delete-orphaned-homepromo.md @@ -0,0 +1,41 @@ +--- +status: pending +priority: p3 +issue_id: 013 +tags: [code-review, cleanup, home] +dependencies: [] +--- + +# Delete orphaned HomePromo.vue + +## Problem Statement + +The standalone LDK Node promo (`HomePromo.vue`) was folded into the Feature +hero and removed from the theme render, but the file remains on disk with no +references — dead code. + +## Findings + +- `docs/.vitepress/theme/components/HomePromo.vue` is no longer imported in + `theme/index.ts` (import + slot entry removed in 02b11881). Grep finds no other + references except a `docs/plans/*.md` pipeline artifact (do not touch). +- Source: frontend + simplicity review agents (both flagged it). + +## Proposed Solutions + +### Option A: Delete the file (recommended) + +`git rm docs/.vitepress/theme/components/HomePromo.vue`. Its content lives in the +Node strip of `HomeServerPromo.vue` now. **Effort:** Small. **Risk:** None. + +## Recommended Action + +_(triage)_ + +## Acceptance Criteria + +- [ ] `HomePromo.vue` is removed; build still passes. + +## Work Log + +- 2026-06-04: Found during `/ce:review` (frontend + simplicity agents). diff --git a/todos/014-complete-p2-diagram-asset-location-inconsistency.md b/todos/014-complete-p2-diagram-asset-location-inconsistency.md new file mode 100644 index 000000000..8ca9b9fb9 --- /dev/null +++ b/todos/014-complete-p2-diagram-asset-location-inconsistency.md @@ -0,0 +1,120 @@ +--- +status: pending +priority: p2 +issue_id: 014 +tags: [code-review, docs, assets, consistency, maintainability] +dependencies: [] +--- + +# Diagram assets are split across `docs/assets/` and `docs/public/img/` with inconsistent references + +## Problem Statement + +Commit `672e8af9` shipped the architecture and peer-management diagrams using +two *different* asset conventions for the same feature, and left dead/stale +duplicate copies in a second directory. Everything renders correctly today +(the production build passes and resolves both forms), so this is a +maintainability / source-of-truth issue, not a user-facing bug — but it is a +real foot-gun for the next person who edits these diagrams. + +## Findings + +- **Path convention split (same feature, two styles):** + - `docs/introduction/architecture.md:2-3` → absolute public paths + `src="/img/ldk-architecture.svg"` / `src="/img/ldk-architecture-dark.svg"` + - `docs/introduction/peer-management.md:2-3` → relative paths + `src="../assets/ldk-peer-management.svg"` / `src="../assets/ldk-peer-management-dark.svg"` + - The relative `../assets/...` form is the repo standard: ~27 markdown + references use it (all blog posts, e.g. `docs/blog/bolt12-has-arrived.md`, + and the only other raw `` in markdown, `docs/blog/announcing-vss.md:58`). + Vite transforms + fingerprints these at build time (confirmed in dist: + `assets/ldk-peer-management.BWhA4J-P.svg`). Files in `public/` bypass Vite — + no hashing, no build-time "missing asset" error, and they break if a + `base` sub-path is ever configured. + +- **Dead duplicate (introduced by this commit):** + `docs/assets/ldk-architecture-dark.svg` is byte-identical to + `docs/public/img/ldk-architecture-dark.svg`, but architecture.md references + the `/img/` (public) copy — so the `assets/` copy is unreferenced. The + pre-existing light counterpart `docs/assets/ldk-architecture.svg` is *also* + already dead for the same reason. Two sources of truth → silent drift on the + next edit. + +- **Stale duplicate (latent foot-gun):** + `docs/public/img/ldk-peer-management.svg` (27,523 B, dated May) is the *old* + diagram — it predates the OnionMessenger/RapidGossipSync work. The page + renders fine because peer-management.md points at `../assets/`, but anyone who + "normalizes" it to the `/img/` convention modeled by architecture.md would + silently render the outdated diagram, and there is no + `public/img/ldk-peer-management-dark.svg` at all (would 404). + +- Confidence: Certain (verified by `git show 672e8af9`, `diff` of the copies, + and inspecting `docs/.vitepress/dist/`). + +## Proposed Solutions + +### Option A: Standardize on `docs/assets/` + relative refs (recommended) + +- Change `docs/introduction/architecture.md:2-3` to + `src="../assets/ldk-architecture.svg"` and + `src="../assets/ldk-architecture-dark.svg"`. +- Delete the now-unused `docs/public/img/ldk-architecture.svg`, + `docs/public/img/ldk-architecture-dark.svg`, and the stale + `docs/public/img/ldk-peer-management.svg` — **after** grepping + `docs/.vitepress/theme/` to confirm no Vue component consumes the `/img/` + paths. +- Pros: matches the dominant repo convention and the peer-management diagram; + single source of truth; Vite catches missing assets at build; survives a + future `base` change. Cons: must verify nothing else references `/img/` copies. +- Effort: Small. Risk: Low (build will fail loudly if a ref is missed). + +### Option B: Standardize on `docs/public/img/` + absolute refs + +- Move/refresh peer-management SVGs into `public/img/` (including the dark + variant), change peer-management.md to `/img/...`, delete the `assets/` copies. +- Pros: consistent the other way. Cons: goes *against* the established + convention used by ~28 other references; loses Vite asset processing. +- Effort: Small-Medium. Risk: Low-Medium. + +### Option C: Minimal — fix only the stale/dead copies + +- Keep the mixed conventions but delete the dead `assets/ldk-architecture*.svg` + and re-sync or delete the stale `public/img/ldk-peer-management.svg`. +- Pros: smallest diff. Cons: leaves the convention split unresolved. +- Effort: Small. Risk: Low. + +## Recommended Action + +_(triage)_ — leaning Option A. + +## Technical Details + +Affected files: +- `docs/introduction/architecture.md` (lines 2-3) +- `docs/assets/ldk-architecture-dark.svg`, `docs/assets/ldk-architecture.svg` (dead) +- `docs/public/img/ldk-architecture.svg`, `docs/public/img/ldk-architecture-dark.svg` +- `docs/public/img/ldk-peer-management.svg` (stale) +- verify: `docs/.vitepress/theme/` (any `/img/` consumers) + +## Acceptance Criteria + +- [ ] Architecture and peer-management diagrams use one consistent asset + location + reference style. +- [ ] No unreferenced/stale duplicate diagram SVGs remain (or they are the + single intended source). +- [ ] `npm run build:vitepress` passes and both diagrams render in light and + dark mode in the built site. + +## Work Log + +- 2026-06-05: Filed from `/ce:review` of commit `672e8af9`. Flagged by + pattern-recognition, code-simplicity, performance, and learnings agents + (consensus). Production build confirmed both reference styles currently + resolve; this is consistency/maintainability, not a blocker. + +- 2026-06-05: RESOLVED (Option A). architecture.md switched to relative `../assets/` references; deleted dead `public/img/ldk-architecture.svg`, `public/img/ldk-architecture-dark.svg`, and the stale `public/img/ldk-peer-management.svg`. `npm run build:vitepress` passes — dist now references Vite-fingerprinted `assets/ldk-architecture*.svg`, no `/img/` refs remain. All four architecture+peer diagrams now live in `docs/assets/` with relative refs. + +## Resources + +- Commit: `672e8af9` +- Repo convention sample: `docs/blog/announcing-vss.md:58` (relative ``) diff --git a/todos/015-complete-p3-embedded-font-vs-vector-path-labels.md b/todos/015-complete-p3-embedded-font-vs-vector-path-labels.md new file mode 100644 index 000000000..b6a15b6f7 --- /dev/null +++ b/todos/015-complete-p3-embedded-font-vs-vector-path-labels.md @@ -0,0 +1,85 @@ +--- +status: pending +priority: p3 +issue_id: 015 +tags: [code-review, docs, svg, diagrams, consistency] +dependencies: [] +--- + +# Peer-management diagram mixes embedded-font `` labels with vector-path labels + +## Problem Statement + +The two new labels added in `672e8af9` (`OnionMessenger`, `RapidGossipSync`) are +live SVG `` elements backed by an embedded `@font-face` (a base64 woff2 +Manrope subset, family `RGSManrope`). Every *other* label in the same diagram +(`PeerManager`, `ChannelManager`, `P2PGossipSync`, `Peer []`, `Net I/O`) is a +pre-outlined vector ``. So one diagram now renders text two different ways. +This is cosmetically invisible (the embedded subset matches Manrope Medium/500, +verified by overlay during authoring) but is a minor maintainability wrinkle. + +## Findings + +- `docs/assets/ldk-peer-management.svg` and `...-dark.svg` each contain one + `@font-face{font-family:'RGSManrope';font-weight:500;src:url(data:font/woff2;base64,...)}` + (decoded payload ~1,920 B) plus `` elements. +- The same font payload is embedded in **both** files (byte-identical), so it + ships twice (~2.6 KB base64 ×2; compresses away under gzip — see todo 017). +- Risk noted by reviewer: if someone later edits the diagram in a vector tool + and "converts text to outlines" (as the original labels were), the embedded + `@font-face` becomes dead weight that lingers. +- Background: the embedded-font route was chosen deliberately because SVGs + loaded via `` cannot use page/web fonts, and the available text→path + tooling (opentype.js `toPathData`) produced truncated glyph paths during + authoring. The font subset guarantees identical cross-browser rendering. +- Confidence: Certain. + +## Proposed Solutions + +### Option A: Keep as-is, document the rationale (recommended) + +- Leave the embedded subset; add a short SVG comment noting the family, + weight (500), and that it is a Manrope subset for the two `` labels. +- Pros: zero risk, preserves guaranteed rendering, tiny byte cost. Cons: the + two-strategy inconsistency remains. +- Effort: Small. Risk: None. + +### Option B: Convert the two `` labels to vector `` outlines + +- Generate outlined paths for both strings in Manrope Medium and replace the + `` elements; drop the `@font-face` from both files. +- Pros: all labels rendered identically (paths); removes embedded font from + both files; no font dependency. Cons: requires a *reliable* text→path tool + (the one tried during authoring truncated output — would need verification + via render-compare); paths are larger/opaque to edit. +- Effort: Medium. Risk: Low-Medium (must visually verify the generated paths). + +## Recommended Action + +_(triage)_ — leaning Option A; the current approach is defensible and the +inconsistency is invisible to readers. + +## Technical Details + +Affected files: +- `docs/assets/ldk-peer-management.svg` +- `docs/assets/ldk-peer-management-dark.svg` + +## Acceptance Criteria + +- [ ] Decision recorded (keep + document, or convert to paths). +- [ ] If converted: both labels render pixel-consistent with the other labels + in light and dark mode, verified by screenshot, and no `@font-face` + remains. + +## Work Log + +- 2026-06-05: Filed from `/ce:review` of `672e8af9` (code-simplicity agent). + Noted the embedded-font choice was intentional (cross-browser `` + rendering); flagged as low-priority consistency only. + +- 2026-06-05: RESOLVED (Option A — keep + document). Added an XML comment to both peer-management SVGs explaining the embedded `RGSManrope` Manrope-500 subset and why (``-loaded SVGs can't use page fonts), pointing to the OFL file. Embedded-font approach kept; build verified. + +## Resources + +- Commit: `672e8af9` diff --git a/todos/016-complete-p3-ofl-attribution-embedded-manrope.md b/todos/016-complete-p3-ofl-attribution-embedded-manrope.md new file mode 100644 index 000000000..bbf074535 --- /dev/null +++ b/todos/016-complete-p3-ofl-attribution-embedded-manrope.md @@ -0,0 +1,76 @@ +--- +status: pending +priority: p3 +issue_id: 016 +tags: [code-review, docs, licensing, fonts, compliance] +dependencies: [] +--- + +# Embedded Manrope subset has no SIL OFL attribution/notice + +## Problem Statement + +Commit `672e8af9` embeds a subset of the Manrope font (as base64 woff2) directly +inside the peer-management SVGs. Manrope is licensed under the SIL Open Font +License 1.1, which permits embedding and redistribution but requires the +copyright/license notice to travel with the font. The repo currently ships the +font bytes with no OFL notice anywhere. This is compliance hygiene, not a +security or functional issue. + +## Findings + +- `docs/assets/ldk-peer-management.svg` / `...-dark.svg`: `@font-face` with an + embedded `data:font/woff2;base64,...` Manrope subset. +- `grep` for `OFL` / `SIL Open Font` across the repo: no matches. +- The author already renamed the embedded family to `RGSManrope` (good — OFL + forbids reusing the "Manrope" Reserved Font Name for a modified/subset font). +- Pre-existing context: `docs/assets/cash-app-architecture.svg` (and its + public/img copy) also *reference* Manrope by name, so attribution was already + absent — but this commit is the first to **embed and redistribute** actual + font bytes, which makes the OFL notice requirement directly applicable. +- Confidence: High (OFL terms are well-known; verify exact requirements against + the Manrope license file). + +## Proposed Solutions + +### Option A: Add an OFL notice file (recommended) + +- Add `docs/assets/fonts/Manrope-OFL.txt` (or a `LICENSES/` entry) containing + the Manrope OFL 1.1 copyright + license text, and reference it from a brief + note where the font is embedded. +- Pros: satisfies OFL with minimal effort. Cons: one extra file to keep. +- Effort: Small. Risk: None. + +### Option B: Stop embedding font bytes (convert labels to paths) + +- See todo 015 Option B — outlining the text removes the embedded font entirely, + sidestepping the redistribution clause. +- Pros: no bundled font to attribute. Cons: more work; see 009 caveats. +- Effort: Medium. Risk: Low-Medium. + +## Recommended Action + +_(triage)_ — Option A unless 009 Option B is chosen. + +## Technical Details + +Affected files: +- `docs/assets/ldk-peer-management.svg`, `docs/assets/ldk-peer-management-dark.svg` +- new: an OFL license/attribution file + +## Acceptance Criteria + +- [ ] Manrope OFL 1.1 notice present in the repo and discoverable from where the + font is used, OR the embedded font removed (see 009). + +## Work Log + +- 2026-06-05: Filed from `/ce:review` of `672e8af9` (security-sentinel, + flagged as compliance/P3, explicitly *not* a security vulnerability). + +- 2026-06-05: RESOLVED (Option A). Added `docs/assets/fonts/Manrope-OFL.txt` (official SIL OFL 1.1, Copyright 2018 The Manrope Project Authors) and referenced it from the comment embedded in both peer-management SVGs. + +## Resources + +- Commit: `672e8af9` +- SIL Open Font License 1.1 diff --git a/todos/017-complete-p3-both-theme-image-variants-fetched.md b/todos/017-complete-p3-both-theme-image-variants-fetched.md new file mode 100644 index 000000000..3f24c732b --- /dev/null +++ b/todos/017-complete-p3-both-theme-image-variants-fetched.md @@ -0,0 +1,80 @@ +--- +status: pending +priority: p3 +issue_id: 017 +tags: [code-review, docs, performance, dark-mode] +dependencies: [] +--- + +# Both light and dark diagram variants are downloaded on every page load + +## Problem Statement + +The light/dark diagram swap uses two real `` elements with the inactive +one hidden via `display:none` (`docs/.vitepress/theme/style.css` `.light-only` / +`.dark-only` rules). Per spec, `display:none` does **not** prevent an `` +from being fetched — so every visitor downloads *both* variants regardless of +theme. For a static docs site with caching this is minor, but it is the one +genuine inefficiency in the change. + +## Findings + +- `docs/introduction/peer-management.md:2-3` and + `docs/introduction/architecture.md:2-3` each render two `` variants. +- Swap CSS: `docs/.vitepress/theme/style.css` (`img.dark-only{display:none}`, + `.dark img.light-only{display:none}`, `.dark img.dark-only{display:initial}`). +- Measured cost of the hidden (wasted) fetch per page load: + - Peer-management: ~13 KB gzip (the hidden variant; embedded font is a + rounding error after gzip). + - Architecture: a hidden **~72 KB** SVG (no embedded font) — the larger waste. +- Important: this `.light-only`/`.dark-only` pattern is **pre-existing and + site-wide** (it predates this commit; see `style.css` comment and + `learnings-researcher` notes). Changing it affects more than these diagrams. +- Confidence: Certain (spec behaviour; sizes measured from the built assets). + +## Proposed Solutions + +### Option A: Leave as-is (reasonable for a docs site) + +- Pros: matches the established site pattern; bytes are within docs-page noise + and cached after first load. Cons: redundant fetch persists. +- Effort: None. Risk: None. + +### Option B: Use `` with theme-conditioned `` (only fetches one) + +- Replace the dual-`` + CSS pattern with a `` whose `` selects the variant, so the browser + fetches exactly one. Biggest payoff on the architecture page (~72 KB saved). +- Pros: eliminates the wasted fetch. Cons: `prefers-color-scheme` tracks the OS + theme, which may not match VitePress's in-app `.dark` toggle — needs + verification; and to stay consistent it should arguably be applied site-wide, + expanding scope beyond this commit. +- Effort: Medium (and a convention decision). Risk: Medium (theme-source mismatch). + +## Recommended Action + +_(triage)_ — likely Option A for now; revisit Option B only as a deliberate +site-wide image-theming change. + +## Technical Details + +Affected files: +- `docs/introduction/peer-management.md`, `docs/introduction/architecture.md` +- `docs/.vitepress/theme/style.css` + +## Acceptance Criteria + +- [ ] Decision recorded. If Option B: only the active-theme variant is fetched + (verify in DevTools Network) and the variant correctly follows VitePress's + dark toggle, not just OS preference. + +## Work Log + +- 2026-06-05: Filed from `/ce:review` of `672e8af9` (performance-oracle). + Noted as the established site-wide pattern; low priority for a docs site. + +- 2026-06-05: RESOLVED — accepted as-is (Option A). Kept the `.light-only`/`.dark-only` pattern. Option B (`` + `prefers-color-scheme`) was rejected: it tracks the OS theme, not VitePress's JS `.dark` toggle, so it would break in-app theme switching. No code change. + +## Resources + +- Commit: `672e8af9` diff --git a/todos/ldk-0.2-api-reference.md b/todos/ldk-0.2-api-reference.md new file mode 100644 index 000000000..d3b664729 --- /dev/null +++ b/todos/ldk-0.2-api-reference.md @@ -0,0 +1,86 @@ +# LDK 0.2 API reference (for docs update) + +Verified June 2026 against docs.rs/lightning/0.2.2, ldk-sample, and ldk-garbagecollected @ v0.2.0.0 +(Java/Kotlin + TypeScript bindings). Versions to cite: + +- Rust `lightning` 0.2.2; siblings (`-net-tokio`, `-background-processor`, `-block-sync`, + `-persister`, `-rapid-gossip-sync`, `-transaction-sync`) 0.2.0; `lightning-invoice` 0.34.x. +- Kotlin `org.lightningdevkit:ldk-java:0.2.0` (release tag v0.2.0.0). +- TypeScript `lightningdevkit@0.2.0-0` (MUST pin; `latest` tag is 0.1.8-0). WASM, pure ESM, + needs `await initializeWasmWebFetch(url)` / `initializeWasmFromBinary(bytes)` before use. + +## Cross-cutting breaking changes vs the current docs +- `ConfirmationTarget`: NO Background/Normal/HighPriority. Now: MaximumFeeEstimate, + UrgentOnChainSweep, MinAllowedAnchorChannelRemoteFee, MinAllowedNonAnchorChannelRemoteFee, + AnchorChannelFee, NonAnchorChannelFee, ChannelCloseMinimum, OutputSpendingFee. +- `ChannelManager::new` needs router + message_router + 3 signer args + current_timestamp. +- `KeysManager::new(seed, secs, nanos, v2_remote_key_derivation: bool)` — new bool. +- `ChainMonitor::new(.., entropy_source, peer_storage_key)` — 2 new trailing args. +- `Persist` keys on `MonitorName` (not OutPoint); returns ChannelMonitorUpdateStatus; + added archive_persisted_channel / get_and_clear_completed_updates. +- `BroadcasterInterface::broadcast_transactions(&[&Transaction])` (batch). +- `Logger::log(Record)` by value; Record has level/args/module_path/file/line/peer_id/channel_id/payment_hash. +- `create_channel(.., user_channel_id: u128, temporary_channel_id: Option, override_config)`. +- Announced flag: UserConfig.channel_handshake_config.announce_for_forwarding (renamed; was announced_channel). +- `funding_transaction_generated(channel_id, counterparty_node_id, tx)` — needs counterparty + ChannelId. +- `close_channel(&channel_id, &counterparty_node_id)`; force_close_broadcasting_latest_txn needs error_message. +- Invoice creation: `channel_manager.create_bolt11_invoice(params)` (Rust) / + `channelManager.create_bolt11_invoice(amount, desc, expiry, minCltv, paymentHash)` (Kotlin/TS). + OLD `utils::create_invoice_from_channelmanager` REMOVED (lightning-invoice 0.34 dropped utils+payment modules). +- Sending: Rust `pay_for_bolt11_invoice(invoice, payment_id, amount, RouteParametersConfig, Retry)` + or `send_payment(payment_hash, RecipientOnionFields, payment_id, RouteParameters, Retry)`. + Kotlin `pay_for_bolt11_invoice(...)` / `send_payment(...)`. TS: PaymentParameters.constructor_from_bolt11_invoice + + RouteParameters.constructor_from_payment_params_and_value + send_payment. `payment_parameters_from_invoice` REMOVED. +- Events (lightning::events::Event): PaymentReceived→PaymentClaimable (+ distinct PaymentClaimed); + PaymentFailed { payment_id, payment_hash: Option, reason: Option } (no rejected_by_dest); + PaymentSent gained payment_id/fee_paid_msat/bolt12_invoice; SpendableOutputs gained channel_id. + PaymentPurpose: Bolt11InvoicePayment / Bolt12OfferPayment / Bolt12RefundPayment / SpontaneousPayment. +- Peer enumeration: `get_peer_node_ids()` REMOVED → Rust `list_peers()`/`peer_by_node_id()`; + Kotlin `peerManager.list_peers()` -> PeerDetails[] (._counterparty_node_id). +- spend_spendable_outputs moved to OutputSpender (Rust `keys_manager.spend_spendable_outputs(.., Option, secp)`; + Kotlin/TS `keysManager.as_OutputSpender().spend_spendable_outputs(.., Option_u32Z locktime)`). +- Background processing: OLD BackgroundProcessor::start(persister, invoice_payer, ...) GONE. + Rust: lightning-background-processor `process_events_async(...)` or `BackgroundProcessor::start(...)` (new shape). + Kotlin: `channelManagerConstructor.chain_sync_completed(kvStore, eventHandler, outputSweeper?, useP2P)`. + TS: NO BackgroundProcessor — drive manually via EventsProvider.process_pending_events + pm.process_events. + +## TypeScript-specific realities (user asked for non-Node TS) +- Must `await ldk.initializeWasmWebFetch("/liblightningjs.wasm")` once before any API call. +- Methods snake_case; static ctors `X.constructor_*`; trait impls `X.new_impl({...} as XInterface)`; + enum members `LDK_`; 64-bit ints are JS `bigint`. +- No `ChannelManagerConstructor` helper (that's Java/C# only) — use ChannelManager.constructor_new + and UtilMethods.constructor_C2Tuple_ThirtyTwoBytesChannelManagerZ_read for restart. +- No BackgroundProcessor, no built-in timers. +- NO browser TCP: connect-to-peers requires implementing SocketDescriptor.new_impl({send_data, + disconnect_socket, eq, hash}) over a WebSocket→TCP proxy; feed bytes via pm.read_event / + pm.new_inbound_connection / pm.new_outbound_connection, then pm.process_events(). +- Best idiomatic source: ldk-garbagecollected ts/test/tests.mts @ v0.2.0.0. + +## Key signatures (condensed) +RUST +- create_bolt11_invoice(params: Bolt11InvoiceParameters) -> Result +- pay_for_bolt11_invoice(&invoice, payment_id, Option, RouteParametersConfig, Retry) +- PeerManager::new(MessageHandler{chan_handler, route_handler, onion_message_handler, + custom_message_handler, send_only_message_handler}, current_time u32, &eph[u8;32], logger, node_signer) +- process_events_async(kv_store, event_handler, chain_monitor, channel_manager, Option, + gossip_sync, peer_manager, Option, Option, logger, Option, sleeper, bool, fetch_time) + +KOTLIN (ChannelManagerConstructor battery) +- fresh: ChannelManagerConstructor(Network, UserConfig, tipHash, tipHeight, entropySource, nodeSigner, + signerProvider, feeEstimator, chainMonitor, networkGraph, scoringDecayParams, scoringFeeParams, + routerWrapper?, txBroadcaster, logger) +- restart: ChannelManagerConstructor(serMgr, serMonitors[], UserConfig, entropySource, nodeSigner, + signerProvider, feeEstimator, chainMonitor, filter?, serNetGraph, scoringDecayParams, scoringFeeParams, + serScorer, routerWrapper?, txBroadcaster, logger) +- chain_sync_completed(kvStore: KVStoreSync, eventHandler, outputSweeper?: OutputSweeperSync, useP2P: Boolean) +- peer_manager / nio_peer_handler are null until chain_sync_completed. + +TS +- ChainMonitor.constructor_new(Option_FilterZ, broadcaster, logger, feeEst, persister, + entropySource, nodeSigner.get_peer_storage_key()) +- ChannelManager.constructor_new(feeEst, chainWatch, broadcaster, router.as_Router(), + msgRouter.as_MessageRouter(), logger, entropySource, nodeSigner, signerProvider, config, params, ts:number) +- PeerManager.constructor_new(chanMsgHandler, routingHandler, onionHandler, customHandler, + chainMonitor.as_SendOnlyMessageHandler(), nonce:number, eph32, logger, nodeSigner) + +