diff --git a/.gitignore b/.gitignore index 94afd5b..1691332 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ docs/.vitepress/cache docs/public/gea-ui-showcase website/docs .worktrees +.idea +.data \ No newline at end of file diff --git a/README.md b/README.md index fe1a1b5..a5817bf 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ This scaffolds a Vite-powered project with TypeScript, a sample store, class and | [`@geajs/core`](packages/gea) | Core framework — stores, components, reactivity, DOM patching | [![npm](https://img.shields.io/npm/v/@geajs/core.svg)](https://www.npmjs.com/package/@geajs/core) | | [`@geajs/ui`](packages/gea-ui) | Headless UI primitives — accessible, composable components built on [Zag.js](https://zagjs.com) | [![npm](https://img.shields.io/npm/v/@geajs/ui.svg)](https://www.npmjs.com/package/@geajs/ui) | | [`@geajs/mobile`](packages/gea-mobile) | Mobile UI primitives — views, navigation, gestures, layout | [![npm](https://img.shields.io/npm/v/@geajs/mobile.svg)](https://www.npmjs.com/package/@geajs/mobile) | +| [`@geajs/ssg`](packages/gea-ssg) | Static site generation — markdown content, dynamic routes, sitemap | [![npm](https://img.shields.io/npm/v/@geajs/ssg.svg)](https://www.npmjs.com/package/@geajs/ssg) | | [`@geajs/vite-plugin`](packages/vite-plugin-gea) | Vite plugin — JSX transform, reactivity wiring, HMR | [![npm](https://img.shields.io/npm/v/@geajs/vite-plugin.svg)](https://www.npmjs.com/package/@geajs/vite-plugin) | | [`create-gea`](packages/create-gea) | Project scaffolder — `npm create gea@latest` | [![npm](https://img.shields.io/npm/v/create-gea.svg)](https://www.npmjs.com/package/create-gea) | | [`gea-tools`](packages/gea-tools) | VS Code / Cursor extension — completions, hover, diagnostics | — | @@ -117,6 +118,7 @@ See the full comparisons: [React vs Gea](docs/comparison/react-vs-gea.md) | [Vue | [router](examples/router) | Client-side routing with `RouterView`, `Link`, and dynamic params | | [kanban](examples/kanban) | Kanban board with drag semantics | | [mobile-showcase](examples/mobile-showcase) | Mobile UI showcase using `@geajs/mobile` components | +| [ssg-basic](examples/ssg-basic) | Static site with markdown content, dynamic routes, and sitemap | ## Documentation @@ -126,6 +128,7 @@ Full documentation is available in the [docs](docs/) directory, covering: - [Stores](docs/core-concepts/stores.md) and [Components](docs/core-concepts/components.md) - [JSX Syntax](docs/core-concepts/jsx-syntax.md) - [Router](docs/gea-router/overview.md) +- [Static Site Generation](docs/tooling/ssg.md) - [Gea UI](docs/gea-ui/overview.md) - [Gea Mobile](docs/gea-mobile/overview.md) - [API Reference](docs/api-reference.md) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 6edcecf..fc00b67 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -46,6 +46,7 @@ ## Tooling - [Vite Plugin](tooling/vite-plugin.md) +- [Static Site Generation](tooling/ssg.md) - [create-gea](tooling/create-gea.md) - [VS Code Extension](tooling/vscode-extension.md) diff --git a/docs/tooling/ssg.md b/docs/tooling/ssg.md new file mode 100644 index 0000000..8bd8c79 --- /dev/null +++ b/docs/tooling/ssg.md @@ -0,0 +1,423 @@ +# Static Site Generation + +`@geajs/ssg` pre-renders your Gea application to static HTML at build time. Every route becomes an HTML file — instant first paint, full SEO, and selective client-side hydration. Pages without interactive components ship zero JavaScript. + +## Installation + +```bash +npm install -D @geajs/ssg +``` + +Requires `@geajs/core` ^1.0.0 and `vite` ^8.0.0 as peer dependencies. + +## Setup + +Add `geaSSG()` to your Vite config after `geaPlugin()`: + +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import { geaPlugin } from '@geajs/vite-plugin' +import { geaSSG } from '@geajs/ssg/vite' + +export default defineConfig({ + plugins: [ + geaPlugin(), + geaSSG({ + contentDir: 'src/content', + sitemap: { hostname: 'https://example.com' }, + robots: true, + minify: true, + }), + ], +}) +``` + +Your `src/App.tsx` must export a `routes` object and `App` (or a default export): + +```tsx +import { Component, RouterView, Link, Head } from '@geajs/core' + +export const routes = { + '/': Home, + '/about': About, + '/blog': Blog, + '/blog/:slug': { component: BlogPost, content: 'blog' }, + '*': NotFound, +} + +export default class App extends Component { + template() { + return ( +
+ + + +
+ ) + } +} +``` + +Running `vite build` renders every route to `dist/`: + +``` +dist/ +├── index.html (/) +├── about/index.html (/about) +├── blog/ +│ ├── index.html (/blog) +│ ├── hello/index.html +│ └── world/index.html +├── 404.html +├── sitemap.xml +└── robots.txt +``` + +## Head Management + +The `Head` component manages per-page ``, meta tags, Open Graph, Twitter Cards, canonical URLs, and JSON-LD structured data. + +### Basic Usage + +```tsx +import { Component, Head } from '@geajs/core' + +class About extends Component { + template() { + return ( + <div> + <Head title="About — My Site" description="Learn about us" /> + <h1>About</h1> + </div> + ) + } +} +``` + +### Full Configuration + +```tsx +class BlogPost extends Component { + template(props) { + const post = ssg.file('blog', props?.slug) + return ( + <article> + <Head + title={post?.frontmatter.title + ' | Blog'} + description={post?.frontmatter.excerpt} + url={'/blog/' + post?.slug} + image="/og.png" + type="article" + lastmod={post?.frontmatter.date} + jsonld={{ + '@type': 'BlogPosting', + headline: post?.frontmatter.title, + datePublished: post?.frontmatter.date, + }} + meta={[{ name: 'author', content: 'John Doe' }]} + link={[{ rel: 'alternate', hreflang: 'tr', href: '/tr/blog/' + post?.slug }]} + /> + <h1>{post?.frontmatter.title}</h1> + <div>{post?.html}</div> + </article> + ) + } +} +``` + +### How It Works + +- Set a default `Head` in your App component, then override per page +- Scalar values (`title`, `description`, `image`, `url`, `type`) are replaced by child pages +- Array values (`meta`, `link`) are merged +- **SSG:** Tags are injected into the static HTML `<head>` +- **Browser:** `document.title` and meta elements are updated on navigation + +### Shorthand Expansion + +| Prop | Generates | +| --- | --- | +| `title` | `<title>`, `og:title`, `twitter:title` | +| `description` | `<meta name="description">`, `og:description`, `twitter:description` | +| `image` | `og:image`, `twitter:image`, `twitter:card` | +| `url` | `<link rel="canonical">`, `og:url` | +| `type` | `og:type` | + +## Markdown Content + +The `ssg` accessor reads markdown files with YAML frontmatter from a content directory. Use it inside `template()`: + +```tsx +import { ssg } from '@geajs/ssg' + +class Blog extends Component { + template() { + const posts = ssg.content<{ title: string; date: string }>('blog', { + sort: (a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime(), + }) + return ( + <ul> + {posts.map(p => `<li>${p.frontmatter.title}</li>`).join('')} + </ul> + ) + } +} +``` + +Each returned file has: + +- `slug` — filename without extension +- `frontmatter` — parsed YAML metadata +- `html` — rendered HTML +- `content` — raw markdown (available at build time; omitted in client payload to reduce size) + +A typical markdown file: + +```markdown +--- +title: Hello World +date: 2026-01-15 +excerpt: Getting started with Gea SSG. +--- + +# Hello World + +This is the body content. It supports **bold**, `code`, and [links](https://example.com). +``` + +### Single File Lookup + +Use `ssg.file()` to look up a single content file by slug: + +```tsx +class BlogPost extends Component { + template(props) { + const post = ssg.file('blog', props?.slug) + if (!post) return '<div>Not found</div>' + return ( + <article> + <h1>{post.frontmatter.title}</h1> + <div>{post.html}</div> + </article> + ) + } +} +``` + +## Dynamic Routes + +### Content-Based Routes + +Parameterized routes auto-generate pages from content files. Each `.md` file's slug becomes a route parameter: + +```tsx +export const routes = { + '/blog/:slug': { component: BlogPost, content: 'blog' }, +} +``` + +With three files in `src/content/blog/` (`hello.md`, `world.md`, `intro.md`), this generates three pages: `/blog/hello`, `/blog/world`, `/blog/intro`. + +### Explicit Paths + +For non-content parameterized routes, provide explicit paths: + +```tsx +export const routes = { + '/user/:id': { + component: UserPage, + paths: [{ params: { id: '1' } }, { params: { id: '2' } }], + }, +} +``` + +## 404 Page + +Add a wildcard `*` route to generate a `404.html`: + +```tsx +export const routes = { + '/': Home, + '/about': About, + '*': NotFound, +} + +class NotFound extends Component { + template() { + return ( + <div> + <Head title="404 — Page Not Found" /> + <h1>Page not found</h1> + <a href="/">Go home</a> + </div> + ) + } +} +``` + +The preview server automatically serves `404.html` for unmatched routes. + +## Layouts + +Route groups with `layout` components work automatically. The SSG renders layouts wrapping page content through `Outlet`, just like client-side rendering: + +```tsx +export const routes = { + '/': Home, + '/docs': { + layout: DocsLayout, + children: { + '/getting-started': GettingStarted, + '/api': ApiReference, + }, + }, +} +``` + +## Active Links + +`Link` components get `data-active` attributes in static output matching the current route. Style them with CSS: + +```css +[data-active] { + font-weight: bold; + color: var(--accent); +} +``` + +## MPA Hydration (Zero JS on Static Pages) + +By default, the SSG build bundles your entire app as a single-page application. With the `hydrate` option, you can switch to **MPA mode**: only pages that contain interactive components get JavaScript — everything else is pure HTML with zero JS. + +```ts +// vite.config.ts +geaSSG({ + contentDir: 'src/content', + hydrate: ['Counter', 'LiveClock'], +}) +``` + +```ts +// src/main.ts +import { hydrate } from '@geajs/ssg' +import Counter from './views/Counter' +import LiveClock from './views/LiveClock' +import './styles.css' + +hydrate({ Counter, LiveClock }) +``` + +### How it works + +1. During build, the SSG renderer tracks which components from the `hydrate` list appear on each page and marks them with `data-gea` attributes. +2. Pages **without** any hydrated components have their `<script type="module">` tags stripped — zero JavaScript. +3. Pages **with** hydrated components keep their JS. On load, `hydrate()` finds each `data-gea` element, creates the component instance with a matching ID, and attaches reactive bindings and event handlers to the existing SSG DOM — no re-render, no flash. + +### Content API in MPA mode + +When `hydrate` is set, no global `_ssg/content.js` file is generated. `ssg.content()` and `ssg.file()` will return empty results on the client. This is by design — in MPA mode content is baked into the SSG HTML at build time. If you need client-side content access, use the default SPA mode (omit `hydrate`). + +### When to use MPA mode + +Sites where most pages are static content (blogs, docs, marketing) and only a few pages need interactivity (forms, counters, live data). + +## Dev Mode + +In development (`vite dev`), content is preloaded and injected into the page so `ssg.content()` and `ssg.file()` work without a build step. Content file changes trigger automatic page reload. + +When using MPA mode with `hydrate`, the dev server falls back to full SPA rendering (Router + all views) so you can navigate and develop normally. The MPA behavior only applies to production builds. + +## Sitemap + +Pass a `sitemap` option to generate `sitemap.xml`: + +```ts +geaSSG({ + sitemap: { + hostname: 'https://example.com', + changefreq: 'weekly', + priority: 0.8, + exclude: ['/404'], + }, +}) +``` + +Pages with a `lastmod` value in their `Head` component get per-URL last-modified dates in the sitemap. + +## robots.txt + +Enable robots.txt generation: + +```ts +geaSSG({ robots: true }) +``` + +When `sitemap.hostname` is set, the Sitemap URL is automatically included. Customize rules: + +```ts +geaSSG({ + robots: { + disallow: ['/admin', '/private'], + allow: ['/public'], + sitemap: false, + }, +}) +``` + +## HTML Minification + +Enable minification to reduce output size: + +```ts +geaSSG({ minify: true }) +``` + +Removes HTML comments, collapses whitespace, and strips inter-tag spaces while preserving `<pre>`, `<code>`, `<script>`, `<style>`, and `<textarea>` content. + +## Trailing Slash + +Control URL format with `trailingSlash`: + +```ts +geaSSG({ trailingSlash: false }) // /about -> about.html +geaSSG({ trailingSlash: true }) // /about -> about/index.html (default) +``` + +Affects output file paths, sitemap URLs, and preview server routing. + +## Plugin Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `entry` | `string` | `'src/App.tsx'` | App entry file | +| `contentDir` | `string` | — | Markdown content directory (relative to project root) | +| `sitemap` | `boolean \| SitemapOptions` | — | Generate sitemap.xml | +| `robots` | `boolean \| RobotsOptions` | — | Generate robots.txt | +| `minify` | `boolean` | `false` | Minify HTML output | +| `trailingSlash` | `boolean` | `true` | URL format for generated files | +| `appElementId` | `string` | `'app'` | Mount element id in index.html | +| `routes` | `RouteMap` | — | Override routes (default: loaded from entry file) | +| `app` | `Component` | — | Override app component (default: loaded from entry file) | +| `hydrate` | `string[]` | — | Component class names to hydrate on client (enables MPA mode) | +| `base` | `string` | `'/'` | Base path for asset URLs | +| `concurrency` | `number` | `4` | Max concurrent page renders | +| `onBeforeRender` | `function` | — | Hook before each page render | +| `onAfterRender` | `function` | — | Hook after render, can transform HTML | +| `onRenderError` | `function` | — | Custom error handler per route | + +## Programmatic API + +```ts +// Build-time / server-side +import { ssg, generate, renderToString, crawlRoutes, parseShell, preloadContent } from '@geajs/ssg' + +// Client-side (browser) +import { hydrate, ssg } from '@geajs/ssg' // aliased to client module by vite-plugin +``` + +All build-time exports are available from the main entry point for custom pipelines. The `hydrate` function is only available from the client entry (`@geajs/ssg/client`), which the Vite plugin aliases automatically. diff --git a/examples/ssg-basic/README.md b/examples/ssg-basic/README.md new file mode 100644 index 0000000..a9e91fe --- /dev/null +++ b/examples/ssg-basic/README.md @@ -0,0 +1,3 @@ +# SSG Basic + +Static site generation example with Gea. Demonstrates `@geajs/ssg` with static routes, parameterized routes using `getStaticPaths`, markdown content loading via `loadContent`, layout support, active link highlighting, and sitemap generation — all pre-rendered to static HTML with zero client JavaScript. diff --git a/examples/ssg-basic/index.html b/examples/ssg-basic/index.html new file mode 100644 index 0000000..d833cd3 --- /dev/null +++ b/examples/ssg-basic/index.html @@ -0,0 +1,18 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <title>SSG Basic - Gea + + + + + + +
+ + + diff --git a/examples/ssg-basic/package-lock.json b/examples/ssg-basic/package-lock.json new file mode 100644 index 0000000..51322e8 --- /dev/null +++ b/examples/ssg-basic/package-lock.json @@ -0,0 +1,1502 @@ +{ + "name": "ssg-basic-gea", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ssg-basic-gea", + "version": "1.0.0", + "dependencies": { + "@geajs/core": "file:../../packages/gea" + }, + "devDependencies": { + "@geajs/ssg": "file:../../packages/gea-ssg", + "@geajs/vite-plugin": "file:../../packages/vite-plugin-gea", + "tsx": "^4.21.0", + "typescript": "~5.8.0", + "vite": "^8.0.0" + } + }, + "../../packages/gea": { + "name": "@geajs/core", + "version": "1.0.4", + "license": "MIT", + "devDependencies": { + "@types/node": "^25.5.0", + "c8": "^11.0.0", + "jsdom": "^29.0.0", + "quill": "^2.0.3", + "tsdown": "^0.21.2", + "tsx": "^4.21.0", + "typescript": "~5.8.0" + } + }, + "../../packages/gea-ssg": { + "name": "@geajs/ssg", + "version": "1.0.0", + "dev": true, + "license": "MIT", + "devDependencies": { + "tsup": "^8.5.1", + "tsx": "^4.21.0", + "typescript": "~5.8.0" + }, + "peerDependencies": { + "@geajs/core": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "../../packages/vite-plugin-gea": { + "name": "@geajs/vite-plugin", + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "eszter": "^0.3.0" + }, + "devDependencies": { + "@codemirror/commands": "^6.10.3", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/language": "^6.12.2", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.40.0", + "@lezer/highlight": "^1.2.3", + "@rollup/plugin-commonjs": "^29.0.2", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@types/node": "^25.4.0", + "c8": "^11.0.0", + "cssstyle": "^6.2.0", + "jsdom": "^28.1.0", + "rollup": "^4.60.0", + "rollup-plugin-esbuild": "^6.2.1", + "tsup": "^8.5.1", + "tsx": "^4.21.0", + "typescript": "^5.9.3" + }, + "peerDependencies": { + "vite": "^8.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@geajs/core": { + "resolved": "../../packages/gea", + "link": true + }, + "node_modules/@geajs/ssg": { + "resolved": "../../packages/gea-ssg", + "link": true + }, + "node_modules/@geajs/vite-plugin": { + "resolved": "../../packages/vite-plugin-gea", + "link": true + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz", + "integrity": "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz", + "integrity": "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz", + "integrity": "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz", + "integrity": "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz", + "integrity": "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz", + "integrity": "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz", + "integrity": "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz", + "integrity": "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz", + "integrity": "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz", + "integrity": "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz", + "integrity": "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz", + "integrity": "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz", + "integrity": "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz", + "integrity": "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz", + "integrity": "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz", + "integrity": "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.11.tgz", + "integrity": "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.11" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.11", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.11", + "@rolldown/binding-darwin-x64": "1.0.0-rc.11", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.11", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.2.tgz", + "integrity": "sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.11", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/examples/ssg-basic/package.json b/examples/ssg-basic/package.json new file mode 100644 index 0000000..f824760 --- /dev/null +++ b/examples/ssg-basic/package.json @@ -0,0 +1,20 @@ +{ + "name": "ssg-basic-gea", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@geajs/core": "file:../../packages/gea" + }, + "devDependencies": { + "vite": "^8.0.0", + "@geajs/vite-plugin": "file:../../packages/vite-plugin-gea", + "@geajs/ssg": "file:../../packages/gea-ssg", + "typescript": "~5.8.0", + "tsx": "^4.21.0" + } +} diff --git a/examples/ssg-basic/src/App.tsx b/examples/ssg-basic/src/App.tsx new file mode 100644 index 0000000..fccc378 --- /dev/null +++ b/examples/ssg-basic/src/App.tsx @@ -0,0 +1,36 @@ +import { Component } from '@geajs/core' +import { Head, Link, RouterView } from '@geajs/core' +import Home from './views/Home' +import About from './views/About' +import Contact from './views/Contact' +import Blog from './views/Blog' +import BlogPost from './views/BlogPost' +import NotFound from './views/NotFound' + +export const routes = { + '/': Home, + '/about': About, + '/contact': Contact, + '/blog': Blog, + '/blog/:slug': { component: BlogPost, content: 'blog' }, + '*': NotFound, +} + +export default class App extends Component { + template() { + return ( +
+ + +
+ +
+
+ ) + } +} diff --git a/examples/ssg-basic/src/content/blog/getting-started-with-gea.md b/examples/ssg-basic/src/content/blog/getting-started-with-gea.md new file mode 100644 index 0000000..752652f --- /dev/null +++ b/examples/ssg-basic/src/content/blog/getting-started-with-gea.md @@ -0,0 +1,40 @@ +--- +title: Getting Started with Gea +date: 2025-03-15 +excerpt: Learn how to build reactive UIs with Gea — a compile-time JSX framework with no virtual DOM. +--- + +Gea is a lightweight reactive UI framework that compiles JSX at build time and uses proxy-based stores for surgical DOM updates. At **~13kb gzipped**, it delivers a fast, minimal footprint without a virtual DOM. + +## Installation + +```bash +npm create gea@latest my-app +``` + +## Your First Component + +Components in Gea are class-based with a `template()` method that returns JSX: + +```tsx +import { Component } from '@geajs/core' + +export default class Hello extends Component { + template() { + return

Hello, Gea!

+ } +} +``` + +## Reactive Stores + +Stores use JavaScript `Proxy` under the hood. When you mutate a property, only the DOM nodes that depend on it get updated — no diffing, no reconciliation: + +```tsx +import { Store } from '@geajs/core' + +const counter = new Store({ count: 0 }) +counter.count++ // only the bound DOM node updates +``` + +Start building with `npm run dev` and enjoy instant HMR powered by Vite. diff --git a/examples/ssg-basic/src/content/blog/reactive-stores-deep-dive.md b/examples/ssg-basic/src/content/blog/reactive-stores-deep-dive.md new file mode 100644 index 0000000..a7606d1 --- /dev/null +++ b/examples/ssg-basic/src/content/blog/reactive-stores-deep-dive.md @@ -0,0 +1,53 @@ +--- +title: Reactive Stores Deep Dive +date: 2025-03-05 +excerpt: How Gea's proxy-based stores track changes and trigger surgical DOM updates. +--- + +Gea stores use JavaScript `Proxy` to intercept property access and mutations. When a store property changes, only the DOM nodes that depend on that specific property are updated. + +## The Proxy Pattern + +Every `Store` instance wraps its data in a `Proxy`: + +```tsx +import { Store } from '@geajs/core' + +class AppState extends Store { + count = 0 + name = 'World' +} + +const state = new AppState() +``` + +When you access `state.count` inside a `template()`, Gea records the dependency. When `state.count` changes, only that specific binding updates. + +## No Virtual DOM + +Unlike React or Vue, Gea doesn't diff a virtual tree. The compile-time JSX transform generates direct DOM update instructions: + +- **React**: render → diff → patch +- **Gea**: store change → update bound node + +This means updates are `O(1)` per changed property, not `O(n)` where n is the tree size. + +## Nested Objects + +Stores support deep reactivity. Nested objects and arrays are automatically wrapped in proxies: + +```tsx +class TodoStore extends Store { + todos = [ + { text: 'Learn Gea', done: false }, + { text: 'Build an app', done: false }, + ] +} + +const store = new TodoStore() +store.todos[0].done = true // triggers update +``` + +## Observer Pattern + +Under the hood, each property maintains an observer tree. Components subscribe during rendering and unsubscribe on disposal — no memory leaks. diff --git a/examples/ssg-basic/src/content/blog/understanding-ssg.md b/examples/ssg-basic/src/content/blog/understanding-ssg.md new file mode 100644 index 0000000..83e8935 --- /dev/null +++ b/examples/ssg-basic/src/content/blog/understanding-ssg.md @@ -0,0 +1,49 @@ +--- +title: Understanding Static Site Generation +date: 2025-03-10 +excerpt: What SSG means, why it matters, and how @geajs/ssg pre-renders your routes at build time. +--- + +Static Site Generation (SSG) pre-renders your pages at build time into **plain HTML files**. This means zero JavaScript is needed for initial content display, giving you instant loads and perfect SEO. + +## How It Works + +The `@geajs/ssg` package does three things at build time: + +1. **Crawls** your route map to discover all pages +2. **Renders** each component via `Component.template()` +3. **Writes** the output HTML to disk + +## Dynamic Routes + +For parameterized routes like `/blog/:slug`, you define a static `getStaticPaths()` method: + +```tsx +export default class BlogPost extends Component { + static getStaticPaths() { + return posts.map(p => ({ + params: { slug: p.slug }, + props: { title: p.title, html: p.html }, + })) + } +} +``` + +## Markdown Support + +You can also write content in Markdown files with frontmatter: + +```md +--- +title: My Post +date: 2025-03-10 +--- + +Content goes here with **bold**, *italic*, and `code`. +``` + +Load them with `loadContent()` and pass the rendered HTML as props. + +## The Result + +Each page becomes a standalone `.html` file — no client JS, no loading spinners. Just instant content. diff --git a/examples/ssg-basic/src/main.ts b/examples/ssg-basic/src/main.ts new file mode 100644 index 0000000..55e60d9 --- /dev/null +++ b/examples/ssg-basic/src/main.ts @@ -0,0 +1,13 @@ +import { hydrate } from '@geajs/ssg' +import Counter from './views/Counter' +import LiveClock from './views/LiveClock' +import './styles.css' + +if (!hydrate({ Counter, LiveClock }) && import.meta.env.DEV) { + // Dev mode only: no SSG content, render full app + import('./App').then(({ default: App }) => { + const root = document.getElementById('app')! + root.innerHTML = '' + new App().render(root) + }) +} diff --git a/examples/ssg-basic/src/styles.css b/examples/ssg-basic/src/styles.css new file mode 100644 index 0000000..32b7a55 --- /dev/null +++ b/examples/ssg-basic/src/styles.css @@ -0,0 +1,254 @@ +:root { + --bg-dark: #0f1419; + --bg-card: #1a2332; + --accent: #00d4aa; + --accent-dim: rgba(0, 212, 170, 0.15); + --text: #e7edf4; + --text-muted: #8b9cad; + --border: #2d3a4d; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: 'DM Sans', system-ui, sans-serif; + background: var(--bg-dark); + color: var(--text); + min-height: 100vh; +} + +#app { + max-width: 640px; + margin: 0 auto; + padding: 24px 20px; +} + +.nav { + display: flex; + gap: 8px; + padding-bottom: 20px; + margin-bottom: 24px; + border-bottom: 1px solid var(--border); + flex-wrap: wrap; +} + +.nav-link { + padding: 10px 18px; + border: 2px solid var(--border); + border-radius: 8px; + background: transparent; + color: var(--text-muted); + font-size: 0.9rem; + font-weight: 500; + text-decoration: none; +} + +.nav-link:hover, +.nav-link[data-active] { + border-color: var(--accent); + color: var(--accent); +} + +.content { + min-height: 300px; +} + +.view h1 { + font-size: 1.5rem; + font-weight: 600; + margin: 0 0 16px 0; +} + +.view p { + color: var(--text-muted); + line-height: 1.6; + margin: 0 0 12px 0; +} + +.view code { + font-family: 'JetBrains Mono', monospace; + font-size: 0.85em; + background: var(--bg-card); + padding: 2px 6px; + border-radius: 4px; + border: 1px solid var(--border); +} + +.feature-list { + list-style: none; + padding: 0; + margin: 0 0 16px 0; +} + +.feature-list li { + padding: 10px 16px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + margin-bottom: 8px; + color: var(--text-muted); + line-height: 1.5; +} + +.feature-list li strong { + color: var(--accent); +} + +.card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + padding: 20px; + margin-bottom: 12px; +} + +.card h2 { + font-size: 1.1rem; + font-weight: 600; + margin: 0 0 8px 0; +} + +.card p { + margin: 0; +} + +.badge { + display: inline-block; + padding: 4px 12px; + border-radius: 20px; + background: var(--accent-dim); + color: var(--accent); + font-size: 0.85rem; + font-weight: 500; + margin-bottom: 16px; +} + +.post-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.post-card { + display: block; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + padding: 20px; + text-decoration: none; + color: inherit; + transition: border-color 0.15s; +} + +.post-card:hover { + border-color: var(--accent); +} + +.post-card time { + display: block; + color: var(--text-muted); + font-size: 0.8rem; + margin-bottom: 6px; +} + +.post-card h2 { + font-size: 1.1rem; + font-weight: 600; + margin: 0 0 6px 0; + color: var(--text); +} + +.post-card p { + margin: 0; + font-size: 0.9rem; +} + +.back-link { + display: inline-block; + color: var(--accent); + text-decoration: none; + font-size: 0.9rem; + margin-bottom: 16px; +} + +.back-link:hover { + text-decoration: underline; +} + +.post time { + display: block; + color: var(--text-muted); + font-size: 0.85rem; + margin-bottom: 8px; +} + +.post-body { + color: var(--text-muted); + line-height: 1.7; +} + +.live-clock { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + font-size: 0.85rem; + color: var(--text-muted); + margin-top: 16px; +} + +.clock-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--accent); + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } +} + +.counter { + display: flex; + align-items: center; + gap: 16px; + margin-top: 12px; +} + +.counter button { + width: 36px; + height: 36px; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--surface); + color: var(--text); + font-size: 1.2rem; + cursor: pointer; + transition: background 0.15s; +} + +.counter button:hover { + background: var(--accent); + color: #fff; +} + +.counter-value { + font-size: 1.5rem; + font-weight: 600; + min-width: 40px; + text-align: center; +} diff --git a/examples/ssg-basic/src/views/About.tsx b/examples/ssg-basic/src/views/About.tsx new file mode 100644 index 0000000..a2407ef --- /dev/null +++ b/examples/ssg-basic/src/views/About.tsx @@ -0,0 +1,37 @@ +import { Component, Head } from '@geajs/core' + +const features = [ + { name: 'Compile-time JSX', description: 'No runtime template parsing — JSX is compiled at build time' }, + { name: 'Proxy-based Stores', description: 'Surgical DOM updates without virtual DOM diffing' }, + { name: 'Tiny Footprint', description: '~13kb gzipped — minimal overhead for maximum performance' }, + { name: 'SSG Support', description: 'Pre-render pages at build time for instant loads' }, +] + +export default class About extends Component { + template() { + return ( +
+ +

About

+

+ Gea is a lightweight reactive UI framework that compiles JSX at build time and uses proxy-based stores for + surgical DOM updates — all without a virtual DOM. +

+ +

Features

+
+ {features + .map( + (f) => ` +
+

${f.name}

+

${f.description}

+
+ `, + ) + .join('')} +
+
+ ) + } +} diff --git a/examples/ssg-basic/src/views/Blog.tsx b/examples/ssg-basic/src/views/Blog.tsx new file mode 100644 index 0000000..ece3901 --- /dev/null +++ b/examples/ssg-basic/src/views/Blog.tsx @@ -0,0 +1,31 @@ +import { Component, Head } from '@geajs/core' +import { ssg } from '@geajs/ssg' + +export default class Blog extends Component { + template() { + const posts = ssg.content('blog', { + sort: (a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime(), + }) + + return ( +
+ +

Blog

+

Articles about Gea and modern web development.

+
+ {posts + .map( + (post) => ` + + +

${post.frontmatter.title}

+

${post.frontmatter.excerpt}

+
+ `, + ) + .join('')} +
+
+ ) + } +} diff --git a/examples/ssg-basic/src/views/BlogPost.tsx b/examples/ssg-basic/src/views/BlogPost.tsx new file mode 100644 index 0000000..3b0eb8d --- /dev/null +++ b/examples/ssg-basic/src/views/BlogPost.tsx @@ -0,0 +1,45 @@ +import { Component, Head } from '@geajs/core' +import { ssg } from '@geajs/ssg' + +export default class BlogPost extends Component { + template(props: any) { + const post = ssg.file('blog', props?.slug) + + if (!post) { + return ( +
+ +

Post not found

+ + ← Back to blog + +
+ ) + } + + return ( +
+ + + ← Back to blog + +
+ +

{post.frontmatter.title}

+
{post.html}
+
+
+ ) + } +} diff --git a/examples/ssg-basic/src/views/Contact.tsx b/examples/ssg-basic/src/views/Contact.tsx new file mode 100644 index 0000000..6d65ec2 --- /dev/null +++ b/examples/ssg-basic/src/views/Contact.tsx @@ -0,0 +1,33 @@ +import { Component, Head } from '@geajs/core' +import Counter from './Counter' +import LiveClock from './LiveClock' + +export default class Contact extends Component { + template() { + return ( +
+ +

Contact

+

Get in touch with us through the channels below.

+
+

GitHub

+

+ + github.com/dashersw/gea + +

+
+
+

Email

+

hello@geajs.dev

+
+
+

Reactive Counter

+

This counter works after JS loads — SSG renders the initial state.

+ +
+ +
+ ) + } +} diff --git a/examples/ssg-basic/src/views/Counter.tsx b/examples/ssg-basic/src/views/Counter.tsx new file mode 100644 index 0000000..e97dce6 --- /dev/null +++ b/examples/ssg-basic/src/views/Counter.tsx @@ -0,0 +1,23 @@ +import { Component } from '@geajs/core' + +export default class Counter extends Component { + count = 0 + + increment() { + this.count++ + } + + decrement() { + this.count-- + } + + template() { + return ( +
+ + {this.count} + +
+ ) + } +} diff --git a/examples/ssg-basic/src/views/Home.tsx b/examples/ssg-basic/src/views/Home.tsx new file mode 100644 index 0000000..a141dc3 --- /dev/null +++ b/examples/ssg-basic/src/views/Home.tsx @@ -0,0 +1,28 @@ +import { Component, Head } from '@geajs/core' + +export default class Home extends Component { + template() { + return ( +
+ + Static Site Generation +

Welcome to Gea SSG

+

+ This page was statically generated at build time. No JavaScript is required to display this content — it is + served as pure HTML. +

+ +
+ ) + } +} diff --git a/examples/ssg-basic/src/views/LiveClock.tsx b/examples/ssg-basic/src/views/LiveClock.tsx new file mode 100644 index 0000000..3a95cae --- /dev/null +++ b/examples/ssg-basic/src/views/LiveClock.tsx @@ -0,0 +1,20 @@ +import { Component } from '@geajs/core' + +export default class LiveClock extends Component { + time = new Date().toLocaleTimeString() + + created() { + setInterval(() => { + this.time = new Date().toLocaleTimeString() + }, 1000) + } + + template() { + return ( +
+ + Live — {this.time} +
+ ) + } +} diff --git a/examples/ssg-basic/src/views/NotFound.tsx b/examples/ssg-basic/src/views/NotFound.tsx new file mode 100644 index 0000000..6a7fec5 --- /dev/null +++ b/examples/ssg-basic/src/views/NotFound.tsx @@ -0,0 +1,16 @@ +import { Component, Head } from '@geajs/core' + +export default class NotFound extends Component { + template() { + return ( +
+ +

404

+

The page you are looking for does not exist.

+ + ← Back to home + +
+ ) + } +} diff --git a/examples/ssg-basic/tsconfig.json b/examples/ssg-basic/tsconfig.json new file mode 100644 index 0000000..43c1fc3 --- /dev/null +++ b/examples/ssg-basic/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../packages/gea/tsconfig.json", + "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts", "../../packages/vite-plugin-gea/gea-env.d.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/examples/ssg-basic/vite.config.ts b/examples/ssg-basic/vite.config.ts new file mode 100644 index 0000000..5cef548 --- /dev/null +++ b/examples/ssg-basic/vite.config.ts @@ -0,0 +1,24 @@ +import { resolve } from 'node:path' +import { defineConfig } from 'vite' +import { geaPlugin } from '@geajs/vite-plugin' +import { geaSSG } from '@geajs/ssg/vite' + +export default defineConfig({ + plugins: [ + geaPlugin(), + ...geaSSG({ + contentDir: 'src/content', + hydrate: ['Counter', 'LiveClock'], + sitemap: { + hostname: 'https://gea-ssg-example.com', + }, + robots: true, + minify: true, + }), + ], + resolve: { + alias: { + '@geajs/core': resolve(__dirname, '../../packages/gea/src'), + }, + }, +}) diff --git a/package-lock.json b/package-lock.json index 51c5eb5..76d0c48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -189,6 +189,7 @@ "integrity": "sha512-Jc360x4yqb3eEg4OY4KEIdGePBxZogivKI+OGIU8aLXgAYPTECvzeOBc90312yHA1hr3AeRlAFl0rIc8lQaIrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", @@ -317,9 +318,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", - "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.3.tgz", + "integrity": "sha512-Q6mU0Z6bfj6YvnX2k9n0JxiIwrCFN59x/nWmYQnAqP000ruX/yV+5bp/GRcF5T8ncvfwJQ7fgfP74DlpKExILA==", "dev": true, "license": "MIT", "dependencies": { @@ -830,9 +831,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", - "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.2.tgz", + "integrity": "sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==", "dev": true, "license": "MIT", "dependencies": { @@ -967,6 +968,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1015,6 +1017,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -1722,6 +1725,10 @@ "resolved": "packages/gea-mobile", "link": true }, + "node_modules/@geajs/ssg": { + "resolved": "packages/gea-ssg", + "link": true + }, "node_modules/@geajs/ssr": { "resolved": "packages/gea-ssr", "link": true @@ -1787,9 +1794,9 @@ } }, "node_modules/@iconify-json/simple-icons": { - "version": "1.2.75", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.75.tgz", - "integrity": "sha512-KvcCUbvcBWb0sbqLIxHoY8z5/piXY08wcY9gfMhF+ph3AfzGMaSmZFkUY71HSXAljQngXkgs4bdKdekO0HQWvg==", + "version": "1.2.74", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.74.tgz", + "integrity": "sha512-yqaohfY6jnYjTVpuTkaBQHrWbdUrQyWXhau0r/0EZiNWYXPX/P8WWwl1DoLH5CbvDjjcWQw5J0zADhgCUklOqA==", "dev": true, "license": "CC0-1.0", "dependencies": { @@ -2052,9 +2059,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.120.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", + "integrity": "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -2107,9 +2114,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==", "cpu": [ "arm64" ], @@ -2118,15 +2125,14 @@ "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==", "cpu": [ "arm64" ], @@ -2135,15 +2141,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.10.tgz", + "integrity": "sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==", "cpu": [ "x64" ], @@ -2152,15 +2157,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.10.tgz", + "integrity": "sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==", "cpu": [ "x64" ], @@ -2169,15 +2173,14 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.10.tgz", + "integrity": "sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==", "cpu": [ "arm" ], @@ -2186,15 +2189,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==", "cpu": [ "arm64" ], @@ -2203,15 +2205,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.10.tgz", + "integrity": "sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==", "cpu": [ "arm64" ], @@ -2220,15 +2221,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==", "cpu": [ "ppc64" ], @@ -2237,15 +2237,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==", "cpu": [ "s390x" ], @@ -2254,15 +2253,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.10.tgz", + "integrity": "sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==", "cpu": [ "x64" ], @@ -2271,15 +2269,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.10.tgz", + "integrity": "sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==", "cpu": [ "x64" ], @@ -2288,15 +2285,14 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.10.tgz", + "integrity": "sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==", "cpu": [ "arm64" ], @@ -2305,21 +2301,19 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.10.tgz", + "integrity": "sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, @@ -2328,9 +2322,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.10.tgz", + "integrity": "sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==", "cpu": [ "arm64" ], @@ -2339,15 +2333,14 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.10.tgz", + "integrity": "sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==", "cpu": [ "x64" ], @@ -2356,17 +2349,15 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", - "license": "MIT", - "peer": true + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.10.tgz", + "integrity": "sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==", + "license": "MIT" }, "node_modules/@rollup/plugin-commonjs": { "version": "29.0.2", @@ -2395,6 +2386,38 @@ } } }, + "node_modules/@rollup/plugin-commonjs/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", @@ -2464,6 +2487,19 @@ } } }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", @@ -3027,6 +3063,15 @@ "undici-types": "~7.18.0" } }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -3056,17 +3101,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", + "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/type-utils": "8.57.1", + "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -3079,7 +3124,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.57.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3095,16 +3140,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", + "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3" }, "engines": { @@ -3120,14 +3166,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", + "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.57.1", + "@typescript-eslint/types": "^8.57.1", "debug": "^4.4.3" }, "engines": { @@ -3142,14 +3188,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", + "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3160,9 +3206,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", + "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", "dev": true, "license": "MIT", "engines": { @@ -3177,15 +3223,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", + "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -3202,9 +3248,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", + "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", "dev": true, "license": "MIT", "engines": { @@ -3216,16 +3262,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", + "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.57.1", + "@typescript-eslint/tsconfig-utils": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3244,16 +3290,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", + "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/scope-manager": "8.57.1", + "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3268,13 +3314,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", + "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.57.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3293,14 +3339,14 @@ "license": "ISC" }, "node_modules/@vue/compiler-core": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz", - "integrity": "sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz", + "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.2", - "@vue/shared": "3.5.31", + "@babel/parser": "^7.29.0", + "@vue/shared": "3.5.30", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -3320,28 +3366,28 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz", - "integrity": "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz", + "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.31", - "@vue/shared": "3.5.31" + "@vue/compiler-core": "3.5.30", + "@vue/shared": "3.5.30" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz", - "integrity": "sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", + "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.2", - "@vue/compiler-core": "3.5.31", - "@vue/compiler-dom": "3.5.31", - "@vue/compiler-ssr": "3.5.31", - "@vue/shared": "3.5.31", + "@babel/parser": "^7.29.0", + "@vue/compiler-core": "3.5.30", + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.8", @@ -3349,14 +3395,14 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz", - "integrity": "sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz", + "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.31", - "@vue/shared": "3.5.31" + "@vue/compiler-dom": "3.5.30", + "@vue/shared": "3.5.30" } }, "node_modules/@vue/devtools-api": { @@ -3396,57 +3442,57 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz", - "integrity": "sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz", + "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==", "dev": true, "license": "MIT", "dependencies": { - "@vue/shared": "3.5.31" + "@vue/shared": "3.5.30" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz", - "integrity": "sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz", + "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.31", - "@vue/shared": "3.5.31" + "@vue/reactivity": "3.5.30", + "@vue/shared": "3.5.30" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz", - "integrity": "sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz", + "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.31", - "@vue/runtime-core": "3.5.31", - "@vue/shared": "3.5.31", + "@vue/reactivity": "3.5.30", + "@vue/runtime-core": "3.5.30", + "@vue/shared": "3.5.30", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz", - "integrity": "sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz", + "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.31", - "@vue/shared": "3.5.31" + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30" }, "peerDependencies": { - "vue": "3.5.31" + "vue": "3.5.30" } }, "node_modules/@vue/shared": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz", - "integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz", + "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==", "dev": true, "license": "MIT" }, @@ -3557,555 +3603,555 @@ } }, "node_modules/@zag-js/accordion": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.38.2.tgz", - "integrity": "sha512-lr3K8R4c8cY1ghcU4lpav4dAXCFqFcNOrieqy4lsS5n5CSrpLbL1EFrOdTBux/CCeijmipFp14ddUZGHWHIDJQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-1.38.0.tgz", + "integrity": "sha512-EtMzxUQ3fXQKCMb5K8JAmEe9qsl5S/G5vlhLzr8Im9ODnR1OLYpSNjMYYk1hoH57igpB7BPuseqfSafoT0/i/w==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/anatomy": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.38.2.tgz", - "integrity": "sha512-3wEwuHkHiErD1r36MrC1Jd4lqvQK+wQ5EgqdEk8r3L2koD7fTdq8CB5KbMcP0JM08eFCOo2CBl3gP3VBmiTNJA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-1.38.0.tgz", + "integrity": "sha512-wmlwq2gvDd72O5W0WJizrjuo6rCuFW9H8d3guGWqTxLTVpdqV25JHkp35Qf5AnceJ9564d72+VolBZYMnjJCyQ==", "license": "MIT" }, "node_modules/@zag-js/aria-hidden": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.38.2.tgz", - "integrity": "sha512-TvMdHUouslt6RZmKZ6k41B1KTqjOFaD4T6c4sTb6qSUkuTYgyYsA7ZqimiR0vcvmmG852nSaOepvr6Jj0sRPNA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-1.38.0.tgz", + "integrity": "sha512-kiBXFexhxEebcOPmF6h5/F7oRy1U04EEj8JzsUywMijACb5ZYIeCUWOFfi0s+IvuY6KptAL4zb27MZAIxzVLIw==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/auto-resize": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.38.2.tgz", - "integrity": "sha512-0nVYusVmxDfa1j8fyycQCxcULT07BUM+23BlEslPy0Hfzt5c7CwMQFan4JiMia82RzgnWv3s+5svVMvI++ZZqw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-1.38.0.tgz", + "integrity": "sha512-R60LDs2lH0x3hZoXhGtWpl+i5zA1BgPJgFj37eYraZjuIkTVL+p10w2oqXBmldG0PArP8bfxz4bbeszO2obYLQ==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/avatar": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.38.2.tgz", - "integrity": "sha512-DmhoaJG+IcKFSWSrtfcI91WJNSJQx2PYDmChnOtNvyGIXibNHkoYXzvZTCrKsVGgKeUXFtOxSqSYMbzxgL2k2Q==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-1.38.0.tgz", + "integrity": "sha512-1sugDuVbmmk5+fSckBbw3oRXtyy6uVGKWk4SV1NE2IkWIkKP7aCWTlNY/QHLnfgxi9HfTLNvQgkFt6PdQ80Y1Q==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/checkbox": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.38.2.tgz", - "integrity": "sha512-AAljBvGog/jLWhdEFc2ADzsGODoGua8fWZs0RUYq/wQ9UBiG4w5VRyPAq5ZEzaIoSruoHm5AHN9D9VdWN9py3g==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-1.38.0.tgz", + "integrity": "sha512-0sVE4ZCbFu5lG1hF/b/dMYWZszDC/Rj5+YhQuOcsYCWemxcdxIuugIoQSYLsNZ0nSHMrgRwGrfyLDmNSUc3QOQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/clipboard": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.38.2.tgz", - "integrity": "sha512-LIDcuZDU70cvMAbIz7LgeXM+CvhRW8Ez84UPkorwT+EyDCOaCNEADabSY2AM4WKn81EV56UKkwSNblkhP3gmZA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-1.38.0.tgz", + "integrity": "sha512-G3bHENAFqjbrC/Um8hYOvy2GQH0NzDvkOdDVgkKTWh5cRhw3hgEnfOKYWmfH4uHT/hcdak4wsu7KAUtbcixTNQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/collapsible": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.38.2.tgz", - "integrity": "sha512-7yBlX/9d/A7Da/9PZoBye0nA+FjVZv7rjrUfmO2NhoERt2IV6r0S143DrwcW3ELjz+76HzV+wlP8ytruqaK1gQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-1.38.0.tgz", + "integrity": "sha512-cMSQ9ZOgBgTTXlsIOHgWfvb4BvRHNTanP9v+PKBdi0F3pbYv85ogLdMbzIKQ9X5T/dYGSkDzxpNq0K+uQKWHqA==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/collection": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.38.2.tgz", - "integrity": "sha512-1Edek5yrjHfpmciK4M5PoUbDFRgEEkFh3HMIwWjMRdcRzea9ibVhSGXvXsenafC/b3so/ApEXBhs1ch7dFn+NQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-1.38.0.tgz", + "integrity": "sha512-vhVzbotw4SyWHh1d5tzj3PGcg7gG6mokts5Bi2AZTU+qfnHUFCRZ6J4LoRDKYBtr5pZ1v0FyqdYYD1EL5VnYAg==", "license": "MIT", "dependencies": { - "@zag-js/utils": "1.38.2" + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/combobox": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.38.2.tgz", - "integrity": "sha512-J2M1vB3Keepv2Wr/sC5kue18ver+nf/7gj9LgHxSU5sgwl9Fr4wZLPH1s9ZQ7FDhBXjWsitILbf9dqR1UpyLJQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-1.38.0.tgz", + "integrity": "sha512-51QsgVyD1iL4ChIyr2+chP6yjTY63PlRLr3d5P9B6YUA0A7shKZgbQtxbez98P9a9bGPWid9ilw4h6oKOr/pag==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/aria-hidden": "1.38.2", - "@zag-js/collection": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/aria-hidden": "1.38.0", + "@zag-js/collection": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/core": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.38.2.tgz", - "integrity": "sha512-UaRU6TVOJV07phQkToR6fNceJbfPiNLx2itSh78ofHn8w9w860mNrS5tyn8HExuNwY5JVQdfMkyFlMF0LfgVLQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-1.38.0.tgz", + "integrity": "sha512-YsVQILNw/smzdf9Oelg88PTGXL+n5ZNXOJGg3Szpk5yWiNx6FCddHpNJsNfCT4r+PguLZz7I0YPUtgz+pkdIpQ==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/dom-query": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/dialog": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.38.2.tgz", - "integrity": "sha512-cVYBXiEnQ30UjudJxODoV8Au/18VyePEvJ9SollGFJYnr+/2mCue5CoraOQJqoZVED3tdUCBodz9kyjS1CcvAw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-1.38.0.tgz", + "integrity": "sha512-LjnmHup8g4YbwLVg1C2FsgMGY5wQMSAxTOtk9wk66EEEj7BAFAxnQ74roFu8yAc8x6+zdnYYJh9Q/j2ntyO+pg==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/aria-hidden": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-trap": "1.38.2", - "@zag-js/remove-scroll": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/aria-hidden": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-trap": "1.38.0", + "@zag-js/remove-scroll": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/dismissable": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.38.2.tgz", - "integrity": "sha512-4B83ZGD8YKnwiXwL4J49txeXSPdS29bPt6p00v99u6nJPlQRRubzPUQo4OJr6NxUuT9cIn2kDqt5oj8ZCwhmng==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-1.38.0.tgz", + "integrity": "sha512-VcI555h/Gm5tm2sWrhofEvvNtwqT5CCsaJpJN9V3oPu2DKZbq6Am/etz4u4riekYNOckCkdtC+vr1+weYp47RQ==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2", - "@zag-js/interact-outside": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/dom-query": "1.38.0", + "@zag-js/interact-outside": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/dom-query": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.38.2.tgz", - "integrity": "sha512-E2plmm/bDjMQhi4fmyhw2uhoQv2cgNYaG+fzLrJgepLqovCOTU85lwzaLFiB7ldG2a9DVmjbAnFX4nVVWvaxGA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-1.38.0.tgz", + "integrity": "sha512-0c1kKEvF8xDSCH1ElSJBTBjRnstuA5VDDlc/f5/7MjLyFmw/x9GucXq5tGgOjsWXr8H22G6W3Z1wGeW+taCTLA==", "license": "MIT", "dependencies": { - "@zag-js/types": "1.38.2" + "@zag-js/types": "1.38.0" } }, "node_modules/@zag-js/file-upload": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.38.2.tgz", - "integrity": "sha512-sA5gKafIRDcKc6zsEYrByQQpl2am+ioRzGcuxhwBdOIacJP3G2vs4bJx0wCbdM8O9Ue11s9KBcqfJhenDlBWMg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-1.38.0.tgz", + "integrity": "sha512-pcjuZNH5+6JmTRAW13wY707aPKHQCh7jmZGrGkfDWBWwgEmYRa6/jMBWcMe/0JuS7joaUVqPKAK0QFP+b0/2xA==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/file-utils": "1.38.2", - "@zag-js/i18n-utils": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/file-utils": "1.38.0", + "@zag-js/i18n-utils": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/file-utils": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.38.2.tgz", - "integrity": "sha512-498kXYSlUrNXdjmn0mKfovd7zQv1PnLXhNW94L0DYX7GcAqd6SQ4QohtxbT4IHrzlV9FfskKhe9ahXxV2IlNVQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-1.38.0.tgz", + "integrity": "sha512-8g5Nngqi3JmQvL48oxlE6iLc+hqvlFGjxTaHZkx8NGroTMOvFbl4kmJXNxOAmu8xG2Wv14cynzbXfwWWDkJGJg==", "license": "MIT", "dependencies": { - "@zag-js/i18n-utils": "1.38.2" + "@zag-js/i18n-utils": "1.38.0" } }, "node_modules/@zag-js/focus-trap": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.38.2.tgz", - "integrity": "sha512-m1CBTmUy7kHsMVBFlzOmxddxESv7ce6XOX+3YhXKWNJLvnt4a3bWMnh+C3CmV7B14pntTkQwfXJALR6e3gfS6Q==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/focus-trap/-/focus-trap-1.38.0.tgz", + "integrity": "sha512-wzR3YckysnN97fRQuoJwLHzw1rLeXbPpa0RIgqWsPMEesTTdADfxWw0faAiAgy8hsN79lvIRF3y1nT8CZDke4g==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/focus-visible": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.38.2.tgz", - "integrity": "sha512-ME0zulSEZLxR9jJqtwDpcWuBnZKO/8xQ+UtuYrJPvqPyUyUVkxFon+jCqNUOGjK4rHB+OjEV19LrHAzq6CQjNg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-1.38.0.tgz", + "integrity": "sha512-6Iabj3LxzzPvYtf4R5QTi9y1CR4d8HnqsGmafMJozQsRoLJ6ZZQAIdYHDmSvJgEOe9AY1UO+uo9ETnfHDBpkxA==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/hover-card": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.38.2.tgz", - "integrity": "sha512-Un/65bC+ppCktMBd798z7x90t6/kvvqkASxezV6ds0Ex56TB26KQnaRPUASV+fW6uH4nLdBOeGlCH3cC8KEPYg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-1.38.0.tgz", + "integrity": "sha512-MHxcuR2Y4jsSotFXIsYxhaOytl/JPoMqSSdIm194ApGomLxiWgCBNZdoZu0xHhVDD9MDP1IVObOUeyw5MBr5BQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/i18n-utils": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.38.2.tgz", - "integrity": "sha512-/thV2gwDtfubrL8gcBXGiNmQVcHACmYIXneLVW94wVADdsuRKV/s0QrMQXe/QH3f3M9QAU5VWQnoz9Z2ySilGQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-1.38.0.tgz", + "integrity": "sha512-ccpUWxl/Drekn3HCnCDQbkzAXX5I5AdIcIwS3RIzGCR8YGyJ/Vm2f9H+a7jFHkeeyHPNi7B73olbfV6BrVGRWQ==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/interact-outside": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.38.2.tgz", - "integrity": "sha512-jszzVoozqcO3vn1iiZ726xYXpy/mR+fLnx1tBs4Dvkg/n52Mm3Uv+pFDBfHgswqS/kRJ/U8P0fItFJjOH0yvlA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-1.38.0.tgz", + "integrity": "sha512-w0VKBfrWbY4J5Id7R+27gFzJfVtbivnmy95AhPKKtqURlj8t4hZmZyBDUZUCF5l9703ka37EYllsi/DVabIR/w==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/dom-query": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/live-region": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.38.2.tgz", - "integrity": "sha512-6qVPHEZRPbO/BKUTGXC3otZXODpsRg/lliBHUHzK7Pg83eSW4yGQI+XKRjmJnoWvB7U5vFXDC3SXG3aO0dyLYQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-1.38.0.tgz", + "integrity": "sha512-h5ehhnF3hmlmvdZakQoqnzo3ghcabYpbOTkwOOSrPi5tL4WxvL5kIkSKnmIsGRopCY9tFHXUL5s0pxqg70Zhqg==", "license": "MIT" }, "node_modules/@zag-js/menu": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.38.2.tgz", - "integrity": "sha512-n0AO5uz31csUcnk1daHf/O5T2Yqtdo8KV9PInrIxwKXwfeLM2uFTHubhP2bv/j7+R+LpnZkK5laXe9WxKueBVQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-1.38.0.tgz", + "integrity": "sha512-VEEalJztk1rECej4YyB9vjZCCEA8YT+PJAxCbohLYH9Lz2IfbpRVYHZmyVDKcYySNnGiMWl5hadZo7Gryyf0VQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/rect-utils": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/rect-utils": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/number-input": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.38.2.tgz", - "integrity": "sha512-WJJA6EusT1HGDJlzW9YpbgOJ6Qv3TM1jcBNftrGCLGNhePXBzLzsTZKp+IZdjkI+11bab5siXwU9wcQWUhmUNw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-1.38.0.tgz", + "integrity": "sha512-J8htY+l69Qg8oA+xflwYVWCUN7EWoQBjHE4yiwXg85DCSrUjrlrXwM/U0X+LmAa+Kzv2LZq78Yn7KmNTxpjHNA==", "license": "MIT", "dependencies": { "@internationalized/number": "3.6.5", - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/pagination": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.38.2.tgz", - "integrity": "sha512-pqdR7Eek6FFK4EyVRZbxwRymvunguGVAZAHrg0Aij/CnPbZZATDk4gQbGKpIgEywSYSk6m4hurjLg8L+shkfVQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-1.38.0.tgz", + "integrity": "sha512-F6tVKQre9YaDutO5WLJFttxgEQI5kdurOybSs7YFBklLhhrpy0omz3PPiRJSo2y7zop680/1icoQrq0Pj3+KAQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/pin-input": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.38.2.tgz", - "integrity": "sha512-kMmlRTOLBqxVaBM4Ly7NZi4VbB5SJUfcmyj6pxHV+YSqzFpuS8/o24CTtbLWUg9Zh4wSuM7vnx8A0viWg92V2Q==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-1.38.0.tgz", + "integrity": "sha512-lPecBshH/0PYCRpwMKbzJl22EOiPNNPzSPpyftm1B04yX/pGhVctd8NdyP7GvNzx+CRps6QVrZvGUxc6a0n68w==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/popover": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.38.2.tgz", - "integrity": "sha512-LPA4Ld/eD34/C+/jsojXU/GiG9Y4rYN1xFAJIeOdUGF9fAvMVr+TEhvNUnVV7FnIbK5hdFOy7EgAAEL34uqBow==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-1.38.0.tgz", + "integrity": "sha512-wOHnwD3w/54oA3PKGERMaDo7JUQEPJ9MK00QqiEhRyJWV6ARYsyVpjEmEIbaIdcFud5TMT6nF3OyMwdvshW7Uw==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/aria-hidden": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-trap": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/remove-scroll": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/aria-hidden": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-trap": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/remove-scroll": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/popper": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.38.2.tgz", - "integrity": "sha512-iqAFCeximjd2SGbsciXxbrHdrB/T5jeV4bf5KAPPGNM8Ce/dMcz5RteAArqBYJt1RDpB5px4RdeO8y+8vZOK/w==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-1.38.0.tgz", + "integrity": "sha512-qvTnAFGIUz9MnVGUy065ijeoycemwnOw3WKcbMBf7lUOuCyaWkRd/dvnHkidsDmwTP5HtTwCe5KJCERKo3C8Dg==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.7.6", - "@zag-js/dom-query": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/dom-query": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/progress": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.38.2.tgz", - "integrity": "sha512-PY8fD2whXqLWafcIAOt8puFcLtnzutk9z074bWrWAV0dhUAzCTuMXF4hPrB/Qf+11CHxZZS467zp8WbgdPhIEw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-1.38.0.tgz", + "integrity": "sha512-F8DijF7GwLlhkRianCnfIeySVBwMsMG6SGEJOSiecFHYJMedlgj37j3B6RRr13GDnqAGj3wAJBtvn+R8IYxv1Q==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/radio-group": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.38.2.tgz", - "integrity": "sha512-hyQq2AVCov/lgtfXraA/y2cKd9NKKdzXJT7sO2/59eKHyRYMylV3oatcQ2WqjrrnPxEfqLcQ7CWxl7NlcdnTow==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-1.38.0.tgz", + "integrity": "sha512-U1cO7fYKun3IWdfXp00c4mzegYNhfgGBm3lFfpj195jr5dXIEDfNYyto8Yf0MMUQigwFJwoQLSRHw2sW4TZ4rg==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/rating-group": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.38.2.tgz", - "integrity": "sha512-MggGwIcMWVvuRgDZJPnEvGnk5xYarbXOJnnP7BQK/31OgjRZmxwAAUKxHFDGY8vxRQ2XVTwmrPNTUVDDpQUJxQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-1.38.0.tgz", + "integrity": "sha512-6cql7pQDDDyxk7uE3RaiaOun5+J6Q7hqesk0WbBz2P8qmvokzd040aoa7xSsI+71Sdt93ESSjR7aN3tdhUHWwQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/rect-utils": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.38.2.tgz", - "integrity": "sha512-VX/EHbtq++h+rFGrj/ql3EFPuJwFQLAYjOxyMvEKF34L8N5imDodIyVqrQfumZl5MMGmKstz/x1kL4nHYAxyWQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-1.38.0.tgz", + "integrity": "sha512-tqLJliO8jLnNRUvqGbkvuH6SBfIza9hGoEoaDGZ1uWr7VHW1VCX9EUc1zunNz/YvU12M/Rh6O2i1E8Na7gYqXQ==", "license": "MIT" }, "node_modules/@zag-js/remove-scroll": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.38.2.tgz", - "integrity": "sha512-2NInlGgJmMQKMOyd5J2pc+L9/wj4NBhz028VwE31yxCK4dm3swcm5NYHdBAdpNG96ymximJsJndTkry7pnNNIA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-1.38.0.tgz", + "integrity": "sha512-j2KsP2LFHBGBvlEbVUeW5mUZoNkZ2uhK8SxApq1GgtR+p+2LB+1BfVMeVtVplodaW+Ryl6f44iqXvFDcmUxvEg==", "license": "MIT", "dependencies": { - "@zag-js/dom-query": "1.38.2" + "@zag-js/dom-query": "1.38.0" } }, "node_modules/@zag-js/select": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.38.2.tgz", - "integrity": "sha512-F0qOw0ntyVO1G80iwktVFF3XYUodP9Tfet5XrCmDN+kM2trumn94wV+1plE/7/SRjDED7uwaCSpaBXaFiv8KUw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-1.38.0.tgz", + "integrity": "sha512-S1pbBZbBxnDEWOZtlQ5Vp96LOp9qmKZCyZamX6IgnknuDz5NfT5771RVASA08A07Xu7PiZ021IMw1L0o0Wy3Hg==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/collection": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/collection": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/slider": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.38.2.tgz", - "integrity": "sha512-EBiCCLpwOA5eMcFgIMcAxNKQDePUqcVE+tTePteUVzzRONK1F/HLsLevIkpdJ4UouhPcDSsu5o2AH+w3bxW4sA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-1.38.0.tgz", + "integrity": "sha512-AGWOIFN71C4zrEaiZu5HoSPIILErWH2fHX6HXJuwHjkjnD+2gMyv7SoRgqdzcbVJLA5IYGOpd98ugHU/xGqVog==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/store": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.38.2.tgz", - "integrity": "sha512-TlJM6M2dW5W9jr6US4/R/VobLmlFrSv48ls18XffmXp+uuwZ7Aisciq0Djtwx7Y7e5KhlUWtk4ncE5PtjP4AzQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-1.38.0.tgz", + "integrity": "sha512-LDX+/oBxEIXB6zXWkYrcI3KX1POW43tXIujJN7FlFjtagD5RL8RxgiPXPFz47d9HUCe0OjqCZJc2tX9K+wfrlw==", "license": "MIT", "dependencies": { "proxy-compare": "3.0.1" } }, "node_modules/@zag-js/switch": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.38.2.tgz", - "integrity": "sha512-EIK0DO8cFcDqE6cobEh66O07nufwQnRnpVvgOWpoB13Ts/LyQ/HUxF377JRPa4EYIY2/hIt6L8zvwfgFHUGAMg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-1.38.0.tgz", + "integrity": "sha512-n67jUnU5LqZbtaQr5WW1az7QDA5uKyEaARs8pwQmhOqG0mEEZsbbzNzwBbvMjTQgLEScq3M2oP26iB+zhivLKQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/tabs": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.38.2.tgz", - "integrity": "sha512-yqCKQ+ugYtwAuF8ya6fkI577Yz96apRwHDawPem4xRq381YC5TYDrLpWvRj4UjakPOjs+1mHf2+53RYQhhEpEg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-1.38.0.tgz", + "integrity": "sha512-El2D4MlzYvLA+2zYKl1GOWblRIxAXZGH7RaWe4gCblcgm7Zqie7iKdbFruWprQo2UgkTaGrTav+sACqDLjDt1A==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/tags-input": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.38.2.tgz", - "integrity": "sha512-WV7uz5UGXThL8PqTor8tFKtLsTO79B7mRjoVNwgSKJ473C+Z9tFhJx+S6cPXvSx0PcDxYBX1DXI0NFWVgWVD4Q==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-1.38.0.tgz", + "integrity": "sha512-aeke5F837hxJrPIIfC2IgtSvpNNmyBy3FlxdK9kyxLj6i8kyMTKWkqWTHLPh1eC9YsktGwrE69///DpQBEM5Ag==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/auto-resize": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/interact-outside": "1.38.2", - "@zag-js/live-region": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/auto-resize": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/interact-outside": "1.38.0", + "@zag-js/live-region": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/toast": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.38.2.tgz", - "integrity": "sha512-VRTfTjRrnn9U8RVKr2dTUWBzMBALNy5PMxr+z7Q1hUufv0JYfzPsSkVXsbSjI5OZPklbJ5P1ugmUrtVrATyGHw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-1.38.0.tgz", + "integrity": "sha512-cGLrdDYiEvivbHO9KxopKDc1PT/klJKKcz6lPBx4lD8AsGysxrTsn92YSBLC98bV3QReepBKOLTD3/D/SQCeGA==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dismissable": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dismissable": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/toggle-group": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.38.2.tgz", - "integrity": "sha512-vl9hDypfxiwR3gcsai+lBwRcPR1NwwNVP8ZhS3mdMwi3v5Lr4R4UCZc0l2/qFPj+IQ0q01VIGJJbkkFCB7a3iQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-1.38.0.tgz", + "integrity": "sha512-p/rHDw3gcJthSJs6jrpFTjZJV+W124rDj5yUkv6BzkNPrCacTPtNiO4l5i7YTKcn9+gWKkM3RnKoPdQDR3dz/g==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/tooltip": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.38.2.tgz", - "integrity": "sha512-pFVWc6KjLujIvyOz2CzFQu4e9NVC61yE0oQpjRIQwzZAICxxiPE3QeOtb0Aw3Jhd0n2Y7qxuLyFI6gcAxI9Sxg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-1.38.0.tgz", + "integrity": "sha512-S5EnwYpR/wREV2gUm4YjiTHq9lCHD9hdwkpjF0vWmpI9av8DQWGCGHA6LMVue7h+nHQRImp3qo5SvNWmZhu8OQ==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/focus-visible": "1.38.2", - "@zag-js/popper": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/focus-visible": "1.38.0", + "@zag-js/popper": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/tree-view": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.38.2.tgz", - "integrity": "sha512-w2R+EI6/ovTjGI6Vu/z6pMS9ZUMjSsj7jAsm7ZKbfwNngSIJBAu+kdAAZbeovl9MW/gnNVgpuJuS6N4yqANdIw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-1.38.0.tgz", + "integrity": "sha512-EjlnUxsfuUluzlX/QN0RKtFd2XGYtw0sTF/jsoSJT1yJbZsvJuhARV29Dz8M4aJOpIg3e61Lz5J897fpmdUiFA==", "license": "MIT", "dependencies": { - "@zag-js/anatomy": "1.38.2", - "@zag-js/collection": "1.38.2", - "@zag-js/core": "1.38.2", - "@zag-js/dom-query": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/anatomy": "1.38.0", + "@zag-js/collection": "1.38.0", + "@zag-js/core": "1.38.0", + "@zag-js/dom-query": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/@zag-js/types": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.38.2.tgz", - "integrity": "sha512-fvUf3J4QOFAliuo5wHTO/awO0GKFAn5AHNOXbGH5IuoxIwa0oK5tSXVwdF8JE7ISOkQh4oB2fq3g4QjxmAXDyA==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-1.38.0.tgz", + "integrity": "sha512-F8SL5eiJbwX+K546AQY2nsroMJDBGML19IiveGkFrOnL6/wOqSh15QIzY+Qk6Qblt6eBS2F+HkYFW7UF0V7SHw==", "license": "MIT", "dependencies": { "csstype": "3.2.3" } }, "node_modules/@zag-js/utils": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.38.2.tgz", - "integrity": "sha512-8UuWDomJ5JLX/KSDjEA4poX9rzqkGL47RiSCuVod+qZpWD0jpyXP7Lf600GUFtirRAiuN3x1+7KE6Elt/0rxtw==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-1.38.0.tgz", + "integrity": "sha512-eFTeTnsgs+G1Pf1qh6CXnaoaH0FL3WC4ksj5Ep3rrVZ+tOGzlJOESOgw9poh2IbRIzpk85+eR4rJ+PXfhNu4KQ==", "license": "MIT" }, "node_modules/@zag-js/vanilla": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/@zag-js/vanilla/-/vanilla-1.38.2.tgz", - "integrity": "sha512-5//7bSqbZ0UbIGYX3ShEZNVRd1xrTxHx30b6Wdac5WbJLqcdoodtAaRy/w7w1azq1Cpj8mmxyyedOeNGdrnoBg==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@zag-js/vanilla/-/vanilla-1.38.0.tgz", + "integrity": "sha512-kPGeV5cu150Q5VvVEYV8OzrXQHupJq5Vd3nUt5nWwmMz9MBIuQsqeNNN+DBVMGmzeLHqI0XB90aRSlDub1pN5g==", "license": "MIT", "dependencies": { - "@zag-js/core": "1.38.2", - "@zag-js/store": "1.38.2", - "@zag-js/types": "1.38.2", - "@zag-js/utils": "1.38.2" + "@zag-js/core": "1.38.0", + "@zag-js/store": "1.38.0", + "@zag-js/types": "1.38.0", + "@zag-js/utils": "1.38.0" } }, "node_modules/acorn": { @@ -4114,6 +4160,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4154,6 +4201,7 @@ "integrity": "sha512-yE5I83Q2s8euVou8Y3feXK08wyZInJWLYXgWO6Xti9jBUEZAGUahyeQ7wSZWkifLWVnQVKEz5RAmBlXG5nqxog==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.16.0", "@algolia/client-abtesting": "5.50.0", @@ -4241,19 +4289,6 @@ "node": ">= 8" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4415,9 +4450,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", - "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", + "version": "2.10.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.9.tgz", + "integrity": "sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4519,6 +4554,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4669,9 +4705,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001781", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", - "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", "dev": true, "funding": [ { @@ -5055,12 +5091,6 @@ "node": ">=8" } }, - "node_modules/devalue": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", - "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==", - "license": "MIT" - }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -5134,9 +5164,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.325", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.325.tgz", - "integrity": "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA==", + "version": "1.5.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", + "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", "dev": true, "license": "ISC" }, @@ -5205,6 +5235,7 @@ "devOptional": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5264,16 +5295,17 @@ } }, "node_modules/eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.1.0.tgz", - "integrity": "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz", + "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", - "@eslint/config-helpers": "^0.5.3", + "@eslint/config-helpers": "^0.5.2", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", @@ -5286,7 +5318,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", + "espree": "^11.1.1", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5454,7 +5486,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -5545,7 +5576,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, "license": "MIT", "dependencies": { "is-extendable": "^0.1.0" @@ -5643,23 +5673,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5739,6 +5752,7 @@ "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tabbable": "^6.4.0" } @@ -5799,10 +5813,9 @@ } }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5838,9 +5851,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", - "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -5926,7 +5939,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dev": true, "license": "MIT", "dependencies": { "js-yaml": "^3.13.1", @@ -5942,7 +5954,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -5952,7 +5963,6 @@ "version": "3.14.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -6154,7 +6164,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6362,14 +6371,14 @@ } }, "node_modules/jsdom": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", - "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.0.tgz", + "integrity": "sha512-9FshNB6OepopZ08unmmGpsF7/qCjxGPbo3NbgfJAnPeHXnsODE9WWffXZtRFRFe0ntzaAOcSKNJFz8wiyvF1jQ==", "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^5.0.1", - "@asamuzakjp/dom-selector": "^7.0.3", + "@asamuzakjp/dom-selector": "^7.0.2", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.1", "@exodus/bytes": "^1.15.0", @@ -6383,7 +6392,7 @@ "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", - "undici": "^7.24.5", + "undici": "^7.24.3", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", @@ -6459,7 +6468,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6520,7 +6528,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6541,7 +6548,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6562,7 +6568,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6583,7 +6588,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6604,7 +6608,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6625,7 +6628,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6646,7 +6648,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6667,7 +6668,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6688,7 +6688,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6709,7 +6708,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6730,7 +6728,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -6916,6 +6913,18 @@ "node": ">=6" } }, + "node_modules/marked": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.5.tgz", + "integrity": "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", @@ -7564,19 +7573,6 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/millify": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/millify/-/millify-6.1.0.tgz", @@ -8001,12 +7997,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -8076,6 +8073,21 @@ "node": ">=18" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", @@ -8095,6 +8107,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -8652,14 +8665,14 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.10.tgz", + "integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==", "license": "MIT", "peer": true, "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.120.0", + "@rolldown/pluginutils": "1.0.0-rc.10" }, "bin": { "rolldown": "bin/cli.mjs" @@ -8668,21 +8681,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.10", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.10", + "@rolldown/binding-darwin-x64": "1.0.0-rc.10", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.10", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.10", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.10", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.10", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.10", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.10", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.10", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.10", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.10" } }, "node_modules/rolldown-plugin-dts": { @@ -8814,6 +8827,7 @@ "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -8929,7 +8943,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dev": true, "license": "MIT", "dependencies": { "extend-shallow": "^2.0.1", @@ -9069,7 +9082,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/string-width": { @@ -9129,7 +9141,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9304,19 +9315,6 @@ "node": ">= 6" } }, - "node_modules/tailwindcss/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tailwindcss/node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -9404,23 +9402,53 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.26.tgz", + "integrity": "sha512-WiGwQjr0qYdNNG8KpMKlSvpxz652lqa3Rd+/hSaDcY4Uo6SKWZq2LAF+hsAhUewTtYhXlorBKgNF3Kk8hnjGoQ==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.27" + "tldts-core": "^7.0.26" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.26.tgz", + "integrity": "sha512-5WJ2SqFsv4G2Dwi7ZFVRnz6b2H1od39QME1lc2y5Ew3eWiZMAeqOAfWpRP9jHvhUl881406QtZTODvjttJs+ew==", "dev": true, "license": "MIT" }, @@ -9523,9 +9551,9 @@ "license": "Apache-2.0" }, "node_modules/tsdown": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.5.tgz", - "integrity": "sha512-TlgNhfPioAD6ECCUnZsxcUsXXuPPR4Rrxz3az741kL/M3oGIET4a9GajSNRNRx+jIva73TYUKQybrEPkDYN+fQ==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.4.tgz", + "integrity": "sha512-Q/kBi8SXkr4X6JI/NAZKZY1UuiEcbuXtIskL4tZCsgpDiEPM/2W6lC+OonNA31S+V3KsWedFvbFDBs23hvt+Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -9536,15 +9564,15 @@ "hookable": "^6.1.0", "import-without-cache": "^0.2.5", "obug": "^2.1.1", - "picomatch": "^4.0.4", - "rolldown": "1.0.0-rc.11", + "picomatch": "^4.0.3", + "rolldown": "1.0.0-rc.9", "rolldown-plugin-dts": "^0.22.5", "semver": "^7.7.4", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig-core": "^7.5.0", - "unrun": "^0.2.33" + "unrun": "^0.2.32" }, "bin": { "tsdown": "dist/run.mjs" @@ -9557,11 +9585,11 @@ }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.21.5", - "@tsdown/exe": "0.21.5", + "@tsdown/css": "0.21.4", + "@tsdown/exe": "0.21.4", "@vitejs/devtools": "*", "publint": "^0.3.0", - "typescript": "^5.0.0 || ^6.0.0", + "typescript": "^5.0.0", "unplugin-unused": "^0.5.0" }, "peerDependenciesMeta": { @@ -9588,10 +9616,20 @@ } } }, + "node_modules/tsdown/node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/tsdown/node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", "cpu": [ "arm64" ], @@ -9606,9 +9644,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", "cpu": [ "arm64" ], @@ -9623,9 +9661,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", "cpu": [ "x64" ], @@ -9640,9 +9678,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", "cpu": [ "x64" ], @@ -9657,9 +9695,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz", - "integrity": "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", "cpu": [ "arm" ], @@ -9674,9 +9712,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", "cpu": [ "arm64" ], @@ -9691,9 +9729,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", "cpu": [ "arm64" ], @@ -9708,9 +9746,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", "cpu": [ "ppc64" ], @@ -9725,9 +9763,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", "cpu": [ "s390x" ], @@ -9742,9 +9780,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ "x64" ], @@ -9759,9 +9797,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], @@ -9776,9 +9814,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", "cpu": [ "arm64" ], @@ -9793,9 +9831,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz", - "integrity": "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", "cpu": [ "wasm32" ], @@ -9810,9 +9848,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", "cpu": [ "arm64" ], @@ -9827,9 +9865,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", "cpu": [ "x64" ], @@ -9844,9 +9882,9 @@ } }, "node_modules/tsdown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz", - "integrity": "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", "dev": true, "license": "MIT" }, @@ -9867,15 +9905,28 @@ "dev": true, "license": "MIT" }, + "node_modules/tsdown/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tsdown/node_modules/rolldown": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.11.tgz", - "integrity": "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.11" + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" }, "bin": { "rolldown": "bin/cli.mjs" @@ -9884,21 +9935,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-x64": "1.0.0-rc.11", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.11", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11" + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" } }, "node_modules/tsdown/node_modules/tinyexec": { @@ -9990,21 +10041,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/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/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10024,6 +10060,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10033,16 +10070,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", + "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" + "@typescript-eslint/eslint-plugin": "8.57.1", + "@typescript-eslint/parser": "8.57.1", + "@typescript-eslint/typescript-estree": "8.57.1", + "@typescript-eslint/utils": "8.57.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10102,9 +10139,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", - "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.5.tgz", + "integrity": "sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==", "dev": true, "license": "MIT", "engines": { @@ -10254,14 +10291,27 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/unrun": { - "version": "0.2.33", - "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.33.tgz", - "integrity": "sha512-urXTjZHOHS6lMnatQerLcBpcTsaKZYGuu9aSZ+HlNfCApkiINRbj7YecC9h9hdZroMT4WQ4KVyzHfBqHKuFX9Q==", + "version": "0.2.32", + "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.32.tgz", + "integrity": "sha512-opd3z6791rf281JdByf0RdRQrpcc7WyzqittqIXodM/5meNWdTwrVxeyzbaCp4/Rgls/um14oUaif1gomO8YGg==", "dev": true, "license": "MIT", "dependencies": { - "rolldown": "1.0.0-rc.11" + "rolldown": "1.0.0-rc.9" }, "bin": { "unrun": "dist/cli.mjs" @@ -10281,10 +10331,20 @@ } } }, + "node_modules/unrun/node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/unrun/node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", "cpu": [ "arm64" ], @@ -10299,9 +10359,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", "cpu": [ "arm64" ], @@ -10316,9 +10376,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", "cpu": [ "x64" ], @@ -10333,9 +10393,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz", - "integrity": "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", "cpu": [ "x64" ], @@ -10350,9 +10410,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz", - "integrity": "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", "cpu": [ "arm" ], @@ -10367,9 +10427,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", "cpu": [ "arm64" ], @@ -10384,9 +10444,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", "cpu": [ "arm64" ], @@ -10401,9 +10461,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", "cpu": [ "ppc64" ], @@ -10418,9 +10478,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", "cpu": [ "s390x" ], @@ -10435,9 +10495,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz", - "integrity": "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ "x64" ], @@ -10452,9 +10512,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz", - "integrity": "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], @@ -10469,9 +10529,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz", - "integrity": "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", "cpu": [ "arm64" ], @@ -10486,9 +10546,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz", - "integrity": "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", "cpu": [ "wasm32" ], @@ -10503,9 +10563,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", "cpu": [ "arm64" ], @@ -10520,9 +10580,9 @@ } }, "node_modules/unrun/node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz", - "integrity": "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", "cpu": [ "x64" ], @@ -10537,21 +10597,21 @@ } }, "node_modules/unrun/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz", - "integrity": "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", "dev": true, "license": "MIT" }, "node_modules/unrun/node_modules/rolldown": { - "version": "1.0.0-rc.11", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.11.tgz", - "integrity": "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==", + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.11" + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" }, "bin": { "rolldown": "bin/cli.mjs" @@ -10560,21 +10620,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.11", - "@rolldown/binding-darwin-x64": "1.0.0-rc.11", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.11", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11" + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" } }, "node_modules/update-browserslist-db": { @@ -10671,16 +10731,16 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.1.tgz", + "integrity": "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==", "license": "MIT", "peer": true, "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", + "picomatch": "^4.0.3", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.10", "tinyglobby": "^0.2.15" }, "bin": { @@ -10748,19 +10808,16 @@ } } }, - "node_modules/vite/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/vitepress": { @@ -11278,27 +11335,13 @@ "@esbuild/win32-x64": "0.21.5" } }, - "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/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", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -11438,17 +11481,18 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.31", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", - "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz", + "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.31", - "@vue/compiler-sfc": "3.5.31", - "@vue/runtime-dom": "3.5.31", - "@vue/server-renderer": "3.5.31", - "@vue/shared": "3.5.31" + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-sfc": "3.5.30", + "@vue/runtime-dom": "3.5.30", + "@vue/server-renderer": "3.5.30", + "@vue/shared": "3.5.30" }, "peerDependencies": { "typescript": "*" @@ -11647,7 +11691,7 @@ }, "packages/gea": { "name": "@geajs/core", - "version": "1.0.7", + "version": "1.0.14", "license": "MIT", "dependencies": { "@types/react": "^19.0.0" @@ -11675,13 +11719,47 @@ "@geajs/core": "^1.0.0" } }, - "packages/gea-ssr": { - "name": "@geajs/ssr", + "packages/gea-ssg": { + "name": "@geajs/ssg", "version": "1.0.0", "license": "MIT", "dependencies": { - "devalue": "^5.6.4" + "gray-matter": "^4.0.3", + "marked": "^17.0.5" }, + "devDependencies": { + "tsup": "^8.5.1", + "tsx": "^4.21.0", + "typescript": "~5.8.0" + }, + "peerDependencies": { + "@geajs/core": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "packages/gea-ssg/node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "packages/gea-ssr": { + "name": "@geajs/ssr", + "version": "1.0.0", + "license": "MIT", "devDependencies": { "@types/node": "^25.5.0", "jsdom": "^29.0.0", @@ -11690,7 +11768,7 @@ "typescript": "~5.8.0" }, "peerDependencies": { - "@geajs/core": "*", + "@geajs/core": "^1.0.0", "vite": "^8.0.0" }, "peerDependenciesMeta": { @@ -11701,6 +11779,8 @@ }, "packages/gea-ssr/node_modules/typescript": { "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11743,7 +11823,7 @@ }, "packages/gea-ui": { "name": "@geajs/ui", - "version": "0.1.3", + "version": "0.1.4", "license": "MIT", "dependencies": { "@zag-js/accordion": "^1.37.0", @@ -11788,22 +11868,7 @@ "typescript": "^5.9.3" }, "peerDependencies": { - "@geajs/core": "^1.0.6" - } - }, - "packages/gea-ui/node_modules/@types/react": { - "version": "19.2.14", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "packages/gea/node_modules/@types/react": { - "version": "19.2.14", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" + "@geajs/core": "^1.0.8" } }, "packages/gea/node_modules/typescript": { @@ -11820,7 +11885,7 @@ }, "packages/vite-plugin-gea": { "name": "@geajs/vite-plugin", - "version": "1.0.7", + "version": "1.0.14", "license": "MIT", "dependencies": { "@acemir/cssom": "^0.9.31", diff --git a/packages/gea-ssg/README.md b/packages/gea-ssg/README.md new file mode 100644 index 0000000..c6dceb5 --- /dev/null +++ b/packages/gea-ssg/README.md @@ -0,0 +1,316 @@ +# @geajs/ssg + +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/dashersw/gea/blob/master/LICENSE) + +Static site generation plugin for [Gea](https://www.npmjs.com/package/@geajs/core). Pre-renders your routes to static HTML at build time — instant first paint, full SEO, and selective client-side hydration. Pages without interactive components ship zero JavaScript. + +## Installation + +```bash +npm install -D @geajs/ssg +``` + +Requires `@geajs/core` ^1.0.0 and `vite` ^8.0.0 as peer dependencies. + +## Configuration + +```ts +// vite.config.ts +import { defineConfig } from 'vite' +import { geaPlugin } from '@geajs/vite-plugin' +import { geaSSG } from '@geajs/ssg/vite' + +export default defineConfig({ + plugins: [ + geaPlugin(), + geaSSG({ + contentDir: 'src/content', + sitemap: { hostname: 'https://example.com' }, + robots: true, + minify: true, + }), + ], +}) +``` + +Your `src/App.tsx` must export `routes` (a `RouteMap`) and `App` (or a default export): + +```tsx +export const routes = { + '/': Home, + '/about': About, + '/blog': Blog, + '/blog/:slug': { component: BlogPost, content: 'blog' }, + '*': NotFound, +} + +export default class App extends Component { + template() { + return ( +
+ + + +
+ ) + } +} +``` + +Run `vite build` and every route is pre-rendered to `dist/`. + +## Features + +### Head Management + +The `Head` component sets per-page title, meta tags, Open Graph, Twitter Cards, canonical URL, and JSON-LD: + +```tsx +import { Component, Head } from '@geajs/core' + +class BlogPost extends Component { + template(props) { + const post = ssg.file('blog', props?.slug) + return ( +
+ +

{post?.frontmatter.title}

+
{post?.html}
+
+ ) + } +} +``` + +Set a default `Head` in your App and override per page — scalar values (title, description) are replaced, arrays (meta, link) are merged. During SSG the tags are injected into the HTML ``. In the browser, `document.title` and meta elements are updated on navigation. + +**Shorthand expansion:** `title` sets ``, `og:title`, `twitter:title`. `description` sets `<meta name="description">`, `og:description`, `twitter:description`. `image` sets `og:image`, `twitter:image`, `twitter:card`. + +### Static Routes + +Every path in your `RouteMap` is rendered to an HTML file: + +``` +/ -> dist/index.html +/about -> dist/about/index.html +/contact -> dist/contact/index.html +``` + +### Markdown Content + +Load markdown files with YAML frontmatter using the `ssg` accessor inside `template()`: + +```tsx +import { ssg } from '@geajs/ssg' + +class Blog extends Component { + template() { + const posts = ssg.content('blog', { + sort: (a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime(), + }) + return <ul>{posts.map(p => `<li>${p.frontmatter.title}</li>`).join('')}</ul> + } +} +``` + +Each file has: `slug`, `frontmatter`, `html` (rendered), and optionally `content` (raw markdown — available at build time, omitted in client payload to reduce size). + +### Dynamic Routes with Content + +Parameterized routes auto-generate pages from content files. Each `.md` file's slug becomes a route parameter: + +```tsx +export const routes = { + '/blog/:slug': { component: BlogPost, content: 'blog' }, +} + +class BlogPost extends Component { + template(props) { + const post = ssg.file('blog', props?.slug) + return post ? <article><h1>{post.frontmatter.title}</h1><div>{post.html}</div></article> : '' + } +} +``` + +### Dynamic Routes with Explicit Paths + +For non-content parameterized routes, provide explicit paths: + +```tsx +export const routes = { + '/user/:id': { + component: UserPage, + paths: [{ params: { id: '1' } }, { params: { id: '2' } }], + }, +} +``` + +### 404 Page + +Add a wildcard route to generate a `404.html`: + +```tsx +import { Component, Head } from '@geajs/core' + +export const routes = { + '/': Home, + '*': NotFound, +} + +class NotFound extends Component { + template() { + return ( + <div> + <Head title="404 - Not Found" /> + <h1>Page not found</h1> + </div> + ) + } +} +``` + +The preview server automatically serves `404.html` for unmatched routes. + +### Layouts and Outlets + +Route groups with `layout` components are rendered with proper nesting — the layout wraps the page content through `Outlet`. + +### Sitemap + +Pass `sitemap: { hostname: 'https://example.com' }` to generate a `sitemap.xml`. Pages with `lastmod` in their `Head` get per-URL last-modified dates. The `/404` route is automatically excluded. + +### robots.txt + +Enable robots.txt generation: + +```ts +geaSSG({ + robots: true, + sitemap: { hostname: 'https://example.com' }, +}) +``` + +Or customize: + +```ts +geaSSG({ + robots: { + disallow: ['/admin', '/private'], + allow: ['/public'], + sitemap: false, + }, +}) +``` + +### HTML Minification + +Enable minification to reduce output size: + +```ts +geaSSG({ minify: true }) +``` + +Removes HTML comments, collapses whitespace, and strips inter-tag spaces while preserving `<pre>`, `<code>`, `<script>`, `<style>`, and `<textarea>` content. + +### Trailing Slash + +Control URL format with `trailingSlash`: + +```ts +geaSSG({ trailingSlash: false }) // /about -> about.html +geaSSG({ trailingSlash: true }) // /about -> about/index.html (default) +``` + +Affects output paths, sitemap URLs, and preview server routing. + +### Active Link State + +`Link` components automatically get `data-active` attributes in static output, matching the current route — no client JavaScript needed. + +### MPA Hydration (Zero JS on Static Pages) + +By default, the SSG build bundles your entire app as a single-page application. With the `hydrate` option, you can switch to **MPA mode**: only pages that contain interactive components get JavaScript — everything else is pure HTML with zero JS. + +```ts +// vite.config.ts +geaSSG({ + contentDir: 'src/content', + hydrate: ['Counter', 'LiveClock'], +}) +``` + +```ts +// src/main.ts +import { hydrate } from '@geajs/ssg' +import Counter from './views/Counter' +import LiveClock from './views/LiveClock' +import './styles.css' + +hydrate({ Counter, LiveClock }) +``` + +**How it works:** + +1. During build, the SSG renderer tracks which components from the `hydrate` list appear on each page and marks them with `data-gea` attributes. +2. Pages **without** any hydrated components have their `<script type="module">` tags stripped — zero JavaScript. +3. Pages **with** hydrated components keep their JS. On load, `hydrate()` finds each `data-gea` element, creates the component instance with a matching ID, and attaches reactive bindings and event handlers to the existing SSG DOM — no re-render, no flash. + +**Content API in MPA mode:** When `hydrate` is set, no global `_ssg/content.js` file is generated. `ssg.content()` and `ssg.file()` will return empty results on the client. This is by design — in MPA mode content is baked into the SSG HTML at build time. If you need client-side content access, use the default SPA mode (omit `hydrate`). + +**When to use MPA mode:** Sites where most pages are static content (blogs, docs, marketing) and only a few pages need interactivity (forms, counters, live data). + +### Dev Mode + +In dev mode (`vite dev`), content is preloaded and injected into the page so `ssg.content()` and `ssg.file()` work without a build step. Content file changes trigger automatic page reload. + +When using MPA mode with `hydrate`, the dev server falls back to full SPA rendering (Router + all views) so you can navigate and develop normally. The MPA behavior only applies to production builds. + +## Plugin Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `entry` | `string` | `'src/App.tsx'` | App entry file | +| `contentDir` | `string` | — | Content directory for markdown files (relative to project root) | +| `sitemap` | `boolean \| SitemapOptions` | — | Generate sitemap.xml | +| `robots` | `boolean \| RobotsOptions` | — | Generate robots.txt | +| `minify` | `boolean` | `false` | Minify HTML output | +| `trailingSlash` | `boolean` | `true` | URL format for generated files | +| `appElementId` | `string` | `'app'` | Mount element id in index.html | +| `routes` | `RouteMap` | — | Override routes (default: from entry file) | +| `app` | `Component` | — | Override app component (default: from entry file) | +| `hydrate` | `string[]` | — | Component class names to hydrate on client (enables MPA mode) | +| `base` | `string` | `'/'` | Base path for asset URLs | +| `concurrency` | `number` | `4` | Max concurrent page renders | +| `onBeforeRender` | `function` | — | Hook called before each page render | +| `onAfterRender` | `function` | — | Hook called after render, can transform HTML | +| `onRenderError` | `function` | — | Custom error handler per route | + +## Programmatic API + +```ts +// Build-time / server-side +import { ssg, generate, renderToString, crawlRoutes, parseShell, preloadContent } from '@geajs/ssg' + +// Client-side (browser) +import { hydrate, ssg } from '@geajs/ssg' // aliased to client module by vite-plugin +``` + +All build-time exports are available from the main entry point for custom pipelines. The `hydrate` function is only available from the client entry (`@geajs/ssg/client`), which the Vite plugin aliases automatically. + +## Related Packages + +- **[@geajs/core](https://www.npmjs.com/package/@geajs/core)** — Core framework +- **[@geajs/vite-plugin](https://www.npmjs.com/package/@geajs/vite-plugin)** — Vite plugin for compile-time JSX +- **[create-gea](https://www.npmjs.com/package/create-gea)** — Project scaffolder + +## License + +[MIT](LICENSE) — Copyright (c) 2017-present Armagan Amcalar diff --git a/packages/gea-ssg/package.json b/packages/gea-ssg/package.json new file mode 100644 index 0000000..e400e67 --- /dev/null +++ b/packages/gea-ssg/package.json @@ -0,0 +1,71 @@ +{ + "name": "@geajs/ssg", + "version": "1.0.0", + "description": "Static site generation for Gea framework", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./vite": { + "source": "./src/vite-plugin.ts", + "types": "./dist/vite-plugin.d.ts", + "import": "./dist/vite-plugin.js" + }, + "./client": { + "source": "./src/client.ts", + "types": "./dist/client.d.ts", + "import": "./dist/client.js" + } + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "sideEffects": false, + "scripts": { + "build": "tsup src/index.ts src/vite-plugin.ts src/client.ts --format esm --dts --clean --external vite --external @geajs/core", + "test": "tsx --conditions source --test 'tests/**/*.test.ts'" + }, + "keywords": [ + "gea", + "ssg", + "static-site-generation", + "vite-plugin", + "pre-rendering" + ], + "author": "Armagan Amcalar <armagan@amcalar.com>", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/dashersw/gea.git" + }, + "bugs": { + "url": "https://github.com/dashersw/gea/issues" + }, + "homepage": "https://github.com/dashersw/gea#readme", + "peerDependencies": { + "@geajs/core": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + }, + "devDependencies": { + "tsup": "^8.5.1", + "tsx": "^4.21.0", + "typescript": "~5.8.0" + }, + "dependencies": { + "gray-matter": "^4.0.3", + "marked": "^17.0.5" + } +} diff --git a/packages/gea-ssg/src/client.ts b/packages/gea-ssg/src/client.ts new file mode 100644 index 0000000..0ba6765 --- /dev/null +++ b/packages/gea-ssg/src/client.ts @@ -0,0 +1,105 @@ +/// <reference lib="dom" /> +/** + * Lightweight browser-only module. + * vite-plugin aliases `@geajs/ssg` to this file for client builds. + * Reads content from `window.__SSG_CONTENT__` injected by the SSG build. + * + * In MPA/hydrate mode, `__SSG_CONTENT__` is NOT available because + * no global `content.js` is generated — content is baked into the SSG HTML + * at build time. `ssg.content()` / `ssg.file()` will return empty results + * in that mode; use them only on pages that ship JS **without** hydrate, + * or access content data through the pre-rendered HTML. + */ +import { ComponentManager } from '@geajs/core' +import type { ContentFile } from './types' +export type { ContentFile } from './types' + +function getData(): Record<string, ContentFile[]> { + const g = globalThis as any + if (g.__SSG_CONTENT__) { + return g.__SSG_CONTENT__ + } + return {} +} + +/** Query loaded content files by subdirectory. */ +export const ssg = { + content<T = Record<string, any>>( + subdir: string, + options?: { sort?: (a: ContentFile<T>, b: ContentFile<T>) => number }, + ): ContentFile<T>[] { + const items = [...(getData()[subdir] || [])] as ContentFile<T>[] + if (options?.sort) items.sort(options.sort) + return items + }, + + file<T = Record<string, any>>(subdir: string, slug: string): ContentFile<T> | null { + const items = (getData()[subdir] || []) as ContentFile<T>[] + return items.find((f) => f.slug === slug) || null + }, +} + +/** + * Hydrate interactive components on SSG-rendered pages. + * + * Scans for elements with `data-gea` attributes and attaches + * component instances to existing DOM without re-rendering. + * Props serialised at build time (via `data-gea-props`) are + * passed to the component constructor so stateful components + * initialise with the correct values. + * + * @returns `true` if at least one component was hydrated, `false` otherwise. + */ +export function hydrate(components: Record<string, new (props?: any) => any>): boolean { + const elements = Array.from(document.querySelectorAll<HTMLElement>('[data-gea]')) + if (!elements.length) return false + + const cm = ComponentManager.getInstance() + const originalGetUid = cm.getUid + let hydrated = 0 + + for (const el of elements) { + const className = el.getAttribute('data-gea') + if (!className) continue + + const Ctor = components[className] + if (!Ctor) { + console.warn(`[gea-ssg] hydrate: "${className}" not found in component map`) + continue + } + + // Parse serialised props (if present) + let props: Record<string, any> | undefined + const propsJson = el.getAttribute('data-gea-props') + if (propsJson) { + try { + props = JSON.parse(propsJson) + } catch { + console.warn(`[gea-ssg] hydrate: invalid props JSON for "${className}"`) + } + } + + // One-shot UID override: only the root component receives the SSG id. + // Any child components created during the constructor get fresh ids + // from the original generator — preventing duplicate-id collisions. + let idConsumed = false + cm.getUid = () => { + if (!idConsumed) { + idConsumed = true + return el.id + } + return originalGetUid() + } + + try { + const instance = new Ctor(props) + instance.render() + hydrated++ + } catch (e) { + console.error(`[gea-ssg] hydrate failed for "${className}":`, e) + } + } + + cm.getUid = originalGetUid + return hydrated > 0 +} diff --git a/packages/gea-ssg/src/content.ts b/packages/gea-ssg/src/content.ts new file mode 100644 index 0000000..4de06ab --- /dev/null +++ b/packages/gea-ssg/src/content.ts @@ -0,0 +1,109 @@ +import type { ContentFile } from './types' +export type { ContentFile } from './types' + +const cache = new Map<string, ContentFile[]>() + +/** + * Load markdown files from immediate subdirectories of `rootDir`. + * Each subdirectory becomes a content collection accessible via `ssg.content(name)`. + * Markdown is parsed with `gray-matter` (frontmatter) and `marked` (HTML). + * + * Note: only one level of subdirectories is scanned — nested subdirectories + * within a collection are not traversed. + */ +export async function preloadContent(rootDir: string): Promise<void> { + const { readdir, readFile } = await import('node:fs/promises') + const { join, basename, extname } = await import('node:path') + const matter = (await import('gray-matter')).default + const { marked } = await import('marked') + + cache.clear() + + const entries = await readdir(rootDir, { withFileTypes: true }) + for (const entry of entries) { + if (!entry.isDirectory()) continue + + const subdir = entry.name + const dirPath = join(rootDir, subdir) + const files = (await readdir(dirPath)).filter((f) => f.endsWith('.md')).sort() + + const items: ContentFile[] = [] + for (const file of files) { + const raw = await readFile(join(dirPath, file), 'utf-8') + const { data, content } = matter(raw) + const html = await marked(content.trim()) + + for (const [key, value] of Object.entries(data)) { + if (value instanceof Date) { + data[key] = value.toISOString() + } + } + + items.push({ + slug: basename(file, extname(file)), + frontmatter: data, + content: content.trim(), + html, + }) + } + + cache.set(subdir, items) + } +} + +/** Clear the in-memory content cache (called automatically after SSG generation). */ +export function clearContentCache(): void { + cache.clear() +} + +/** Serialize the full content cache including raw markdown `content` field. */ +export function serializeContentCache(): string { + const obj: Record<string, ContentFile[]> = {} + for (const [key, value] of cache) { + obj[key] = value + } + return JSON.stringify(obj) +} + +/** Serialize for client bundle — strips raw markdown `content` field to reduce payload */ +export function serializeContentCacheForClient(): string { + const obj: Record<string, Omit<ContentFile, 'content'>[]> = {} + for (const [key, value] of cache) { + obj[key] = value.map(({ content: _content, ...rest }) => rest) + } + return JSON.stringify(obj) +} + +/** Return all slugs in a content subdirectory (used for dynamic route generation). */ +export function getContentSlugs(subdir: string): string[] { + return (cache.get(subdir) || []).map((f) => f.slug) +} + +export const ssg = { + content<T = Record<string, any>>( + subdir: string, + options?: { sort?: (a: ContentFile<T>, b: ContentFile<T>) => number }, + ): ContentFile<T>[] { + const g = globalThis as any + if (g.__SSG_CONTENT__) { + const items = [...(g.__SSG_CONTENT__[subdir] || [])] as ContentFile<T>[] + if (options?.sort) items.sort(options.sort) + return items + } + + const items = [...(cache.get(subdir) || [])] as ContentFile<T>[] + if (options?.sort) items.sort(options.sort) + return items + }, + + file<T = Record<string, any>>(subdir: string, slug: string): ContentFile<T> | null { + const g = globalThis as any + if (g.__SSG_CONTENT__) { + const items = (g.__SSG_CONTENT__[subdir] || []) as ContentFile<T>[] + return items.find((f) => f.slug === slug) || null + } + + const items = (cache.get(subdir) || []) as ContentFile<T>[] + return items.find((f) => f.slug === slug) || null + }, +} diff --git a/packages/gea-ssg/src/crawl.ts b/packages/gea-ssg/src/crawl.ts new file mode 100644 index 0000000..8076da8 --- /dev/null +++ b/packages/gea-ssg/src/crawl.ts @@ -0,0 +1,151 @@ +import type { RouteMap, RouteEntry, RouteGroupConfig } from '@geajs/core' +import type { StaticRoute } from './types' +import { getContentSlugs } from './content' + +export async function crawlRoutes(routes: RouteMap, basePath: string = ''): Promise<StaticRoute[]> { + const result: StaticRoute[] = [] + + for (const [pattern, entry] of Object.entries(routes)) { + await collectPaths(pattern, entry, basePath, [], result) + } + + return result +} + +async function collectPaths( + pattern: string, + entry: RouteEntry, + basePath: string, + parentLayouts: any[], + result: StaticRoute[], +): Promise<void> { + const fullPath = normalizePath(basePath + pattern) + + if (typeof entry === 'string') return + + if (isRedirectConfig(entry)) return + + if (isRouteGroupConfig(entry)) { + const layouts = entry.layout ? [...parentLayouts, entry.layout] : parentLayouts + + for (const [childPattern, childEntry] of Object.entries(entry.children)) { + await collectPaths(childPattern, childEntry, fullPath, layouts, result) + } + return + } + + if (isSSGRouteConfig(entry)) { + const component = (entry as any).component + + if ((entry as any).content) { + const subdir = (entry as any).content as string + const slugs = getContentSlugs(subdir) + const paramName = extractParamName(pattern) + + if (!paramName) { + console.warn(`[gea-ssg] Route "${fullPath}" has content but no param in pattern, skipping.`) + return + } + + if (!slugs.length) { + console.warn(`[gea-ssg] No content found for "${subdir}" — route "${fullPath}" will have no pages.`) + return + } + + for (const slug of slugs) { + const params = { [paramName]: slug } + result.push({ + path: resolveParams(fullPath, params), + component, + layouts: parentLayouts, + params, + }) + } + return + } + + if ((entry as any).paths) { + const paths = (entry as any).paths as Array<{ params: Record<string, string> }> + for (const pathEntry of paths) { + result.push({ + path: resolveParams(fullPath, pathEntry.params), + component, + layouts: parentLayouts, + params: pathEntry.params, + }) + } + return + } + + if (pattern === '*') { + result.push({ path: '/404', component, layouts: parentLayouts, params: {} }) + return + } + + // Guard: skip routes with unresolved :param segments — they need + // either `content` or `paths` to resolve parameters. + if (fullPath.includes(':')) { + console.warn(`[gea-ssg] Route "${fullPath}" has unresolved params — provide "content" or "paths". Skipping.`) + return + } + + result.push({ path: fullPath, component, layouts: parentLayouts, params: {} }) + return + } + + let component = entry + if (isLazyComponent(entry)) { + const mod = await (entry as () => Promise<any>)() + component = 'default' in mod ? mod.default : mod + } + + if (pattern === '*') { + result.push({ path: '/404', component: component as any, layouts: parentLayouts, params: {} }) + return + } + + if (pattern.includes(':')) return + + result.push({ + path: fullPath, + component, + layouts: parentLayouts, + params: {}, + }) +} + +function extractParamName(pattern: string): string | null { + const match = pattern.match(/:(\w+)/) + return match ? match[1] : null +} + +function resolveParams(path: string, params: Record<string, string>): string { + return Object.entries(params).reduce((p, [key, value]) => p.replace(`:${key}`, value), path) +} + +function normalizePath(path: string): string { + const cleaned = '/' + path.replace(/\/+/g, '/').replace(/^\/|\/$/g, '') + return cleaned === '/' ? '/' : cleaned.replace(/\/$/, '') +} + +function isRouteGroupConfig(entry: RouteEntry): entry is RouteGroupConfig { + return typeof entry === 'object' && entry !== null && 'children' in entry +} + +function isRedirectConfig(entry: RouteEntry): boolean { + return typeof entry === 'object' && entry !== null && 'redirect' in entry +} + +function isLazyComponent(entry: RouteEntry): boolean { + return typeof entry === 'function' && !(entry as any).prototype +} + +function isSSGRouteConfig(entry: RouteEntry): boolean { + return ( + typeof entry === 'object' && + entry !== null && + 'component' in entry && + !('children' in entry) && + !('redirect' in entry) + ) +} diff --git a/packages/gea-ssg/src/generate.ts b/packages/gea-ssg/src/generate.ts new file mode 100644 index 0000000..ae726b2 --- /dev/null +++ b/packages/gea-ssg/src/generate.ts @@ -0,0 +1,305 @@ +import { writeFile, mkdir, readFile } from 'node:fs/promises' +import { join, dirname, resolve, relative } from 'node:path' + +import { RouterView, Link, Head } from '@geajs/core' +import { renderToString } from './render' +import { crawlRoutes } from './crawl' +import { parseShell, injectIntoShell } from './shell' +import { preloadContent, clearContentCache, serializeContentCache, serializeContentCacheForClient } from './content' +import { buildHeadTags, replaceTitle, minifyHtml } from './head' +import type { HeadConfig } from './head' +import type { SSGOptions, GenerateResult, GeneratedPage, StaticRoute, RobotsOptions } from './types' + +export async function generate(options: SSGOptions): Promise<GenerateResult> { + const { + routes, + shell: shellPath, + outDir = 'dist', + appElementId = 'app', + onBeforeRender, + onAfterRender, + onRenderError, + concurrency = 4, + sitemap, + robots, + minify = false, + trailingSlash = true, + } = options + + const startTime = performance.now() + const pages: GeneratedPage[] = [] + const errors: Array<{ path: string; error: Error }> = [] + const headConfigs = new Map<string, HeadConfig>() + + try { + if (options.contentDir) { + await preloadContent(options.contentDir) + ;(globalThis as any).__SSG_CONTENT__ = JSON.parse(serializeContentCache()) + } + + const shellHtml = await readFile(shellPath, 'utf-8') + const shellParts = parseShell(shellHtml, appElementId) + + const staticRoutes = await crawlRoutes(routes) + + if (!staticRoutes.length) { + console.warn('[gea-ssg] No static routes found.') + return { pages, duration: performance.now() - startTime, errors } + } + + const absOut = resolve(outDir) + const seenPaths = new Set<string>() + for (const route of staticRoutes) { + const target = resolve(getOutputPath(route.path, outDir, trailingSlash)) + const rel = relative(absOut, target) + if (rel.startsWith('..') || resolve(rel) === rel) { + throw new Error(`[gea-ssg] Path traversal detected: "${route.path}" escapes outDir.`) + } + if (seenPaths.has(route.path)) { + console.warn(`[gea-ssg] Duplicate path "${route.path}" — later render will overwrite.`) + } + seenPaths.add(route.path) + } + + console.log(`[gea-ssg] Found ${staticRoutes.length} routes, rendering...`) + + const renderRoute = async (route: StaticRoute, index: number): Promise<void> => { + try { + if (onBeforeRender) { + await onBeforeRender({ + path: route.path, + params: route.params, + component: route.component, + layouts: route.layouts, + }) + } + + Head._current = null + let html: string + let hasHydrationMarkers = false + try { + RouterView._ssgRoute = { + component: route.component, + layouts: route.layouts, + params: route.params, + } + Link._ssgCurrentPath = route.path + const result = renderToString(options.app, undefined, { seed: index, hydrate: options.hydrate }) + html = result.html + hasHydrationMarkers = result.hasHydrationMarkers + } finally { + RouterView._ssgRoute = null + Link._ssgCurrentPath = null + } + + let fullHtml = injectIntoShell(shellParts, html) + + // In MPA/hydrate mode content is baked into SSG HTML at build time — + // no global content.js is generated, so ssg.content() / ssg.file() + // will return empty results on the client. Content access should + // happen through the pre-rendered HTML instead. + if (options.contentDir && !options.hydrate) { + const base = (options.base || '/').replace(/\/?$/, '/') + fullHtml = fullHtml.replace(/(<head[^>]*>)/i, `$1\n<script defer src="${base}_ssg/content.js"></script>`) + } + + if (Head._current) { + const headConfig = { ...Head._current } as HeadConfig + headConfigs.set(route.path, headConfig) + + if (headConfig.title) { + fullHtml = replaceTitle(fullHtml, headConfig.title) + } + + const headTags = buildHeadTags(headConfig) + if (headTags) { + fullHtml = fullHtml.replace('</head>', headTags + '\n</head>') + } + } + + if (onAfterRender) { + const transformed = await onAfterRender( + { + path: route.path, + params: route.params, + component: route.component, + layouts: route.layouts, + }, + fullHtml, + ) + if (transformed !== undefined) fullHtml = transformed + } + + // Strip client JS from pages that have no interactive components. + // Uses the explicit flag from renderToString instead of scanning the + // HTML text — avoids false positives when article content mentions + // "data-gea" in code samples or prose. + if (options.hydrate && !hasHydrationMarkers) { + fullHtml = fullHtml.replace(/<script type="module"[^>]*><\/script>/g, '') + fullHtml = fullHtml.replace(/<link[^>]*rel="modulepreload"[^>]*\/?>/g, '') + } + + if (minify) { + fullHtml = minifyHtml(fullHtml) + } + + const outputPath = getOutputPath(route.path, outDir, trailingSlash) + await mkdir(dirname(outputPath), { recursive: true }) + await writeFile(outputPath, fullHtml, 'utf-8') + + pages.push({ + path: route.path, + outputPath, + size: Buffer.byteLength(fullHtml, 'utf-8'), + }) + } catch (error) { + const err = error as Error + errors.push({ path: route.path, error: err }) + if (onRenderError) { + onRenderError(route.path, err) + } else { + console.error(`[gea-ssg] Render error: ${route.path}`, err.message) + } + } + } + + await runWithConcurrency(staticRoutes, renderRoute, concurrency) + + // In MPA/hydrate mode content.js is intentionally skipped — see note above. + if (options.contentDir && !options.hydrate) { + const ssgDir = join(outDir, '_ssg') + await mkdir(ssgDir, { recursive: true }) + const clientJson = serializeContentCacheForClient().replace(/<\//g, '<\\/') + await writeFile(join(ssgDir, 'content.js'), `window.__SSG_CONTENT__=${clientJson}`, 'utf-8') + } + + if (sitemap) { + const sitemapOpts = typeof sitemap === 'boolean' ? { hostname: 'https://example.com' } : sitemap + if (typeof sitemap === 'boolean') { + console.warn( + '[gea-ssg] sitemap: true uses placeholder hostname "https://example.com". Pass { hostname: "https://your-site.com" } for production.', + ) + } + await generateSitemap(pages, sitemapOpts, outDir, headConfigs, trailingSlash) + } + + if (robots) { + await generateRobots(robots, sitemap, outDir) + } + + const duration = performance.now() - startTime + console.log(`[gea-ssg] ✓ ${pages.length} pages generated (${errors.length} errors), ${Math.round(duration)}ms`) + + return { pages, duration, errors } + } finally { + clearContentCache() + delete (globalThis as any).__SSG_CONTENT__ + Head._current = null + } +} + +function getOutputPath(routePath: string, outDir: string, trailingSlash: boolean = true): string { + if (routePath === '/' || routePath === '') { + return join(outDir, 'index.html') + } + if (routePath === '/404') { + return join(outDir, '404', 'index.html') + } + if (trailingSlash) { + return join(outDir, routePath, 'index.html') + } + return join(outDir, routePath + '.html') +} + +async function runWithConcurrency<T>( + items: T[], + fn: (item: T, index: number) => Promise<void>, + limit: number, +): Promise<void> { + const executing = new Set<Promise<void>>() + + for (let i = 0; i < items.length; i++) { + const p = fn(items[i], i).then(() => { + executing.delete(p) + }) + executing.add(p) + + if (executing.size >= limit) { + await Promise.race(executing) + } + } + + await Promise.all(executing) +} + +function escapeXml(str: string): string { + return str + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +async function generateSitemap( + pages: GeneratedPage[], + options: { hostname: string; changefreq?: string; priority?: number; exclude?: string[] }, + outDir: string, + headConfigs: Map<string, HeadConfig>, + trailingSlash: boolean, +): Promise<void> { + const { hostname, changefreq = 'weekly', priority = 0.8, exclude = [] } = options + + const urls = [...pages] + .sort((a, b) => a.path.localeCompare(b.path)) + .filter((p) => !exclude.includes(p.path) && p.path !== '/404') + .map((p) => { + const head = headConfigs.get(p.path) + const lastmod = head?.lastmod ? `\n <lastmod>${head.lastmod}</lastmod>` : '' + const loc = trailingSlash && p.path !== '/' ? `${hostname}${p.path}/` : `${hostname}${p.path}` + return ` <url> + <loc>${escapeXml(loc)}</loc>${lastmod} + <changefreq>${changefreq}</changefreq> + <priority>${priority}</priority> + </url>` + }) + .join('\n') + + const xml = `<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> +${urls} +</urlset>` + + await mkdir(outDir, { recursive: true }) + await writeFile(join(outDir, 'sitemap.xml'), xml, 'utf-8') +} + +async function generateRobots( + options: boolean | RobotsOptions, + sitemap: SSGOptions['sitemap'], + outDir: string, +): Promise<void> { + const lines: string[] = ['User-agent: *'] + const hostname = typeof sitemap === 'object' && sitemap ? sitemap.hostname : undefined + + if (typeof options === 'object') { + if (options.allow) { + for (const path of options.allow) lines.push(`Allow: ${path}`) + } + if (options.disallow) { + for (const path of options.disallow) lines.push(`Disallow: ${path}`) + } else { + lines.push('Allow: /') + } + if (options.sitemap !== false && hostname) { + lines.push('', `Sitemap: ${hostname}/sitemap.xml`) + } + } else { + lines.push('Allow: /') + if (hostname) { + lines.push('', `Sitemap: ${hostname}/sitemap.xml`) + } + } + + await writeFile(join(outDir, 'robots.txt'), lines.join('\n') + '\n', 'utf-8') +} diff --git a/packages/gea-ssg/src/head.ts b/packages/gea-ssg/src/head.ts new file mode 100644 index 0000000..feda756 --- /dev/null +++ b/packages/gea-ssg/src/head.ts @@ -0,0 +1,99 @@ +export interface HeadConfig { + title?: string + description?: string + image?: string + url?: string + type?: string + lastmod?: string + jsonld?: Record<string, any> | Record<string, any>[] + meta?: Array<{ name?: string; property?: string; content: string }> + link?: Array<Record<string, string>> +} + +/** Build `<meta>`, `<link>`, and JSON-LD `<script>` tags from a HeadConfig. */ +export function buildHeadTags(config: HeadConfig): string { + const tags: string[] = [] + + if (config.description) { + tags.push(`<meta name="description" content="${escAttr(config.description)}">`) + } + + if (config.title) tags.push(`<meta property="og:title" content="${escAttr(config.title)}">`) + if (config.description) tags.push(`<meta property="og:description" content="${escAttr(config.description)}">`) + if (config.image) tags.push(`<meta property="og:image" content="${escAttr(config.image)}">`) + if (config.url) tags.push(`<meta property="og:url" content="${escAttr(config.url)}">`) + tags.push(`<meta property="og:type" content="${escAttr(config.type || 'website')}">`) + + if (config.title) tags.push(`<meta name="twitter:title" content="${escAttr(config.title)}">`) + if (config.description) tags.push(`<meta name="twitter:description" content="${escAttr(config.description)}">`) + if (config.image) { + tags.push(`<meta name="twitter:image" content="${escAttr(config.image)}">`) + tags.push(`<meta name="twitter:card" content="summary_large_image">`) + } + + if (config.url) tags.push(`<link rel="canonical" href="${escAttr(config.url)}">`) + + if (config.meta) { + for (const m of config.meta) { + const attr = m.property ? `property="${escAttr(m.property)}"` : `name="${escAttr(m.name || '')}"` + tags.push(`<meta ${attr} content="${escAttr(m.content)}">`) + } + } + + if (config.link) { + for (const l of config.link) { + const attrs = Object.entries(l) + .map(([k, v]) => `${k}="${escAttr(v)}"`) + .join(' ') + tags.push(`<link ${attrs}>`) + } + } + + if (config.jsonld) { + const items = Array.isArray(config.jsonld) ? config.jsonld : [config.jsonld] + for (const item of items) { + const ld = { '@context': 'https://schema.org', ...item } + const json = JSON.stringify(ld).replace(/<\//g, '<\\/') + tags.push(`<script type="application/ld+json">${json}</script>`) + } + } + + return tags.length ? '\n' + tags.join('\n') : '' +} + +/** Replace the `<title>` content in an HTML string. If no `<title>` exists, inserts one after `<head>`. */ +export function replaceTitle(html: string, title: string): string { + const escaped = `<title>${escHtml(title)}` + if (/[^<]*<\/title>/i.test(html)) { + return html.replace(/<title>[^<]*<\/title>/i, escaped) + } + // No existing <title> — insert after <head...> + return html.replace(/(<head[^>]*>)/i, `$1\n${escaped}`) +} + +/** Minify HTML by collapsing whitespace, preserving `<pre>`, `<code>`, `<script>`, `<style>`, and `<textarea>` content. */ +export function minifyHtml(html: string): string { + const preserved: string[] = [] + let result = html.replace(/<(pre|code|script|style|textarea)\b[^>]*>[\s\S]*?<\/\1>/gi, (match) => { + preserved.push(match) + return `__PRESERVE_${preserved.length - 1}__` + }) + + result = result.replace(/<!--(?!\[if)[\s\S]*?-->/g, '') + result = result.replace(/\s+/g, ' ') + result = result.replace(/>\s+</g, '><') + + for (let i = 0; i < preserved.length; i++) { + result = result.replace(`__PRESERVE_${i}__`, preserved[i]) + } + + return result.trim() +} + +function escAttr(str: string): string { + return str.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>') +} + +function escHtml(str: string): string { + return str.replace(/&/g, '&').replace(/</g, '<') +} diff --git a/packages/gea-ssg/src/index.ts b/packages/gea-ssg/src/index.ts new file mode 100644 index 0000000..948f808 --- /dev/null +++ b/packages/gea-ssg/src/index.ts @@ -0,0 +1,45 @@ +export { renderToString } from './render' +export type { RenderOptions, RenderResult } from './render' + +export { crawlRoutes } from './crawl' + +export { generate } from './generate' + +export { parseShell, injectIntoShell } from './shell' +export type { ShellParts } from './shell' + +export { + ssg, + preloadContent, + clearContentCache, + serializeContentCache, + serializeContentCacheForClient, + getContentSlugs, +} from './content' +export type { ContentFile } from './types' + +export { buildHeadTags, replaceTitle, minifyHtml } from './head' +export type { HeadConfig } from './head' + +export { geaSSG } from './vite-plugin' + +/** + * Server-side no-op for `hydrate`. + * The real implementation lives in `./client.ts` which Vite aliases + * `@geajs/ssg` to during browser builds. This stub ensures + * `import { hydrate } from '@geajs/ssg'` type-checks without the alias. + */ +export function hydrate(_components: Record<string, new (props?: any) => any>): boolean { + return false +} + +export type { + SSGOptions, + SSGPluginOptions, + StaticRoute, + RenderContext, + SitemapOptions, + RobotsOptions, + GenerateResult, + GeneratedPage, +} from './types' diff --git a/packages/gea-ssg/src/render.ts b/packages/gea-ssg/src/render.ts new file mode 100644 index 0000000..18d0bd4 --- /dev/null +++ b/packages/gea-ssg/src/render.ts @@ -0,0 +1,114 @@ +import { resetUidCounter, Component, ComponentManager } from '@geajs/core' + +export interface RenderOptions { + seed?: number + /** Component class names to track and mark with data-gea attributes */ + hydrate?: string[] + onRenderError?: (error: Error) => void +} + +/** + * Render a Gea component tree to an HTML string for SSG output. + * + * When `hydrate` is provided, components whose class name matches the list + * are tracked during instantiation. Their root elements receive `data-gea` + * and (if applicable) `data-gea-props` attributes so the client-side + * `hydrate()` function can reattach JS behaviour without re-rendering. + */ +export interface RenderResult { + html: string + /** `true` when at least one component was marked with `data-gea` for client hydration. */ + hasHydrationMarkers: boolean +} + +export function renderToString( + ComponentClass: new (props?: any) => any, + props?: Record<string, any>, + options: RenderOptions = {}, +): RenderResult { + const { seed = 0, hydrate = [], onRenderError } = options + + resetUidCounter(seed) + Component._ssgMode = true + + const cm = ComponentManager.getInstance() + const tracked: Array<{ id: string; className: string; props?: Record<string, any> }> = [] + + // Save original so we can always restore it — never rely on prototype-chain + // delete tricks which break if ComponentManager defines an own-property later. + const originalSetComponent = cm.setComponent + + if (hydrate.length) { + ;(cm as any).setComponent = function (comp: any) { + originalSetComponent.call(cm, comp) + if (hydrate.includes(comp.constructor.name)) { + const entry: { id: string; className: string; props?: Record<string, any> } = { + id: comp.id, + className: comp.constructor.name, + } + // Capture JSON-serialisable props for client-side reconstruction. + // Warn on silent data loss (Date→string, undefined→dropped, etc.) + if (comp.props && typeof comp.props === 'object' && Object.keys(comp.props).length) { + try { + const serialized = JSON.stringify(comp.props) + const roundTripped = JSON.parse(serialized) + // Detect lossy conversion: keys that changed type or disappeared + for (const key of Object.keys(comp.props)) { + const orig = comp.props[key] + const rt = roundTripped[key] + if (orig !== undefined && rt === undefined) { + console.warn( + `[gea-ssg] hydrate: prop "${key}" on ${comp.constructor.name} was dropped during serialization (functions/symbols are not serialisable).`, + ) + } else if (orig instanceof Date && typeof rt === 'string') { + console.warn( + `[gea-ssg] hydrate: prop "${key}" on ${comp.constructor.name} is a Date — it will become a string on the client.`, + ) + } + } + entry.props = roundTripped + } catch { + console.warn( + `[gea-ssg] hydrate: props on ${comp.constructor.name} are not JSON-serialisable — skipping prop transfer.`, + ) + } + } + tracked.push(entry) + } + } + } + + let instance: any = null + + try { + instance = new ComponentClass(props) + let html = String(instance.template(instance.props)).trim() + + for (const { id, className, props: cProps } of tracked) { + const propsAttr = cProps ? ` data-gea-props="${escAttr(JSON.stringify(cProps))}"` : '' + html = html.replace(` id="${id}"`, ` data-gea="${className}"${propsAttr} id="${id}"`) + } + + return { html, hasHydrationMarkers: tracked.length > 0 } + } catch (error) { + if (onRenderError) { + onRenderError(error as Error) + return { html: '', hasHydrationMarkers: false } + } + throw error + } finally { + Component._ssgMode = false + if (hydrate.length) { + ;(cm as any).setComponent = originalSetComponent + } + if (instance && typeof instance.dispose === 'function') { + try { + instance.dispose() + } catch {} + } + } +} + +function escAttr(str: string): string { + return str.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>') +} diff --git a/packages/gea-ssg/src/shell.ts b/packages/gea-ssg/src/shell.ts new file mode 100644 index 0000000..14cbdb7 --- /dev/null +++ b/packages/gea-ssg/src/shell.ts @@ -0,0 +1,75 @@ +export interface ShellParts { + before: string + after: string +} + +/** + * Split an HTML shell at the app mount point (`<div id="app">`). + * + * Uses depth tracking so nested `<div>` elements inside the mount + * point are handled correctly — the matching `</div>` is always found, + * not just the first one. + */ +export function parseShell(html: string, appElementId: string = 'app'): ShellParts { + // Escape regex metacharacters in the ID so values like "app.main" work correctly. + const safeId = appElementId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const openTagRegex = new RegExp(`(<\\s*div[^>]*\\bid\\s*=\\s*["']${safeId}["'][^>]*>)`, 'i') + const match = html.match(openTagRegex) + + if (!match || match.index === undefined) { + throw new Error(`[gea-ssg] <div id="${appElementId}"> not found in shell HTML.`) + } + + const openTagEnd = match.index + match[0].length + + // Depth-tracking: walk forward counting nested <div> / </div> pairs + // until we find the matching close tag at depth 0. + let depth = 0 + let pos = openTagEnd + let closeIndex = -1 + + while (pos < html.length) { + const nextOpen = html.indexOf('<div', pos) + const nextClose = html.indexOf('</div>', pos) + + if (nextClose === -1) break + + // A nested <div opens before the next </div> + if (nextOpen !== -1 && nextOpen < nextClose) { + depth++ + // Move past the opening tag + const tagEnd = html.indexOf('>', nextOpen) + pos = tagEnd === -1 ? nextOpen + 4 : tagEnd + 1 + } else { + if (depth === 0) { + closeIndex = nextClose + break + } + depth-- + pos = nextClose + 6 // skip past </div> + } + } + + if (closeIndex === -1) { + throw new Error(`[gea-ssg] Closing </div> not found for <div id="${appElementId}">.`) + } + + const before = html.slice(0, openTagEnd) + const after = html.slice(closeIndex) + + return { before, after } +} + +/** Inject rendered HTML into the shell, optionally adding head tags. */ +export function injectIntoShell(shell: ShellParts, renderedHtml: string, headTags?: string): string { + let result = shell.before + renderedHtml + shell.after + + if (headTags) { + const headInsertPos = result.toLowerCase().indexOf('</head>') + if (headInsertPos !== -1) { + result = result.slice(0, headInsertPos) + headTags + result.slice(headInsertPos) + } + } + + return result +} diff --git a/packages/gea-ssg/src/types.ts b/packages/gea-ssg/src/types.ts new file mode 100644 index 0000000..1f62b35 --- /dev/null +++ b/packages/gea-ssg/src/types.ts @@ -0,0 +1,74 @@ +export interface ContentFile<T = Record<string, any>> { + slug: string + frontmatter: T + /** Raw markdown — available during build, omitted in client payload */ + content?: string + html: string +} + +export interface SSGOptions { + routes: Record<string, any> + app: new (props?: any) => any + shell: string + outDir?: string + appElementId?: string + contentDir?: string + /** Base path for assets (default: '/') */ + base?: string + /** Component class names to hydrate on the client (enables MPA mode) */ + hydrate?: string[] + sitemap?: SitemapOptions | boolean + robots?: boolean | RobotsOptions + minify?: boolean + trailingSlash?: boolean + onBeforeRender?: (context: RenderContext) => void | Promise<void> + onAfterRender?: (context: RenderContext, html: string) => string | Promise<string> + onRenderError?: (path: string, error: Error) => void + concurrency?: number +} + +export interface RenderContext { + path: string + params: Record<string, string> + component: any + layouts: any[] +} + +export interface StaticRoute { + path: string + component: any + layouts: any[] + params: Record<string, string> +} + +export interface SitemapOptions { + hostname: string + changefreq?: string + priority?: number + exclude?: string[] +} + +export interface RobotsOptions { + disallow?: string[] + allow?: string[] + sitemap?: boolean +} + +export interface SSGPluginOptions extends Omit<SSGOptions, 'shell' | 'outDir' | 'app' | 'routes'> { + entry?: string + contentDir?: string + routes?: Record<string, any> + app?: new (props?: any) => any +} + +export interface GenerateResult { + pages: GeneratedPage[] + duration: number + errors: Array<{ path: string; error: Error }> +} + +export interface GeneratedPage { + path: string + outputPath: string + size: number +} diff --git a/packages/gea-ssg/src/vite-plugin.ts b/packages/gea-ssg/src/vite-plugin.ts new file mode 100644 index 0000000..570856d --- /dev/null +++ b/packages/gea-ssg/src/vite-plugin.ts @@ -0,0 +1,224 @@ +import { dirname, join, extname, resolve, normalize } from 'node:path' +import { existsSync, createReadStream } from 'node:fs' +import { fileURLToPath } from 'node:url' +import type { Plugin, ResolvedConfig } from 'vite' +import type { SSGPluginOptions } from './types' + +const __dir = dirname(fileURLToPath(import.meta.url)) +const SSG_SRC_DIR = __dir.endsWith('/dist') || __dir.endsWith('\\dist') ? join(__dir, '..', 'src') : __dir + +export function geaSSG(options: SSGPluginOptions = {}): Plugin[] { + let config: ResolvedConfig + let contentLoaded = false + let cachedContentJson = '{}' + + return [ + { + name: 'gea-ssg', + apply: 'build', + + config() { + return { + resolve: { + alias: { '@geajs/ssg': SSG_SRC_DIR + '/client.ts' }, + }, + } + }, + + configResolved(resolvedConfig) { + config = resolvedConfig + }, + + async closeBundle() { + console.log('[gea-ssg] Starting static page generation...') + + try { + const { createServer, loadConfigFromFile } = await import('vite') + + let userPlugins: any[] = [] + let userAlias: Record<string, string> = {} + + if (config.configFile) { + const loaded = await loadConfigFromFile( + { command: 'build', mode: config.mode }, + config.configFile, + config.root, + ) + if (loaded?.config) { + userPlugins = ((loaded.config.plugins || []) as any[]) + .flat(Infinity) + .filter((p: any) => p && typeof p === 'object' && 'name' in p && !p.name.startsWith('gea-ssg')) + const alias = loaded.config.resolve?.alias + if (alias && typeof alias === 'object') { + if (Array.isArray(alias)) { + for (const a of alias) { + if (a && typeof a === 'object' && 'find' in a && 'replacement' in a) { + const key = typeof a.find === 'string' ? a.find : String(a.find) + userAlias[key] = a.replacement as string + } + } + } else { + userAlias = alias as Record<string, string> + } + } + } + } + + const viteServer = await createServer({ + configFile: false, + root: config.root, + server: { middlewareMode: true, hmr: false, watch: null }, + appType: 'custom', + plugins: userPlugins, + optimizeDeps: { noDiscovery: true, include: [] }, + resolve: { + alias: { ...userAlias, '@geajs/ssg': SSG_SRC_DIR + '/index.ts' }, + }, + }) + + try { + const { generate } = await viteServer.ssrLoadModule(`${SSG_SRC_DIR}/generate.ts`) + + const ssgOpts: Record<string, any> = { + shell: `${config.build.outDir}/index.html`, + outDir: config.build.outDir, + base: config.base || '/', + appElementId: options.appElementId || 'app', + contentDir: options.contentDir ? resolve(config.root, options.contentDir) : undefined, + sitemap: options.sitemap, + robots: options.robots, + minify: options.minify, + trailingSlash: options.trailingSlash, + onBeforeRender: options.onBeforeRender, + onAfterRender: options.onAfterRender, + onRenderError: options.onRenderError, + concurrency: options.concurrency, + hydrate: options.hydrate, + } + const entry = options.entry || 'src/App.tsx' + const needsEntry = !options.routes || !options.app + const ssgEntry = needsEntry ? await viteServer.ssrLoadModule(entry) : null + + ssgOpts.routes = options.routes ?? ssgEntry?.routes + ssgOpts.app = options.app ?? ssgEntry?.App ?? ssgEntry?.default + + if (!ssgOpts.routes || !ssgOpts.app) { + throw new Error( + `[gea-ssg] Could not resolve routes and app. Either pass them via geaSSG({ routes, app }) or export them from "${entry}".`, + ) + } + + const result = await generate(ssgOpts) + if (result.errors.length) { + console.error(`[gea-ssg] ${result.errors.length} page(s) failed to render:`) + for (const { path, error } of result.errors) { + console.error(` - ${path}: ${error.message}`) + } + throw new Error(`[gea-ssg] Build completed with ${result.errors.length} rendering error(s).`) + } + } finally { + await viteServer.close() + } + // Note: Vite's middlewareMode may leak internal handles that + // prevent Node from exiting. We intentionally do NOT call + // process.exit() here — doing so would kill other plugins' + // parallel closeBundle hooks. The build CLI already handles + // exit; if the process hangs, the user can investigate with + // --why (Node 22+) or wtfnode. + } catch (error) { + console.error('[gea-ssg] SSG error:', error) + throw error + } + }, + }, + + { + name: 'gea-ssg:dev', + apply: 'serve', + + config() { + return { + resolve: { + alias: { '@geajs/ssg': SSG_SRC_DIR + '/client.ts' }, + }, + } + }, + + configResolved(resolvedConfig) { + config = resolvedConfig + }, + + async transformIndexHtml(_html, ctx) { + if (!options.contentDir || !ctx.server) return + + if (!contentLoaded) { + const mod = await ctx.server.ssrLoadModule(`${SSG_SRC_DIR}/content.ts`) + await mod.preloadContent(resolve(config.root, options.contentDir)) + cachedContentJson = mod.serializeContentCacheForClient() + contentLoaded = true + } + + const safeJson = cachedContentJson.replace(/<\//g, '<\\/') + return [ + { + tag: 'script', + children: `window.__SSG_CONTENT__=${safeJson}`, + injectTo: 'head-prepend' as const, + }, + ] + }, + + configureServer(server) { + if (!options.contentDir) return + + const contentDir = resolve(config.root, options.contentDir) + + server.watcher.add(contentDir) + const invalidate = (file: string) => { + if (normalize(file).startsWith(normalize(contentDir)) && file.endsWith('.md')) { + contentLoaded = false + server.ws.send({ type: 'full-reload' }) + } + } + server.watcher.on('change', invalidate) + server.watcher.on('add', invalidate) + server.watcher.on('unlink', invalidate) + }, + }, + + { + name: 'gea-ssg:preview', + + configResolved(resolvedConfig) { + config = resolvedConfig + }, + + configurePreviewServer(server) { + const ts = options.trailingSlash !== false + + server.middlewares.use((req, res, next) => { + if (!req.url) return next() + + const url = req.url.split('?')[0] + if (extname(url) || url === '/') return next() + const testPath = ts ? join(config.build.outDir, url, 'index.html') : join(config.build.outDir, url + '.html') + + if (existsSync(testPath)) { + req.url = ts ? (url.endsWith('/') ? url + 'index.html' : url + '/index.html') : url + '.html' + return next() + } + + // Try trailingSlash-style first, then flat-file fallback + const notFound = ts ? join(config.build.outDir, '404', 'index.html') : join(config.build.outDir, '404.html') + if (existsSync(notFound)) { + res.statusCode = 404 + createReadStream(notFound).pipe(res) + return + } + + next() + }) + }, + }, + ] +} diff --git a/packages/gea-ssg/tests/content.test.ts b/packages/gea-ssg/tests/content.test.ts new file mode 100644 index 0000000..5434f8b --- /dev/null +++ b/packages/gea-ssg/tests/content.test.ts @@ -0,0 +1,186 @@ +import { describe, it, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import { writeFile, mkdir, rm } from 'node:fs/promises' +import { join } from 'node:path' + +import { preloadContent, clearContentCache, serializeContentCache, ssg, getContentSlugs } from '../src/content' + +const tmpDir = join(import.meta.dirname, '.tmp-content-test') + +beforeEach(async () => { + await mkdir(join(tmpDir, 'posts'), { recursive: true }) +}) + +afterEach(async () => { + clearContentCache() + await rm(tmpDir, { recursive: true, force: true }) +}) + +describe('preloadContent', () => { + it('loads all markdown files from subdirectories', async () => { + await writeFile( + join(tmpDir, 'posts', 'hello-world.md'), + '---\ntitle: Hello World\ndate: 2025-03-15\n---\n\n# Introduction\n\nThis is a **bold** paragraph.', + ) + await writeFile(join(tmpDir, 'posts', 'second-post.md'), '---\ntitle: Second Post\n---\n\n# Second') + + await preloadContent(tmpDir) + + const posts = ssg.content('posts') + assert.equal(posts.length, 2) + assert.equal(posts[0].slug, 'hello-world') + assert.equal(posts[1].slug, 'second-post') + }) + + it('parses frontmatter and renders markdown to html', async () => { + await writeFile( + join(tmpDir, 'posts', 'test.md'), + '---\ntitle: Test Post\ntags:\n - a\n - b\n---\n\nA paragraph with *emphasis*.', + ) + + await preloadContent(tmpDir) + + const posts = ssg.content('posts') + assert.equal(posts[0].frontmatter.title, 'Test Post') + assert.deepEqual(posts[0].frontmatter.tags, ['a', 'b']) + assert.ok(posts[0].content.includes('*emphasis*')) + assert.ok(posts[0].html.includes('<em>emphasis</em>')) + }) + + it('normalizes Date values to ISO strings', async () => { + await writeFile(join(tmpDir, 'posts', 'dated.md'), '---\ntitle: Dated\ndate: 2025-03-15\n---\n\nContent') + + await preloadContent(tmpDir) + + const posts = ssg.content('posts') + assert.equal(typeof posts[0].frontmatter.date, 'string') + assert.ok(posts[0].frontmatter.date.startsWith('2025-03-15')) + }) + + it('handles files without frontmatter', async () => { + await writeFile(join(tmpDir, 'posts', 'no-meta.md'), '# Just content\n\nSome text.') + + await preloadContent(tmpDir) + + const posts = ssg.content('posts') + assert.equal(posts[0].slug, 'no-meta') + assert.deepEqual(posts[0].frontmatter, {}) + assert.ok(posts[0].html.includes('<h1>Just content</h1>')) + }) + + it('ignores non-directory entries', async () => { + await writeFile(join(tmpDir, 'readme.md'), '# Root file') + await writeFile(join(tmpDir, 'posts', 'post.md'), '---\ntitle: Post\n---\n# Post') + + await preloadContent(tmpDir) + + const posts = ssg.content('posts') + assert.equal(posts.length, 1) + assert.equal(ssg.content('readme.md').length, 0) + }) + + it('ignores non-md files in subdirectories', async () => { + await writeFile(join(tmpDir, 'posts', 'post.md'), '---\ntitle: MD\n---\n# MD') + await writeFile(join(tmpDir, 'posts', 'readme.txt'), 'not markdown') + + await preloadContent(tmpDir) + + assert.equal(ssg.content('posts').length, 1) + }) +}) + +describe('ssg.content', () => { + it('returns empty array for unknown subdir', async () => { + await preloadContent(tmpDir) + assert.deepEqual(ssg.content('unknown'), []) + }) + + it('supports sort option', async () => { + await writeFile(join(tmpDir, 'posts', 'aaa.md'), '---\norder: 2\n---\n# A') + await writeFile(join(tmpDir, 'posts', 'bbb.md'), '---\norder: 1\n---\n# B') + + await preloadContent(tmpDir) + + const sorted = ssg.content('posts', { + sort: (a: any, b: any) => a.frontmatter.order - b.frontmatter.order, + }) + assert.equal(sorted[0].slug, 'bbb') + assert.equal(sorted[1].slug, 'aaa') + }) + + it('does not mutate original cache when sorting', async () => { + await writeFile(join(tmpDir, 'posts', 'bbb.md'), '---\norder: 1\n---\n# B') + await writeFile(join(tmpDir, 'posts', 'aaa.md'), '---\norder: 2\n---\n# A') + + await preloadContent(tmpDir) + + ssg.content('posts', { sort: (a: any, b: any) => a.frontmatter.order - b.frontmatter.order }) + + const unsorted = ssg.content('posts') + assert.equal(unsorted[0].slug, 'aaa') + }) +}) + +describe('ssg.file', () => { + it('finds a file by slug', async () => { + await writeFile(join(tmpDir, 'posts', 'hello.md'), '---\ntitle: Hello\n---\n# Hello') + await writeFile(join(tmpDir, 'posts', 'world.md'), '---\ntitle: World\n---\n# World') + + await preloadContent(tmpDir) + + const file = ssg.file('posts', 'hello') + assert.ok(file) + assert.equal(file!.frontmatter.title, 'Hello') + }) + + it('returns null for unknown slug', async () => { + await preloadContent(tmpDir) + assert.equal(ssg.file('posts', 'nonexistent'), null) + }) + + it('returns null for unknown subdir', async () => { + await preloadContent(tmpDir) + assert.equal(ssg.file('unknown', 'anything'), null) + }) +}) + +describe('getContentSlugs', () => { + it('returns slugs for a subdir', async () => { + await writeFile(join(tmpDir, 'posts', 'aaa.md'), '# A') + await writeFile(join(tmpDir, 'posts', 'bbb.md'), '# B') + + await preloadContent(tmpDir) + + const slugs = getContentSlugs('posts') + assert.deepEqual(slugs, ['aaa', 'bbb']) + }) + + it('returns empty for unknown subdir', () => { + assert.deepEqual(getContentSlugs('nope'), []) + }) +}) + +describe('serializeContentCache', () => { + it('returns valid JSON', async () => { + await writeFile(join(tmpDir, 'posts', 'test.md'), '---\ntitle: Test\n---\n# Test') + + await preloadContent(tmpDir) + + const json = serializeContentCache() + const parsed = JSON.parse(json) + assert.ok(Array.isArray(parsed.posts)) + assert.equal(parsed.posts[0].slug, 'test') + }) +}) + +describe('clearContentCache', () => { + it('clears all cached content', async () => { + await writeFile(join(tmpDir, 'posts', 'test.md'), '# Test') + + await preloadContent(tmpDir) + assert.equal(ssg.content('posts').length, 1) + + clearContentCache() + assert.equal(ssg.content('posts').length, 0) + }) +}) diff --git a/packages/gea-ssg/tests/crawl.test.ts b/packages/gea-ssg/tests/crawl.test.ts new file mode 100644 index 0000000..7f4172f --- /dev/null +++ b/packages/gea-ssg/tests/crawl.test.ts @@ -0,0 +1,160 @@ +import { describe, it, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import { writeFile, mkdir, rm } from 'node:fs/promises' +import { join } from 'node:path' +import { crawlRoutes } from '../src/crawl' +import { preloadContent, clearContentCache } from '../src/content' + +class HomePage { + template() { + return '<div>home</div>' + } +} +class AboutPage { + template() { + return '<div>about</div>' + } +} +class NotFoundPage { + template() { + return '<div>404</div>' + } +} +class LayoutComponent { + template() { + return '<div>layout</div>' + } +} +class BlogPostPage { + template() { + return '<div>post</div>' + } +} + +const tmpDir = join(import.meta.dirname, '.tmp-crawl-test') + +beforeEach(async () => { + await mkdir(join(tmpDir, 'blog'), { recursive: true }) +}) + +afterEach(async () => { + clearContentCache() + await rm(tmpDir, { recursive: true, force: true }) +}) + +describe('crawlRoutes', () => { + it('collects static routes', async () => { + const routes = { '/': HomePage, '/about': AboutPage } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[0].path, '/') + assert.equal(result[1].path, '/about') + }) + + it('skips string redirects', async () => { + const routes = { '/': HomePage, '/old': '/new' } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 1) + }) + + it('skips redirect configs', async () => { + const routes = { '/': HomePage, '/legacy': { redirect: '/modern' } } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 1) + }) + + it('generates 404 page from wildcard routes', async () => { + const routes = { '/': HomePage, '*': NotFoundPage } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + const notFound = result.find((r) => r.path === '/404') + assert.ok(notFound) + assert.equal(notFound!.component, NotFoundPage) + }) + + it('resolves lazy components', async () => { + const routes = { '/': HomePage, '/lazy': () => Promise.resolve({ default: AboutPage }) } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[1].component, AboutPage) + }) + + it('handles route groups with layouts', async () => { + const routes = { + '/': { layout: LayoutComponent, children: { '/': HomePage, '/about': AboutPage } }, + } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[0].layouts.length, 1) + assert.equal(result[0].layouts[0], LayoutComponent) + }) + + it('handles nested route groups', async () => { + const routes = { + '/admin': { + layout: LayoutComponent, + children: { + '/dashboard': HomePage, + '/settings': { children: { '/profile': AboutPage } }, + }, + }, + } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[0].path, '/admin/dashboard') + assert.equal(result[1].path, '/admin/settings/profile') + }) + + it('normalizes double slashes', async () => { + const routes = { '/': { children: { '/about': AboutPage } } } + const result = await crawlRoutes(routes as any) + assert.equal(result[0].path, '/about') + }) + + it('returns empty array for empty routes', async () => { + const result = await crawlRoutes({}) + assert.equal(result.length, 0) + }) + + it('generates routes from content slugs', async () => { + await writeFile(join(tmpDir, 'blog', 'hello-world.md'), '---\ntitle: Hello\n---\n# Hello') + await writeFile(join(tmpDir, 'blog', 'second-post.md'), '---\ntitle: Second\n---\n# Second') + await preloadContent(tmpDir) + + const routes = { + '/blog/:slug': { component: BlogPostPage, content: 'blog' }, + } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[0].path, '/blog/hello-world') + assert.equal(result[0].params.slug, 'hello-world') + assert.equal(result[1].path, '/blog/second-post') + }) + + it('generates routes from explicit paths', async () => { + const routes = { + '/users/:id': { + component: HomePage, + paths: [{ params: { id: '1' } }, { params: { id: '2' } }], + }, + } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 2) + assert.equal(result[0].path, '/users/1') + assert.equal(result[1].path, '/users/2') + }) + + it('treats { component } without content/paths as static route', async () => { + const routes = { '/about': { component: AboutPage } } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 1) + assert.equal(result[0].path, '/about') + assert.equal(result[0].component, AboutPage) + }) + + it('skips parameterized routes without content or paths', async () => { + const routes = { '/user/:id': HomePage } + const result = await crawlRoutes(routes as any) + assert.equal(result.length, 0) + }) +}) diff --git a/packages/gea-ssg/tests/generate.test.ts b/packages/gea-ssg/tests/generate.test.ts new file mode 100644 index 0000000..f446b9d --- /dev/null +++ b/packages/gea-ssg/tests/generate.test.ts @@ -0,0 +1,617 @@ +import { describe, it, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import { mkdtemp, rm, readFile, writeFile, mkdir, access } from 'node:fs/promises' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { generate } from '../src/generate' +import { RouterView, Outlet, Head } from '@geajs/core' + +const SHELL_HTML = `<!DOCTYPE html> +<html> +<head> + <title>Test App + + + +
+ +` + +class HomePage { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + return '

Home

' + } + dispose() {} +} + +class AboutPage { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + return '

About

' + } + dispose() {} +} + +class UserPage { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + return `

User ${this.props.id || 'unknown'}

` + } + dispose() {} +} + +class ThrowingPage { + constructor() { + throw new Error('render failed') + } + template() { + return '' + } +} + +class MockLayout { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + const outletHtml = Outlet._ssgHtml || '' + return `
Layout Header
${outletHtml}
` + } + dispose() {} +} + +class MockNestedLayout { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + const outletHtml = Outlet._ssgHtml || '' + return `
${outletHtml}
` + } + dispose() {} +} + +class MockApp { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + let routeContent = '' + if (RouterView._ssgRoute) { + const { component, layouts, params } = RouterView._ssgRoute + + if (!layouts.length) { + const child = new component(params) + routeContent = String(child.template(child.props)).trim() + if (typeof child.dispose === 'function') child.dispose() + } else { + const leaf = new component(params) + let innerHtml = String(leaf.template(leaf.props)).trim() + if (typeof leaf.dispose === 'function') leaf.dispose() + + for (let i = layouts.length - 1; i >= 0; i--) { + Outlet._ssgHtml = innerHtml + const layout = new layouts[i]({ ...params }) + innerHtml = String(layout.template(layout.props)).trim() + if (typeof layout.dispose === 'function') layout.dispose() + Outlet._ssgHtml = null + } + routeContent = innerHtml + } + } + return `
${routeContent}
` + } + dispose() {} +} + +let tempDir: string +let shellPath: string + +describe('generate', () => { + beforeEach(async () => { + tempDir = await mkdtemp(join(tmpdir(), 'gea-ssg-test-')) + shellPath = join(tempDir, 'index.html') + await writeFile(shellPath, SHELL_HTML, 'utf-8') + }) + + afterEach(async () => { + await rm(tempDir, { recursive: true, force: true }) + }) + + it('generates HTML files for static routes', async () => { + const result = await generate({ + routes: { '/': HomePage, '/about': AboutPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 2) + assert.equal(result.errors.length, 0) + + const indexHtml = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(indexHtml.includes('

Home

')) + assert.ok(indexHtml.includes('')) + + const aboutHtml = await readFile(join(tempDir, 'about', 'index.html'), 'utf-8') + assert.ok(aboutHtml.includes('

About

')) + }) + + it('creates nested directories', async () => { + const result = await generate({ + routes: { '/docs/getting-started': AboutPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 1) + const html = await readFile(join(tempDir, 'docs', 'getting-started', 'index.html'), 'utf-8') + assert.ok(html.includes('

About

')) + }) + + it('catches render errors per route', async () => { + const capturedErrors: string[] = [] + const result = await generate({ + routes: { '/': HomePage, '/broken': ThrowingPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + onRenderError: (path, err) => { + capturedErrors.push(`${path}: ${err.message}`) + }, + }) + + assert.equal(result.pages.length, 1) + assert.equal(result.errors.length, 1) + assert.equal(capturedErrors[0], '/broken: render failed') + }) + + it('passes params to route components via { component, paths }', async () => { + const result = await generate({ + routes: { + '/users/:id': { + component: UserPage, + paths: [{ params: { id: '1' } }, { params: { id: '2' } }], + }, + } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 2) + + const user1 = await readFile(join(tempDir, 'users', '1', 'index.html'), 'utf-8') + assert.ok(user1.includes('

User 1

')) + + const user2 = await readFile(join(tempDir, 'users', '2', 'index.html'), 'utf-8') + assert.ok(user2.includes('

User 2

')) + }) + + it('generates routes from { component, content } config', async () => { + const contentDir = join(tempDir, 'content') + await mkdir(join(contentDir, 'blog'), { recursive: true }) + await writeFile(join(contentDir, 'blog', 'hello.md'), '---\ntitle: Hello\n---\n# Hello') + await writeFile(join(contentDir, 'blog', 'world.md'), '---\ntitle: World\n---\n# World') + + class PostPage { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + return `

Post: ${this.props.slug}

` + } + dispose() {} + } + + const result = await generate({ + routes: { + '/blog/:slug': { component: PostPage, content: 'blog' }, + } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + contentDir, + }) + + assert.equal(result.pages.length, 2) + + const hello = await readFile(join(tempDir, 'blog', 'hello', 'index.html'), 'utf-8') + assert.ok(hello.includes('

Post: hello

')) + + const world = await readFile(join(tempDir, 'blog', 'world', 'index.html'), 'utf-8') + assert.ok(world.includes('

Post: world

')) + }) + + it('renders with a single layout wrapping the component', async () => { + const result = await generate({ + routes: { + '/': { + layout: MockLayout, + children: { '/': HomePage, '/about': AboutPage }, + }, + } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 2) + + const indexHtml = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(indexHtml.includes('
Layout Header
')) + assert.ok(indexHtml.includes('

Home

')) + }) + + it('renders with nested layouts', async () => { + const result = await generate({ + routes: { + '/': { + layout: MockLayout, + children: { + '/dashboard': { + layout: MockNestedLayout, + children: { '/': HomePage }, + }, + }, + }, + } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 1) + + const html = await readFile(join(tempDir, 'dashboard', 'index.html'), 'utf-8') + assert.ok(html.includes('
Layout Header
')) + assert.ok(html.includes('')) + assert.ok(html.includes('

Home

')) + }) + + it('calls onBeforeRender hook', async () => { + const paths: string[] = [] + await generate({ + routes: { '/': HomePage, '/about': AboutPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + onBeforeRender: (ctx) => { + paths.push(ctx.path) + }, + }) + + assert.ok(paths.includes('/')) + assert.ok(paths.includes('/about')) + }) + + it('calls onAfterRender hook and uses transformed HTML', async () => { + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + onAfterRender: (_ctx, html) => html.replace('', ''), + }) + + const html = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(html.includes('')) + }) + + it('keeps scripts in output for client-side takeover', async () => { + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + const html = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(html.includes('main.js')) + }) + + it('preserves non-JS scripts like JSON-LD', async () => { + const shellWithJsonLd = SHELL_HTML.replace( + '', + '\n', + ) + await writeFile(shellPath, shellWithJsonLd, 'utf-8') + + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + const html = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(html.includes('application/ld+json')) + assert.ok(html.includes('main.js')) + }) + + it('generates sitemap when enabled', async () => { + await generate({ + routes: { '/': HomePage, '/about': AboutPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + sitemap: { hostname: 'https://example.com' }, + }) + + const sitemap = await readFile(join(tempDir, 'sitemap.xml'), 'utf-8') + assert.ok(sitemap.includes('https://example.com/')) + assert.ok(sitemap.includes('https://example.com/about/')) + }) + + it('returns duration in result', async () => { + const result = await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + assert.ok(result.duration > 0) + }) + + it('returns page sizes in result', async () => { + const result = await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + assert.ok(result.pages[0].size > 0) + }) + + it('rejects path traversal in route paths', async () => { + await assert.rejects( + () => + generate({ + routes: { '/../../escape': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }), + /Path traversal detected/, + ) + }) + + it('generates 404.html from wildcard route', async () => { + class NotFoundPage { + props: any + constructor(props?: any) { + this.props = props || {} + } + template() { + return '

Not Found

' + } + dispose() {} + } + + const result = await generate({ + routes: { '/': HomePage, '*': NotFoundPage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + }) + + assert.equal(result.pages.length, 2) + const notFoundPage = result.pages.find((p) => p.path === '/404') + assert.ok(notFoundPage) + + const html = await readFile(join(tempDir, '404', 'index.html'), 'utf-8') + assert.ok(html.includes('

Not Found

')) + }) + + it('generates robots.txt with boolean option', async () => { + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + robots: true, + sitemap: { hostname: 'https://example.com' }, + }) + + const robots = await readFile(join(tempDir, 'robots.txt'), 'utf-8') + assert.ok(robots.includes('User-agent: *')) + assert.ok(robots.includes('Allow: /')) + assert.ok(robots.includes('Sitemap: https://example.com/sitemap.xml')) + }) + + it('generates robots.txt with custom options', async () => { + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + robots: { disallow: ['/admin', '/private'] }, + }) + + const robots = await readFile(join(tempDir, 'robots.txt'), 'utf-8') + assert.ok(robots.includes('Disallow: /admin')) + assert.ok(robots.includes('Disallow: /private')) + }) + + it('minifies HTML output when enabled', async () => { + await generate({ + routes: { '/': HomePage } as any, + app: MockApp as any, + shell: shellPath, + outDir: tempDir, + minify: true, + }) + + const html = await readFile(join(tempDir, 'index.html'), 'utf-8') + assert.ok(!html.includes('

text

') + assert.ok(!result.includes('comment')) + assert.ok(result.includes('

text

')) + }) + + it('preserves conditional comments', () => { + const result = minifyHtml('

ok

') + assert.ok(result.includes('
  preserved  

more

' + const result = minifyHtml(input) + assert.ok(!result.includes('comment')) + assert.ok(result.includes(' preserved ')) + }) +}) diff --git a/packages/gea-ssg/tests/render.test.ts b/packages/gea-ssg/tests/render.test.ts new file mode 100644 index 0000000..b5c1270 --- /dev/null +++ b/packages/gea-ssg/tests/render.test.ts @@ -0,0 +1,89 @@ +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import { renderToString } from '../src/render' + +// Minimal mock component — simulates what Gea components do +class MockComponent { + props: any + constructor(props?: any) { + this.props = props || {} + } + template(_props: any) { + return '
Hello World
' + } +} + +class MockComponentWithProps { + props: any + constructor(props?: any) { + this.props = props || {} + } + template(props: any) { + return `

${props.title || 'default'}

` + } +} + +class ThrowingComponent { + props: any + constructor() { + throw new Error('Component init failed') + } + template() { + return '' + } +} + +describe('renderToString', () => { + it('renders a simple component to HTML', () => { + const result = renderToString(MockComponent) + assert.equal(result.html, '
Hello World
') + assert.equal(result.hasHydrationMarkers, false) + }) + + it('passes props to the component', () => { + const result = renderToString(MockComponentWithProps, { title: 'SSG Test' }) + assert.equal(result.html, '

SSG Test

') + }) + + it('uses default props when none provided', () => { + const result = renderToString(MockComponentWithProps) + assert.equal(result.html, '

default

') + }) + + it('trims whitespace from output', () => { + class SpaceyComponent { + props: any + constructor() { + this.props = {} + } + template() { + return '
spaced
' + } + } + const result = renderToString(SpaceyComponent) + assert.equal(result.html, '
spaced
') + }) + + it('throws on render error by default', () => { + assert.throws(() => renderToString(ThrowingComponent), { message: 'Component init failed' }) + }) + + it('catches errors when onRenderError is provided', () => { + let capturedError: Error | null = null + const result = renderToString(ThrowingComponent, undefined, { + onRenderError: (err) => { + capturedError = err + }, + }) + assert.equal(result.html, '') + assert.equal(result.hasHydrationMarkers, false) + assert.ok(capturedError) + assert.equal((capturedError as Error).message, 'Component init failed') + }) + + it('produces deterministic output with same seed', () => { + const r1 = renderToString(MockComponent, undefined, { seed: 42 }) + const r2 = renderToString(MockComponent, undefined, { seed: 42 }) + assert.equal(r1.html, r2.html) + }) +}) diff --git a/packages/gea-ssg/tests/shell.test.ts b/packages/gea-ssg/tests/shell.test.ts new file mode 100644 index 0000000..a334ec4 --- /dev/null +++ b/packages/gea-ssg/tests/shell.test.ts @@ -0,0 +1,92 @@ +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import { parseShell, injectIntoShell } from '../src/shell' + +const BASIC_SHELL = ` + + + Test + + +
+ + +` + +describe('parseShell', () => { + it('parses a basic HTML shell', () => { + const parts = parseShell(BASIC_SHELL) + assert.ok(parts.before.endsWith('
')) + assert.ok(parts.after.startsWith('
')) + }) + + it('throws when app element is not found', () => { + assert.throws(() => parseShell(''), { message: /not found in shell HTML/ }) + }) + + it('throws when closing div is missing', () => { + assert.throws(() => parseShell('
'), { + message: /Closing <\/div> not found/, + }) + }) + + it('supports custom appElementId', () => { + const html = '
' + const parts = parseShell(html, 'root') + assert.ok(parts.before.endsWith('
')) + }) + + it('handles existing content inside app div', () => { + const html = '
loading...
' + const parts = parseShell(html) + assert.ok(parts.before.endsWith('
')) + assert.ok(parts.after.startsWith('
')) + }) + + it('escapes regex metacharacters in appElementId', () => { + const html = '
' + const parts = parseShell(html, 'app.main') + assert.ok(parts.before.endsWith('
')) + assert.ok(parts.after.startsWith('
')) + }) + + it('handles nested divs inside app div', () => { + const html = + '
inner
f
' + const parts = parseShell(html) + assert.ok(parts.before.endsWith('
')) + assert.ok(parts.after.startsWith('
')) + assert.ok(parts.after.includes('
f
')) + // Ensure the nested content is NOT included in "before" or "after" + assert.ok(!parts.before.includes('spinner')) + assert.ok(!parts.after.includes('spinner')) + }) +}) + +describe('injectIntoShell', () => { + it('injects rendered HTML between shell parts', () => { + const parts = parseShell(BASIC_SHELL) + const result = injectIntoShell(parts, '

Hello

') + assert.ok(result.includes('

Hello

')) + }) + + it('injects head tags before ', () => { + const parts = parseShell(BASIC_SHELL) + const result = injectIntoShell(parts, '

content

', '') + assert.ok(result.includes('')) + }) + + it('skips head injection when no headTags provided', () => { + const parts = parseShell(BASIC_SHELL) + const result = injectIntoShell(parts, '

content

') + assert.equal(result.includes('undefined'), false) + }) + + it('produces valid full HTML', () => { + const parts = parseShell(BASIC_SHELL) + const result = injectIntoShell(parts, '
SSG Content
') + assert.ok(result.startsWith('')) + assert.ok(result.includes('
SSG Content
')) + assert.ok(result.includes('')) + }) +}) diff --git a/packages/gea-ssg/tsconfig.json b/packages/gea-ssg/tsconfig.json new file mode 100644 index 0000000..0b27536 --- /dev/null +++ b/packages/gea-ssg/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "emitDeclarationOnly": true, + "lib": ["ES2020"], + "types": ["node"], + "declaration": true, + "strict": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true + }, + "include": ["./src/**/*.ts", "./tests/**/*.ts"] +} diff --git a/packages/gea/src/index.ts b/packages/gea/src/index.ts index 1c9bb3c..c5fb69d 100644 --- a/packages/gea/src/index.ts +++ b/packages/gea/src/index.ts @@ -6,11 +6,14 @@ export type { DOMEvent } from './lib/types' export { default as ComponentManager } from './lib/base/component-manager' export { applyListChanges } from './lib/base/list' export type { ListConfig } from './lib/base/list' -export { createRouter, Router, router, matchRoute, Link, Outlet, RouterView } from './lib/router' +export { createRouter, Router, router, matchRoute, resolveRoute, Link, Outlet, RouterView } from './lib/router' +export { Head } from './lib/head' export type { RouteMap, RouteEntry, RouteGroupConfig, + SSGRouteConfig, + ResolvedRoute, RouterOptions, GuardFn, GuardResult, diff --git a/packages/gea/src/lib/base/component-manager.ts b/packages/gea/src/lib/base/component-manager.ts index 09cbdcb..ee7ca83 100644 --- a/packages/gea/src/lib/base/component-manager.ts +++ b/packages/gea/src/lib/base/component-manager.ts @@ -136,6 +136,10 @@ interface ComponentLike { } const createElement = (() => { + if (typeof document === 'undefined') { + return (htmlString: string): HTMLElement => htmlString as any + } + let template: HTMLTemplateElement | null = null return (htmlString: string): HTMLElement => { diff --git a/packages/gea/src/lib/base/component.tsx b/packages/gea/src/lib/base/component.tsx index fa34aae..40389af 100644 --- a/packages/gea/src/lib/base/component.tsx +++ b/packages/gea/src/lib/base/component.tsx @@ -15,6 +15,7 @@ export default class Component

> extends Store { declare setState: (...args: any[]) => void declare forceUpdate: (...args: any[]) => void static __componentClasses: Map = new Map() + static _ssgMode = false id_: string element_: HTMLElement | null @@ -77,11 +78,13 @@ export default class Component

> extends Store { ComponentManager.getInstance().setComponent(this) - this.created(this.props) - this.createdHooks(this.props) + if (!Component._ssgMode) { + this.created(this.props) + this.createdHooks(this.props) - if (typeof (this as any).__setupLocalStateObservers === 'function') { - ;(this as any).__setupLocalStateObservers() + if (typeof (this as any).__setupLocalStateObservers === 'function') { + ;(this as any).__setupLocalStateObservers() + } } } diff --git a/packages/gea/src/lib/base/uid.ts b/packages/gea/src/lib/base/uid.ts index d7419f8..a56a4d1 100644 --- a/packages/gea/src/lib/base/uid.ts +++ b/packages/gea/src/lib/base/uid.ts @@ -24,10 +24,7 @@ export function resetUidCounter(seed: number = 0): void { /** Register a context-scoped UID provider (called by SSR package). * Provider returns next UID string, or null to fall back to global counter. * Reset returns true if it handled the reset, false to fall through. */ -export function setUidProvider( - provider: () => string | null, - reset: (seed: number) => boolean, -): void { +export function setUidProvider(provider: () => string | null, reset: (seed: number) => boolean): void { uidProvider = provider resetProvider = reset } diff --git a/packages/gea/src/lib/head.ts b/packages/gea/src/lib/head.ts new file mode 100644 index 0000000..4e9bcd1 --- /dev/null +++ b/packages/gea/src/lib/head.ts @@ -0,0 +1,142 @@ +import Component from './base/component' + +export class Head extends Component { + static _current: Record | null = null + + template() { + if (!Head._current) Head._current = {} + const props = this.props || {} + for (const [key, value] of Object.entries(props)) { + if (key === 'id') continue + if (Array.isArray(value) && Array.isArray(Head._current[key])) { + Head._current[key] = [...Head._current[key], ...value] + } else { + Head._current[key] = value + } + } + + if (typeof document !== 'undefined' && !Component._ssgMode) { + this._updateHead() + return `

` + } + + return '' + } + + _updateHead() { + const props = this.props || {} + + // Remove previously injected custom meta/link tags from prior route + document.querySelectorAll('[data-gea-head]').forEach((el) => el.remove()) + + this._removeStale(props) + + document.title = props.title || '' + + this._setMeta('description', props.description) + this._setMeta('og:title', props.title) + this._setMeta('og:description', props.description) + this._setMeta('og:image', props.image) + this._setMeta('og:url', props.url) + this._setMeta('og:type', props.type || (props.title ? 'website' : undefined)) + this._setMeta('twitter:title', props.title) + this._setMeta('twitter:description', props.description) + this._setMeta('twitter:image', props.image) + if (props.image) this._setMeta('twitter:card', 'summary_large_image') + + if (props.url) { + let el = document.querySelector('link[rel="canonical"]') as HTMLLinkElement + if (!el) { + el = document.createElement('link') + el.rel = 'canonical' + document.head.appendChild(el) + } + el.href = props.url + } + + if (props.meta) { + for (const tag of props.meta) { + const key = tag.property || tag.name + if (key) { + const isOg = key.startsWith('og:') || key.startsWith('twitter:') + const attr = isOg ? 'property' : 'name' + const el = document.createElement('meta') + el.setAttribute(attr, key) + el.content = tag.content || '' + el.setAttribute('data-gea-head', '') + document.head.appendChild(el) + } + } + } + + if (props.link) { + for (const attrs of props.link) { + const el = document.createElement('link') + el.setAttribute('data-gea-head', '') + for (const [k, v] of Object.entries(attrs)) { + el.setAttribute(k, v as string) + } + document.head.appendChild(el) + } + } + + if (props.jsonld) { + let el = document.querySelector('script[data-head-jsonld]') as HTMLScriptElement + if (!el) { + el = document.createElement('script') + el.type = 'application/ld+json' + el.setAttribute('data-head-jsonld', '') + document.head.appendChild(el) + } + const data = Array.isArray(props.jsonld) ? props.jsonld : [props.jsonld] + const items = data.map((d: any) => ({ '@context': 'https://schema.org', ...d })) + el.textContent = JSON.stringify(items.length === 1 ? items[0] : items) + } else { + const el = document.querySelector('script[data-head-jsonld]') + if (el) el.remove() + } + } + + _removeStale(props: Record) { + if (!props.url) { + const el = document.querySelector('link[rel="canonical"]') + if (el) el.remove() + } + if (!props.image) { + this._removeMeta('og:image') + this._removeMeta('twitter:image') + this._removeMeta('twitter:card') + } + if (!props.description) { + this._removeMeta('description') + this._removeMeta('og:description') + this._removeMeta('twitter:description') + } + } + + _removeMeta(nameOrProperty: string) { + const isOg = nameOrProperty.startsWith('og:') || nameOrProperty.startsWith('twitter:') + const attr = isOg ? 'property' : 'name' + const escaped = typeof CSS !== 'undefined' ? CSS.escape(nameOrProperty) : nameOrProperty + const el = document.querySelector(`meta[${attr}="${escaped}"]`) + if (el) el.remove() + } + + _setMeta(nameOrProperty: string, content?: string) { + const isOg = nameOrProperty.startsWith('og:') || nameOrProperty.startsWith('twitter:') + const attr = isOg ? 'property' : 'name' + const escaped = typeof CSS !== 'undefined' ? CSS.escape(nameOrProperty) : nameOrProperty + if (!content) { + const el = document.querySelector(`meta[${attr}="${escaped}"]`) + if (el) el.remove() + return + } + let el = document.querySelector(`meta[${attr}="${escaped}"]`) as HTMLMetaElement + if (!el) { + el = document.createElement('meta') + el.setAttribute(attr, nameOrProperty) + document.head.appendChild(el) + } + el.content = content + } +} diff --git a/packages/gea/src/lib/router/index.ts b/packages/gea/src/lib/router/index.ts index a0e5ffe..464027a 100644 --- a/packages/gea/src/lib/router/index.ts +++ b/packages/gea/src/lib/router/index.ts @@ -32,10 +32,13 @@ export { Link } export { Outlet } export { RouterView } export { matchRoute } from './match' +export { resolveRoute } from './resolve' export type { RouteMap, RouteEntry, RouteGroupConfig, + SSGRouteConfig, + ResolvedRoute, RouterOptions, GuardFn, GuardResult, diff --git a/packages/gea/src/lib/router/link.ts b/packages/gea/src/lib/router/link.ts index 3c55aef..a503454 100644 --- a/packages/gea/src/lib/router/link.ts +++ b/packages/gea/src/lib/router/link.ts @@ -18,6 +18,7 @@ export interface LinkProps { export default class Link extends Component { static _router: any = null + static _ssgCurrentPath: string | null = null private _clickHandler: ((e: MouseEvent) => void) | null = null private _observerRemover: (() => void) | null = null @@ -27,7 +28,20 @@ export default class Link extends Component { const target = props.target ? ` target="${escapeAttr(props.target)}"` : '' const rel = props.rel ? ` rel="${escapeAttr(props.rel)}"` : '' const content = props.children ?? props.label ?? '' - return `${content}` as any + + let activeAttr = '' + const ssgPath = Link._ssgCurrentPath + if (ssgPath !== null) { + const to = props.to + const active = props.exact + ? ssgPath === to + : to === '/' + ? ssgPath === '/' + : ssgPath === to || ssgPath.startsWith(to + '/') + if (active) activeAttr = ' data-active' + } + + return `${content}` as any } onAfterRender() { diff --git a/packages/gea/src/lib/router/outlet.ts b/packages/gea/src/lib/router/outlet.ts index 2b683b9..ef410ff 100644 --- a/packages/gea/src/lib/router/outlet.ts +++ b/packages/gea/src/lib/router/outlet.ts @@ -3,6 +3,7 @@ import type { Router } from './router' export default class Outlet extends Component<{ router?: Router | null }> { static _router: Router | null = null + static _ssgHtml: string | null = null __isRouterOutlet = true _routerDepth = -1 @@ -14,6 +15,9 @@ export default class Outlet extends Component<{ router?: Router | null }> { private _observerRemovers: Array<() => void> = [] template() { + if (Outlet._ssgHtml) { + return `
${Outlet._ssgHtml}
` as any + } return `
` as any } diff --git a/packages/gea/src/lib/router/resolve.ts b/packages/gea/src/lib/router/resolve.ts index 3b1bf7d..7342ffe 100644 --- a/packages/gea/src/lib/router/resolve.ts +++ b/packages/gea/src/lib/router/resolve.ts @@ -195,6 +195,17 @@ function tryResolveEntry( return childResult } + // --- SSG route config: { component, ... } --- + if ( + typeof entry === 'object' && + entry !== null && + 'component' in entry && + !('children' in entry) && + !('redirect' in entry) + ) { + return tryResolveEntry(pattern, (entry as any).component, path, search, result) + } + // --- Leaf: function or component --- const match = matchRoute(pattern, path) if (!match) return null diff --git a/packages/gea/src/lib/router/router-view.ts b/packages/gea/src/lib/router/router-view.ts index 571e797..399c16a 100644 --- a/packages/gea/src/lib/router/router-view.ts +++ b/packages/gea/src/lib/router/router-view.ts @@ -1,9 +1,17 @@ import Component from '../base/component' -import type { Router } from './router' +import { Router } from './router' import type { RouteMap } from './types' import Outlet from './outlet' +export interface SSGRoute { + component: any + layouts: any[] + params: Record +} + export default class RouterView extends Component<{ router?: Router; routes?: RouteMap }> { + static _ssgRoute: SSGRoute | null = null + __isRouterOutlet = true _routerDepth = 0 @@ -15,6 +23,41 @@ export default class RouterView extends Component<{ router?: Router; routes?: Ro private _routesApplied = false template() { + if (RouterView._ssgRoute) { + const { component, layouts, params } = RouterView._ssgRoute + + if (!layouts.length) { + const child = new component(params) + let html: string + try { + html = String(child.template(child.props)).trim() + } finally { + if (typeof child.dispose === 'function') child.dispose() + } + return `
${html}
` as any + } + + const leaf = new component(params) + let innerHtml: string + try { + innerHtml = String(leaf.template(leaf.props)).trim() + } finally { + if (typeof leaf.dispose === 'function') leaf.dispose() + } + + for (let i = layouts.length - 1; i >= 0; i--) { + Outlet._ssgHtml = innerHtml + const layout = new layouts[i]({ ...params }) + try { + innerHtml = String(layout.template(layout.props)).trim() + } finally { + if (typeof layout.dispose === 'function') layout.dispose() + Outlet._ssgHtml = null + } + } + + return `
${innerHtml}
` as any + } return `
` as any } @@ -36,7 +79,17 @@ export default class RouterView extends Component<{ router?: Router; routes?: Ro } onAfterRender() { - const router = this._getRouter() + let router = this._getRouter() + + if (!router && this.props?.routes) { + router = new Router(this.props.routes) + this._router = router + this._routesApplied = true + this._rebindRouter(router) + this._updateView() + return + } + if (!router) return if (this.props?.routes && !this._routesApplied) { diff --git a/packages/gea/src/lib/router/types.ts b/packages/gea/src/lib/router/types.ts index 8aff9f0..755303b 100644 --- a/packages/gea/src/lib/router/types.ts +++ b/packages/gea/src/lib/router/types.ts @@ -31,11 +31,18 @@ export interface RouteGroupConfig { children: RouteMap } +export interface SSGRouteConfig { + component: ComponentOrLazy + content?: string + paths?: Array<{ params: Record }> +} + export type RouteEntry = | ComponentOrLazy // direct component or lazy | string // static redirect | RedirectConfig // full redirect control | RouteGroupConfig // nested group with layout/guard/children + | SSGRouteConfig // SSG static generation config // Note: dynamic redirects (functions returning strings) are expressed as RedirectConfig // with redirect as a function, NOT as bare functions. This avoids ambiguity with LazyComponent.